TS-35: Python
🚧 DRAFT
This technical standard provides guidelines for writing Python code that is clear, maintainable, and consistent. It is based on PEP 8, the language’s own style guide, and Google’s Python Style Guide, supplemented with the typing-related PEPs and current community tooling conventions. Other sources are listed in the references section at the end of this document.
Terminology
Throughout this document, the following words shall have the following meanings:
- "Module" means a single
.pyfile. - "Package" means a directory of modules containing an
init.pyfile (a "regular package") or, since Python 3.3, one without (a "namespace package"). This standard’s use of "package" always means the import-time construct, never a distributable unit published to a package index — that meaning is written out in full as "distribution package" wherever it is intended. - "Dunder" means a name with a double underscore on both ends, eg.
init,repr. Short for "double underscore". - "Callable" means any object that can be invoked with
()— a function, a method, a class, or an instance implementingcall. - "Type hint" and "type annotation" are used interchangeably to mean the optional static-typing syntax introduced by PEP 484 and extended by later PEPs.
Source files
Source files are distinguished by the .py extension. Module names MUST follow the naming rules in
Naming conventions: all lower case, with underscores if it improves readability, and MUST NOT collide with a
standard library module name (eg. do not name a module json.py or types.py).
Encoding and line endings
Source files MUST be encoded using UTF-8, with no byte order mark. Python 3 assumes UTF-8 source by default; an
explicit # -- coding: utf-8 -- declaration is unnecessary and SHOULD NOT be added.
Source files MUST use Unix-style line endings (LF, \n), not Windows-style (CRLF, \r\n).
Indentation MUST use 4 spaces per level. Tab characters MUST NOT be used for indentation, and MUST NOT be mixed with
spaces — Python 3 raises a TabError if a file mixes the two inconsistently, but a mix that happens to be
consistent within one file is still a readability hazard for anyone whose editor renders tabs at a different width.
Line length
Lines SHOULD be wrapped at 88 characters, the default enforced by Black and adopted by Google’s Python Style Guide. This is a deliberate departure from PEP 8’s stricter 79-character limit: 88 characters better matches modern wide-screen editors while still comfortably supporting two files side by side, and is now the more common convention in new Python codebases. A project MAY instead adopt PEP 8’s 79-character limit if it must interoperate closely with a codebase that already enforces it, but MUST NOT mix the two limits within a single project.
Docstrings and comments SHOULD be wrapped more conservatively, at 72 characters, matching PEP 8’s recommendation for prose.
Module layout
A module’s contents MUST be laid out in the following order, from top to bottom:
- Module docstring (see Docstrings).
from futureimports, if any.- Standard library imports.
- Third-party imports.
- Local application/library imports.
- Module-level "dunder" attributes (
all,version), if any. - Module-level constants.
- Classes and functions.
- A
if name == "main":guard, if the module is also runnable as a script.
Each of the three import groups (stdlib, third-party, local) MUST be separated from the others by a single blank
line, and the imports within each group SHOULD be sorted alphabetically. isort, or an equivalent formatter,
SHOULD be used to enforce this automatically rather than maintained by hand.
"""Utilities for parsing and validating inbound webhook payloads.""" from __future__ import annotations import hashlib import json from typing import Any import requests from pydantic import BaseModel from myapp.errors import ValidationError from myapp.settings import get_settings MAX_PAYLOAD_BYTES = 1_048_576
Wildcard imports (from module import *) MUST NOT be used, except in the init.py of a package that is
deliberately re-exporting a curated public API — and even then, the re-exported names SHOULD be listed explicitly
in all rather than left implicit.
Relative imports (from . import sibling, from ..pkg import thing) MAY be used within a package, but MUST NOT
reach more than one level up (from …pkg import thing and deeper). A module that needs to reach further up the
package tree SHOULD use an absolute import instead.
Module size
A single module SHOULD NOT exceed roughly 1,000 lines. This is a soft guideline, not a hard limit — the underlying
signal is whether the module still has one clear responsibility, and a module can earn its length by covering one
cohesive area. Once a module accumulates unrelated responsibilities, it SHOULD be split along those responsibility
boundaries, usually into a package (a directory with an init.py) containing several smaller modules.
Naming conventions
Spelling
All file names, code, comments, and docstrings MUST be written in English, with American English preferred for spelling.
Modules and packages
Modules MUST be named using lower_snake_case, eg. payment_gateway.py. Short, all-lowercase names with no
underscore are also acceptable where the name is a single word, eg. errors.py.
Packages (directories containing an init.py) MUST be named using all lower case, and SHOULD avoid underscores
where a single concatenated word is readable, eg. webapi, matching the convention recommended by
PEP 8. This differs from module naming, where underscores are preferred for
multi-word names — the distinction mirrors PEP 8’s own guidance that package names should stay short.
Classes and exceptions
Classes MUST be named using UpperCamelCase, eg. PaymentGateway, UserRepository.
Class names are typically nouns or noun phrases. They SHOULD be descriptive and unambiguous, and SHOULD NOT be overly long.
Exception classes MUST be named using UpperCamelCase and MUST end with the suffix Error, eg. ValidationError,
PaymentDeclinedError — not Exception, which is too generic to be useful in a except SomeException: clause,
and not the bare noun without a suffix, which reads ambiguously against non-exception classes of the same name.
Functions, methods, and variables
Functions, methods, and variables (including local variables and instance attributes) MUST be named using
lower_snake_case, eg. send_message, compute_total, user_count.
Function and method names SHOULD, typically, be verbs or verb phrases. Variable names SHOULD, typically, be nouns or noun phrases.
A single leading underscore (_internal_helper) MUST be used to signal that a name is internal to a module or
class and not part of its public interface. This is a convention only — Python does not enforce it — but it MUST
be honored by callers: code outside the module or class MUST NOT reference a single-underscore name directly.
A double leading underscore with no trailing underscore (__really_private) invokes Python’s name-mangling within
a class body. This SHOULD be reserved for the narrow case where a subclass accidentally overriding an attribute
would break the base class — it is a mechanism for avoiding name collisions in an inheritance hierarchy, not a
stronger form of "private", and MUST NOT be used as the default way to mark internal attributes (use a single
underscore for that).
A single trailing underscore (class_, type_) MAY be used to avoid colliding with a Python keyword or a
built-in name, in preference to misspelling the identifier (klass) or shadowing the built-in.
Dunder names (init, repr, eq) are reserved for the interpreter’s own protocol methods and MUST
NOT be invented for application-level names — defining a new dunder to mean something the language does not
already give it a meaning for is confusing to any reader who has learned the standard set.
One- or two-character names SHOULD be reserved for small scopes with an obvious, conventional meaning: loop
counters (i, j), coordinates (x, y), or a comprehension’s throwaway variable (_). They SHOULD NOT be used
for anything with a lifetime longer than a few lines or a scope wider than a single function body.
Constants
Module-level constants MUST be named using UPPER_SNAKE_CASE, eg. MAX_RETRIES, DEFAULT_TIMEOUT_SECONDS.
As in TS-33's definition, a "constant" here means a module-level value that is set once, at
import time, and never reassigned. Python has no const keyword, so this is a naming convention only, signaling
intent to the reader rather than an enforced guarantee.
MAX_RETRIES = 3
DEFAULT_TIMEOUT_SECONDS = 30.0
SUPPORTED_CURRENCIES = frozenset({"USD", "EUR", "GBP"})Type variable names
Type variables, declared with typing.TypeVar or the class Foo[T] / def foo[T] generic syntax (Python 3.12+),
SHOULD be named with a single capital letter, optionally followed by a digit, eg. T, KT, VT, following the
convention established by the typing module itself.
Code style
Automated formatting
Code formatting — indentation, blank lines, line wrapping, quote-character choice, trailing commas — MUST be delegated to an autoformatter, run in CI and as a pre-commit hook, rather than maintained by hand or enforced through code review. Black or Ruff’s formatter (which is Black-compatible) are RECOMMENDED. This standard’s line-length and blank-line rules describe what such a formatter already does by default; they are stated here so the standard is self-contained, not as an instruction to apply them manually.
A project MUST pick one formatter and apply it uniformly. Reformatting the entire codebase in one commit when
adopting a formatter, isolated from any functional change, is RECOMMENDED, so that git blame is not muddied by
mixing the reformat with unrelated diffs.
Blank lines
Two blank lines MUST separate top-level function and class definitions. One blank line MUST separate method definitions within a class. A single blank line MAY be used sparingly within a function body to group related statements.
import os
class PaymentGateway:
def __init__(self, api_key: str) -> None:
self.api_key = api_key
def charge(self, amount: int) -> None:
...
def main() -> None:
...Whitespace
A single space MUST surround binary operators (x = a + b, not x=a+b), with the exception of keyword arguments
and default parameter values in a function signature, which MUST NOT be surrounded by spaces when unannotated
(def f(x=1):), but MUST be surrounded by spaces when the parameter carries a type annotation
(def f(x: int = 1):) — following PEP 8’s rule that the = here is read as part of the annotated declaration,
not a bare assignment.
No space MUST appear immediately inside parentheses, brackets, or braces (spam(ham[1], {eggs: 2}), not
spam( ham[ 1 ], { eggs: 2 } )), and no space MUST appear before a comma, semicolon, or colon, but a space MUST
follow one (except at the end of a line).
Trailing commas
A trailing comma SHOULD be included after the last element of a multi-line list, tuple, dict, set, or function call/signature. This keeps the diff to a single line when a further element is added later, and is what Black inserts automatically in "magic trailing comma" mode.
SUPPORTED_REGIONS = [
"us-east-1",
"eu-west-1",
"ap-southeast-2",
]A single-line collection or call MUST NOT carry a trailing comma before the closing bracket, except the
one-element tuple, where the trailing comma is mandatory syntax ((value,)), not a style choice.
String quotes
Single and double quotes are equally acceptable for string literals; PEP 8 takes no position, and this standard does not either — a project SHOULD adopt whichever its autoformatter enforces (Black defaults to double quotes) and apply it consistently, rather than mixing both by hand. Whichever is chosen, use the other quote character to avoid backslash-escaping a quote that appears inside the string, per PEP 8.
Triple double-quotes (""") MUST be used for docstrings, regardless of which quote style is chosen for ordinary
string literals — see Docstrings.
f-strings (f"{value}") SHOULD be preferred over %-formatting or .format() for constructing strings that
interpolate values, for readability and because they are evaluated at the point of use rather than requiring
positional or keyword argument matching.
Comparisons
Singletons (None, True, False) MUST be compared with is / is not, never == / !=.
# ✓
if value is None:
...
# ✗
if value == None:
...Emptiness of a sequence, string, or collection SHOULD be tested with an implicit boolean check (if not items:),
not an explicit length comparison (if len(items) == 0:), unless the distinction between "empty" and "falsy for
another reason" genuinely matters for the value in question — for example, a value that could be either None or
0, where collapsing both to "falsy" would be a bug.
Type checks SHOULD use isinstance(), not a direct comparison of type(), because isinstance() respects
subclassing.
# ✓
if isinstance(value, int):
...
# ✗
if type(value) == int:
...Programming constructs
EAFP over LBYL
Python idiom generally prefers "Easier to Ask Forgiveness than Permission" (EAFP) — attempt the operation and handle the exception — over "Look Before You Leap" (LBYL) — check preconditions before attempting the operation. EAFP SHOULD be preferred where the failure case is a routine, expected outcome (a missing dictionary key, a file that may not exist, a type that may not support an operation), because it avoids a race condition between the check and the use, and because the CPython interpreter is optimized for the successful path.
# ✓ EAFP
try:
value = mapping[key]
except KeyError:
value = default
# ✗ LBYL — has a race, and reads worse
if key in mapping:
value = mapping[key]
else:
value = defaultLBYL MAY still be preferred where the check is cheap, the failure path would be expensive or side-effecting to trigger deliberately, or where an explicit guard clause reads more clearly than a broad exception handler.
Comprehensions and generator expressions
A list, dict, or set comprehension SHOULD be preferred over an equivalent for loop with .append() when the
body is a single, simple expression.
# ✓
squares = [n * n for n in range(10)]
# ✗ — same result, more ceremony
squares = []
for n in range(10):
squares.append(n * n)A comprehension SHOULD be abandoned in favor of an explicit for loop once it needs a second for clause beyond
simple flattening, a non-trivial if condition, or any side effect — a comprehension with a side effect (calling
a function for its effect rather than its return value) is a code smell, since the construct exists to build a
collection, not to execute statements.
A generator expression ((x for x in iterable), no brackets) SHOULD be preferred over a list comprehension when
the result will only be iterated once and does not need to be indexed, reused, or measured with len() — it
avoids materializing the whole collection in memory.
Context managers
Anything that acquires a resource requiring explicit release — a file handle, a network connection, a lock, a
database transaction — MUST be managed with a with statement, not a manual acquire()/release() or
open()/close() pair. The with statement guarantees the resource is released even if the body raises.
# ✓
with open(path, encoding="utf-8") as f:
contents = f.read()
# ✗ — leaks the handle if read() raises
f = open(path, encoding="utf-8")
contents = f.read()
f.close()A class that manages its own resource SHOULD implement the context manager protocol (enter/exit)
rather than exposing separate open/close-style methods, so its callers get the same with-statement guarantee.
contextlib.contextmanager SHOULD be used to implement a simple context manager as a generator function, in
preference to hand-writing a class with enter/exit when no other state needs to live on the object.
Mutable default arguments
A mutable object (a list, dict, or set) MUST NOT be used as a default argument value. Default argument values are evaluated once, at function definition time, not on each call — so a mutable default is silently shared and mutated across every call that relies on it, which is very rarely the intended behavior.
# ✗ — the same list is reused and grows across calls
def add_item(item, items=[]):
items.append(item)
return items
# ✓
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return itemsUnpacking
Tuple/iterable unpacking SHOULD be used in preference to indexed access when assigning several related values at once, including the starred form for "the rest of the values".
first, *middle, last = values
Swapping two variables SHOULD use tuple unpacking (a, b = b, a), not a
temporary variable.
Loops
enumerate() SHOULD be used instead of manually tracking an index with
range(len(…)).
# ✓
for index, item in enumerate(items):
...
# ✗
for index in range(len(items)):
item = items[index]
...zip() SHOULD be used to iterate over two or more sequences in lockstep, instead of indexing each by a shared
counter.
A for/else or while/else construct MAY be used where its semantics (the else block runs only if the loop
completed without break) genuinely fit the logic, but authors SHOULD add a short comment noting that the else
is loop-else, not the more commonly expected if-else — this construct is unfamiliar enough that an unmarked
use is a readability tax on the reader.
Error handling
Exception handlers MUST catch the narrowest exception type that is actually expected, never a bare except: and,
except at a genuine top-level boundary (a request handler, a task runner) that must not crash regardless of cause,
not a bare except Exception: either.
Custom exceptions SHOULD be defined for a library or module’s own error conditions, subclassing the most specific
applicable built-in exception (or Exception directly if none fits), so callers can distinguish this code’s
failures from unrelated ones.
Where an exception is re-raised after partial handling — logging it, adding context, or translating it to a
different type — raise … from err SHOULD be used to preserve the original traceback as the new exception’s
cause, rather than losing it or, worse, swallowing it with a bare raise NewError() that discards where the
failure actually originated.
try:
parse(payload)
except ValueError as err:
raise ValidationError("invalid payload") from errTypes and typing
Type hints
Every public function, method, and module-level variable MUST carry type hints for its parameters and return value, per PEP 484 and PEP 526. Private (single-underscore) helpers SHOULD also be annotated, but the requirement is stricter for public interfaces because they are the contract other code depends on.
def calculate_total(items: list[Item], tax_rate: float) -> Decimal:
...The builtin generic syntax (list[Item], dict[str, int]) MUST be used instead of the deprecated typing.List,
typing.Dict, etc., which are retained only for backward compatibility with Python versions predating
PEP 585 (3.9). A project supporting an older Python version MUST use the
typing equivalents, or from future import annotations to defer evaluation and use the builtin syntax
anyway — see Deferred evaluation of annotations.
A value that may be absent MUST be annotated as X | None (Python 3.10+, per
PEP 604) or the equivalent Optional[X] on earlier versions — never left as a
bare X with an implicit None default silently tolerated by the caller. A function returning X | None signals
to every caller that the None case MUST be handled.
Any SHOULD be avoided. It disables type checking for the value it is applied to and for everything derived from
it, so its use SHOULD be confined to genuine boundary cases — deserializing untrusted external data before it has
been validated into a concrete shape, or interoperating with an untyped third-party library — and MUST NOT be used
merely to silence a type checker error that reflects a real type mismatch.
Static type checking
A static type checker — mypy or pyright — SHOULD run in CI against the whole codebase, not merely on a subset of files, and its failures SHOULD block the build. Where a codebase is being migrated to typed code incrementally, the checker MAY be configured to only enforce strict checking on already-annotated modules, with a plan to widen that scope over time, rather than left permanently partial.
Type-checking suppressions (# type: ignore) MUST carry the specific error code being suppressed
(# type: ignore[assignment]) and a brief comment explaining why, so the suppression doesn’t silently swallow an
unrelated future error in the same line.
Deferred evaluation of annotations
from future import annotations (per PEP 563) SHOULD be added at the top
of a module that uses type hints, so that annotations are stored as strings and evaluated lazily rather than at
function-definition time. This allows the newer X | None union syntax and builtin generic syntax to be used even
on Python versions that would otherwise require typing.Optional and typing.List, and avoids a class needing to
forward-reference its own name in a method signature.
Structured data
A dataclass (@dataclasses.dataclass) SHOULD be used, rather than a bare dict, tuple, or hand-written class with
a manual init, whenever a piece of code needs a record type with named fields.
from dataclasses import dataclass
@dataclass(frozen=True)
class Money:
amount: int
currency: strfrozen=True SHOULD be set unless the dataclass genuinely needs to be mutated after construction — an immutable
record is easier to reason about and safe to share across threads.
typing.NamedTuple MAY be preferred over a dataclass where tuple semantics (unpacking, positional access,
hashability by default) are actually wanted; a plain, untyped tuple or collections.namedtuple SHOULD NOT be
used for a new record type where either of the typed alternatives is available.
A TypedDict SHOULD be used, rather than an untyped dict[str, Any], to describe the shape of dictionary data
with a fixed, known set of string keys — most commonly, JSON payloads deserialized from an external API.
Protocols (typing.Protocol) SHOULD be used to define a structural interface — "anything with this method
signature" — in preference to an abstract base class, when the point is to accept any object with the right shape
rather than to share implementation via inheritance.
Documentation and comments
Docstrings
Every public module, class, function, and method MUST have a docstring, per PEP 257. A private (single-underscore) function SHOULD have one if its behavior is not obvious from its name and signature alone.
Docstrings MUST use triple double-quotes ("""), even for a one-line docstring, for consistency and because
triple-quoting is required as soon as the docstring spans multiple lines.
A one-line docstring MUST fit on a single line, phrased as a command ("Return the total.", not "Returns the
total." or "This function returns the total."), and the closing """ MUST sit on the same line as the opening
one.
def is_valid(value: str) -> bool:
"""Return True if value is a syntactically valid account ID."""
...A multi-line docstring MUST have a one-line summary on the first line, followed by a blank line, followed by
further detail. The closing """ MUST sit on its own line.
This standard adopts
Google-style
docstring sections — Args:, Returns:, Raises: — over the alternative reStructuredText (:param:,
Sphinx-native) or NumPy styles, because they read cleanly as plain text even before any documentation
generator processes them.
def charge(account_id: str, amount: Decimal) -> Receipt:
"""Charge the given account and return a receipt.
Args:
account_id: The account to charge, in "acct_<id>" form.
amount: The amount to charge, in the account's home currency.
Returns:
A Receipt recording the transaction ID and settled amount.
Raises:
InsufficientFundsError: If the account balance is too low.
AccountNotFoundError: If account_id does not exist.
"""
...A class’s docstring MUST describe the class’s purpose and, where it carries public attributes not already
documented via dataclass field types or property docstrings, MUST document them under an Attributes:
section following the same style as Args:.
A module’s docstring — the first statement in the file, before any import — MUST summarize what the module contains, in enough detail that a reader can decide whether to open it without doing so.
Comments
Comments MUST explain why the code does something non-obvious, not what the code does when that is already clear from reading it. A comment that merely restates the following line in English adds noise, not information.
# ✓ — explains a non-obvious constraint retries = 3 # the upstream gateway rate-limits after 3 attempts/second # ✗ — restates the code retries = 3 # set retries to 3
Inline comments (on the same line as code) SHOULD be used sparingly, separated from the code by at least two spaces, and SHOULD be reserved for a short clarification that doesn’t warrant its own line.
Block comments SHOULD be complete sentences, with the first word capitalized (unless it is an identifier that begins with a lowercase letter), and SHOULD be kept up to date — a comment that describes behavior the code no longer has is worse than no comment, since it actively misleads the next reader.
# TODO(username): description SHOULD be used to mark unfinished work, following the convention recommended
by Google’s Python Style Guide, with the
username identifying who to ask for context — not necessarily who will do the work.
# TODO(kieran): remove once the legacy v1 endpoint is retired.
Commented-out code MUST NOT be committed. Version control already preserves history; a block of dead code left in a comment only accumulates and rots.
Project structure and tooling
Project metadata
Every Python project MUST declare its metadata and dependencies in
pyproject.toml, per
PEP 621 — not the legacy setup.py / setup.cfg combination, which
SHOULD only be retained where a build backend genuinely requires it (eg. a package with a compiled extension
using a build backend that has not yet adopted PEP 621).
A lock file — generated by whichever dependency manager the project uses (eg. uv.lock, poetry.lock) —
MUST be committed alongside pyproject.toml for any application (as opposed to a library intended for reuse
under a range of dependency versions), so that every environment installs identical dependency versions.
Virtual environments
Dependencies MUST be installed into an isolated virtual environment, never into the system-wide interpreter.
uv, Poetry, or the standard library’s venv module
are all acceptable; uv is RECOMMENDED for new projects for its speed and because it unifies environment
creation, dependency resolution, and locking in one tool.
A committed .python-version file (or the equivalent pin in pyproject.toml) SHOULD specify the exact
interpreter version a project targets, so that contributors and CI resolve to the same version rather than
whatever happens to be on PATH.
Package layout
A distributable library SHOULD use the "src layout" — application code under src/<package_name>/, with
tests in a sibling top-level tests/ directory — rather than a "flat layout" with the package at the
repository root. The src layout prevents the package from being accidentally importable from the repository
root without being installed, which catches packaging mistakes (a missing entry in the package manifest) that
a flat layout would hide during local development.
project/
├── pyproject.toml
├── uv.lock
├── src/
│ └── myapp/
│ ├── __init__.py
│ └── ...
└── tests/
└── ...A standalone application that is deployed rather than distributed as an installable package (eg. a service deployed as a container image) MAY use a flat layout instead, since the packaging-mistake risk the src layout guards against does not apply the same way.
Linting, formatting, and type checking
Ruff is RECOMMENDED as the project’s linter, covering the majority of what
flake8, isort, pyupgrade, and several other single-purpose tools historically covered separately, and
MAY also be used as the project’s formatter (see Automated formatting) in place of Black. Where both
Ruff’s formatter and Black are viable, either is acceptable; a project MUST NOT run both, since they can
disagree on edge cases.
Static type checking (see Static type checking) SHOULD run as a separate step from linting — mypy or pyright are not a substitute for Ruff, nor Ruff for them, since Ruff performs no type inference.
Linting, formatting (in check mode), and type checking SHOULD each run as a required CI check, and SHOULD also be available locally as pre-commit hooks so that violations are caught before a commit is pushed, not only in CI.
Testing
pytest is RECOMMENDED over the standard library’s unittest for new test suites,
for its plain assert-based assertions, fixture system, and parametrization support. A project already built
on unittest MAY continue using it — pytest can run unittest-style test cases unmodified — rather than
being forced through a disruptive rewrite.
Test files MUST be named test_<module>.py or <module>_test.py (consistently, whichever a project chooses)
so that pytest’s default discovery picks them up without additional configuration.
References
- Astral. Ruff.
- Astral. uv.
- Cannon, B (2018). PEP 563: Postponed Evaluation of Annotations.
- Goodger, D and Van Rossum, G (2001). PEP 257: Docstring Conventions.
- Google. Google Python Style Guide.
- Levkivskyi, I, Roach, S, Van Rossum, G, and Lehtosalo, J (2017). PEP 526: Syntax for Variable Annotations.
- Moss, A and Elliott, C (2019). PEP 604: Allow writing union types as X | Y.
- Python Software Foundation. src layout vs flat layout.
- Python Software Foundation. The Python Tutorial.
- Solomatin, E (2019). PEP 585: Type Hinting Generics In Standard Collections.
- Van Rossum, G, Lehtosalo, J, and Langa, Ł (2014). PEP 484: Type Hints.
- Van Rossum, G, Warsaw, B, and Coghlan, N (2001). PEP 8: Style Guide for Python Code.