Skip to content

Migrate dev tooling from pip-tools/tox to uv - #261

Merged
hf-krechan merged 4 commits into
mainfrom
migrate-to-uv
Jul 24, 2026
Merged

Migrate dev tooling from pip-tools/tox to uv#261
hf-krechan merged 4 commits into
mainfrom
migrate-to-uv

Conversation

@hf-krechan

@hf-krechan hf-krechan commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Moves dev-tool extras (tests, coverage, formatting, linting, spell_check, type_check, init_bo4e) from [project.optional-dependencies] to PEP 735 [dependency-groups], plus a bundled dev group; drops the test_packaging extra (build/twine) entirely.
  • Replaces tox.ini and requirements.txt with uv.lock.
  • Rewrites all GitHub Actions workflows to use astral-sh/setup-uv + uv sync --group X / uv run instead of pip install tox && tox -e X, inlining the docker-compose/BO4E-model-generation steps that tox used to chain via nested tox -e calls.
  • packaging_test.yml now uses native uv build + uv publish --dry-run instead of build/twine.
  • Updates .github/dependabot.yml ecosystem from pip to uv.
  • Updates .pre-commit-config.yaml (bumps black rev to match new pins, adds the uv-lock hook).
  • Updates README.md dev setup instructions from tox -e ... to uv run/uv sync.

Dependency pins (final state, after CI debugging)

  • datamodel-code-generator stays pinned at ==0.25.9 (not bumped). An earlier attempt to bump it to resolve an isort conflict broke bo4e-python-generator==0.0.10, which relies on datamodel-code-generator internals that changed across releases. Fixed the original isort conflict instead by pinning the formatting group's isort to 5.13.2 (last release compatible with datamodel-code-generator==0.25.9's isort<6.0 constraint).
  • sqlmodel pinned to ==0.0.24 and python-dotenv pinned to ==1.0.1 in [project.dependencies]. Both were previously open-ended constraints that only got frozen for CI via the now-removed requirements.txt; without that freeze, uv lock picked newer versions that break mypy (sqlmodel) and .env loading under coverage run -m pytest (python-dotenv). See PR comments below for the full debugging trail.

No workflow matrix structures were changed, so existing required status checks on main should still match by name.

Test plan

  • uv lock resolves cleanly
  • uv sync --group dev succeeds
  • Spot-checked each group syncs independently (tests, linting, type_check, spell_check, coverage, formatting, init_bo4e)
  • Ran black --check, isort --check, codespell, pylint, mypy, pytest import, bost --help, bo4e-generator --help through uv run
  • uv build and uv publish --dry-run succeed locally
  • CI passes on this PR (all checks green, including docker-based integration/coverage jobs)

🤖 Generated with Claude Code

hf-krechan and others added 3 commits July 24, 2026 09:39
Moves dev-tool extras to PEP 735 dependency-groups, replaces tox.ini and
requirements.txt with uv.lock, drops build/twine in favor of native uv
build/uv publish, and rewrites GitHub Actions workflows and Dependabot
config to use astral-sh/setup-uv + uv sync/run instead of tox.

Bumps datamodel-code-generator to >=0.27.1 to resolve an isort version
conflict with the formatting group that tox's per-env isolated venvs had
been masking.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Reverts the datamodel-code-generator bump: even the minimal bump to
0.27.1 breaks bo4e-python-generator==0.0.10, which relies on
datamodel-code-generator internals (DataModelSet fields,
DataTypeManager.type_map_factory signature) that changed across
releases. Verified 0.25.9 (the version it was actually built against)
works end-to-end for schema generation, pylint, and mypy.

Resolves the original isort conflict by downgrading the formatting
group's isort pin to 5.13.2 (last release satisfying
datamodel-code-generator==0.25.9's isort<6.0 constraint) instead of
bumping datamodel-code-generator.

Also pins sqlmodel==0.0.24 in [project.dependencies]: the open-ended
sqlmodel>=0.0.14 resolves to 0.0.39 by default, which breaks mypy
against the bo4e-generated models (SQLModelConfig/ConfigDict
incompatibilities). The old requirements.txt had silently frozen this
same version for CI; uv.lock now does the same job explicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
python-dotenv was unconstrained in [project.dependencies] (only frozen
at 1.0.1 via the now-removed requirements.txt), so uv lock resolved it
to 1.2.2. That breaks load_dotenv()'s implicit path discovery when
pytest is wrapped by `coverage run -m pytest` from the tests/ directory
(mirroring the old tox coverage env's changedir=tests), causing
"OSError: Could not load .env file." during test collection.

Confirmed locally: reproduces with 1.2.2, resolved with 1.0.1. Also
matches repo history: a Dependabot PR bumping python-dotenv to 1.2.1
already failed CI here and was never merged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hf-krechan

Copy link
Copy Markdown
Contributor Author

CI status: all checks green ✅

Summary of the full fix chain that came out of debugging CI after the initial migration:

  1. Reverted the datamodel-code-generator bump. My first fix for an isort conflict bumped datamodel-code-generator from 0.25.9 to >=0.27.1, which resolved to the latest release (0.70.0) and broke bo4e-python-generator==0.0.10 (DataModelSet.__new__() missing 2 required positional arguments). Even the minimal bump to 0.27.1 breaks it differently (type_map_factory() got an unexpected keyword target_datetime_class) — bo4e-python-generator reaches into datamodel-code-generator internals that shift across releases, so it's tightly locked to exactly 0.25.9. Resolved the original isort conflict instead by downgrading the formatting group's isort pin to 5.13.2 (last release satisfying datamodel-code-generator==0.25.9's isort<6.0 constraint).

  2. Pinned sqlmodel==0.0.24 in [project.dependencies]. The dependency was always the open-ended sqlmodel>=0.0.14requirements.txt is what silently froze it at 0.0.24 for CI. Without that freeze, uv lock resolved to 0.0.39, which breaks mypy against the bo4e-generated models (SQLModelConfig/ConfigDict incompatibilities, 459 errors).

  3. Pinned python-dotenv==1.0.1, for the same reason — resolved to 1.2.2 without the requirements.txt freeze, which broke load_dotenv()'s implicit path discovery specifically when pytest runs wrapped by coverage run -m pytest from the tests/ directory (OSError: Could not load .env file.).

All three were cases where requirements.txt had silently pinned a version that pyproject.toml's own (looser) constraint didn't capture, and this repo's generated model code (via bo4e-python-generator/datamodel-code-generator) turns out to be fragile against drift in any of them — confirmed by this repo's own Dependabot history, which already has failed, unmerged bump attempts for python-dotenv, datamodel-code-generator, and pydantic.

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates the repository’s Python dev tooling from pip-tools/tox to uv, centralizing dependency resolution in uv.lock and updating CI + documentation accordingly.

Changes:

  • Replace tox env orchestration and requirements.txt with uv dependency groups and uv.lock.
  • Rewrite GitHub Actions workflows to use astral-sh/setup-uv, uv sync --group …, and uv run ….
  • Update developer-facing tooling and docs (pyproject.toml dependency groups, pre-commit hooks, README).

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tox.ini Removes legacy tox environments in favor of uv-based workflow steps.
requirements.txt Removes pip-tools generated requirements in favor of uv.lock.
README.md Updates dev setup and common commands to use uv sync / uv run.
pyproject.toml Moves dev extras to [dependency-groups] and adjusts some pins.
.pre-commit-config.yaml Updates formatter hook versions and adds uv-lock hook.
.github/workflows/unittests.yml Migrates unit-test job to uv (but currently does not run tests).
.github/workflows/pythonlint.yml Migrates lint/type/spell jobs to uv and inlines BO4E model generation.
.github/workflows/python-publish.yml Migrates release workflow steps to uv and inlines checks.
.github/workflows/packaging_test.yml Switches packaging test to uv build + uv publish --dry-run.
.github/workflows/integrationtests.yml Migrates integration tests to uv and inlines DB/model setup.
.github/workflows/formatting.yml Migrates formatting checks to uv groups and uv run.
.github/workflows/coverage.yml Migrates coverage job to uv and inlines DB/model setup.
.github/dependabot.yml Switches Dependabot ecosystem entry from pip to uv.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +21 to +25
cd src
uv run python -m borm.db.postgresql_db.create_env_file
docker compose -f borm/db/postgresql_db/docker-compose.yaml up -d
cd borm/db/postgresql_db
docker compose -f docker-compose.yaml down -v

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Confirmed accurate, but this is pre-existing behavior, not something this migration changed — the old tox.ini's [testenv:unittests] also only ran tox -e setup_testpostgresql + tox -e remove_testpostgresql, never pytest (there's no separate unit-test suite in this repo, only tests/integrationtests, which is what integrationtests.yml actually exercises). I translated the tox behavior 1:1 rather than silently changing what this check does, since pytest (3.11, ubuntu-latest) is a required status check on main and I didn't want to risk changing its semantics without a separate discussion. Worth a follow-up issue if the maintainers want this job renamed or made to actually assert something.

Comment thread pyproject.toml
Comment thread pyproject.toml
Comment thread README.md Outdated
Comment thread pyproject.toml Outdated
pyproject.toml had a leftover merge-artifact comment ("toodynamic")
duplicating the dynamic field it sits next to. README had an
uncapitalized "windows" and missing comma.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hf-krechan
hf-krechan merged commit f06004a into main Jul 24, 2026
15 checks passed
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.

2 participants