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, 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
Methods in a classDunders, properties, private, public
Enum membersAlphabetical
Pydantic BaseModel and TypedDict fieldsRequired before optional
Dataclass and NamedTuple fieldsSource order held
Parameters and keyword argumentsKeyword-only and call keywords alphabetical, positional held
Dict literal keysSingle-line entries before multi-line, alphabetical within each
ImportsAlphabetical within each section
Docstring entriesParameter entries mirror the signature, all else alphabetical

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.

Inside a class body, the constants (bare assignments and ClassVar-annotated values) and the annotated data fields tier through one shared graph, so a constant a method default, base class, or decorator reads stays above the definition reading it, and each ClassVar value sorts among the constants while every other annotation holds its place on the required-before-optional field run. Where the class header generates a positional constructor the field run holds entirely, leaving the constants around it to sort on their own. Where honoring a reference would strand a reader, the constrained members keep source order.

A recognized section marker splits a sort run into sections that each alphabetize on their own while the marker holds its place, so an author who groups members under a divider keeps that grouping through the sort. A hand-drawn banner (an own-line comment whose body is a run of repeated rule characters around an optional label, # --- Lifecycle ---) and a ## hash heading both read as dividers, across a class body, a module, and an import group alike. An ordinary prose comment is not a divider, so it stays attached to the member directly below it and travels with that member through the sort.

never reorders a function's positional-or-keyword parameters, free function and method alike, because a parameter's slot is part of the call contract and a single-file formatter cannot reach the callers a reorder would rebind (another module's positional call, a framework invoking a hook by slot, a dynamically-dispatched method). The keyword-only block past the * still sorts, since a keyword-only parameter binds by name at every call site. The companion lint reports any positional run that sits out of order, where a reorder is at least worth a human's read of the callers.

The same contract governs a class whose header generates its constructor. A NamedTuple or msgspec.Struct base, and a @dataclass, attrs, or pydantic.dataclasses decorator, each turn the annotated field run into that constructor's positional-or-keyword parameters, so the run holds its source order and a call like Window(1920, 1080) keeps binding what its author wrote. A kw_only=True argument on the generator, and the block below a dataclasses.KW_ONLY sentinel, bind by name at every call site, so those fields sort as any other. A TypedDict or a pydantic.BaseModel accepts no positional field argument at all, leaving its fields sorting throughout, and a class constant is never a constructor parameter, so constants keep sorting around a held run. A held run that sits out of order still draws

, the same report the rule files against a signature.

At a call site, keyword arguments already in name=value form alphabetize, on any callee including a method, because their order never affects which parameter each binds. Positional arguments hold their slot, since naming them would require resolving the callee's signature, which Prose does only for a plain in-module function and never for a method.

A dict literal's keys sort by default, because most dicts are lookup tables nobody iterates and the sort gives them the same landmarks every other surface gets. Dict insertion order has been a language guarantee since Python 3.7 though, observable through iteration, .items(), and ** expansion, so a dict that feeds a rendered table or a serialized payload changes what a program outputs once its keys move. Setting sort-dict-keys = false holds every dict in its authored order across a project, whereas # prose: keep holds the one literal that matters, the facet setting the default and the directive overriding a single site. Set literals sort on regardless, since a set carries no observable order to disturb.

A docstring entry naming a parameter of the signature it documents takes that parameter's position as the rule leaves the signature, which for the positional run is source order and for the keyword-only block is sorted order. An entry naming nothing in the signature (a parameter renamed or removed since the docs were written) sinks below the mirrored entries, stragglers alphabetizing among themselves. A section with no parameter entries (Raises:, Returns:) alphabetizes throughout.

Pair with

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

Configuration ​

KeyTypeDefaultMeaning
enabledbooltrueToggles the rule on or off.
group-methodsbooltrueGroups methods into dunders, properties, privates, and publics before sorting within each group. false sorts methods by plain name alone.
sort-definitionsbooltrueReorders class and function definitions alphabetically, holding each behind any sibling it names at evaluation time. false freezes definitions in source order while other surfaces still sort.
sort-dict-keysbooltrueReorders the keyed entries of a dict literal, single-line entries before multi-line and alphabetical by key within each. false keeps the authored order, which iteration, .items(), and ** expansion all observe.
sort-docstring-entriesbooltrueReorders name: description entries within Title-case-headed docstring sections, parameter entries mirroring the signature as the rule leaves it and stragglers alphabetizing below. false keeps narrative-curated entry order while still sorting every other surface.
sort-dunder-listsbooltrueReorders the string items inside __all__ and __slots__. false keeps a hand-curated public-API order.

The ordering itself follows fixed per-construct conventions. Method groups follow the dunders-properties-privates-publics rhythm. Pydantic fields follow required-then-optional.

partitions consecutive imports into their canonical sections (bare first, then external from, then local-package) and sorts the names within each, the imports.first-party list under [imports] (see the configuration reference) naming the packages that lift into the local-package section alongside relative imports. Each sort pass also switches off on its own through the facets above, so a project can keep its methods grouped while leaving its definitions in source order, or hold a hand-curated __all__ while every other surface still sorts.

The Canonical Case ​

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

class Alpha:
    pass


class Beta:
    pass


class Gamma:
    pass
python

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.

@dataclass(kw_only=True) generates an __init__ that accepts no positional field arguments, so every call site binds by name and the field run sorts freely. Only a literal True unlocks the sort, leaving a computed value pinned.

A _: KW_ONLY pseudo-field marks where a dataclass stops binding by position, so the fields above it hold their slots while the fields below it sort. The sentinel itself stays put, keeping the boundary where the author drew it.

A hand-drawn banner divides a class body into sections, so the methods sort within each section while the # --- Lifecycle --- and # --- Helpers --- dividers hold the place the author gave them.

A banner between imports divides the run into sections, so each sorts on its own and no import crosses the divider into the group above it.

A banner at module scope divides the top-level functions into sections, each sorting on its own while the dividers stay between the clusters they separate.

ACCOUNT_TYPE_CHOICES reads REGULAR, GOLD, and PLATINUM while the class body runs, so the sort holds it below the names it reads rather than hoisting it above them. The three choices still alphabetize among themselves, and a tier-blind reorder would raise NameError at class-definition time.

RETRIES carries a ClassVar[int] annotation, which marks a constant rather than a data field, so it routes to the constant family and sorts ahead of TIMEOUT. The bare-typed fields host and port stay on the required-then-optional field run below, where every non-ClassVar annotation lands.

A ## hash heading divides a class body into sections, so the methods sort within each while the headings stay above the cluster they label.

The pin covers the class whose own header generates the constructor, so a plain class nested inside a @dataclass still sorts its fields. Each class header settles the question for its own body rather than inheriting the answer from an enclosing one.

Parameter entries mirror the signature's order even when a stale entry rides along. The entry naming a parameter the signature no longer carries (retries here) sinks below the mirrored parameters, stragglers alphabetizing among themselves.

HALF reads the annotated field width, crossing from the constant family into the field family. The two families tier through one shared graph, so the fields alphabetize among themselves while HALF holds its tier below the width it reads. A single graph replaces the blanket bail that once froze both families on any cross-family reference.

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 constant is not a constructor parameter, so the pin covers the data fields alone. Inside a @dataclass the bare and ClassVar constants still sort while the annotated fields below them hold their positional order.

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.

Three module-level functions each sit under a value-binding decorator (pytest.mark.parametrize, hypothesis.given, click.argument). The function definitions sort alphabetically among themselves, while every signature holds its positional order. The hold owes nothing to the decorator each function carries, because reordering positionals would rebind callers a single-file pass cannot see.

With sort-definitions = false, class and function definitions freeze in source order, holding back the rule's most disruptive move, while a class's data fields still sort required-before-optional.

With sort-dict-keys = false, the dict literal keeps its authored key order, which iteration and ** expansion observe. Both set literals still sort, the one nested inside a held dict value as well as the one beside it, because a set carries no observable order to disturb.

Multi-line dict literals partition entries by rendered line count, single-line entries sorting ahead of multi-line ones. A blank divider fences each multi-line entry only once two or more share the dict, so a lone multi-line entry sorts last with no divider and reads as one block with the scalars above it. **unpacked items pin in source position, and the partition fires recursively at every level of nesting.

A signature holds its parameter order, so its Args: entries reorder to mirror that source order rather than the bare alphabet. The scrambled entries settle into target, source, retries, matching the signature the reader sees.

The docstring mirror tracks the signature as the rule leaves it. The keyword-only block re-sorts in the signature, and the docstring's matching entries follow it, keeping the docs aligned with the signature a reader scans.

With sort-dunder-lists = false, the __all__ and __slots__ string lists keep their authored order, while every other surface, the method run included, still sorts.

Keyed entries sort only within their run, so "d" and "c" order ahead of **defaults and "b" and "a" order after it, neither crossing the spread.

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.

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.

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.

With group-imports = false, the imports lose their section dividers and sort as one flat block, the bare import statements ahead of the from imports and each alphabetical within.

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 pinned signature carries its docstring along. Parameter entries mirror the signature's held order, while sections naming other things (Raises: here) keep sorting alphabetically.

The positional hold covers only the positional-or-keyword sub-list. Keyword-only parameters bind by name at every call site, so they keep sorting with the required-before-optional partition, while the positional parameters ahead of the * hold their source order as they do in every function.

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.

With group-methods = false, methods sort by plain name rather than grouping into dunders, properties, privates, and publics, so a @property sorts by its name among the rest rather than grouping above the public methods.

The pin covers the annotated field run alone, so the methods of a @dataclass still sort while its fields hold their positional order. A method is never a constructor parameter, so its slot carries no call-site binding to protect.

A function's positional-or-keyword parameters hold their source order, free function and method alike, because reordering them would rebind callers a single-file pass cannot see. Only the keyword-only block past the * sorts, with its required-before-optional partition, since a keyword-only parameter binds by name at every call site. The companion

lint reports the positional run when alphabetizing it would help.

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.

An Args: section lists type-bearing entries out of signature order. The reorder reads the parenthesized type as part of the entry head and sorts each entry to its parameter's position, mapping port (int) and host (str) onto the signature by their bare names.

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

A ** merge lets a later key override an earlier one, so the keyed entries sort only within the run between two spreads. Here "b" and "a" keep their places on either side of **defaults rather than crossing it.

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

A lambda's positional-or-keyword parameters keep source order, in a class body and at module scope alike, exactly as a def's do, because reordering them would rebind callers a single-file pass cannot see. Only its keyword-only block past the * sorts, since those bind by name.

No Change

A @dataclass generates an __init__ whose parameters follow the field run in order, so every field holds its source slot. Sorting them would rebind each positional call site, which a single-file rewrite cannot reach.

No Change

A method's positional-or-keyword parameters never reorder, exactly as a free function's do not, because reordering them would rebind callers a single-file pass cannot see. The hold is universal across def and lambda at every scope, owing nothing to a method's decorators or call sites.

No Change

A NamedTuple base makes the field run the tuple's element order, so Point(1, 2) binds by slot and every field holds its source position. The y and x pair reads in the order the author chose rather than alphabetically.