Python Bible (Python 3.14)
Targets: Python 3.14 · Previous line: Python 3.13 (trailing section) · Tooling: uv · ruff · mypy/ty · prek · pytest
Version Policy
This skill documents one Python line: the current one. Keep it that way when
you update it.
- Main path = the current feature release. Today that is Python 3.14. When
3.15 ships, the main path moves to it in the same edit.
- Unmarked prose means the current line, and there is no marker form. Python
ships features only in a feature release, and the main path always is the
current feature release — so nothing in it can post-date the line, and a version
suffix would have nothing to qualify. (This is where the sibling
policy differs: Django adds features within a line, so it needs version
markers; Python does not.) Do not write "On Python 3.X+, ..." or "if you are on
3.13" in the main path — a conditional in the main path is the defect this rule
exists to stop.
- Older lines get one trailing section each, at the end of , and
nothing anywhere else — the reference files carry the current line only and get
no trailing section of their own. Today that is
## Previous line: Python 3.13
.
It carries the diff a reader on that version needs: what is missing, what to
use instead, and what to change on upgrade.
- Research order when refreshing.
https://docs.python.org/3/whatsnew/
and
the library reference first. Then the sources already cited under Canonical
Sources. Blogs and newsletters last, briefly, for what the docs do not cover.
- Rotation. Each October a new line ships: re-base unmarked prose on it,
replace the trailing section with the line being retired, drop stale markers.
Canonical Sources
Reference Files (load as needed)
| File | Covers | When to load |
|---|
references/style-and-typing.md
| Style, Pythonic idioms, type annotations, readability rules | Writing or reviewing any Python code |
references/oop-and-design.md
| OOP patterns, data models, factories, properties, design principles | Designing classes, data models, module structure |
references/testing-and-tooling.md
| Testing patterns, pytest setup, tooling (uv/ruff/mypy/prek), quality gates | Writing tests, setting up a project, CI |
Dependencies
Standalone. No dependencies on other skills.
When used alongside ac-django, provides generic Python guidelines that complement Django-specific patterns.
Trigger QA (Release Gate)
Before shipping skill changes, validate activation behavior with sample prompts:
- Should trigger:
- "Write a CLI script that processes CSV files."
- "Review this Python class for design issues."
- "Set up testing for this Python microservice."
- Should NOT trigger:
- "Add a Django model field and migration."
- "Set up git worktrees for a ticket."
- "Implement project delivery workflow."
If behavior under-triggers or over-triggers, tighten
cues before release.
Example: Adding a new data model
User says: "Add a
data model with validation"
- Load
references/oop-and-design.md
for data model patterns
- Load
references/style-and-typing.md
for typing conventions
- Use a dataclass or attrs/pydantic if the project already uses them
- Add full type annotations, use for derived attributes
- Name the factory
build_loan_application(...)
if needed in tests
- Mirror the module path in for the test file
Prime Directives
Pythonic first
- Prefer the idiomatic Python solution over a generic one.
- Use list comprehensions, walrus operator (), , where they clarify intent.
- Avoid intermediate variables that are only used once.
- Prefer t-strings (, PEP 750) over f-strings when building SQL, HTML, shell commands, or templates from interpolated values. A t-string evaluates to a
string.templatelib.Template
, not a : the static text and each stay separable, so a processing function can escape every interpolated value for its target grammar. The safety is in that function — the stdlib ships none, so use a library's or write one. A passed where a is expected is an error, not an escape.
- Use the stdlib for zstd rather than the third-party package. , , and are the preferred import names for those modules too; the originals still work and are not deprecated. , and read and write zstd archives.
- Omit the brackets in a multi-type when there is no clause (PEP 758):
except TimeoutError, ConnectionRefusedError:
. With , the brackets are still required.
- Never , , or out of a block — it swallows the in-flight exception, and the compiler emits a for it (PEP 765).
Types are documentation
- Full, modern annotations everywhere: , , .
- Use the statement for recurring complex types.
- No duck-typing: if you are checking for attribute existence before acting, the type hint is too broad — narrow it.
No docstrings — names are the docs
- Use short, expressive function and variable names.
- Comment ONLY the non-obvious WHY. Never restate the code; never write a signature-echo docstring.
- A long comment is a code smell — refactor or rename instead of explaining. Multi-line comments are legit only for a genuine non-obvious why, never to narrate the code.
- Use vertical whitespace to group related lines.
Clean imports
- All statements at the top of the file.
- No function-level imports, no guards. (Repo-specific conventions may relax this for established circular-import patterns.)
- Assume all dependencies are installed.
No dirty hacks
- Pick the straightforward solution. If it doesn't exist, reconsider the design.
- Do not fight third-party frameworks — follow their intended patterns.
Review Checklist
Style
Typing
Imports
OOP / Design
Best Practices
Architectural Health (Module-Level)
Apply to full files touched by the diff, not just changed lines:
Testing
Integration-First Testing (Non-Negotiable)
Write integration tests for happy paths, unit tests for edge cases. This maximizes confidence per line of test code.
- Integration tests exercise multiple modules together with real (or near-real) I/O. They catch the bugs that matter most: broken contracts between modules, wrong assumptions about state, ordering issues. Mock only what you cannot control (network, external processes, databases).
- Unit tests target specific functions in isolation — but only when the function has edge cases worth covering (error handling, boundary values, complex branching). Don't unit-test trivial glue code.
- Don't duplicate coverage. If a happy path is already covered by an integration test, don't write a unit test for the same path. Unit tests should cover what integration tests cannot reach efficiently (rare error branches, corner cases).
- E2E tests (browser, full-stack) are separate — they belong in frontend/application test suites, not in library or infrastructure code. Write them when testing user-visible behavior end-to-end.
Test Conciseness (Non-Negotiable)
Tests must be easy to read and maintain. Verbose tests get skimmed, misunderstood, and copy-pasted without thought.
- One assertion per concept. A test method should test one behavior. Multiple assertions are fine when they verify different facets of the same behavior.
- Use fixtures for setup, not copy-paste. Repeated setup code across test methods means a missing fixture or parametrize.
- Parametrize over copy-paste. When multiple tests differ only in input/output values, use . Five test methods that differ by one string are one parametrized test.
- Name tests after the behavior, not the method.
test_returns_error_when_db_missing
beats .
- No unnecessary mocks. Only mock what you cannot control (network, clock, external processes). Over-mocking makes tests tautological — they test the mocks, not the code.
- Flat over nested. Prefer module-level test functions or flat test classes. Deeply nested class hierarchies in tests add complexity without value.
- Prefer context managers () over . Context managers clearly isolate the scope that needs the mock. Use only when it genuinely simplifies the test (e.g., env vars, , patching attributes on fixtures that outlive the test body).
Checklist
Previous line: Python 3.13
Everything above targets Python 3.14. On 3.13:
- No t-strings. Build SQL/HTML with the target library's own parameterisation
or escaping (
cursor.execute(sql, params)
, ), never f-string
interpolation.
- No . The third-party package is the option;
// do not read or write zstd.
- Annotations are evaluated eagerly.
from __future__ import annotations
is
still needed for forward references, and there is no — use
. The main path's ruff config bans that import
(lint.flake8-tidy-imports.banned-api."__future__.annotations"
in
references/testing-and-tooling.md
); drop
that one entry while the project targets 3.13, and restore it on 3.14.
- needs brackets for multiple types:
except (TimeoutError, ConnectionRefusedError):
.
- Free-threading is experimental, not officially supported, and carries a much
larger single-threaded penalty than 3.14's ~5–10%.
- Pin / to in the mypy/ty config while
the project targets it.