Skip to content

Consolidate Python dependencies to pyproject.toml with uv as single source of truth #280

@IanWorley

Description

@IanWorley

Summary

Use pyproject.toml as the only source of truth for Python dependencies and uv for installs and locking. Migrate from Poetry's format to PEP 621, update Docker and CI to install via uv, and remove or generate requirements.txt so the two files cannot drift. Use three dependency groups: runtime (non-dev), dev, and test (for CI/CD). uv is supported on macOS, Linux, and Windows.

Motivation

We currently maintain two dependency lists that are out of sync:

  • src/python/requirements.txt — 4 runtime packages (bottle, cryptography, pexpect, tblib). Both Dockerfiles use this.
  • src/python/pyproject.toml — 8 runtime packages (same four plus requests, timeout-decorator, urllib3, certifi) in Poetry format. Not used for Docker installs.

Having both leads to drift. The image only installs from requirements.txt, so any runtime deps only in pyproject.toml are missing in the container.

Current State

  • Docker: docker-image/Dockerfile and test-image/Dockerfile copy and pip install -r requirements.txt; test image also installs pytest, parameterized, testfixtures, webtest, timeout-decorator manually.
  • pyproject.toml: Poetry config ([tool.poetry], [tool.poetry.dependencies], [tool.poetry.group.dev.dependencies]); no lockfile in tree (removed per CHANGELOG).

Implementation

1. Migrate pyproject.toml to PEP 621 (uv format)

uv uses PEP 621, not Poetry's format. Convert in place:

  • Replace [tool.poetry] / [tool.poetry.dependencies] with [project] (name, version, description, requires-python, dependencies as a list of strings).
  • Replace [tool.poetry.group.dev.dependencies] with [dependency-groups] (PEP 735) and three groups:
    • Runtime (non-dev): Keep in [project].dependencies only — what the app needs to run (bottle, cryptography, pexpect, requests, tblib, timeout-decorator, urllib3, certifi). No separate "runtime" table; this is the default install when no groups are requested.
    • dev: Local development tooling (e.g. ruff, pyright, or other lint/format/type-check tools when added). Synced by default for local uv sync.
    • test: Testing dependencies for CI/CD (pytest, parameterized, testfixtures, webtest, timeout-decorator if only needed for tests). Used in test image and CI; not included in runtime image.
  • Keep the same package names and version constraints; only the table layout and syntax change.

2. Add uv and lockfile

  • Document or script uv install for local dev (e.g. install script, Homebrew, or pipx).
  • Run uv lock and commit uv.lock so installs are reproducible.

3. Update Dockerfiles to use uv

  • src/docker/build/docker-image/Dockerfile — Install uv, then uv sync --frozen --no-dev (and exclude test group) so only [project].dependencies are installed. No requirements.txt copy.
  • src/docker/build/test-image/Dockerfile — Same base; use uv sync --frozen --group test (and optionally --no-group dev) so CI/test image gets runtime + test group only.

4. Resolve requirements.txt

  • Either remove requirements.txt and stop using it everywhere, or generate it only (e.g. uv export --no-dev -o requirements.txt) in CI/script and never edit by hand.

5. CI

  • Ensure CI (e.g. .github/workflows/ci.yml) installs Python deps via uv: install uv, then uv sync --frozen --group test (or equivalent) so CI has runtime + test group for running tests.

6. Verify

  • Confirm runtime image has only runtime deps; test image and CI have runtime + test group.
  • Run existing tests and smoke checks in the built images and in CI.

Acceptance Criteria

  • Support a dev group, a non-dev (runtime) group, and a test group for CI/CD.
  • pyproject.toml is PEP 621 (no Poetry tables); only source of truth for deps.
  • Runtime (non-dev) in [project].dependencies; dev group for local tooling; test group for CI/CD (pytest, webtest, etc.).
  • uv.lock committed; installs use uv sync --frozen with appropriate --no-dev / --group test for each context.
  • uv used for dependency installs in Docker and CI and dev.
  • Runtime Docker image installs only runtime deps; test image and CI install runtime + test group.
  • No hand-maintained requirements.txt (removed or generated only).
  • Docker builds and CI succeed; tests pass in the test image.

Metadata

Metadata

Assignees

No one assigned

    Labels

    refactorCode refactoring and cleanup

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions