ABOUTME: Complete Python development with uv package manager, quality tools, and Docker
ABOUTME: Modern workflow with uv, ruff, ty, pytest, Pydantic, and containerization
Python Development
Quick Reference
uv init myproject && cd myproject
uv add requests pydantic httpx
uv add --dev pytest ruff
uv sync --locked
uv run python main.py
uv run ruff check . && uv run ruff format --check . && uvx ty check && uv run pytest
See also: _AST_GREP.md, _PATTERNS.md, source-control
Version (determine, don't assume)
See ../_LANG_COMMON.md. Fetch the truth:
python3 --version # local interpreter
cat .python-version 2>/dev/null # pin file (if present)
grep -E '^python' pyproject.toml 2>/dev/null # project manifest
curl -s https://endoflife.date/api/python.json | jq -r '.[0].latest' # latest upstream stable
Pre-Commit Verification (MANDATORY)
make check && make test-e2e must pass (enforced by the pre-commit-gate hook; see ../_LANG_COMMON.md). What make check expands to for Python:
uv run ruff check .
uv run ruff format --check .
uvx ty check
uv run pytest --cov=myproject
Package Management (uv)
UV is the ONLY way. Do NOT use pip/poetry/pipenv. Universal lockfile, fast resolver.
pyproject.toml
[project]
name = "myproject"
requires-python = ">=3.13"
dependencies = ["httpx>=0.27.0", "pydantic>=2.10.0"]
[dependency-groups]
dev = ["pytest>=8.0.0", "ruff>=0.8.0"]
[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"
Code Quality
Ruff config:
[tool.ruff]
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM", "TCH", "RUF", "PERF"]
Testing: pytest with fixtures, AAA pattern, parametrize. Coverage via --cov.
Pydantic v2
Use Pydantic for all external data boundaries (API I/O, config, queue payloads). Prefer model_validate_json over parse-then-validate. Field(...) for constraints, @field_validator for custom rules.
Code Review Checklist
- Public functions have type hints, no unjustified
Any - Pydantic for external data,
X | NonenotOptional[X] - Ruff/type checker pass, no bare
except: - Context managers,
asyncio.gather()for concurrency - Async is not concurrent: no single stateful client (e.g. SQLAlchemy
AsyncSession) shared acrossgather()tasks; one session per task via a factory. A shared session corrupts under concurrent use. - No CPU-bound work inside
async defwithoutasyncio.to_thread(parsing, hashing, rendering): it blocks the event loop and serializes every concurrent task. Rule of thumb: a function thatawaits nothing probably needsto_thread. - AAA tests, parametrized
For Docker, CI/CD, async patterns, and detailed testing examples, see references/python-patterns.md.