alphabetize-siblings
OrderingSorts sibling entries whose order carries no meaning, covering import names, dict keys, class-body members, keyword arguments, and docstring entries.
reflow-calls breaks a call to one argument per line under any of three triggers, and leaves a shorter call inline:
max-args on a call whose every argument can be written as a keyword.code-line-length from the column the call sits at.The count trigger writes each argument as name=value, and the exploded form under any trigger puts each argument one indent step inside the row the ( sits on and the closing ) back at that row's indent. That indent is the one the row settles to, so a row that opens on the closer of a bracket opened earlier takes the indent of that opener's row rather than the continuation column the source wrote. A nested call that meets a trigger of its own explodes in the same pass.
The count trigger fires only where every argument can be written as a keyword. A positional argument takes its parameter name from the function the call resolves to in the same module, so the exploded form reads name=value whatever order the source passed the arguments in. A bare generator expression, a walrus binding, and a yield each gain a pair of parentheses around the value. A positional-only prefix, a * or ** unpacking, a callee that does not resolve to a function defined in the module, and a from x import * anywhere in the module each leave the call inline. The count trigger skips such a call rather than keeping it as written, so a broken list past max-args that the rule cannot name rejoins onto one row the way a broken list under the cap does, and code-line-length is the only trigger left that reaches it.
Every width is measured at the column a construct ends up at once its parent settles, reading the row as strip-stranded-padding leaves it. A nested call that fits the row it ends up on stays inline, a call following a sibling the rule has just joined or exploded is measured on the row that sibling leaves it on, and a keyword value is measured from the column align-equals shifts it to, with the comma closing its row counted at the position alphabetize-siblings later puts it at. A call inside a literal that reflow-collections expands, or inside the parameters of a signature that reflow-signatures lays out one per line, is left to that rule, which reshapes the call where its entry or parameter ends up.
The span trigger reads the argument itself rather than the list, and that argument explodes the list to one argument per line, whatever the count and the joined width, so a call carrying a literal kept multi-line, a nested list already written one entry per line, or a stacked string run takes the same layout a long call does. A call with a single such argument explodes around it, and that argument moves whole into the exploded form the way reflow-collections moves a member it keeps as written.
Where no trigger fires and the source still spans lines, the rule reads where the break sits. An argument list whose opening ( ends its line and whose closing ) opens its own is the layout the explode writes, so it stays. Every other break rejoins onto one row, measured across the whole row rather than the list alone, so the rejoin never writes a line the width trigger would reopen.
An exploded keyword's value that was already broken across lines re-indents to the keyword's column, unless it runs through a multi-line string. A value whose first row leaves a bracket open puts the rows beneath it one indent step inside that bracket and drops the closing bracket back to the column the value starts at, so the contents read as sitting inside the bracket rather than beside it.
No trigger reaches a call inside an f-string or t-string replacement field, because a line break inside one is PEP 701 syntax that fails to parse before Python 3.12, so an over-wide interpolation is left for line-overflow to report.
The rule changes layout alone, leaving argument order to alphabetize-siblings, the spacing around = to align-equals, and the trailing comma to strip-trailing-commas.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Turns the rule on or off. |
max-args | positive int | false | 3 | Explodes a call whose every argument can be written as a keyword to one name=value per line once its argument count exceeds the cap. false turns the count trigger off and leaves only the code-line-length budget. |
connect(h, p, t, u) passes four positionals, past max-args, to a connect defined in the same module. The call explodes to one argument per line and each positional gains its parameter name, so the rows read host=h through user=u, because the rule reads the names from the resolved signature, and the closing ) returns to the indent of the row the call starts on.
def connect(host, port, timeout, user):
return (host, port, timeout, user)
session = connect(
host=h,
port=p,
timeout=t,
user=u
)
The print call's one argument continues across a \ line break onto a second row. The call explodes around the argument and the argument keeps its break, with the continuation row re-indented to the argument's column, because a backslash is a line break rather than a token, so closing the row behind it would leave the backslash before a space and the text would no longer parse. The backslash itself is left to shed-backslash-continuations, which runs before this rule in the default pipeline.
helper(alpha, beta, gamma) and session.record(first, second) both arrive broken across rows, each continuation aligned under the callee's (. Each list rejoins onto one row, because both joined forms fit inside the line budget, leaving a single line in place of a hanging indent.
wrap(alpha, beta=2, **rest) arrives broken across two rows, with **rest pushed onto the second. The call rejoins onto one row and beta=2 keeps its tight spelling, because the joined form fits and a keyword inside a condensed call takes no spaces around its =.
Under code-line-length = 30, emit passes alpha=1, a beta=[...] list carrying a helper(aaaaaa, bbbbbb, cccccc) call, gamma=3, and delta=4. emit explodes to one keyword per line, the list re-indents to the keyword column, and helper explodes inside it, because each construct is measured at the row it ends up on rather than the one the source wrote, and helper overflows from its deeper row.
The ValueError call runs past the budget with a single f-string argument carrying len(image_mean) inside a replacement field. The call explodes, the f-string moves onto a row of its own, and len(image_mean) inside the replacement field stays as written, because a line break inside a single-quoted replacement field parses only on Python 3.12 and later, so the still-long interior is left for line-overflow to report.
emit(prefix + join(alpha, beta, gamma) + suffix) overflows code-line-length = 32, its single argument an operator expression rather than a call. emit explodes and join explodes in place between prefix and suffix, because the nested join is measured on the row the expression ends up on, and that row overflows too.
build(first_value_here, second_value_here, third_value_here) carries three arguments, sitting at max-args, and its row crosses code-line-length = 60. The call explodes to one keyword per line, each positional bound to its parameter name, because the width trigger fires whatever the count, so code-line-length is a hard limit the count knob sits beneath.
Under code-line-length = 40, the chain obj.first(alpha, beta).second(gamma, delta).third(0) runs well past the budget. obj.first(alpha, beta) stays inline, .second explodes gamma and delta onto their own rows, and .third(0) stays inline, because each link is measured at the column it ends up at once its receiver has settled rather than the column the source wrote, and the explode drops .third(0) back to the margin on the closing ) row, where it fits.
The write_header call arrives broken across two rows with a trailing comment on the module's last row, under code-line-length = 57. The call rejoins onto one row and the comment follows it, even though the finished row runs past the budget, because the comment closes the tail measure exactly as a comment on any other row does, so its own width never counts toward whether the call may rejoin.
render(first_value, second_value, third_value, fourth_value) arrives hand-wrapped across two rows, under code-line-length = 40. The call explodes to one argument per line, first_value through fourth_value each on its own row, because the call is measured by the single row its join would produce, and that joined form crosses the budget.
Each [ hangs at the end of the row that opens it, with its contents written one indent step in from that row. Exploding parser.Header and parser.HeaderLabel puts each [ on a row of its own and moves the parser.ValueTerminal rows two steps further in, one step inside the [ that now opens above them, so they read as sitting inside the bracket rather than beside it, while the inner ] stays on the last parser.ValueTerminal row and the three outer closers each take the indent their own block starts at.
client.request(headers=h, method=m, params=p, url=u) is a method call, so no signature in this module resolves for it. The call explodes to one keyword per line exactly as a free function's does, because its four arguments already name their parameters, so no signature lookup is needed.
emit passes alpha=1, a mapping= dict written across several rows, and zeta=2, with the two scalars packed onto the rows the dict's braces open and close. Each sibling moves onto a row of its own, because an argument spanning rows explodes the list around it, and the call sits inside both max-args and the line budget, so the row break alone fires the explode.
emit passes alpha=1, a note= triple-quoted string whose line break falls inside the literal, and zeta=2. emit explodes around the string, putting alpha=1 and zeta=2 on rows of their own, and the string's second line stays at its source column, because the string spans rows the way any multi-line argument does, and indenting its second line would insert spaces into the string's content and change the value.
outer(alpha=1, beta=inner(east=2, north=3, south=4, west=5), gamma=6, delta=7) fits the default budget but passes max-args, with a four-keyword inner call as its beta= value. outer explodes to one argument per line and inner explodes alongside it, even though inner's own span would fit, because each call passes max-args on its own and the recursion resolves both levels in one pass.
configure(alpha=first_option, beta=second_option, gamma=fn(1)) overflows code-line-length = 56. configure explodes to one keyword per line and the nested fn(1) stays inline, because the nested call is measured at the column it ends up at inside the exploded row rather than the column it occupied in the source, and gamma=fn(1) sits well inside the budget there.
wrap passes alpha and a helper block whose closer sits flush with wrap's own, and the block still spans rows after every break that could close has closed. alpha and the helper block each take a row of their own, and the block re-indents to the item column, because an argument spanning rows explodes the list around it.
place(alpha_value, beta_value, gamma_value) calls a place that declares x and y positional-only through the / marker, so the call cannot take full keyword form, and the row overflows code-line-length = 40. The call explodes positionally, one argument per line with the closing ) back at the statement's indent, because an over-budget call never stays inline.
emit passes five keywords, one of them a mapping= dict spanning rows, under max-args = false. Every keyword takes its own line and the dict block re-indents to the item column, because max-args = false clears only the count trigger, whereas an argument spanning rows explodes the call on its own.
build(alpha=1, beta=2, gamma=3, opts={"k": "v"}) passes four keywords, the last a single-line dict. build explodes to one keyword per line and opts={"k": "v"} moves with its keyword and stays inline, because a collection already on one row has no rows to re-indent.
gather carries a single argument, a dict bound to mapping= that spans rows, with the ( and { opening on the calling row and the } and ) closing together. The call explodes around the dict, so the ( ends its row, the dict block indents one step, and the ) takes a row of its own, because an argument spanning rows explodes the list around it.
emit passes alpha=1, a note= value written as an implicitly concatenated string run stacked across two rows, and zeta=2. alpha, note, and zeta each take a row of their own and the run's continuation moves to the item column, because an argument spanning rows explodes the call around it, and left inline the second row would read as another argument at a stray indent.
some_function_name(argument_one_x, argument_two_x)[0] arrives broken across two rows with a [0] subscript trailing the closer, under code-line-length = 64. The call explodes rather than rejoining, because a subscript trailing the call on its row can never be broken across lines, so its whole [0] counts toward the joined row where a breakable bracket would count only its opening column, and a join that would fit only by ignoring it is rejected.
fetch(headers=h, method=m, params=p, url=u) passes four keyword arguments to a function defined nowhere in this module. The call explodes to one argument per line, because each argument already names its parameter, so no signature lookup is needed and the missing definition does not block the count trigger.
The self.gggg… call sits inside a grouping pair passed as the first argument of qq.relative_to, under code-line-length = 40. qq.relative_to explodes, putting the pair and walk_up=True on rows of their own, and inside the pair the inner call's argument row sits one indent step past the row its opener is on with its closer returning to that row's indent, the column the pair opened at in the source playing no part in either placement.
The format call's ( sits on the second row of a concatenated string, one row below the row the statement opens on. alpha through zeta hang one indent step inside that second row and the closing ) drops back to that row's indent, rather than to the column the statement started at, because the explode works against the row the ( sits on.
configure passes name, other, more, and a settings= dict already written one entry per line. configure explodes to one keyword per line and the dict re-indents with it, so "alpha": 1 through "delta": 4 sit one step past settings={ and the } closes at the keyword column rather than the block being spliced in at its original indentation.
render passes name, other, more, and an items= list already spread across rows, with "a", "b", "c" and "d", "e", "f" packed onto its two rows. render explodes to one keyword per line and the list takes the same re-indent a dict value does, so the two packed rows move one step past items=[ and the ] closes at the keyword column.
The TypeError call and the format call inside it both arrive broken across rows. The whole raise rejoins onto one row in a single pass, rather than waiting for the inner rejoin to be written first, because measuring whether the outer call fits back on one row counts the nested call at its joined width, not at the truncated width its opening row shows.
compute(alpha_value, beta_value, gamma_value) calls a function defined nowhere in this module, so its positionals cannot be bound to parameter names, and the row overflows code-line-length = 40. The call explodes positionally, with alpha_value through gamma_value each on a row of its own and the closing ) back at the statement's indent, because no configuration leaves an over-budget call inline.
wrap(alpha, helper(beta, gamma, delta, epsilon)) arrives with helper's four arguments broken across rows, and helper resolves to no function defined in this module. The broken list closes onto one row, because beta through epsilon can never reach name=value form, so the count trigger cannot explode the call, and nothing downstream reopens it, leaving no reason to keep a layout neither trigger can improve.
fetch(headers=h, method=m, params=p, url=u) runs under max-args = false and code-line-length = 40. The call still explodes, because max-args = false switches off only the count trigger, whereas the width trigger stays live and the row crosses the budget, so no configuration leaves an over-wide call inline.
place(d, h, w) passes exactly three arguments to a place defined above it, sitting at the default max-args rather than over it, and the row fits comfortably. The call stays inline untouched, because neither the count trigger nor the width trigger fires.
The guess_quote_and_delimiter(sample_text, delimiters) call opens on the row after its =, behind a \ continuation, at the column the source indented it to. The call stays inline, because align-equals shifts no continued row, so the width check reads that column as written and finds the call well inside the budget.
helper(alpha, beta) would fit on one row with room to spare, yet it arrives with the ( ending its row and the ) opening its own. The layout stays as written rather than rejoining, whatever the joined form would measure, because a list whose ( ends its row and whose ) opens its own is exactly the layout the explode writes, so the rule leaves it in place.
bisect_left(data, target, low, high) calls the def above it in a module that also carries from _bisect import *. The four positionals stay exactly as written, because the star import can rebind bisect_left at runtime, so the visible def is not a signature the rule can safely name arguments against, and the keyword rewrite is skipped.
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 if condition is wrapped in a grouping pair, with the line break inside the find call's own parentheses rather than inside the pair. reflow-parentheses joins the interior onto one row and drops the redundant pair in a single pass, because the pair owns none of the breaks, leaving reflow-calls a call that already fits its row, so it changes nothing.
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.
deque([(start, 0)]), {start} assigns a call and a one-item set on one row that crosses the 50-column budget. reflow-collections never breaks a one-item set, so the row's tail counts at its full width rather than only up to its {. deque( explodes with its list argument packed on a row of its own, where it fits, rather than the inner tuple exploding under a column the explode would then have moved.
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.
The list in the cccc keyword of g crosses the 40-column budget, and its first member is the call fn(100, 1000). reflow-collections explodes the list, and reflow-calls leaves fn(100, 1000) on one line, because exploding it would measure against a tail the explode is about to remove, so the call is measured where its entry ends up and fits there.
some_function( argument_one_here, argument_two_here ) carries a space just inside each parenthesis, and the row crosses the 66-column budget by exactly those two spaces. strip-stranded-padding removes them, and reflow-calls measures the argument list at the width that removal leaves, so the call stays on one row rather than exploding and joining back once the padding is gone.
The tuple on the left of the = breaks before its closing parenthesis, which the source indents under the tuple's first element. reflow-collections moves that closer to the indent of the row its opener sits on, the statement's own indent. reflow-calls then explodes the type(self).prepare_context(...) call after the closer with its arguments hanging from the statement's indent, because the call is measured from that row rather than from the continuation row the source wrote.
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.
Two tuples sit on one row that crosses the 88-column budget, one inside isinstance(...) and one after in. The tuple inside isinstance comes first, so the overflow goes to that call, and the rest of the row counts at its full width rather than only up to the second tuple's (. isinstance( explodes with toklist and (type(None), basestring, list) each on a row of their own, and (None, "", []) stays on one row, where it already fits, the tuple inside isinstance staying packed as well.
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.
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.
base.first(alpha, is hand-wrapped mid-call, with beta).second(gamma).third(delta) on the next line. reflow-calls joins first(alpha, beta) onto one row, which settles the chain's width before stack-method-chains counts its links, and the link count breaks the chain, wrapping the expression in parentheses and stacking .second(gamma) and .third(delta) under .first(alpha, beta).
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.
SecureConnection.__init__ calls super(SecureConnection, self).__init__(...) with host, port, and timeout wrapped one per line under the open parenthesis. reflow-calls joins the three onto one row, and shed-super-args then removes SecureConnection and self from the call, leaving super().__init__(host, port, timeout), because the hand-aligned continuation lines are gone by the time the shortened call is measured.
The % tuple in the TypeError call crosses the 60-column budget, with type(other).__qualname__ as its first element and two closing parentheses stacked at the end of its last row. reflow-collections explodes the tuple to one element per row with its ) on a row of its own, and reflow-calls moves the TypeError closer onto its own row as well. The type(other).__qualname__ call stays on one line, because the explode moves self.__class__.__qualname__ and the two closers off its row, and type(other).__qualname__, alone fits the budget.
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.
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf)) passes two calls joined by +, and the row crosses the 40-column budget. _flatten( explodes directly, because reflow-calls measures it with the + self._options( that trails it on the same row counted in, whereas measuring the call alone would have left the row for reflow-collections to break at the tuple first and the call would then have exploded on a later pass anyway.
f returns an outer chain whose first link takes an inner chain as its argument, on a row past the 60-column budget. stack-method-chains wraps the outer chain in parentheses, and the row holding o.first_link_that_is_quite_long( still crosses the budget, so reflow-calls explodes that argument list. The inner chain stays joined on one line, because it is measured from the column it ends up at, one indent step past that row, rather than from the wider column the joined row would have given it, and it fits there.
dirlist.extend([ ... ]) writes a space just inside each bracket of its list, and strip-stranded-padding reports both. reflow-collections explodes the list, rebuilding the bracket interior and carrying that padding away before the strip is written, and the first member call ends up alone on a row inside the exploded list with no padding ahead of it. The _os.path.expanduser(...) call is measured from that row and explodes, because at that column it crosses the 60-column budget, rather than being measured from the row the source wrote.
_c returns two calls joined by and, each wrapped in a redundant grouping pair, on a row past the 60-column budget. reflow-calls explodes the first call and puts its closing ) back at the statement's indent, and reflow-parentheses measures the second pair from the column that closer leaves, where it fits, so the pair's interior joins onto that row and both pairs drop, rather than the second staying wrapped for a width the explode has already removed.
specs is assigned a tuple whose last element is tuple(_mapdict_values(statemap)), on a row past the 40-column budget. reflow-calls leaves a literal that reflow-collections is about to explode unwalked, so reflow-collections explodes the outer tuple and lays out the tuple( and _mapdict_values( calls inside it against the rows the explode leaves. Both calls explode at the column they end up at, because at that deeper column neither fits the budget.
The % tuple in warning ends with a self.userCfg[configType].Get(...) call, and exploding the tuple moves that final item four columns right, past the 79-column budget. reflow-collections reshapes the reflow-calls layout of that call against the row it ends up on, so the call explodes in the same run as the move, rather than on a later pass.
add_exception calls super(Waiter, self).add_exception(future) on a row past the 40-column budget. shed-super-args runs ahead of reflow-calls, so the bare super() is the text the length trigger measures, and the add_exception call explodes on that width rather than on the wider text the shed removes.
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.
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.
out.write(...) takes a chain of two .replace calls on repr(value), already stacked one link per line. stack-method-chains wraps the chain in parentheses and puts the receiver and its first link together on the row below the opener, and that row, holding repr(value).replace(, crosses the 88-column budget. reflow-calls explodes that link against the row it sits on rather than against the pair's opener, its two string arguments one indent step in from that row and its ) back at that row's indent, while the second .replace keeps its place in the . column below.
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.
Sorts sibling entries whose order carries no meaning, covering import names, dict keys, class-body members, keyword arguments, and docstring entries.
Expands a list, tuple, dict, or set literal across lines once it overflows code-line-length or a dict passes max-dict-entries, and rejoins a construct broken anywhere but an entry boundary.
Writes a function signature either on one line or one parameter per line, expanding it once it overflows code-line-length, passes max-params, or carries a parameter spanning rows.
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.
Removes the trailing comma from a collection, signature, call, class base list, or type-parameter list, leaving tuples alone.