Skip to content

uv run pytest cannot collect tests for scripts with third-party deps #1

Description

@sahilds1

Background

uv run pytest (the documented test command in CLAUDE.md) currently fails collection for any test whose target script imports a third-party dependency. The pytest environment resolves against pyproject.toml, but the scripts declare their dependencies only in their inline # /// script PEP 723 blocks, which apply to uv run <script>.py — not to uv run pytest. As a result, importing the script module at test-collection time raises ModuleNotFoundError. This blocks running the test suite and undermines the "tests import directly from the script module" pattern described in CLAUDE.md.

Existing Behavior

uv run pytest collects 9 items but errors on 3 test modules during import:

test_strava.py -> strava.py:21 import llm -> No module named 'llm'
test_llm_commit_message.py-> llm_commit_message.py:13 import openai -> No module named 'openai'
test_websters1913.py -> websters1913.py:18 from bs4 import ... -> No module named 'bs4'

Only tests whose scripts depend on already-present packages (e.g. requests for test_septa.py) collect successfully. The failure is pre-existing and independent of any single script; it stems from the split between PEP 723 inline deps and the pytest environment.

Files/docs involved:

  • pyproject.toml (test config lives here: configfile: pyproject.toml, testpaths: .)
  • Script modules: strava.py, llm_commit_message.py, websters1913.py (and any future script with external deps)
  • CLAUDE.md (documents uv run pytest as the test command)

Acceptance Criteria

  • uv run pytest collects all test modules with 0 collection errors
  • The full suite runs (pass/fail on assertions is fine; no ModuleNotFoundError at import)
  • test_strava.py, test_llm_commit_message.py, and test_websters1913.py collect and run
  • No script's inline # /// script dependency block is removed (scripts must still run standalone via uv run <script>.py)
  • The mechanism keeps script inline deps and test deps from drifting, or documents how they're kept in sync
  • CLAUDE.md reflects the test-dependency setup if any new step is required

Approach

Add a dev dependency group in pyproject.toml that includes the third-party packages the tests need to import, then run tests against it.

[dependency-groups]
dev = [
"pytest",
"llm==0.31",
"stravalib==2.4",
"python-dotenv==1.0.1",
"openai",
"beautifulsoup4",
]

uv run pytest installs the dev group automatically. Pin versions to match each script's inline block to avoid behavioral drift.

Alternatives considered:

  • Per-test uv run --with: invoke tests with explicit deps. Rejected — clumsy, must be repeated per script, easy to forget.
  • pytest-forked / subprocess running each script via uv run: heavier, changes the test model away from direct imports.
  • Keep as-is and document that only a subset of tests run. Rejected — leaves the documented command broken.

Note the duplication risk: inline blocks and the dev group list the same packages/versions in two places. Acceptable for a small repo; call it out in CLAUDE.md so future scripts get added to both.

References

Risks and Rollback

Low risk — changes are confined to pyproject.toml (test tooling); no script runtime behavior changes. Main risk is version drift between a script's inline deps and the dev group causing tests to exercise a different version than production. Rollback: revert the pyproject.toml change; the repo returns to today's state (3 collection errors), with no impact on uv run <script>.py.

Screenshots / Recordings

Before:
collected 9 items / 3 errors
ERROR test_llm_commit_message.py - No module named 'openai'
ERROR test_strava.py - No module named 'llm'
ERROR test_websters1913.py - No module named 'bs4'
!!! Interrupted: 3 errors during collection !!!

After (target):
collected N items
... (all modules collect; tests pass/fail on assertions only)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions