- This template targets Python projects managed by
uv. src/contains the main application package (src layout).tests/contains all tests.- No web-framework-specific assumptions.
-
Never manually edit dependency versions in
pyproject.toml; useuv add. -
Add runtime dependencies with:
uv add <package>
-
Add development dependencies with:
uv add --group dev <package>
-
Always run code through
uv runto ensure the correct virtual environment. -
Lock dependencies with
uv lock; commituv.lockto version control. -
Use
uv sync --all-groupsto install all dependency groups.
When introducing new dependencies, prefer these unless compatibility requires a change:
// python
// fast JSON serialization/deserialization
orjson >= 3.11.7
// high-performance struct-based serialization
msgspec >= 0.20.0
// async-first HTTP client
httpx >= 0.28.1
// drop-in asyncio event loop replacement (Linux/macOS)
uvloop >= 0.22.1
// structured logging
structlog >= 25.5.0
// data validation (only when full validation is needed)
pydantic >= 2.12.5-security
// CLI framework (for complex CLIs; argparse for simple ones)
click >= 8.3.1
// DataFrame operations
polars >= 1.39.3
// structured concurrency
anyio >= 4.12.1
// gRPC
grpcio >= 1.78.0
// database ORM (async mode preferred)
sqlalchemy >= 2.0.48
// async SQLite
aiosqlite >= 0.22.1
// async PostgreSQL
asyncpg >= 0.31.0
// async Redis client
redis >= 7.4.0
// metrics
prometheus-client >= 0.24.1
// OpenTelemetry tracing
opentelemetry-api >= 1.40.0
// OpenTelemetry SDK
opentelemetry-sdk >= 1.40.0
// in-process caching utilities
cachetools >= 7.0.5- JSON preference:
orjsonoverjson(stdlib) andujson. - Serialization preference:
msgspecoverpydanticfor pure serialization (no validation needed). - HTTP client preference:
httpx(withh2for HTTP/2) overrequestsandaiohttp. - DataFrame preference:
polarsoverpandasfor new code. - Event loop preference:
uvloopover default asyncio loop. - Logging preference:
structlogoverlogging(stdlib). - Forbidden by default:
requests(sync-only),pandas(usepolars),print()for logging.
- Type annotations:
- All public functions and methods must have full type annotations.
- Use
from __future__ import annotationsat the top of every module. - Use modern union syntax (
X | None) instead ofOptional[X]. - Use
tyfor type checking; code must passty checkwith zero errors.
- Error handling:
- Define explicit exception hierarchies per module/package.
- Never use bare
except:orexcept Exception:without re-raising. - Use
contextlib.suppress()for intentional exception swallowing. - Prefer returning typed result objects over raising exceptions in hot paths.
- Async/Await:
- Default to
asyncfor I/O-bound code. - Use
asyncio.TaskGroup(Python 3.11+) for structured concurrency. - Never mix
asyncio.run()with already-running event loops.
- Default to
- Observability:
- Logging:
structlogwith JSON output in production. - Metrics/traces: OpenTelemetry OTLP gRPC.
- Never use
print()for logging or diagnostics in library code.
- Logging:
- Configuration:
- Use environment variables with
pydantic-settingsormsgspecfor config parsing. - Prefer TOML configuration files.
- Use environment variables with
- Security:
- Never hardcode secrets; use environment variables or secret managers.
- Validate all external input at system boundaries.
- Modularity: Design each module so it can be imported independently with clear boundaries and minimal hidden coupling.
- Performance: Prefer zero-copy patterns, memory-mapped I/O for large datasets, vectorized operations, and pre-allocated buffers.
- Extensibility: Use Protocols (
typing.Protocol) and abstract base classes for pluggable implementations. - Type Safety: Maintain strong static typing across interfaces and internals; minimize use of
Any.
- Avoid allocations in hot loops; prefer pre-allocated lists,
array.array, or NumPy/Polars for bulk data. - Use
__slots__on data-heavy classes to reduce per-instance memory overhead. - Prefer
struct.pack/struct.unpackormemoryviewfor binary protocol parsing. - Use generator expressions and
itertoolsto avoid materializing large intermediate lists. - Profile before optimizing; use
py-spy,scalene, orcProfileto identify real bottlenecks.
- Use
uvloopas the event loop policy for production servers. - Use
asyncio.TaskGroupfor structured concurrent I/O. - Use
concurrent.futures.ProcessPoolExecutorfor CPU-bound parallelism. - Use
asyncio.to_thread()to offload blocking calls from the event loop. - Prefer
asyncio.Queuefor async producer-consumer patterns. - Offload blocking I/O (file reads, DNS, HTTP) via
asyncio.to_thread()or dedicated async libraries instead of running it directly in async coroutines. - Use
anyiofor cross-runtime portability (asyncio and trio). - Limit concurrent connections with
asyncio.Semaphoreto prevent resource exhaustion. - Channel selection:
- Async-to-Async:
asyncio.Queue/anyio.create_memory_object_stream - CPU parallelism:
multiprocessingorconcurrent.futures.ProcessPoolExecutor - Avoid threading for CPU-bound work due to the GIL (use
multiprocessingor native extensions)
- Async-to-Async:
- Use
__slots__on data-heavy classes to reduce per-instance memory overhead. - Use
msgspec.Structoverdataclasses/pydantic.BaseModelfor high-throughput data objects. - Prefer
bytes/bytearray/memoryviewoverstrfor binary data; avoid repeated encode/decode. - Use
orjsonfor JSON serialization — it returnsbytesdirectly, avoiding intermediate string allocation. - For large datasets, use memory-mapped files (
mmap) or Arrow-backed DataFrames (polars). - Use
sys.getsizeof()andtracemallocto profile memory usage. - Prefer
array.arrayoverlistfor homogeneous numeric data. - Use weak references (
weakref) for caches that should not prevent garbage collection.
- Use
msgspec.Structwithfrozen=Truefor immutable data transfer objects. - Use
dataclasseswithslots=True, frozen=Truefor simple value types. - Use
enum.IntEnumoverenum.Enumfor performance-critical flag/state types. - Prefer
typing.NamedTupleover plain tuples for self-documenting return types. - Keep error types lightweight; avoid attaching large payloads to exception instances.
- Use
typing.TypeAliasfor complex type expressions to improve readability.
- Use
cffiorctypesfor calling C libraries; prefercffifor new code. - Use
pyo3/maturinfor writing performance-critical modules in Rust. - Use
Cythononly when interfacing with existing C/C++ codebases. - Keep the Python ↔ native boundary coarse-grained; minimize per-call overhead.
- Always release the GIL (
nogilin Cython,py.allow_threadsin PyO3) for long-running native computations.
- Lint with
ruff— all code must passruff checkwith zero errors. - Format with
ruff format— consistent style, no debates. - Type check with
ty— all code must passty checkwith zero errors. - Use Gherkin +
behavefor outer-loop acceptance tests. - Use
pytestfor inner-loop TDD — tests must pass before claiming completion. - Use Hypothesis under
tests/when you need generated coverage for invariants; these tests must run through the normaluv run pytestpath. - Use
pytest-benchmarkfor performance-sensitive code. - Use
pytest-covto track test coverage.
- Do not block the event loop with synchronous I/O — use
asyncio.to_thread()or async libraries instead. - Do not use mutable default arguments — use
Noneand initialize inside the function body instead. - Catch specific exceptions with
except SpecificErrorand re-raise when propagating errors. - Pass state via function parameters or dependency injection instead of using
globalor module-level mutable state in library code. - Keep imports at the module top level; lazy imports inside functions are only acceptable when intentionally documented.
- Handle
KeyboardInterruptandSystemExitseparately fromException.
- Incomplete implementations: finish features before submitting.
- Large, sweeping changes: keep changes focused and reviewable.
- Mixing unrelated changes: keep one logical change per commit.
- Using
# type: ignorewithout a specific error code and justification comment.
When fixing failures, identify root cause first, then apply idiomatic fixes instead of suppressing warnings or patching symptoms.
Use outside-in development for behavior changes:
- Git Restrictions: NEVER use
git worktree. All code modifications MUST be made directly on the current branch in the existing working directory. - start with a failing Gherkin scenario under
features/, - drive implementation with failing
pytesttests, - keep example-based
pytesttests as the default inner loop for named cases and edge cases, - add Hypothesis properties under
tests/when the rule is an invariant instead of a single named example, - treat Atheris as conditional planning work rather than baseline template scaffolding,
- keep step definitions thin and reuse Python domain modules.
After each feature or bug fix, run:
just format
just lint
just test
just bdd
just test-allIf any command fails, report the failure and do not claim completion.
- BDD scenarios: place Gherkin features under
features/and step definitions underfeatures/steps/. - Use BDD to define acceptance behavior first, then use
pytestfor the inner TDD loop. - Keep example-based and Hypothesis-based tests together under
tests/;just testmust exercise both without a separate property-test command. - Unit tests: place in
tests/mirroring the source structure. - Integration tests: place in
tests/integration/. - Fuzz tests: only plan or add Atheris when a module parses hostile text or binary input, decodes file or protocol formats, or wraps native extensions where crash resistance matters.
- Fuzz workflow: when fuzzing is justified, keep the harness targeted to the affected module instead of adding a default workspace-wide fuzz command to every starter.
- Performance tests: use
pytest-benchmarkwith@pytest.mark.benchmarkonly for code with an explicit latency SLA, throughput target, or hot-path requirement. - Treat benchmark work as optional planning scope; keep the standard
pytestloop as the default when no performance requirement exists. - For
/pb-planwork, mark fuzzing asconditionalorN/Aunless the scope explicitly includes parser-like, protocol, binary-decoding, hostile-input, or native-extension-heavy code. - Add tests for behavioral changes and public API changes.
- Use
pytestfixtures for setup/teardown; avoidsetUp/tearDownmethods. - Use
pytest.raisesfor exception testing;pytest.approxfor floating-point comparisons.
- Documentation, comments, and commit messages must be English only.