Skip to content

alphabetize โ€‹

A reader who already knows the codebase carries a mental map of where things live. When sibling members within a class, an enum, a dataclass, or a function call sit in arrival order, every reader builds a different map, which slows each new reader's first read.

gives everyone the same landmarks:

SurfaceOrder
Classes in a moduleAlphabetical
Module-level constantsLeading and trailing bands, each by dependency tier
Methods in a classDunders, properties, private, public
Enum membersAlphabetical
Dataclass and Pydantic fieldsRequired before optional
Parameters and keyword argumentsAlphabetical
ImportsCanonical groups, alphabetical within each
Docstring entriesAlphabetical within each Title-case section

The rule fires on siblings whose order does not carry meaning. It leaves alone every surface where ordering is load-bearing (positional-only parameters before the / separator, enum members with explicit integer or string values, tuple-unpacking targets bound to positional results).

A class or function definition also holds its place behind any sibling it names at evaluation time (a base class, a decorator, a parameter default, a non-deferred annotation, or a class-body value), since sorting it ahead of that sibling would move the name out of scope and raise NameError on import. A run whose references form a cycle stays in source order untouched.

At module scope, a constant lifts out of arrival order into a band. One whose value reaches only imports, builtins, literals, or fellow leading constants rides a leading band directly below the imports, and one that names a function or class pools into a trailing band beneath the definitions, so a module reads as its imports, its leading constants, its definitions, then the constants derived from them. Each band alphabetizes within its dependency tier, and a constant a function reads only inside its body still hoists above it, because only an eval-time reference (a right-hand side, a decorator, a default argument, a base class, a non-deferred annotation) binds the order. A constant the rule cannot place safely (a reassigned name, an unresolved reference, a line a suppression directive or a # prose: keep marker covers) pins where the author left it.

When a function's parameters reorder,

rewrites each in-module call's positional arguments to keyword form, keyed to the parameter each bound to under the original order, so the reorder never silently rebinds a caller. Calls that forward *args, unpack **, or reach the function through a reassigned name stay as they read.

Pair with

to align the import keyword across the freshly-sorted block, with to align dataclass-field annotations after the sort, and with for the blank-line discipline around class members and the single blank line between the import groups.

Configuration โ€‹

KeyTypeDefaultMeaning
enabledbooltrueToggle the rule on or off

The ordering itself follows fixed per-construct conventions. Method groups follow the dunders-properties-privates-publics rhythm. Pydantic fields follow required-then-optional. Consecutive imports group into their canonical order (bare first, then external from, then local-package), sorted within each group, with the imports.first-party list under [imports] (see the configuration reference) naming the packages that lift into the local-package group alongside relative imports. Set alphabetize = { docstring-entries = false } to skip the docstring-entry reorder while keeping every AST-level surface sorted.

The Canonical Case โ€‹

Classes inside a module sort alphabetically, giving every reader the same first-pass landmarks.

python
class Alpha:
    pass


class Beta:
    pass


class Gamma:
    pass

More Examples โ€‹

The string-literal items inside __all__ alphabetize by string value. Detection keys on the target's simple name, because the dunder-list convention is what makes the list documentary, leaving ordinary list assignments untouched.

The string-literal items inside __slots__ alphabetize by string value, matching __all__. The tuple form parses identically through the rule's list-or-tuple branch.

A trailing # prose: keep comment on the opening { line preserves source order for that single dict. The unmarked sibling alphabetizes and partitions through the same machinery, so the contrast lands side by side from identical scrambled input.

A constant whose value touches only literals rides the leading band directly below the imports, hoisting above a function that reads it inside its body, because a body reference defers to call time and constrains no order.

A constant calling a function that reaches a class through a default argument pools beneath both the function and the class, since the call's eval-time surface threads CONFIG past build and the Widget its default names.

A constant naming a module-level function pools into the trailing band beneath the definitions, never rising above the make it names, because evaluating its right-hand side reaches that definition at once.

Arguments binding to positional-only parameters (ahead of the /) cannot take keyword form, so they stay verbatim while the keyword-eligible suffix rewrites to name=value. Positional-only parameters never reorder, leaving the pinned prefix binding correctly.

The Args: section's name: description entries reorder alphabetically by parameter name, matching how the rule treats the function signature itself.

Async compound shapes (async def, async for, async with) recurse identically to their synchronous counterparts. Each arm's imports flow through the same compound-statement recursion, so the is_async flag changes nothing about how the body sorts.

Every bare import statement at a body's top level collapses into a single alphabetized block. Blank lines the user wrote between imports are removed, so the form reads as one paragraph regardless of source arrangement.

Configured first-party imports lift into the local-package group. myapp imports join relative imports as local-package, bare before from, while unrelated packages stay in the bare and external from groups.

A custom Title-case heading whose body carries name: description entries reorders alphabetically by name, the same as the canonical Google headings. The heading shape qualifies the section, not its name.

A @dataclass-decorated class has its annotated fields alphabetized by AST shape, not decorator name. Required fields land before optional, each sub-group sorted by name. The same structural detection covers attrs, msgspec, and any hand-rolled annotated container.

Three decorators bind values into the signature positionally and so block parameter alphabetization: pytest.mark.parametrize, hypothesis.given, and click.argument. Functions carrying any of them keep their parameter order intact.

Multi-line dict literals partition entries by rendered line count. Single-line entries sort first, then a blank divider, then multi-line entries. **unpacked items pin in source position, and the partition fires recursively at every level of nesting.

Enum members alphabetize as a single group. Detection runs against the simple name of any base class, so StrEnum qualifies the same way Enum and IntEnum do.

A reordered function resolved through BindingAnalysis rewrites each of its in-module call sites independently, every positional argument keyed to its original parameter.

Fields whose default arrives through Annotated[type, Field(default=...)] or Annotated[type, Field(default_factory=...)] register as optional. A structural walk finds the nested Call carrying a default or default_factory keyword, covering Pydantic, msgspec, and attrs without naming the constructor.

Every from-import statement at a body's top level collapses into a single alphabetized block. Blank lines the user wrote between imports are removed, so the form reads as one paragraph regardless of source arrangement.

Function parameters alphabetize within two sub-groups: required (no default) before optional (has default). self and cls pin first in method lists. The fixture exercises a free function and two methods so all three pin shapes appear in one snapshot.

A scrambled block of bare, external from, and relative imports reorders into the canonical bare โ†’ external from โ†’ local-package grouping, sorting within each group and ranking relative imports by level then module.

Keyword arguments at call sites alphabetize within splat-bounded segments. A **kwargs splat carries no arg name and acts as a hard partition, so named kwargs never cross it. The fixture covers a call with no splat and one with a mid-list splat.

A class body whose statements live inside if / else arms sorts each arm under class scope. Methods inside an arm fall into the four-group ordering and annotated fields sort required-then-optional, just as they would at the class's top level.

A three-tier dependency chain reorders within each tier without crossing tier boundaries. RADIUS is tier 0, DIAMETER is tier 1, and AREA is tier 2, so two tier-0 constants swap relative to source while staying above tier 1.

A module-level run terminates at a # fmt: off block. Assigns above the block sort within their run, the suppression directive bounds its own verbatim scope, and no run forms across the bracket.

A NamedTuple body has its annotated fields alphabetized with required-then-optional. Default-bearing fields land last per Python's positional-default invariant, which the required-then-optional split already enforces.

When alphabetize reorders a function's parameters, every in-module call's positional arguments rewrite to name=value, each keyed to the parameter that argument bound to under the original order, so the call binds the same values whatever order the parameters now sit in. The keyword run lands alphabetized, leaving the rewritten call a fixed point.

Pydantic fields split into required (no default) and optional (has default) sub-groups. Each sub-group alphabetizes independently and the required group lands above the optional group regardless of source order.

Set-literal elements alphabetize because Python sets are mathematically unordered. The sort key is each element's source text, so mixed-type sets sort by representation. Star unpacks (*defaults) pin in their source slot to preserve the visual blend.

Reordering a multi-line dict keeps every entry's trailing # comment, with the separator , placed after the value and before the comment. Values carrying their own #, such as hex color strings, stay intact because the comma resolves against the value span rather than a raw # scan.

A TypedDict body has its annotated fields alphabetized. Detection runs against the AST shape rather than the base-class name, so class T(TypedDict) and a hand-rolled annotated class would both fire identically.

A Raises: entry whose description carries an indented code block keeps the verbatim block attached to its parent through the reorder. The block sits beneath the entry's hanging column, so the walker reads it as a continuation rather than a sibling.

No Change

Alpha names Beta as a base class, so the alphabetical sort holds Beta ahead of Alpha instead of moving the subclass above the name it inherits, leaving the module import-safe.

No Change

Any Call on a binding's right-hand side skips the entire run. The call could carry observable side effects, so reordering would risk running them out of source order. Source order survives intact.