align-colons
AlignmentPads the space before : so consecutive dict entries, annotated assignments, signature annotations, and docstring entries share one column, with a docstring entry's parenthesized type in a column of its own.
align-equals pads the space before = so that consecutive assignments share one column. A stretch of bindings then reads as a list of names beside a list of values, and the eye finds each value by dropping straight down the operators rather than searching for the = on every line.
The rule aligns three kinds of run:
+=, |=) joins the run with its operator one column before the shared =, whereas a walrus (:=) never joins.param: type = default entries aligns its = the way a stretch of module-level bindings does.name = value column the way a signature aligns its defaults.Every aligned row reads as name = value, with the name padded out to the column and one space after the operator. A keyword alone on its line with no column to share still takes one space on each side of its =, whereas a keyword that shares its line with another argument keeps the tight name=value form PEP 8 gives a call-site keyword.
A positional argument, a ** unpacking, an interior comment, or a keyword sharing a line with another argument ends the run, whereas a multi-line value or default joins its run and then closes it, leaving the entries after it to align as a separate group. A blank line, a comment line, or a statement of another kind ends a run of assignments, so each contiguous run aligns on its own. Once a group aligns, strip-stranded-padding removes the padding of any one-member group, and a lone binding then reads as plain code.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Turns the rule on or off. |
max-shift | positive int | 0 | false | 16 | How far apart the widest and narrowest rows of a run may be for the run to still align on one column. A positive N caps that gap, 0 forbids any padding so every row sits flush, and false lifts the cap so a run of any width aligns on one column. A row marked # prose: skip stays out of its group. |
max-shift limits how much padding one row may take. The rule reads each run in source order and extends a column while the gap between the widest and narrowest names stays within the limit, starting a new column at the first row that would exceed it. Setting max-shift to false removes the limit, so a run of any width aligns on one column, and 0 forbids padding altogether, so every = sits one space after its name. The per-rule facets reference covers the full semantics.
x, foo, and bar_baz are assigned on three consecutive lines. Every = moves to the column one space past bar_baz, the widest name, so the values 1, 2, and 3 line up and read as one list.
x = 1
foo = 2
bar_baz = 3
alpha and beta sit above a blank line, with long_name and short below it. The blank line splits the assignments into two groups, so beta pads out to alpha's column above the gap and short pads out to long_name's column below it, and neither column reaches across the blank line.
The # tuning comment sits between port and timeout inside the connect call. The comment ends the keyword run, so the keywords on each side align as two groups, host with port on one = column above it and timeout with retries on another below.
alpha and beta sit above the # break comment, with long_name and short below it. The comment splits the assignments into two groups, exactly as a blank line does, so beta pads out to alpha's column and short pads out to long_name's, and neither column reaches across the comment.
host's value arrives as (1 + 2) inside the exploded connect call, with port=3 beneath it. host and port align on one = column and the parentheses stay in place, because the rewrite changes only the spacing around each = and stops before the (.
x=(1 + 2) and yy=3 are written with no space around their =. Each = gains a space on both sides and x pads out to yy's width, with the parentheses around 1 + 2 left as written, so the first row reads x = (1 + 2). The rewrite reads the ( as the start of the value and stops before it.
The exploded record call opens with the positional event and follows it with the keywords name, target_id, and ok. event stays out of the keyword run, because it carries no =, and the three keywords below it align on one column set by the widest name, target_id.
q's name is one character wide and its string value is long, with wider_name and also_wide below it, under a code-line-length of 40. Padding q out to wider_name's column would carry its value past the cap, so q keeps its single space while wider_name and also_wide align beneath it, leaving no line over budget.
label, verbose, and dry are assigned with no space around their =, under a code-line-length of 35. Aligning them gives each = a space on both sides, and the fit check measures every row with both of those spaces counted.
Padded out to verbose's column, label's row would reach exactly the budget without the space after the = and one column past it with that space counted, so label keeps a single space on each side and verbose and dry align below it.
really_really_long_target sits between short and medium_size above it and longer and tiny below it. Padding the narrower names out to its width would take more than max-shift allows, so the wide row ends the run and keeps a single space around its =, and the rows on either side align on columns of their own, short with medium_size and longer with tiny.
beta: int = 2 sits between the plain assignments alpha = 1 and gamma_long = 3. The annotated line joins the run, its width measured through int, so all three = share the column one space past gamma_long.
beta += 2 sits between the plain assignments alpha = 1 and gamma_long = 3. The += line joins the run with its = on the shared column, so the + sits one column before the stacked = signs.
The connect call is already exploded, with host, port, and use_tls each written tight as name=value on its own line. Every = moves to the column one space past use_tls, the widest keyword, so all three rows read name = value with the operators stacked.
x=1, yy=2, and zzz=3 arrive with no space on either side of any =. Every = moves to the column one space past zzz and gains a space before its value, so each line reads name = value with the operators stacked in one column.
Inside the foo call, a=1 has a line to itself, whereas b=2 and c=3 share the next line. Only a gains a space on each side of its =, whereas b=2 and c=3 keep the tight name=value form, because a keyword that shares its line with another argument is never spaced.
configure declares host: str, port: int, and delay: float, each with a default and each on its own line. Every = moves to the column one space past delay: float, the widest parameter, so host: str and port: int take padding between their annotation and the =.
Each keyword in the exploded configure call is written with no space around its =, under a code-line-length of 40. Joining an aligned column gives each = a space on both sides, and the fit check counts both of those spaces.
Padded out to verbose's column, label's row would reach exactly the budget without the space after the = and one column past it with that space counted, so label keeps a single space on each side and verbose and dry align below it.
a = b = 1 carries two = tokens, and xx = 2 follows it. The chained assignment is skipped, because neither = is the single operator a row aligns on, and that leaves xx = 2 alone, so nothing is padded.
fn's Callable[...] annotation spans several lines before its =, between host and timeout in configure's signature. The multi-line parameter splits the run, leaving host and timeout alone on either side, so host: str = "localhost" and timeout: float = 30.0 keep their single spaces even though the two would align as a pair.
a, bb, and ccc arrive with their = already on the column one space past ccc. The rule writes no edit, so the three lines pass through exactly as written.
_openers is assigned a dict with no space after any :, above CHECK_DELAY = 100. strip-stranded-padding writes a space after each :, which widens the _openers row by three columns.
align-equals leaves the two = unaligned, because it measures the row at the width it has once that edit is written, and padding _openers onto a shared column with CHECK_DELAY would take the row past the 40-column budget.
evaluate opens with three record.get assignments and then guards on a multi-line BoolOp. align-equals pads the assignment run's = into one column, and align-comparisons right-aligns the is, ==, and < operators in a column of their own inside the if, the two columns independent of each other.
padx += and border += sit in one run inside measure, each assigned a nested widget.tk.getint(widget.cget(...)) call, and the border row runs past the 64-column budget. The widget.tk.getint(...) call on the border row explodes, its widget.cget('border') argument moving to a row of its own, because reflow-calls measures the value from the row's own indent rather than one column short, while the padx row fits and stays whole.
The comment # the convergence floor the solver stops at sits on its own line directly below DEFAULT_EPSILON = 0.1, describing it, with MAX_INT = 5 after a blank line. band-constants moves the comment onto the end of the DEFAULT_EPSILON line as it packs and sorts the band, and align-equals then pads all three constants onto one = column, whereas a comment left on its own line would have split the run in two and left DEFAULT_EPSILON out of the alignment.
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.
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.
_handlers carries a trailing comment written #map of ..., tight against the hash, above _handlerList, and normalize-comment-spacing later writes one space after the #. _handlers and _handlerList stay unaligned, because align-equals counts that space before it sets its column and a shared column would carry the _handlers row past the 79-column budget, whereas _ok and _status below pad onto one column as usual.
zebra, foo, and bar_baz sit above a # fmt: off block that contains the hand-laid matrix. band-constants sorts the three and align-equals pads their = into one column, and the matrix between # fmt: off and # fmt: on stays byte for byte as written, because the directives mark a region no rule edits and the run stops at that boundary.
function_nm takes a single parameter whose default is a nested outer_call_name(...) call, and the signature crosses the 66-column budget. reflow-signatures puts the parameter on its own line at an indent of four, and the call in its default explodes from that column rather than from the column it occupied in the source.
reflow-calls leaves the parameters of a signature that is about to explode alone, so nothing measures them twice, and the inner inner_call_nm call is measured against the exploded row with the buffer align-equals writes around keyword_name = counted in.
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.
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.
window(seen := len(rows), page_size, start, rows) passes a walrus as its first positional, and reflow-calls names it anchor as it explodes the call. A named expression used as a keyword value takes parentheses of its own, so the value opens one column further right than the plain = gap align-equals writes, and counting that column takes the row past the 30-column budget, so the nested len call explodes.
process_env_var(tokenizer.read().text.replace(".", "_")) passes a method chain as its one positional. stack-method-chains wraps the chain in parentheses and stacks .text.replace(...) under tokenizer.read(), and reflow-calls then writes the argument in keyword form as env_var = .... The parentheses stay around the value, because the value spans rows, and the stacked chain keeps its layout through the = alignment instead of being re-wrapped.
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.
_filter(flist=os.listdir(self.left_folders), skip=self.hide) crosses the 40-column budget. reflow-calls puts each keyword on its own row, and align-equals writes a buffer around each =, which moves every value two columns right. The nested os.listdir(...) call explodes, because it is measured from that settled column rather than from where it sat before the buffer, and those two columns take it past the budget.
dispatch opens with the assignments x, yz, and qrs above a match whose arms each return one of those bindings. align-equals pads the three = into one column one space past qrs, and align-match-case joins each single return onto its case line with every arm : padded one space past the widest pattern, "alpha".
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.
Inside ensure_running, the cmd %= (...) statement spans two rows between fds_to_pass and main_kws["authkey_r"] above it and exe and args below. align-equals builds its runs the same way when it reserves as when it aligns, so the multi-row statement closes the run above it, and exe and args pad onto a narrow column of their own rather than the wider one fds_to_pass and main_kws["authkey_r"] set. reflow-collections reads that same run boundary and joins the tuple within the 80-column budget, rather than exploding it from a column the aligner never uses.
screen._drawpoly(...) is a method call whose signature reflow-calls cannot resolve, so it leaves the positionals unnamed and explodes the arguments in source order. Each keyword value is still measured from the column the align-equals buffer puts it at, with the comma that closes its row counted in, and those two together take the outline value past the 40-column budget, so it explodes, while fill and top fit their rows and width = self._outlinewidth, an attribute access with nothing to explode, stays on its row past the budget.
retries, timeout_seconds, and window are annotated assignments with trailing comments, and three alignment rules pad the same rows, each checking its own padding against the 72-column budget. Every rule measures the row's settled form rather than the intermediate text the rule before it left, which is what lets the :, the =, and the # all reach their columns with the widest row, retries, still inside the budget.
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.
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.
configure's signature arrives exploded, its four typed parameters carrying defaults at widths from host to timeout_seconds. align-colons pads every name to one : column set by timeout_seconds, and align-equals pads every annotation to one = column set by float, so all four rows read as name, type, default.
Pads the space before : so consecutive dict entries, annotated assignments, signature annotations, and docstring entries share one column, with a docstring entry's parenthesized type in a column of its own.
Pads the space before the import keyword across consecutive from imports, or before as across consecutive aliased imports, so the keywords share one column.
Folds each single-statement case arm onto one line and pads the space before its : so consecutive arms share one column.
Removes padding that lines up with nothing, before the : of a one-member group and just inside a bracket, and settles the gap after a : to one space.