Skip to content

[MNT] Setup uv#529

Open
surchs wants to merge 7 commits intomainfrom
issue527
Open

[MNT] Setup uv#529
surchs wants to merge 7 commits intomainfrom
issue527

Conversation

@surchs
Copy link
Contributor

@surchs surchs commented Feb 5, 2026

Update workflows

Changes proposed in this pull request:

  • use uv to handle dependency lockfile for the project
  • move dependencies to pyproject.toml
  • add dependabot support to update the uv.lock file (will have to be replaced by an updated dependabot sync template)

Note

We want to also have our local dev environments use the lockfile dependencies (to make sure local dev is the same as prod). Achieving this without installing uv locally is going to be quite hard / impossible with this setup. So our local setup guide will now include having uv available to handle local venv and installation.

Note

In the CI commands and the docker build, we do not use uv now. This is mainly to keep the diff limited. We can do this by first asking uv to build a requirements.txt lock file from the uv.lock file - and then using pip to install from the requirements.txt file as before.

Checklist

This section is for the PR reviewer

  • PR has an interpretable title with a prefix ([ENH], [FIX], [REF], [TST], [CI], [MNT], [INF], [MODEL], [DOC]) (see our Contributing Guidelines for more info)
  • PR has a label for the release changelog or skip-release (to be applied by maintainers only)
  • PR links to GitHub issue with mention Closes #XXXX
  • Tests pass
  • Checks pass
  • If the PR changes the SPARQL query template, the default Neurobagel query file has also been regenerated

For new features:

  • Tests have been added

For bug fixes:

  • There is at least one test that would fail under the original bug conditions.

Summary by Sourcery

Adopt uv-based Python project and dependency management and update CI to use it for installing and running development dependencies.

Enhancements:

  • Define project metadata, runtime dependencies, and development dependency groups in a new pyproject.toml.
  • Configure code style and type-checking tools (Black, isort, mypy) via pyproject.toml.

CI:

  • Update test workflow to install uv and use it to sync dependencies instead of pip/requirements.txt.

Chores:

  • Add uv.lock to track the resolved dependency set for reproducible environments.

Update workflows
@sourcery-ai
Copy link

sourcery-ai bot commented Feb 5, 2026

Reviewer's Guide

This PR introduces uv-based dependency management for the project, wiring CI to use uv and adding a pyproject.toml with project metadata, dependency groups, and tooling configuration, plus a generated uv.lock lockfile.

Flow diagram for uv dependency groups in pyproject.toml

graph TD
  Project[project_neurobagel-api]
  Core_Dependencies[core_runtime_dependencies]
  Dependency_Groups[dependency_groups]
  Dev_Group[dev_group]
  Test_Group[test_group]
  Coverage_Group[coverage_group]
  Lint_Group[lint_group]

  Project --> Core_Dependencies
  Project --> Dependency_Groups

  Dependency_Groups --> Dev_Group
  Dependency_Groups --> Test_Group
  Dependency_Groups --> Coverage_Group
  Dependency_Groups --> Lint_Group

  Dev_Group --> Test_Group
  Dev_Group --> Coverage_Group
  Dev_Group --> Lint_Group
Loading

File-Level Changes

Change Details Files
Switch CI test workflow to use uv for Python dependency installation instead of pip and requirements.txt.
  • Add astral-sh/setup-uv action step before dependency installation in the test workflow.
  • Replace manual pip install commands with uv sync --frozen --extra dev to install runtime and dev dependencies from the lockfile.
  • Remove reliance on requirements.txt for CI dependency installation.
.github/workflows/test.yaml
Define project metadata, dependencies, and development tooling configuration via pyproject.toml to support uv and standard packaging.
  • Declare project name, dynamic version, supported Python range, and core runtime dependencies.
  • Introduce dependency groups (dev, test, coverage, lint) for targeted installs via uv.
  • Configure build system to use setuptools with setuptools.build_meta backend and define app package discovery.
  • Add tool configuration blocks for black, isort, and mypy to standardize formatting and type-checking settings.
pyproject.toml
Add uv.lock lockfile to pin dependency versions for reproducible installs.
  • Check in uv.lock so CI (and developers) can perform frozen syncs with uv.
  • Aligns uv sync --frozen call in CI with a committed lockfile to guarantee deterministic environments.
uv.lock

Possibly linked issues

  • #(not specified): PR implements uv-based dependency management and workflow changes directly fulfilling the “Adopt uv for dependency management” issue.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've reviewed this pull request using the Sourcery rules engine

@codecov
Copy link

codecov bot commented Feb 5, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.31%. Comparing base (659081c) to head (79af27b).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #529   +/-   ##
=======================================
  Coverage   97.31%   97.31%           
=======================================
  Files          34       34           
  Lines        1304     1304           
=======================================
  Hits         1269     1269           
  Misses         35       35           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Will have to be replaced by updated global
recipe from workflows repo
@alyssadai
Copy link
Contributor

@surchs, initial comment here before I have time for a full review. I noticed in this PR that you added a dependency group for some linters/formatters that we already run automatically as part of pre-commit hooks (and don't need to be installed locally) as well as pre-commit CI on PRs.

What does this add to our existing pre-commit setup?

Copy link
Contributor

@alyssadai alyssadai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot @surchs for implementing this and for your patience on my review! I've tested out uv locally using your branch and I think it will simplify dependency management considerably - happy to be rid of these restrictive requirements.txt files 🎉

Since we haven't updated dependencies in a while, I think the main issue atm is incompatibilities between locked package versions in uv.lock and the newer Python versions we want to support. Specifically:

  • uv.lock currently specifies some pretty old versions of tools
  • that don't have wheels for e.g., 3.12 or 3.13
  • meanwhile, the new pyproject.toml allows 3.11-3.13
  • but the test.yaml only runs tests against 3.10 (so the issues with newer Pythons are not caught in CI)

Left more details below and and a suggestion of how we can resolve this!

Two other high-level comments (with details below):

  • I think we can include a bit more guidance in the README on using uv. Given how much uv abstracts away and the many commands available, I find that things like knowing which env I'm installing things into, how to add or update a dependency, etc. aren't obvious coming from pip + venv where each step is explicit. I think a few brief explainers of commands could go a long way in this regard (also to save ourselves confusion during this transition period). As mentioned below, we might want to eventually move this info into to our CONTRIBUTING.md
  • I don't follow the NOTE in your PR description about not using uv in the test.yaml and Dockerfile. From what I can see those files have been updated anyways in this PR, but I find the temporary solution harder to review than if we were to fully use uv for the Docker image and test wf. Can we consider making those changes here to avoid a second PR that reverts the changes?

"pytest-asyncio",
"pytest-env",
]
coverage = ["coverage"]
Copy link
Contributor

@alyssadai alyssadai Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not include this as part of the test group? to me that feels more intuitive rather than having a separate dependency group just for coverage alone

Copy link
Contributor

@alyssadai alyssadai Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

side node: I think this is a good time to migrate these older repos to using pytest-cov for code coverage reports, which is the more modern/streamlined approach than using the standalone coverage,

for an example, see the pyproject.toml and test.yaml for configure-nb.

will leave up to you if you want to include the migration in the uv PRs or in another issue!

Comment on lines +30 to +35
lint = [
"pre-commit",
"flake8",
"black",
"isort"
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
lint = [
"pre-commit",
"flake8",
"black",
"isort"
]

since flake8, black, and isort are already configured as pre-commit hooks in pre-commit-config.yaml, we don't need a separate dependency group for them (pre-commit will handle running these tools locally & as part of pre-commit CI which is enabled on the repo).

instead, I'd recommend simply moving pre-commit to the dev group!

Comment on lines +44 to +45
[tool.black]
line-length = 79
Copy link
Contributor

@alyssadai alyssadai Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for centralizing the linter/formatter configs! with this, we can remove these config args from .pre-commit-config.yaml since pre-commit can respect pyproject.toml (for an example, see our py-app-template .pre-commit-config.yaml)

mind updating .pre-commit-config.yaml as part of this PR?

Comment on lines +38 to +39
requires = ["setuptools"]
build-backend = "setuptools.build_meta"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any specific reason to use setuptools over hatch? we use hatch currently in the bagel-cli and configure-nb (mostly because I was inspired by Nipoppy) - to my understanding it's more modern and has more features (e.g., dynamic versioning without much additional config)

might be good to be consistent across repos with the build system

@@ -0,0 +1,57 @@
[project]
name = "neurobagel-api"
dynamic = ["version"]
Copy link
Contributor

@alyssadai alyssadai Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this line tells the build system that the version will be provided dynamically, but we still need to configure the build backend to say where the version actually comes from. e.g., in our py-app-template repo, we use hatch-vcs for the source: https://github.com/neurobagel/py-app-template/blob/c6b729a4a7d996b2de2e523ddbd1d6849b1232af/pyproject.toml#L57-L62

(see also my comment about switching to hatch from setuptools)

[![Main branch check status](https://img.shields.io/github/check-runs/neurobagel/api/main?style=flat-square&logo=github)](https://github.com/neurobagel/api/actions?query=branch:main)
[![Tests Status](https://img.shields.io/github/actions/workflow/status/neurobagel/api/test.yaml?branch=main&style=flat-square&logo=github&label=tests)](https://github.com/neurobagel/api/actions/workflows/test.yaml)
[![Codecov](https://img.shields.io/codecov/c/github/neurobagel/api?token=ZEOGQFFZMJ&style=flat-square&logo=codecov&link=https%3A%2F%2Fcodecov.io%2Fgh%2Fneurobagel%2Fapi)](https://app.codecov.io/gh/neurobagel/api)
[![Python versions static](https://img.shields.io/badge/python-3.10-blue?style=flat-square&logo=python)](https://www.python.org)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update this to 3.10-3.13?

Suggested change
[![Python versions static](https://img.shields.io/badge/python-3.10--3.14-blue?style=flat-square&logo=python)](https://www.python.org)

Comment on lines 45 to 50
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics --per-file-ignores=./app/api/models.py:F722
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

this step is leftover from before we had pre-commit AFAIK, I think this is a good time to delete it since flake8 is already run as part of pre-commit CI

# Note: this configuration only has an effect in repositories that have
# a requirements.txt file / use python / pip.
- package-ecosystem: "pip"
# This is a temporary fix until we update the global sync workflow
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch 👍
mind creating an issue for this in https://github.com/neurobagel/workflows and then adding it to the uv milestone so we don't forget?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow your reasoning behind not 'fully' migrating to uv in the CI test and Dockerfile in this PR: is the plan to open a separate follow-up PR in each Python repo for the remaining changes?

If so, I think that actually makes the review harder rather than easier, since in the current PR I have to verify that the uv + requirements.txt approach makes sense for these two files, only for it to be replaced shortly after.

Would it be possible to do the full migration here instead, since both test.yaml and Dockerfile necessarily already have diffs in this PR anyways? I'm assuming we'd follow the guides here:

which doesn't look too complicated from a first glance.

LMK if I'm missing something!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comment above for using uv to install the project here instead (the current diff to me looks similar in complexity to the changes we'd need to make for the full switch)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adopt uv for dependency management

2 participants