Skip to content

align-imports โ€‹

An import block carries two kinds of structure that the reader's eye wants to follow as columns. The module column says where a thing comes from, and the name column says what's pulled in. When the two columns float at varying widths, every line reads as a fresh sentence rather than a row in a table.

gathers consecutive from ... import ... statements (or consecutive import ... as ... statements) into a shared column for the import (or as) keyword, leaving the module column flush left and the name column flush right.

The rule reads each block as the run of consecutive imports at the same indentation. A blank line, a comment, or a non-import statement resets the run. Pair with

to sort entries within each block before alignment, with to separate import groups by category, and with to canonicalize bare-versus-from before the alignment pass.

Configuration โ€‹

KeyTypeDefaultMeaning
enabledbooltrueToggle the rule on or off
max-shiftpositive int16Ceiling on per-line padding
max-shift-policy"split" | "drop""split"How to handle a group whose widest member exceeds max-shift. See the per-rule knobs for the full semantics

max-shift caps the per-line padding the alignment can introduce. When a block's widest module name would push the import keyword past the cap, max-shift-policy decides the fallback shape, with a worked example per policy. The per-rule knobs reference covers the full semantics.

The Canonical Case โ€‹

A run of from ... import ... statements lines up on the import keyword, so the module column flushes left and the name column flushes right.

python
from collections import OrderedDict
from typing      import Optional
from sys         import path
from os.path     import join

More Examples โ€‹

A run of from ... import ... statements whose module prefixes spread past the operator-alignment default still aligns on the import keyword, because align-imports seeds a wider max-shift than the operator rules.

The shift-limit policy reaches import M as A runs by the same distance rules as from-imports. A widest module overshooting max-shift = 8 greedily partitions the run into sub-groups, each aligning the as keyword at its own tightened column independently of the rest.

A run of import M as A statements aligns the as keyword across them. The widest module name fixes the shared column, and shorter rows pad between the module and as to reach it.

A comment between two imports, whether an own-line block comment or a trailing comment on an import line, breaks adjacency because the aligner aborts on any comment token in the inter-statement gap. Each comment-bracketed sub-run aligns only with its own contiguous neighbors.

A bare import M carries no as keyword to align against, so it breaks the surrounding import-as run. A non-import statement between imports breaks it the same way, and each side aligns only with its own contiguous neighbors.

Under max-shift-policy = "drop", an import M as A run whose widest module overshoots the cap drops that outlier from the aligned set. The remaining members tighten onto their own column while the dropped line keeps its original single-space spacing.

No Change

Input whose gaps already carry the target width of spaces is idempotent under the rule. The from-import and import-as groups already sit on their columns, so the rule emits zero edits and the output equals the input byte-for-byte.