4.9 KiB
4.9 KiB
name, description
| name | description |
|---|---|
| python-practices | Use when writing, reviewing, refactoring, or debugging Python code in this repository, including style, typing, error handling, async work, tests, packaging, security, and verification. |
Python Practices
Core Rule
Write Python that is boring to maintain: explicit, typed, small, tested, and easy to inspect in production. Prefer this repository's existing conventions over generic advice. When a library, framework, SDK, CLI, or cloud-service detail matters, fetch current docs with Context7; use Tavily for broader ecosystem practice research.
Source Priority
- Project files:
AGENTS.md,pyproject.toml, existingcpv3/code, tests. - Current official docs and PEPs.
- High-quality community research for trade-offs, checked against project needs.
- Memory or intuition, only after the above.
Readability
- Keep functions small enough to read in one pass. Extract helpers when branching, parsing, or side effects start competing for attention.
- Use intention-revealing names:
snake_casefor functions and variables,CapWordsfor classes,UPPER_SNAKE_CASEfor constants. - Prefer direct control flow, early returns, and clear condition names over nested cleverness.
- Avoid ad hoc string parsing when Python, Pydantic, SQLAlchemy, JSON, pathlib, or another structured API can express the operation.
- Comments should explain non-obvious reasons, not repeat the code.
Types And Data
- Annotate public functions, service/repository boundaries, async helpers, and values that cross module boundaries.
- Prefer precise built-in generics such as
list[str],dict[str, Any], andtuple[int, int]; avoid baredict,list, orAnyunless the shape is genuinely unknown. - Use Pydantic schemas for API and validation boundaries. Use dataclasses or small typed objects for internal value bundles when Pydantic validation is not needed.
- Do not mutate default containers. Use
Noneplus initialization or a factory. - Convert external data at the boundary, then keep internal code working with typed values instead of raw payload dictionaries.
Errors And Logging
- Catch specific exceptions. Avoid bare
except, silent fallbacks, and swallowing errors without observability. - Preserve context with exception chaining:
raise DomainError(...) from exc. - Define reusable user-facing or domain error strings as
ERROR_...constants. - Use
logging.getLogger(__name__)for diagnostics. Do not useprintin application code. - Error messages should help the next maintainer find the failing input, dependency, or state without leaking secrets.
Async And Resources
- Do not block the event loop. Use async APIs, subprocess APIs, or
anyio.to_thread.run_syncfor sync I/O and CPU-bound library calls. - Use context managers for files, sessions, locks, temporary resources, and network clients. Cleanup must be visible in normal and exceptional paths.
- Add timeouts around network, subprocess, and external-service operations when the called API supports them.
- Do not share mutable state across concurrent tasks unless access is synchronized.
- For this backend, do not share one SQLAlchemy
AsyncSessionacross concurrent tasks; use a session per task/request.
Tests
- Test behavior, not implementation details. Name tests after the observable rule.
- Prefer deterministic fixtures, explicit inputs, and parametrized edge cases.
- Cover failure paths for parsing, permissions, missing data, external services, and persistence boundaries.
- Use unit tests for pure helpers and service/repository rules. Use integration tests for endpoint contracts and dependency overrides.
- For bug fixes, write or update the failing test first when practical, then make the smallest code change that proves the fix.
Security And Configuration
- Keep secrets in environment-backed settings, never source files, tests, fixtures, generated docs, or logs.
- Validate paths, object keys, URLs, filenames, and user-controlled identifiers at the boundary.
- Prefer allowlists for modes, providers, file types, and status transitions.
- Treat serialization as a boundary: expose only fields that should leave the module or API.
Project Tooling
Use the repository's Python 3.11+ and uv workflow. Keep Ruff formatting with the
configured 100-character line length.
uv run ruff format cpv3 tests
uv run ruff check cpv3 tests
uv run pytest
Run narrower checks while iterating, then broaden when the change touches shared behavior. If verification cannot run, record the exact blocker.
Project Overlay
- FastAPI HTTP concerns belong in
router.py. - Pydantic DTOs belong in
schemas.pyand usually inheritcpv3.common.schemas.Schema. - Business orchestration belongs in
service.py. - SQLAlchemy statements belong in
repository.py. - Database shape changes need Alembic migrations and tests.
- Do not rely on
.claude/directory contents.