Python Code Standards
This rule file summarizes the Python-specific policies for this repository.
Toolchain
- Formatting — Black: All Python code must be formatted with Black (default settings). Command:
poetry run black .
- Linting — Ruff: Python code must pass Ruff using the project configuration. Command:
poetry run ruff check . Suppressions require pre-authorization per python-suppressions.instructions.md or explicit user approval.
- Type Checking — Pyright: All Python code must be fully type-annotated and pass Pyright. Avoid
Any unless unavoidable and commented. Command: poetry run pyright
- Testing — Pytest: All tests use Pytest. New logic must have test coverage >= 90%. Command:
poetry run pytest --cov --cov-report=term-missing
Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files. Do not stop the loop until all four steps complete without errors in a single pass.
If the environment prevents running tools, stop implementation and provide a plan and proposed diffs only, clearly marked unverified.
Coding Standards
- PEP 8 naming:
snake_case for functions/methods/variables, PascalCase for classes/exceptions, CONSTANT_CASE for module constants.
- Strong typing: All public functions and methods must have full type hints for parameters and return values.
- Dataclasses: Prefer
@dataclass for value objects. Use frozen=True where appropriate. Enforce invariants in __post_init__ (dataclasses) or __init__.
- Protocols: Use
typing.Protocol or abc.ABC when multiple implementations are expected.
- Imports: Prefer absolute imports. Avoid circular dependencies.
- Error handling: Fail fast with specific exceptions. Avoid broad
except: or except Exception: without context. Reserve broad handlers for well-defined boundaries (CLI/entrypoints) with context logging.
- Assertions: Use
assert only for internal sanity checks, not for user-facing validation.
- Logging: Use the standard
logging module. No ad-hoc print statements for permanent behavior.
Python Design Rules
Small, cohesive modules
- Each class or module has one clear purpose. Avoid grab-bag utilities.
- Prefer explicit names and straightforward control flow.
- Keep the public surface area small. Internal helpers are
_prefixed or live in _internal modules.
Classes vs functions
Create a class when at least one of the following is true:
- A clear domain concept with data and behavior.
- State and invariants must travel together.
- Multiple implementations behind a common interface are expected.
- A multi-step workflow shares context across steps.
Create a standalone function when:
- The operation is pure, stateless, and simple.
- It is a small helper that does not naturally belong on a specific domain class.
- It is a simple transformation from inputs to outputs.
Keep methods small and focused. Avoid god objects that know about too many unrelated concerns. Avoid long, deeply branching functions; factor logic into smaller helpers.
Strong typing by default (Pyright-clean)
- All public functions, methods, and constructors must have complete type hints.
- Avoid
Any. If unavoidable, isolate it by wrapping untyped libraries behind small typed adapters.
- Use line-specific
# type: ignore[...] only when justified with a brief comment.
- Prefer
typing.Protocol (or abc.ABC) only when multiple implementations are expected.
Dependency seams (testability without frameworks)
Introduce the smallest seam that enables reliable testing:
- Inject collaborators via constructor parameters (preferred).
- Accept optional callables with sensible defaults for time/randomness (for example,
clock: Callable[[], datetime] = datetime.now).
- Extract boundary interactions into a tiny helper function and patch that helper in tests.
Do not introduce generic service-locator patterns or heavy dependency-injection frameworks.
Pytest Rules
- Use Pytest as the test runner.
- One behavior per test. Follow Arrange–Act–Assert structure.
- Use descriptive
test_... function names.
- Prefer behavioral assertions over implementation detail.
- Use
pytest.mark.parametrize for boundary matrices.
- Fixtures should be narrow by default (function scope unless justified).
- Mock sparingly; prefer real pure code paths. Use
monkeypatch for environment variables and module attributes.
- Patch at the import location used by the unit under test, not where a symbol originated.
- No sleeps, retries, or timing hacks.
- Organize tests to mirror code structure (for example,
tests/test_module_name.py for module_name.py).
- No external dependencies (network, databases, external processes, runtime filesystem temp files) in unit tests.
- Repository-wide line coverage must remain >= 80%.
- Any new module, class, or method must reach >= 90% coverage.
- Coverage regression on changed lines is a blocking finding.
Prohibited Behaviors
- Broad refactors across multiple modules outside the approved scope.
- Adding new dependencies without explicit user instruction.
- Reducing typing strictness to make Pyright pass.
- Weakening tests to make them pass (removing assertions, overbroad exception checks).
- Using runtime temp files or external services in unit tests.
- Claiming success without running the toolchain.
1---2name: python3description: Python-specific toolchain and coding standards.4---56# Python Code Standards78This rule file summarizes the Python-specific policies for this repository.910## Toolchain11121. **Formatting — Black**: All Python code must be formatted with Black (default settings). Command: `poetry run black .`132. **Linting — Ruff**: Python code must pass Ruff using the project configuration. Command: `poetry run ruff check .` Suppressions require pre-authorization per `python-suppressions.instructions.md` or explicit user approval.143. **Type Checking — Pyright**: All Python code must be fully type-annotated and pass Pyright. Avoid `Any` unless unavoidable and commented. Command: `poetry run pyright`154. **Testing — Pytest**: All tests use Pytest. New logic must have test coverage >= 90%. Command: `poetry run pytest --cov --cov-report=term-missing`1617Run the toolchain in order: format → lint → type-check → test. Restart from step 1 if any step fails or changes files. Do not stop the loop until all four steps complete without errors in a single pass.1819If the environment prevents running tools, stop implementation and provide a plan and proposed diffs only, clearly marked **unverified**.2021## Coding Standards2223- **PEP 8 naming**: `snake_case` for functions/methods/variables, `PascalCase` for classes/exceptions, `CONSTANT_CASE` for module constants.24- **Strong typing**: All public functions and methods must have full type hints for parameters and return values.25- **Dataclasses**: Prefer `@dataclass` for value objects. Use `frozen=True` where appropriate. Enforce invariants in `__post_init__` (dataclasses) or `__init__`.26- **Protocols**: Use `typing.Protocol` or `abc.ABC` when multiple implementations are expected.27- **Imports**: Prefer absolute imports. Avoid circular dependencies.28- **Error handling**: Fail fast with specific exceptions. Avoid broad `except:` or `except Exception:` without context. Reserve broad handlers for well-defined boundaries (CLI/entrypoints) with context logging.29- **Assertions**: Use `assert` only for internal sanity checks, not for user-facing validation.30- **Logging**: Use the standard `logging` module. No ad-hoc `print` statements for permanent behavior.3132## Python Design Rules3334### Small, cohesive modules3536- Each class or module has one clear purpose. Avoid grab-bag utilities.37- Prefer explicit names and straightforward control flow.38- Keep the public surface area small. Internal helpers are `_prefixed` or live in `_internal` modules.3940### Classes vs functions4142Create a class when at least one of the following is true:4344- A clear domain concept with data and behavior.45- State and invariants must travel together.46- Multiple implementations behind a common interface are expected.47- A multi-step workflow shares context across steps.4849Create a standalone function when:5051- The operation is pure, stateless, and simple.52- It is a small helper that does not naturally belong on a specific domain class.53- It is a simple transformation from inputs to outputs.5455Keep methods small and focused. Avoid god objects that know about too many unrelated concerns. Avoid long, deeply branching functions; factor logic into smaller helpers.5657### Strong typing by default (Pyright-clean)5859- All public functions, methods, and constructors must have complete type hints.60- Avoid `Any`. If unavoidable, isolate it by wrapping untyped libraries behind small typed adapters.61- Use line-specific `# type: ignore[...]` only when justified with a brief comment.62- Prefer `typing.Protocol` (or `abc.ABC`) only when multiple implementations are expected.6364### Dependency seams (testability without frameworks)6566Introduce the smallest seam that enables reliable testing:6768- Inject collaborators via constructor parameters (preferred).69- Accept optional callables with sensible defaults for time/randomness (for example, `clock: Callable[[], datetime] = datetime.now`).70- Extract boundary interactions into a tiny helper function and patch that helper in tests.7172Do not introduce generic service-locator patterns or heavy dependency-injection frameworks.7374## Pytest Rules7576- Use **Pytest** as the test runner.77- One behavior per test. Follow Arrange–Act–Assert structure.78- Use descriptive `test_...` function names.79- Prefer behavioral assertions over implementation detail.80- Use `pytest.mark.parametrize` for boundary matrices.81- Fixtures should be narrow by default (function scope unless justified).82- Mock sparingly; prefer real pure code paths. Use `monkeypatch` for environment variables and module attributes.83- Patch at the **import location used by the unit under test**, not where a symbol originated.84- No sleeps, retries, or timing hacks.85- Organize tests to mirror code structure (for example, `tests/test_module_name.py` for `module_name.py`).86- No external dependencies (network, databases, external processes, runtime filesystem temp files) in unit tests.87- Repository-wide line coverage must remain >= 80%.88- Any new module, class, or method must reach >= 90% coverage.89- Coverage regression on changed lines is a blocking finding.9091## Prohibited Behaviors9293- Broad refactors across multiple modules outside the approved scope.94- Adding new dependencies without explicit user instruction.95- Reducing typing strictness to make Pyright pass.96- Weakening tests to make them pass (removing assertions, overbroad exception checks).97- Using runtime temp files or external services in unit tests.98- Claiming success without running the toolchain.