- Directories: Use snake_case for top-level features or modules (e.g.,
data_pipeline,user_auth). - Files: Use snake_case.py; name for contents (e.g.,
http_client.py,task_queue.py). - Classes: Use PascalCase.
- Variables & Functions: Use snake_case.
- Constants: Use UPPER_SNAKE_CASE for module-level constants.
- Private/Internal: Prefix with a single underscore (
_) for non-exported helpers or internal APIs.
- Use typing everywhere. Enable and maintain full static type coverage. Use Pyright for type-checking.
- Use
TypedDictorDataclassfor structured data where appropriate. For internal-only usage, prefer@dataclass(slots=True). - Avoid
Any. UseUnknown, generics, orcast()when necessary—always document whyAnyis acceptable if used. - Be explicit with returns. Use
-> None,-> str, etc., for all public functions and class methods.- Favour immutability. Prefer tuples to lists, and
frozendictortypes.MappingProxyTypewhere appropriate.
- Favour immutability. Prefer tuples to lists, and
- Enable Ruff. Use Ruff to lint for performance, security, consistency, and style issues. Enable fixers and formatters.
- Use
pyproject.tomlto configure tools like Ruff, Pyright, and Pytest. - Enforce
strictin Pyright. Treat all Pyright warnings as CI errors. Use# pyright: ignoresparingly and with explanation. - Avoid side effects at import time. Modules should not modify global state or perform actions on import.
- Use
.envor settings modules for environment-specific configuration. Never hardcode secrets.
- Use Ruff for linting (replacing flake8, isort, pyflakes, etc.).
- Use Ruff for formatting. Let Ruff handle whitespace and formatting entirely—don't fight it.
- Use docstrings. Document public functions, classes, and modules using NumPy format. For example:
def scale(values: list[float], factor: float) -> list[float]:
"""
Scale a list of numbers by a given factor.
Parameters
----------
values : list of float
The list of numeric values to scale.
factor : float
The multiplier to apply to each value.
Returns
-------
list of float
The scaled numeric values.
"""
return [v * factor for v in values]- Explain tricky code. Use inline comments for non-obvious logic or decisions.
- Colocate documentation. Keep README.md or
docs/near reusable packages; include usage examples.
- Keep tests in the top-level
tests/tree. Do not place pytest unit tests in package module directories orunittests/subdirectories. Coverage runs through xdist-backed SlipCover support, which currently requires tests to be outside the import package tree.
tests/
user_auth/
test_models.py
test_login_flow.py
-
Structure integration tests separately. When tests span multiple components, use
tests/integration/:tests/ integration/ test_login_flow.py test_user_onboarding.py -
Use
pytestidioms. Prefer fixtures over setup/teardown methods. Parametrize broadly. Avoid unnecessary mocks. -
Group related tests using
classwith method names prefixed bytest_. -
Write tests from a user's perspective. Test public behaviour, not internals.
-
Avoid mocking too much. Prefer test doubles only for external services or non-deterministic behaviours.
# login_flow.py
def login_user(username: str, password: str) -> bool:
"""Return True if the user is authenticated."""
...
# login_flow_test.py
def test_login_success():
assert login_user("alice", "correct-password") is True
def test_login_failure():
assert not login_user("alice", "wrong-password")This style guide aims to foster clean, consistent, and maintainable Python 3.13 code with modern tooling. The priority is correctness, clarity, and developer empathy.