Skip to content

Commit 0ea459c

Browse files
authored
Merge pull request #501 from nanotaboada/chore/migrate-to-uv-pep735
chore(deps): migrate to uv with PEP 735 dependency groups (#447)
2 parents 805b08c + 7811858 commit 0ea459c

File tree

13 files changed

+1511
-66
lines changed

13 files changed

+1511
-66
lines changed

.github/copilot-instructions.md

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,34 @@ tests/ — pytest integration tests
5454
### Quick Start
5555

5656
```bash
57-
pip install -r requirements.txt
58-
pip install -r requirements-test.txt
59-
pip install -r requirements-lint.txt
60-
uvicorn main:app --reload --port 9000 # http://localhost:9000/docs
61-
pytest # run tests
62-
pytest --cov=./ --cov-report=term # with coverage (target >=80%)
63-
flake8 .
64-
black --check .
57+
# Setup (using uv)
58+
uv venv
59+
source .venv/bin/activate # Linux/macOS; use .venv\Scripts\activate on Windows
60+
uv pip install --group dev
61+
62+
# Run application
63+
uv run uvicorn main:app --reload --port 9000 # http://localhost:9000/docs
64+
65+
# Run tests
66+
uv run pytest # run tests
67+
uv run pytest --cov=./ --cov-report=term # with coverage (target >=80%)
68+
69+
# Linting and formatting
70+
uv run flake8 .
71+
uv run black --check .
72+
73+
# Docker
6574
docker compose up
6675
docker compose down -v
6776
```
6877

6978
### Pre-commit Checks
7079

7180
1. Update `CHANGELOG.md` `[Unreleased]` section (Added / Changed / Fixed / Removed)
72-
2. `flake8 .` — must pass
73-
3. `black --check .` — must pass
74-
4. `pytest` — all tests must pass
75-
5. `pytest --cov=./ --cov-report=term` — coverage must be >=80%
81+
2. `uv run flake8 .` — must pass
82+
3. `uv run black --check .` — must pass
83+
4. `uv run pytest` — all tests must pass
84+
5. `uv run pytest --cov=./ --cov-report=term` — coverage must be >=80%
7685
6. Commit message follows Conventional Commits format (enforced by commitlint)
7786

7887
### Commits
@@ -95,7 +104,7 @@ Example: `feat(api): add player stats endpoint (#42)`
95104
### Ask before changing
96105

97106
- Database schema (`schemas/player_schema.py` — no Alembic, use tools/ seed scripts manually)
98-
- Dependencies (`requirements*.txt`)
107+
- Dependencies (`pyproject.toml` with PEP 735 dependency groups)
99108
- CI/CD configuration (`.github/workflows/`)
100109
- Docker setup
101110
- API contracts (breaking Pydantic model changes)

.github/workflows/python-cd.yml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ name: Python CD
66
on:
77
push:
88
tags:
9-
- 'v*.*.*-*'
9+
- "v*.*.*-*"
1010

1111
env:
12-
PYTHON_VERSION_FILE: '.python-version'
12+
PYTHON_VERSION_FILE: ".python-version"
1313
PACKAGE_NAME: nanotaboada/python-samples-fastapi-restful
1414

1515
jobs:
@@ -69,20 +69,20 @@ jobs:
6969
uses: actions/setup-python@v6.2.0
7070
with:
7171
python-version-file: ${{ env.PYTHON_VERSION_FILE }}
72-
cache: 'pip'
72+
73+
- name: Set up uv
74+
uses: astral-sh/setup-uv@v4
75+
with:
76+
version: "latest"
7377

7478
- name: Install test dependencies
7579
run: |
76-
python -m pip install --upgrade pip
77-
pip install -r requirements-test.txt
80+
uv venv
81+
uv pip install --group dev
7882
7983
- name: Run tests with pytest
8084
run: |
81-
pytest -v
82-
83-
- name: Generate coverage report
84-
run: |
85-
pytest --cov=./ --cov-report=xml --cov-report=term
85+
uv run pytest --cov=./ --cov-report=xml --cov-report=term -v
8686
8787
- name: Log in to GitHub Container Registry
8888
uses: docker/login-action@v3.7.0

.github/workflows/python-ci.yml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ name: Python CI
55

66
on:
77
push:
8-
branches: [ master ]
8+
branches: [master]
99
pull_request:
10-
branches: [ master ]
10+
branches: [master]
1111

1212
jobs:
1313
lint:
@@ -21,24 +21,28 @@ jobs:
2121
- name: Lint commit messages
2222
uses: wagoid/commitlint-github-action@v6.2.1
2323

24+
- name: Set up uv
25+
uses: astral-sh/setup-uv@v4
26+
with:
27+
version: "latest"
28+
2429
- name: Set up Python
2530
uses: actions/setup-python@v6.2.0
2631
with:
27-
python-version-file: '.python-version'
28-
cache: 'pip'
32+
python-version-file: ".python-version"
2933

3034
- name: Install lint dependencies
3135
run: |
32-
python -m pip install --upgrade pip
33-
pip install -r requirements-lint.txt
36+
uv venv
37+
uv pip install --group lint
3438
3539
- name: Lint with Flake8
3640
run: |
37-
flake8 .
41+
uv run flake8 .
3842
3943
- name: Check code formatting with Black
4044
run: |
41-
black --check .
45+
uv run black --check .
4246
4347
test:
4448
needs: lint
@@ -49,24 +53,28 @@ jobs:
4953
- name: Checkout repository
5054
uses: actions/checkout@v6.0.2
5155

56+
- name: Set up uv
57+
uses: astral-sh/setup-uv@v4
58+
with:
59+
version: "latest"
60+
5261
- name: Set up Python
5362
uses: actions/setup-python@v6.2.0
5463
with:
55-
python-version-file: '.python-version'
56-
cache: 'pip'
64+
python-version-file: ".python-version"
5765

5866
- name: Install test dependencies
5967
run: |
60-
python -m pip install --upgrade pip
61-
pip install -r requirements-test.txt
68+
uv venv
69+
uv pip install --group dev
6270
6371
- name: Run tests with pytest
6472
run: |
65-
pytest -v
73+
uv run pytest -v
6674
6775
- name: Generate coverage report
6876
run: |
69-
pytest --cov=./ --cov-report=xml --cov-report=term
77+
uv run pytest --cov=./ --cov-report=xml --cov-report=term
7078
7179
- name: Upload coverage report artifact
7280
uses: actions/upload-artifact@v7.0.0

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ ipython_config.py
113113
# https://pdm.fming.dev/#use-with-ide
114114
.pdm.toml
115115

116+
# uv
117+
# uv uses a lock file for reproducible builds. Include it in version control.
118+
# No need to ignore uv.lock - it should be committed.
119+
116120
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
117121
__pypackages__/
118122

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,17 @@ This project uses famous football coaches as release codenames, following an A-Z
6666
- `logger.error(f"...")` replaced with `logger.exception("...: %s", error)` in all `SQLAlchemyError` handlers (#66)
6767
- EN dashes replaced with ASCII hyphens in `seed_002` log and argparse strings (#66)
6868
- `logger.error` replaced with `logger.exception` in `sqlite3.Error` handlers in `seed_001` and `seed_002` (#66)
69+
- `pyproject.toml` migrated to full PEP 735 format: `[project]` (with `dependencies` field) and `[dependency-groups]` (`test`, `lint`, `dev`) (#447)
70+
- GitHub Actions CI/CD (`python-ci.yml`, `python-cd.yml`) updated to install and run via `uv` instead of `pip` (#447)
71+
- Dockerfile updated: builder stage uses `uv export | pip wheel` for reproducible offline wheel builds; runtime installs from pre-built wheels with no network access (#447)
72+
- `uv.lock` added for fully pinned, reproducible dependency resolution across all environments (#447)
6973

7074
### Deprecated
7175

7276
### Removed
7377

7478
- `postman_collections/` directory replaced by `rest/players.rest` (#493)
79+
- `requirements.txt`, `requirements-lint.txt`, `requirements-test.txt` replaced by `pyproject.toml` with PEP 735 dependency groups (#447)
7580

7681
### Fixed
7782

Dockerfile

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
# ------------------------------------------------------------------------------
22
# Stage 1: Builder
3-
# This stage builds the application and its dependencies.
3+
# This stage resolves and pre-builds all dependency wheels for offline installation.
4+
# No application source code is copied here — only pyproject.toml and uv.lock.
45
# ------------------------------------------------------------------------------
56
# Python version should match .python-version file (currently 3.13.3)
67
FROM python:3.13.3-slim-bookworm AS builder
78

89
WORKDIR /app
910

10-
# Install system build tools for packages with native extensions
11+
# Install system build tools required to compile native extensions (e.g. gevent, greenlet)
1112
RUN apt-get update && \
1213
apt-get install -y --no-install-recommends build-essential gcc libffi-dev libssl-dev && \
1314
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb
1415

15-
# Build all dependencies into wheels for reproducibility and speed
16-
COPY --chown=root:root --chmod=644 requirements.txt .
17-
RUN pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r requirements.txt
16+
# Resolve and build all dependency wheels from pyproject.toml and uv.lock:
17+
# uv export reads uv.lock to produce a pinned, reproducible dependency list;
18+
# pip wheel compiles each resolved package into a .whl file for offline installation
19+
COPY --chown=root:root --chmod=644 pyproject.toml uv.lock ./
20+
RUN pip install --no-cache-dir uv==0.10.1 --quiet && \
21+
uv export --frozen --no-dev --no-hashes | pip wheel --no-cache-dir --wheel-dir=/app/wheelhouse -r /dev/stdin
1822

1923
# ------------------------------------------------------------------------------
2024
# Stage 2: Runtime
@@ -42,9 +46,8 @@ COPY assets/ ./assets/
4246
# Copy pre-built wheels from builder
4347
COPY --from=builder /app/wheelhouse/ /app/wheelhouse/
4448

45-
# Install dependencies
46-
COPY requirements.txt .
47-
RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse -r requirements.txt && \
49+
# Install all pre-built wheels from the builder stage; no network access required
50+
RUN pip install --no-cache-dir --no-index --find-links /app/wheelhouse /app/wheelhouse/*.whl && \
4851
rm -rf /app/wheelhouse
4952

5053
# Copy application source code
@@ -54,8 +57,7 @@ COPY models/ ./models/
5457
COPY routes/ ./routes/
5558
COPY schemas/ ./schemas/
5659
COPY services/ ./services/
57-
58-
# https://rules.sonarsource.com/docker/RSPEC-6504/
60+
COPY tools/ ./tools/
5961

6062
# Copy entrypoint and healthcheck scripts
6163
COPY --chmod=755 scripts/entrypoint.sh ./entrypoint.sh
@@ -66,6 +68,7 @@ COPY --chmod=755 scripts/healthcheck.sh ./healthcheck.sh
6668
COPY --chmod=755 storage/ ./hold/
6769

6870
# Add non-root user and make volume mount point writable
71+
# Avoids running the container as root (see: https://rules.sonarsource.com/docker/RSPEC-6504/)
6972
RUN adduser --system --disabled-password --group fastapi && \
7073
mkdir -p /storage && \
7174
chown fastapi:fastapi /storage

README.md

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,53 @@ This project uses `.python-version` to specify the required Python version. If y
3636

3737
Alternatively, ensure you have Python 3.13.3 (or the version specified in `.python-version`) installed.
3838

39+
## Setup
40+
41+
### Prerequisites
42+
43+
- Python 3.13+
44+
- [uv](https://docs.astral.sh/uv/) (recommended) or pip
45+
46+
### Installation
47+
48+
1. Install uv (if you haven't already):
49+
50+
```bash
51+
curl -LsSf https://astral.sh/uv/install.sh | sh
52+
```
53+
54+
2. Create a virtual environment and install dependencies:
55+
56+
```bash
57+
uv venv
58+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
59+
uv pip install --group dev
60+
```
61+
3962
## Install
4063

41-
```console
42-
pip install -r requirements.txt
43-
pip install -r requirements-lint.txt
44-
pip install -r requirements-test.txt
64+
Dependencies are defined in `pyproject.toml` using PEP 735 standards. Install them with:
65+
66+
```bash
67+
uv pip install --group dev
4568
```
4669

70+
Or with specific groups:
71+
72+
- `uv pip install` - Install production dependencies only
73+
- `uv pip install --group test` - Install test dependencies
74+
- `uv pip install --group lint` - Install linting dependencies
75+
- `uv pip install --group dev` - Install all (test + lint + production)
76+
4777
## Start
4878

49-
```console
79+
```bash
80+
uv run uvicorn main:app --reload --port 9000
81+
```
82+
83+
Or using pip:
84+
85+
```bash
5086
uvicorn main:app --reload --port 9000
5187
```
5288

@@ -62,11 +98,38 @@ http://localhost:9000/docs
6298

6399
The [`rest/players.rest`](rest/players.rest) file covers all CRUD operations and can be run directly in VS Code with the [REST Client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension.
64100

101+
## Running Tests
102+
103+
```bash
104+
uv run pytest -v
105+
```
106+
107+
## Code Quality
108+
109+
### Linting
110+
111+
```bash
112+
uv run flake8 .
113+
```
114+
115+
### Code Formatting
116+
117+
```bash
118+
uv run black --check .
119+
uv run black . # Auto-format
120+
```
121+
122+
### Coverage
123+
124+
```bash
125+
uv run pytest --cov=./ --cov-report=term
126+
```
127+
65128
## Container
66129

67130
### Docker Compose
68131

69-
This setup uses [Docker Compose](https://docs.docker.com/compose/) to build and run the app and manage a persistent SQLite database stored in a Docker volume.
132+
This setup uses [Docker Compose](https://docs.docker.com/compose/) to build and run the app and manage a persistent SQLite database stored in a Docker volume. The Dockerfile uses PEP 735 dependency groups defined in `pyproject.toml`.
70133

71134
#### Build the image
72135

0 commit comments

Comments
 (0)