alphabetize-siblings
OrderingSorts sibling entries whose order carries no meaning, covering import names, dict keys, class-body members, keyword arguments, and docstring entries.
space-statements sets the number of blank lines around module-level definitions, class members, import groups, and the if __name__ == "__main__": guard, so every file in the project reads with the same rhythm. Blank lines tell the reader where one unit ends and the next begins, and a consistent count across a file lets the reader skim by section without parsing each statement.
A module-level def or class takes two blank lines before it and two after, whatever top-level statement follows. One blank line is the gap everywhere else the rule reaches:
if __name__ == "__main__":.from-import groups.Inside a function body the rule leaves the blank lines alone, since the in-body rhythm stays a per-author choice.
An own-line comment block that reads as a description of the statement below it binds tight against that statement, with no blank line between them whether or not the author left one, so the reordering rules move the comment with the statement it heads. A block that anchors in place instead of binding keeps one blank line below it, so it reads as a divider, and the canonical gap above is measured from the topmost comment in the block either way.
A block counts as a divider where any of its lines carries one of these:
=, -, *, _, #, ~, ─, ━, or ═.#.A block opening the file is spaced differently, in that the blank lines above it are removed entirely and the blank lines below it are capped at one. That one blank line is present where the block is a divider or where the author already left one and absent otherwise, so a module leading with a license header keeps the spacing it was written with and a module leading with a banner gains one blank line.
On the import block this rule reads an order group-imports and alphabetize-siblings have already settled, writes the blank-line separators between groups, and leaves align-imports to align the import keyword afterward. The Pipeline Order reference lists where each sits.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Turns the rule on or off. |
The canonical blank-line counts are fixed to PEP 8's 2-between-top-level and 1-between-methods cadence, so the rule carries enabled as its only facet. A project on a different cadence disables the rule and keeps its editor's blank-line conventions.
The for setup in initial_steps: loop sits one blank line above def main():. The gap widens to two blank lines, the canonical count before every module-level def, so the reader finds the same separation between top-level units everywhere in the file.
for setup in initial_steps:
setup.run()
def main():
return 0
A # ==== banner block sits between first and second with no blank line on either side. The banner reads as a section divider rather than as a description of second, so the canonical two blank lines between module-level definitions are written above the banner and one blank line separates it from def second():.
print("ready") sits on the line directly after configure's body ends. The gap widens to two blank lines, the same count any other statement after a module-level definition takes.
second carries @decorator and sits one blank line below first. The blank run is measured above the decorator line rather than between the decorator and the def, so the one blank line widens to two above @decorator and the decorator stays flush against def second():.
The # Section: Configuration banner opens the file flush against import os. One blank line is written between the banner and the import even though the author wrote none, because a banner reads as a section divider rather than as a description of the statement below it, the same as when it sits between two statements.
### Codec APIs sits two blank lines below import codecs and one blank line above class Codec(codecs.Codec):. The heading, opening with several #, reads as a section divider rather than as a description of the class, so the two blank lines above it stay and the one blank line below it stays. Codec's body separately gains one blank line between its header and pass.
trailing = True sits three blank lines below the if __name__ == "__main__": block. The gap collapses to one blank line, narrower than the two a def or a class would take, because the count after a __main__ guard is one.
PORT = 8080 sits flush below the end of class Posting:. The gap widens to two blank lines, the canonical count after a module-level class whatever the source wrote, and the class header gains its one blank line before the title field.
# describes second sits two blank lines below first and four blank lines above def second():. The comment reads as second's leading block, so the four blank lines below it close to none and the comment sits flush against the definition it describes, whereas the two blank lines above it already match the canonical count and stay.
Three blank lines separate first from second inside Posting. The gap collapses to one blank line, the canonical count between methods, and Posting's header gains its one blank line before first. A pair written with no gap at all would widen to the same one blank line.
Three blank lines separate # Licensed under the MIT License, opening the file, from import os. The run collapses to one blank line, because the cap below a leading block applies to a description as much as to a banner, and one blank line is what the author's spacing already carried.
Posting's docstring, written in triple-apostrophe quotes, sits flush against def m1(self):, and three blank lines separate m1 from m2. One blank line is written between the docstring and the first method, and the three blank lines between the two methods collapse to one.
import os and from sys import argv are separated by one blank line, with the rule running alone. The blank line stays as written, because space-statements alone sets no count between two imports, and the one-blank-line boundary between the bare form and the from form applies only when group-imports is enabled.
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.
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.
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.
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.
convert names Alias in its annotations while Alias = int is written below it, under from __future__ import annotations. band-constants moves the assignment into the leading band, and prune-inert-imports removes the directive, because it counts the hoisted binding as written ahead of the annotations that name it.
convert names Sequence in its annotations while from collections.abc import Sequence is written below it, the case the directive exists for. band-constants moves the import above the definition, and from __future__ import annotations is removed, because the relocated binding counts as written ahead of the annotations that name it and they resolve without the directive.
LIMIT = 3 sits between from pkg import alpha and from pkg import beta, which would ordinarily keep the two apart. band-constants moves the constant below the imports, and reflow-imports merges the two pkg statements onto one line in the same run, because it measures against the order the move leaves rather than the order as written, so no second pass is needed once the constant has moved.
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.
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.
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.
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.
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.
from pkg import a is never read and is the line the # Local imports comment heads, with X = 1 between it and from pkg import b. band-constants moves X = 1 below the imports, which brings the two pkg statements together, and with them adjacent from pkg import b moves up onto the line a vacates as a is removed, leaving the comment heading a real import rather than a gap.
Sorts sibling entries whose order carries no meaning, covering import names, dict keys, class-body members, keyword arguments, and docstring entries.
Pads the space before the import keyword across consecutive from imports, or before as across consecutive aliased imports, so the keywords share one column.
Reports an unaliased bare import reached through at most max-attributes distinct attributes, which a from x import … would replace.