python
Python
House conventions for Python 3.11+. Apply them to code you are writing or changing — don't refactor untouched files to match unless asked.
Conventions
-
Type every public signature and keep
mypy --strictgreen. Types on internals are optional; types at the boundary are what stop a caller passing the wrong thing. -
Built-in generics and
X | None—list[str],dict[str, int],str | None.typing.ListandOptional[str]are the pre-3.10 spelling and only cost an import. -
Protocolover ABC inheritance. Structural typing lets any correctly-shaped object satisfy the contract — including a test double — with no inheritance tree to maintain. -
Dataclasses for data,
slots=Trueandfrozen=Truewhere they fit. They generate__init__,__repr__, and__eq__correctly; a hand-written__init__is where field drift starts. Pydantic is for validation and (de)serialisation at a boundary, not for plain records. -
pathlib, notos.path. Operator joins can't silently produce a wrong path from a stray separator. -
Never a mutable default argument.
def f(items=[])shares one list across every call — a bug that only appears on the second call. Default toNoneand build inside. -
asyncio.TaskGroupover baregather(3.11+): it cancels siblings on failure and reports viaExceptionGroup, so a crashed task can't leave the rest running detached. Useasync with asyncio.timeout(n)for deadlines. -
Hold a reference to every
create_task. The event loop only keeps a weak reference, so a fire-and-forget task can be garbage-collected mid-execution and simply vanish — no error, no result. Keep them in asetand discard on completion:_tasks: set[asyncio.Task[None]] = set()