Skip to content

alphabetize-siblings

alphabetize-siblings sorts sibling members whose order carries no meaning, so every reader meets the same landmarks:

ConstructOrder
Classes and functions in a moduleClasses above functions, alphabetical within each band
Methods in a classDunders, properties, private, public
Enum membersAlphabetical
Pydantic BaseModel and TypedDict fieldsRequired before optional
Dataclass and NamedTuple fieldsSource order kept
Parameters and keyword argumentsKeyword-only parameters and call keywords alphabetical, positional kept in place
Dict literal keysScalar entries before collection entries, alphabetical within each
ImportsAlphabetical within each group-imports section
Docstring entriesParameter entries follow the signature order, all else alphabetical

Order that carries meaning stays as written, covering positional-only parameters ahead of the /, enum members whose values come from auto or from a __new__ that numbers them, and tuple-unpacking targets.

At module scope the classes and the functions sort as one run with every class above every function, each band alphabetical and the function band grouped as dunder, private, public the way methods are, so a module reads its classes first and its functions below them whatever order the author interleaved them in.

A definition stays behind any sibling it names at evaluation time (a base class, a decorator, a parameter default, a non-deferred annotation, a class-body value), and a module-level statement that reads one pins its run the same way. Several other statements pin a run or fence it:

  • A module-level statement that binds a name pins the run, covering an assignment, an unpack target, a for or with target, a walrus, each alias of an import, an except ... as name, and a del. A definition naming that name then keeps the side of the binding the source put it on, where a reader below never rises above it and one above never sinks below it.
  • A module-level call reaches through to what it runs, pinning the run against the names its target reads at evaluation time, where a method call on a class reaches the whole class body, and a definition reaches the same way through the decorators, bases, metaclass, and defaults it evaluates as it binds.
  • A class whose base list runs a call or a subscript on a name the module does not itself define fences the run at its own slot. The fence holds because a metaclass and a __class_getitem__ hook both run at class creation, and a hook reached through a compiled module calls back into the module that imported it, so no static read follows where it reaches. The fence is one-sided, in that nothing written above such a class may sort below it, whereas every definition written below it was unbound when the hook ran and still sorts freely.
  • An enumeration whose members take their value from auto or from a __new__ that numbers them as it runs keeps the order its author wrote, since sorting it would change each member's value, whereas one that spells every value out still sorts.
  • A decorated definition at module scope keeps its slot outright whereas a decorated method still sorts, and a reference cycle leaves its run in source order.
  • Inside a class body the constants and the annotated fields sort through one dependency graph, so a constant a method default or base class reads stays above it.

A section marker splits a run into sections that each sort on their own while the marker keeps its place, covering a banner (# --- Lifecycle ---), the same banner drawn with its rule closing the label rather than opening it (# Lifecycle -------#), a ## heading, and a suppression directive. An ordinary comment is no divider and travels with the member below it, and a group that packs several members onto one row keeps its order across a comment, because its members swap in place with every gap kept as written and the comment would stay put while they moved past it.

Positional-or-keyword parameters never reorder, since a slot is part of the call contract, whereas the keyword-only block past the * sorts, and unsorted-positionals reports a run out of order. A class whose header generates its constructor follows the same contract, so a NamedTuple or msgspec.Struct base and a @dataclass, attrs, or pydantic.dataclasses decorator each pin the field run, whereas kw_only=True, a dataclasses.KW_ONLY block, a TypedDict, and a pydantic.BaseModel sort throughout.

At a call site, keyword arguments in name=value form sort on any callee while positional arguments keep their slots. Dict keys sort by default, and because insertion order is observable through iteration, .items(), and ** expansion, sort-dict-keys = false keeps every dict in a project as written and # prose: keep keeps one literal. The same marker keeps one __all__ or __slots__ where sort-dunder-lists = false keeps them all. In both a call and a dict, an entry whose value runs code (a call, a comprehension, an await) keeps its slot, and set literals sort regardless.

A docstring entry naming a parameter takes that parameter's position as the rule leaves the signature, and an entry naming nothing in the signature sinks below the mirrored ones.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueTurns 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-definitionsbooltrueSorts class and function definitions alphabetically, keeping each below any sibling it names at evaluation time. false keeps definitions in source order while everything else still sorts.
sort-dict-keysbooltrueSorts the entries of a dict literal, scalar values before collection values and alphabetical by key within each. false keeps the order as written, which iteration, .items(), and ** unpacking all follow.
sort-docstring-entriesbooltrueSorts the name: description entries of a Title-case-headed docstring section, parameter entries in the signature's order as the rule leaves it and every other entry alphabetical below them. false keeps the entries in the order written while everything else still sorts.
sort-dunder-listsbooltrueSorts the string items inside __all__ and __slots__. false keeps the order written, for a hand-ordered public API.

The order itself follows fixed per-construct conventions, with method groups following the dunders, properties, privates, publics order and Pydantic fields following required then optional. group-imports moves consecutive imports into their canonical sections (a from __future__ import first, then bare, then external from, then local-package) and alphabetize-siblings sorts the names within each, with the imports.first-party list under [imports] (see the configuration reference) naming the packages that join the local-package section alongside relative imports. Each sort 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 keep a hand-curated __all__ while everything else still sorts.

The Canonical Case

Three sibling classes sit at module level with Gamma written first. The run sorts them into Alpha, Beta, Gamma, so a reader scanning the module meets its classes in name order rather than the order the file grew in.

class Alpha:
    pass


class Beta:
    pass


class Gamma:
    pass
python

More Examples

__all__ lists "render", "Posting", "aggregate", and "Catalog", and regular_list = ["b", "a", "c"] sits below it. The __all__ strings alphabetize by value, "Catalog" and "Posting" moving ahead of "aggregate" and "render" because uppercase sorts before lowercase, whereas regular_list stays untouched, because the rule keys on the assignment target's name and the __all__ convention is what marks the list as documentary.

Posting.__slots__ is the tuple ("title", "company", "date_posted"). The strings alphabetize by value to ("company", "date_posted", "title"), just as __all__ entries do, because slot order carries no meaning at runtime, and the rule accepts the tuple form the same as a list, so the parentheses change nothing.

unmarked and marked contain identical entries in the same scrambled order, and the only difference between them is the # prose: keep trailing marked's opening {. unmarked sorts, its scalar entries "name" and "version" moving ahead of the nested "config" dict, whereas marked keeps its source order untouched, which puts the two outcomes side by side from the same input.

The first __all__ and the __slots__ tuple each carry a trailing # prose: keep, one on the opening bracket's line and one on the closing bracket's line, whereas the second __all__ carries no marker and plain_list is an ordinary list. Both marked sequences keep their authored order and the unmarked __all__ sorts, showing what the marker suppressed, and plain_list keeps its order too, because the sort reaches a sequence by its assignment target rather than by its form.

RETRIES carries a ClassVar[int] annotation beside the bare constant TIMEOUT and the plainly annotated fields host and port. RETRIES moves ahead of TIMEOUT in the constant run, because ClassVar marks a class-level constant rather than a per-instance field, whereas host and port stay in the field run below, where every non-ClassVar annotation sorts.

@dataclass(kw_only=True) generates an __init__ that accepts every field by keyword only, so no call site can depend on the declared field order. The fields sort, and company and date_posted move above title. Only a literal True unpins them, because a computed kw_only value cannot be read while formatting and leaves the fields pinned.

The _: KW_ONLY pseudo-field marks where Posting stops accepting positional arguments. title and company above it keep their places, whereas date_posted and url below it sort, and the sentinel itself stays where the author drew the keyword-only boundary.

The # --- Lifecycle --- and # --- Helpers --- banners split Session's methods into two sections. The methods sort within each section and each banner stays where the author drew it, so close moves above open in the first, decode above encode in the second, and no method crosses a banner.

The # --- Typing --- banner divides four imports into two sections. Each section sorts on its own, os moving above sys and abc above typing, and no import crosses the banner, even though abc would sort ahead of everything were the banner ignored.

The # ===== Parsing ===== and # ===== Emitting ===== banners divide four top-level functions into two sections. Each section sorts on its own, lex moving above tokenize and emit above render, and each banner stays between the functions it separates, so no function crosses from one section into the other.

ACCOUNT_TYPE_CHOICES builds its tuple from REGULAR, GOLD, and PLATINUM, all read while the class body executes. It stays below the three names it reads even though it alphabetizes first, whereas the three sort among themselves into GOLD, PLATINUM, REGULAR, because moving the reader above them would raise NameError at class-definition time.

The __all__ list and the keyword run each carry a trailing comment containing a comma, # first, second and # one, two. Both sort, each comment moving with its entry, because a comma inside a comment is trivia rather than a positional member sitting between two entries.

Steps: is not one of the canonical Google headings, but its body carries name: description entries. The entries reorder alphabetically by name, emit rising to the top and validate dropping to the bottom, because the entry form of a section's body, rather than the heading's name, is what qualifies it for sorting.

visit names Node in its annotations while the class Node defining it sits below, and from __future__ import annotations defers those annotations past definition time. Node sorts above visit, because the reference never runs when the definitions execute, so it constrains nothing, and every annotation still resolves in the order the rule writes.

The ## Encoding and ## Decoding hash headings divide Codec's body into two sections. The methods sort within each section rather than across the whole class, configure and encode swapping under the first heading and assemble and decode under the second, and each heading stays above the methods it labels.

The Args: section sits in a module docstring, listing zebra before alpha. The entries sort alphabetically, alpha: moving ahead of zebra:, because a module takes no parameters, so it has no signature for the entries to mirror and no order is borrowed from anywhere else.

Outer is decorated with @dataclass, and the undecorated Inner class sits inside it. Outer's fields zeta and alpha keep their order, whereas Inner's bravo and yankee sort, because the decorator turns Outer's fields into the generated constructor's signature, so reordering them would change every call site, but the pin does not extend into a nested class carrying no such decorator.

*overrides sits between verbose=True and retries=3, two keywords out of order. The keyword texts swap in place and the *overrides row stays in its own place, because the run keeps every gap between members verbatim, whereas rebuilding the argument block from scratch would have dropped that row along with the rest of the original layout.

"alpha"'s value is one string literal written as a parenthesized implicit concatenation across two lines, beside the one-line "mike" and "zulu". The entry sorts as a scalar and moves above "mike" and "zulu" instead of sinking to the collection tail its height suggests, because the scalar-before-collection partition reads the value's AST kind rather than counting its lines.

The outer set contains two frozenset calls, {"beta", "carrot"} and {"delta", "alpha"}. Each inner set sorts its own elements first, so {"delta", "alpha"} becomes {"alpha", "delta"}, and that element then moves to the first position, where its sorted form belongs rather than second where its original spelling would have put it, because the outer set keys each element on the text the inner sort produced rather than the text the source wrote.

update(self, target, source) carries Args: entries for source, retries, and target, and retries names a parameter the signature no longer has. target: and source: rearrange to mirror the signature and retries: sinks below them, where any stale entries sort among themselves.

ValueError's description ends in :: and continues into an indented doctest block, with OSError listed after it. Sorting moves OSError above ValueError and the verbatim block moves with ValueError, because its lines sit deeper than the entry's hanging column, which marks them as a continuation of that entry rather than as sibling entries of their own.

Widget subclasses Generic[str], where Generic comes from a module this file cannot read, and subscripting it runs a hook inside that module while class Widget is being defined. zzz_helper above Widget keeps its place, whereas yyy_render and aaa_build below it sort among themselves, because a hook reached that way can call back into the module that imported it, and everything written above Widget was already bound when the hook ran, so nothing above it may sort below it. The fence works in one direction only, since every definition below Widget was still unbound at that moment, and a base naming no call and no subscript runs nothing and fences nothing.

HALF = width reads the annotated field width, a reference reaching from the constant family into the field family. height and width alphabetize among themselves and HALF stays below the width it reads, rather than the whole class body freezing, because both families order through one shared dependency graph.

Import pairs sit out of order inside an async def body, an async for body, and an async with body. Each pair sorts in place, alpha before zeta, beta before gamma, and delta before omega, because the rule reads an async body exactly as it reads the synchronous form, so the async keyword changes nothing about how a body sorts.

A blank line separates the standard-library trio zlib, argparse, and os from the aliased numpy and pandas pair. The blank line is removed and the five statements interleave into argparse, numpy, os, pandas, zlib, because every bare import at a body's top level sorts as one block regardless of how the source grouped them.

render, Widget, build, and Gadget are written with classes and functions interleaved. The classes group above the functions and each group sorts by name, so the module reads Gadget, Widget, build, render, rather than keeping the interleaving and sorting each kind where it already sat.

Posting is decorated with @dataclass, and its body contains the ClassVar-annotated RETRIES, the bare MAX_AGE, and the annotated fields title and company. MAX_AGE moves above RETRIES while title and company keep their positional order, because @dataclass builds the constructor from the annotated data fields alone, so the pin covers those two, whereas MAX_AGE and RETRIES never become constructor parameters.

The case runs under sort-definitions = false, with class Zebra above class Apple and withdraw above deposit inside Zebra. Both the classes and the methods freeze in source order, whereas Zebra's fields still sort, id: int climbing above name: str, because the key covers definitions rather than the field list, switching off the rule's most disruptive move.

Five dicts mix scalar values, list and dict values, a **defaults spread, and a load_metadata() call. Every dict partitions its entries by the kind of value they carry, scalars sorting ahead of collections however many lines each takes, so in mixed that puts "author", "name", and "version" first, with "config", "deps", "metadata", and "tags" following.

Three entries stay where they were written, **defaults in with_spread and the "metadata": load_metadata() entry in both mixed and single_line_only, because an entry whose value runs code holds its slot while the others sort around it. Blank dividers open around block entries only once a dict contains two of them, which is why "settings" separates its "logging" and "network" blocks while mixed leaves its single "config" block undivided, and the partition repeats at every nesting level, so "format" sorts below "stderr" and "stdout" inside "logging".

The case runs under sort-dict-keys = false, with COLUMNS listing "model" through "tags" and two set literals, one stored under "tags" and TAGS beside the dict. COLUMNS keeps its authored key order, the order iteration and ** expansion observe, whereas both sets still sort, because a set has no observable order to disturb.

merge(target, source, retries=3) keeps its parameter order because the parameters bind by position, and its Args: entries are scrambled. The entries reorder to target, source, retries, mirroring the signature rather than the bare alphabet, the same order the reader sees one line up.

update's keyword-only block is written retries=3, mode, and its Args: entries follow that source order. The block re-sorts to mode, retries=3, the required mode ahead of the defaulted retries, and the mode: and retries: entries under Args: swap to match, because the docstring mirrors the signature as the rule leaves it rather than as the source wrote it, keeping the docs in the same order as the signature a reader scans beside them.

The case runs under sort-dunder-lists = false, with __all__ listing "render", "Posting", "Catalog", Buffer.__slots__ listing "size" before "data", and Buffer's methods out of order. Both dunder lists keep their authored order, whereas the methods still sort, read climbing above write, because the setting covers only the dunder string lists.

C's statements sit inside if FLAG: and else: arms. Each arm sorts as the class's top level would, so the methods in the if arm reorder to __init__, alpha, beta with the dunder first, and the fields in the else arm put the default-less z ahead of x and y, required before optional.

**defaults splits the dict into "d" and "c" above it and "b" and "a" below it. Each run sorts on its own side and no entry crosses the spread, "c" and "d" ordering above and "a" and "b" below, because moving a key across the spread could change which value wins the merge.

OnetSkillType extends StrEnum and lists six members starting with TECHNOLOGY. The members alphabetize as a single group, ABILITY rising to the top and TECHNOLOGY dropping to the bottom, because the rule reads the simple name of any base class, so StrEnum qualifies the class the same way Enum or IntEnum would.

title and tags take their defaults through Field(default="Untitled") and Field(default_factory=list) inside Annotated, whereas company and description carry no default. The required pair sorts ahead of the optional pair, each half alphabetized, because a field whose nested call carries a default or default_factory keyword counts as optional. The rule finds the call by structure rather than by name, so the detection does not depend on the constructor being pydantic's Field, and the two imports also swap into alphabetical order.

Five from imports sit out of order, with a blank line between extract_fields and BaseModel. They collapse into a single alphabetized block sorted by module from collections up through pydantic, and the blank line is removed, so the imports read as one paragraph whatever arrangement they arrived in.

The imports arrive scrambled across bare import statements, external from imports, and relative imports. The block reorders into bare, then external from, then local-package order, so import os and import sys lead, from collections import Counter and from pathlib import Path follow, and the relative imports close the block, with from ..shared import base above the single-dot pair because relative imports rank by level first and module name second.

The case runs under group-imports = false, with two bare imports and two from imports interleaved. The four imports sort as one flat block with no blank-line dividers between sections, import os and import sys leading because bare import statements sort ahead of from imports, and from collections import Counter and from typing import Any following in name order.

The Posting call passes five keywords with nothing dividing them, whereas **overrides splits the combine call in two. Keyword arguments alphabetize within the segments a ** unpacking bounds, so all five Posting keywords sort as one run from company= through url=, whereas in combine first=1 and second=2 sort ahead of the unpacking and fourth=4 and third=3 sort behind it, and no keyword crosses that boundary.

update's positional parameters target and source are pinned, and its docstring carries both an Args: section following the signature and a Raises: section out of order. The Args: entries stay as target then source, mirroring the pinned signature, whereas the Raises: section sorts, moving KeyError ahead of ValueError, because Raises: names exceptions rather than parameters, so nothing pins it.

update takes self, target, source ahead of the * and retries=3, atomic=True, mode behind it. The positionals stay exactly as declared and the keyword-only block sorts, the required mode moving to the front with atomic=True and retries=3 alphabetizing after it, because the positional pin covers only the parameters ahead of the *, whereas keyword-only parameters bind by name at every call site, so reordering them changes no caller.

The case runs under group-methods = false, and Report defines archive, the @property total, and __len__. The methods sort by plain name, __len__ first because _ sorts before letters, then archive, then total last, rather than grouping into dunders, properties, privates, and publics, which would have put the property above the public methods.

Posting is decorated with @dataclass and contains the fields title and company followed by the methods summary and render. title and company keep their positional order while render moves above summary, because the pin covers the annotated field run alone, and a method never becomes a constructor parameter, so its place binds no call site the way the fields do.

merge takes the positionals target and source, then atomic=True and mode past the *. target and source keep their source order, and the keyword-only block sorts with the default-less mode moving ahead of atomic=True, because positionals can bind by position, so reordering them would rebind call sites a single-file pass cannot see, whereas keyword-only parameters bind by name at every call site and sort with required entries ahead of optional ones.

Posting extends BaseModel with six fields, half of them defaulted, in scrambled order. The fields split into required and optional sub-groups, each alphabetized, so company, date_posted, and url rise above the defaulted description, location, and title regardless of source order, because a field with no default counts as required.

allowed and priorities are plain set literals, and mixed places *defaults second among its strings. The elements of every set alphabetize by their source text, {3, 1, 2} becoming {1, 2, 3}, whereas *defaults keeps its second slot and "a", "m", and "z" fill the remaining slots in sorted order, "a" moving above the unpacking, because a Python set is unordered, so reordering the literal changes nothing at runtime, whereas the unpacking itself cannot safely move.

theme lists "ruler", "accent", and "background", each value a hex string containing # and each entry ending in a trailing # comment. Sorting moves "ruler" below "accent" and "background", and each trailing comment moves with its entry, the , sitting after the value and before the comment. A value containing its own #, such as "#ff8800", stays intact because the comma is placed from the parsed value span rather than from the first # on the line.

The Args: entries carry parenthesized types and sit out of signature order, port (int) listed before host (str) while connect(host, port) declares them the other way around. host (str) moves above port (int) to mirror the signature, each parenthesized type staying with its entry, because the rule matches entries to parameters by their bare names.

Config(TypedDict) lists timeout, name, retries, and host. The annotated fields alphabetize to run host through timeout, because detection reads the AST form of the fields rather than the base-class name, so a plain class carrying the same bare annotated fields would sort identically.

No Change

"b": 1 sits above **defaults and "a": 2 below it. Nothing moves, because a later dict entry overrides an earlier one carrying the same key, so "a": 2 cannot cross **defaults without possibly changing which value wins, and the spread bounds the sort to the run on each side of it, each of which contains a single entry.

No Change

class Alpha(Beta) inherits from the Beta defined above it, and Beta must already exist when Alpha's header evaluates. Beta stays ahead of Alpha and the file passes through unchanged, because alphabetizing would move Alpha above the name it inherits and the module would fail to import.

No Change

print(Zeta) sits between class Zeta and class Apple and reads Zeta when the module imports. The run keeps its source order, because sorting Apple above Zeta would push class Zeta below the print that reads it and raise a NameError, reordering into a module that cannot run.

No Change

Ranker binds score to a lambda whose parameters are self, weight, bias. The parameters keep their source order even though bias sorts before weight, because positional-or-keyword parameters bind callers by position exactly as a def's do, so reordering them would change call sites a single-file pass cannot see.

No Change

@dataclass generates an __init__ for Posting whose positional parameters follow the fields in declaration order, from title down to date_posted. Nothing moves, because sorting company above title would reorder those parameters and silently break every positional call site, which a single-file rewrite cannot reach.

No Change

All three definitions sit under decorators that bind values by position, pytest.mark.parametrize matching "a, b" to the parameters, hypothesis.given passing its strategies in order, and the stacked click.argument calls filling source and dest in the order they appear. Every definition keeps its place and every signature keeps its positional order, so test_add(b, a) and copy(dest, source) stay exactly as written despite both sorting the other way by name, because applying a decorator runs a call whose order a reorder would change.

No Change

Boundary values its members through auto(), which counts the members written ahead of it, and ParameterKind numbers each member in a __new__ reading len(cls.__members__). Both enums keep the order their author wrote, because sorting either one would rewrite the value every member carries, whereas an enum spelling each value out carries no such tie and still sorts.

No Change

forward takes input, hidden and the @staticmethod lerp takes start, end, weight. Neither parameter list reorders, even though hidden sorts before input and end before start, because positional-or-keyword parameters bind callers by position, so reordering them would change call sites a single-file pass cannot see, and the @staticmethod on lerp changes nothing about that.

No Change

Point extends NamedTuple and declares y, x, and the defaulted color. Every field keeps its source position, y staying above x even though a sort would swap them and color keeping its final place, because the NamedTuple base makes the field run the tuple's element order, so building a Point positionally binds y first and x second.

No Change

__all__ packs several strings onto each row, with own-line comments and a blank line between the rows. The group keeps its order, because a packed group swaps member slices with every gap kept verbatim, so a comment between its rows would stay put while the members flowed across it, the same way a group opening mid-row is left as written.

  1. The "alpha" and "beta" arms of dispatch each return a long inline dict, and the wildcard arm returns None. Each dict explodes to one entry per line, its keys sort so "comment_text" leads, and the : of each entry pads into one column within its arm, while align-match-case joins the wildcard's return None onto its case _: line.

  2. visit names Node in its annotations and sits above class Node, under from __future__ import annotations. alphabetize-siblings moves the class above visit, and the output reads as if the directive were no longer needed. from __future__ import annotations stays, because prune-inert-imports runs before the sort, reads Node as a binding the sort is free to move either way, and keeps the directive rather than depending on where the sort puts the class.

  3. import sys, import os, and import json arrive out of order, and each module is used once below through a single attribute, os.getcwd(), sys.argv, and json.loads. alphabetize-siblings sorts the statements so json leads, and bare-imports reports all three, each finding landing on the import's row in the sorted output, so json is reported on row 1 and sys on row 3.

  4. A one-line configure call carries a four-entry dict in its settings keyword. reflow-collections explodes the dict, reflow-calls re-indents it to the keyword column, alphabetize-siblings sorts the keywords and the dict entries, and align-equals and align-colons pad the = and : columns. The dict entries sit one indent step past the keyword column, with the closing } back at the keyword column.

  5. draw(w, h, d, c) passes four positional arguments to a def draw(width, height, depth, color) defined above it. reflow-calls explodes the call to keyword form, naming each argument after its parameter so w becomes width = w, and alphabetize-siblings sorts the keywords so color leads while align-equals pads their = into one column.

  6. import sys, os and import myapp.core, abc each name two modules in one statement, with myapp in first-party. reflow-imports splits each into one module per line ahead of group-imports and alphabetize-siblings, so every split-off module reaches its own group and its sorted position, and myapp.core ends up in the local section rather than beside the stdlib abc it was joined to.

  7. CONFIG is written as one overlong line with its keys out of order. The dict explodes to one entry per row, the keys sort, and every : pads into one column one space past the widest key, "beta_extended", so even the short "zeta" row takes the full padding to reach that column.

  8. tagdefs writes two spaces after each key's :, and alphabetize-siblings moves "stderr" ahead of "stdout", the comment above "stdout" moving with it. The "stdout" value, last after the sort, stays on one row at exactly the 40-column budget, because reflow-collections measures a value from the plain ": " past its key rather than from the two spaces the source wrote, and the row is written back at that width, whereas the "stderr" value explodes.

  9. The module top carries from __future__ import annotations, three aliased imports out of order, and three blank lines before def add. The directive is removed, collections, numpy, and requests sort, their as keywords pad into one column, and the gap closes to two blank lines before the function.

  10. __all__ lists six entries on one overlong line, three short names interleaved with three thirty-character ones. alphabetize-siblings sorts them so the short names lead, and reflow-collections packs them onto rows within the 88-column budget. The packing measures the widths the sort leaves, not the widths as written, which is why "cc" shares a row with the first long name rather than joining the two short entries above it, and the six entries settle onto three rows of two with no row widening afterward.

  11. The dict passed to self.call_exception_handler lists 'message', 'exception', and 'loop', each with a trailing comma, and the sort moves 'message' last. alphabetize-siblings settles which entry ends the dict, so reflow-collections measures each entry with the separator that order leaves after it, and the 'message' entry, last after the sort, drops its comma, fits its row within the 79-column budget, and the layout settles once the sort is written.

  12. aa=lambda: g(111111, 2222, 333333) is the last keyword of f, and alphabetize-siblings moves it ahead of bb=1, which gives its row a trailing comma it did not carry while it was last. The g(...) call inside the lambda explodes, because a nested call ending at the end of its row still counts whatever the enclosing row writes after it, and with the align-equals buffer around the = that comma crosses the 40-column budget.

  13. Service declares three fields and three methods, each set out of order. The fields sort and pad both their : and their = into one column each, the methods sort separately so _cleanup leads restart and shutdown, and space-statements writes one blank line between every member and one directly under the class Service: header.

  14. The first element of the set s is written across two lines as (1, and 2). alphabetize-siblings sorts each element by the text it has once joined onto one row, not by the broken text in the source, so (1, 2) sorts after the long tuple.

    Sorting puts the long tuple first, which gives it a trailing comma, and measuring it with that comma takes it one column past the 40-column budget, so it explodes rather than staying on one row.

  15. d is a dict with # prose: keep at the end of its line, written with "b" before "a" and too wide for the 40-column budget. reflow-collections explodes the dict, which moves the marker onto the closing } line, and the marker still keeps the dict out of alphabetize-siblings, so "b" stays ahead of "a" and the entries keep their order and their separators, because the marker applies from the line of either the { or the }.

  16. f(bb=1, *args, aa=lambda: g(111111, 222222, 3333)) puts *args between two keywords. A keyword sort run is bounded by a ** unpacking and not by a positional, so *args sits inside one run rather than splitting it in two. reflow-calls measures aa, the keyword the sort would move first, with the comma it would gain, which takes it past the 40-column budget, so its lambda value explodes, and once a value spans rows the sort leaves the group in place, *args staying exactly where it was written.

  17. _FORMATS lists FMT_BINARY and FMT_XML in the order alphabetize-siblings leaves them, so the last entry gains no separator, and align-colons pads FMT_XML onto a : column that puts its row exactly at the 45-column budget. The column stays, because reflow-collections measures that row at exactly the budget rather than past it, and the padding is not stripped back.

  18. Three methods of Dispatcher sit out of order, and handle_alpha contains a match whose arms each return a single value. The methods sort so handle_alpha leads, align-match-case joins each return onto its case line and pads the : into one column, and space-statements writes one blank line between the methods.

  19. zeta, alpha, and beta arrive out of order, alpha with a two-line docstring and the other two with one-line docstrings. alphabetize-siblings sorts the methods, expand-docstrings rewrites the one-line docstrings of beta and zeta to multi-line form, and frame-docstrings puts every """ on its own line, with space-statements writing one blank line between the methods.

  20. PRIMARY overflows its line with values written in the older Optional and Union forms, and target-version = "3.10" allows the | syntax. modernize-annotations rewrites each value to | form first, so every rule downstream measures the shorter text, and the dict then explodes, its keys sort so "delta_long" moves ahead of "gamma", and align-colons pads the : column against the rewritten widths. With nothing left reading either name, from typing import Optional, Union is removed.

  21. d opens with { # note and lists "zz" before "a": 1, "b": 2 packed on one row. The comment on the { row keeps reflow-collections from splitting that row, and a packed row is never reordered, so the dict keeps its source order under alphabetize-siblings. The "zz" entry is measured with the comma it carries as written rather than one a sort would have moved, its value is too wide for the 40-column budget, so that value explodes while "a": 1, "b": 2 stay packed on their shared row.

  22. s is a set packed across three rows with # note at the end of the first. The comment inside the set keeps its order fixed under alphabetize-siblings, so no element moves, and reflow-collections measures each element with the comma it carries as written rather than the one a sort would have left. The one element too wide for the 40-column budget explodes onto rows of its own, and the packed rows around it keep their commas and their places.

  23. sup.__init__(option_strings=[], dest=dest, help=help, metavar=metavar) is written across two rows, and sorting the keywords produces no row wider than the widest already in the source, so alphabetize-siblings reorders them. align-equals then writes a buffer around the = of option_strings, the keyword left alone on its row, and that buffer does not trigger a second width check, because the reorder was already measured against the source's own widest row.

  24. Bare, external from, and local imports sit scrambled together, with myapp named in first-party. group-imports partitions them into bare, external, and local sections, alphabetize-siblings sorts the names within each section, space-statements writes one blank line between sections, and align-imports pads the import keyword into a column within each section.

  25. y is a four-element set on one line past the 30-column budget. reflow-collections packs the rows at the widths alphabetize-siblings leaves in each slot, not the widths the source wrote, so the rows fill the budget as densely as a list's would and the sort is written without widening any row.

  26. render's parameters template, context_map, and escape_html already list in the same order as the docstring's Args: entries. alphabetize-siblings leaves that order as written, align-colons pads the three entries' : into one column, and wrap-docstrings wraps each description with its continuation lines hanging under that column, because Args: entries mirror the signature rather than sorting alphabetically.

  27. table carries # prose: skip[alphabetize-siblings] on its closing line, listing "bb" before "aa". reflow-collections ordinarily measures each entry with the comma the sort is about to write, and the skip directive turns the sort off, and with it the measurement against the sorted order, so every entry is measured with the comma it already carries, and "bb" counts a trailing comma and breaks after its : rather than being measured as the last entry.

  28. foo({"alpha": 1, "beta": 2, "gamma": 3, "delta": 4}) passes one dict as its only argument. reflow-collections explodes the dict to one entry per row, and the call explodes around the dict, its ( and { on separate rows and its } and ) on separate rows, because an argument that spans rows explodes the call whatever the argument count and however short the one-line form. Inside, alphabetize-siblings sorts the keys so "delta" moves ahead of "gamma", and align-colons pads each : into one column.

  29. zebra, alpha, and mango sit out of alphabetical order at module level with no spacing between them. alphabetize-siblings sorts the definitions and space-statements writes two blank lines between each pair of top-level functions.

  30. configure(...) carries a coords keyword whose tuple of three calls is too wide for one line. reflow-collections explodes the tuple and reflow-calls re-indents its elements under the keyword, alphabetize-siblings sorts more, name, and other, and align-equals pads each = into one column. coords keeps its source position, because its tuple calls functions such as compute_alpha_coordinate() and an entry whose value runs code is never moved, and the tuple's elements sit one indent step deeper than the keyword rows with its ) back at the keyword column, as a list or dict argument would.

  31. configure takes five typed parameters on one line, two with defaults. reflow-signatures explodes the signature to one parameter per line, align-colons pads each name to one : column, and align-equals pads the = of mango and delta into one column, while the parameters keep their source order even with alphabetize-siblings in the run, because reordering parameters would change every positional call.