chore: migrate backend to uv package manager#84
Merged
keting merged 18 commits intoketing:mainfrom May 6, 2026
Merged
Conversation
…om anyio
The test test_ensure_repo_sync_retries_retryable_fetch_failure was
asserting that time.sleep is called exactly once during a retryable
fetch failure, but it was failing with:
AssertionError: Expected 'sleep' to have been called once.
Called 32 times.
Root cause
----------
The test patched 'services.git_service.time.sleep', which replaces the
'sleep' attribute on the shared time module object itself. Because
patch operates on the module-level name binding, this ends up replacing
time.sleep globally for the duration of the test. The anyio library
(used by pytest-anyio, which is loaded as a plugin) runs an internal
thread pool that polls with exponential back-off using time.sleep
directly. Those 31 extra calls (0.001, 0.002, 0.004, ... 0.05 × 25)
belong to anyio's scheduler, not to git_service, yet they were captured
by the same mock, causing the assertion to fail.
Fix
---
Introduce a module-level alias in git_service:
_sleep = time.sleep
and use _sleep(delay) inside _retry_git_operation instead of calling
time.sleep(delay) directly.
The test now patches 'services.git_service._sleep' instead of
'services.git_service.time.sleep'. This replaces only the name binding
inside the git_service module namespace; time.sleep elsewhere in the
process (including inside anyio) is unaffected. The mock therefore
captures exactly the one call made by the retry logic, and
assert_called_once() passes reliably.
Replace the manual venv + pip workflow with uv in both the English and Chinese README: - Remove: python3.12 -m venv .venv && source .venv/bin/activate - Remove: pip install -r requirements-dev.txt - Replace: uvicorn main:app ... → uv run uvicorn main:app ... - Add a note explaining that uv auto-creates the venv from pyproject.toml and that dev deps can be installed with
…pyproject.toml + uv.lock)
Contributor
Author
|
073e552 之后新增了以下改动:
|
Without these flags, uv run syncs the full environment (including dev dependencies) at container startup, causing network failures in air-gapped or --network none deployments.
keting
approved these changes
May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR migrates the backend from plain
pip+requirements.txtto uv, a fast Python package manager. All 211 backend tests pass.Changes
src/backend/pyproject.toml(new)Adds a standard
pyproject.tomldeclaring the project metadata and pinned dependencies. Replacesrequirements.txtas the source of truth for the backend's dependency set. A[dependency-groups] devsection lists test-only packages (pytest,httpx).src/backend/uv.lock(new)Lock file generated by
uv, ensuring fully reproducible installs across all environments.src/backend/Dockerfileuvvia pip in the base image layer.COPY requirements.txt+pip install -r requirements.txtwithCOPY pyproject.toml+uv sync --no-dev.CMDfrom bareuvicorntouv run uvicornso the server runs inside the uv-managed virtual environment.src/docker-compose.ymlUpdate the backend service
commandoverride fromuvicorn ...touv run uvicorn ...to match the Dockerfile change.src/backend/main.pyAdd
load_dotenv()at startup so environment variables defined in a.envfile are loaded beforeconfig.settingsis evaluated. This fixes a subtle ordering issue when running locally outside of Docker.src/backend/services/git_service.py+tests/test_git_service.pyIntroduce a module-level alias
_sleep = time.sleepand patchservices.git_service._sleepin tests instead ofservices.git_service.time.sleep. The old patch target replacedtime.sleepglobally, causing anyio's internal thread-pool scheduler (which also callstime.sleep) to inflate the mock call count from 1 to 32, breaking the assertion. Patching the private alias isolates the mock to git_service only.README.md/README.zh-CN.mdUpdate the Local Development backend setup instructions to reflect the uv workflow:
python3.12 -m venv .venv && source .venv/bin/activate) andpip install -r requirements-dev.txt.uvicorn main:app --reload …withuv run uvicorn main:app --reload ….pyproject.tomlon first run and thatuv syncinstalls dev dependencies explicitly.