Skip to content

call-layout

A call carrying enough arguments to read as a wall of positionals folds better one argument per line, where the eye reads each binding on its own and a later edit touches a single line.

takes a call whose argument count exceeds max-args and breaks it so each argument lands on its own line in keyword form, leaving shorter calls inline.

The pass fires only on a call every argument of which is keyword-expressible. A positional argument resolves to its parameter name through the call site's in-module binding, so the exploded form reads name=value whatever order the source passed it. A bare generator expression, a walrus binding, and a yield or yield from parse as a positional argument and not after a name= prefix, so each takes a grouping pair as it binds to its parameter name and the exploded call still parses. A positional-only prefix, a * or ** unpacking, and a positional call whose callee does not resolve to a module function each leave the call inline, because the rule cannot name those arguments. A from x import * clouds resolution the same way, since the wildcard can rebind any name the visible def appears to define, leaving the call inline even though the source shows a matching signature. The expanded form lays each argument one indent step past the call, the closing ) dropping to the call's own indent, and a nested eligible call in an argument value explodes in the same pass, a chained call settling its receiver before the link that carries it so each link of a method chain measures the column it lands at.

A call whose source already spans lines answers the budget the same way a single-line call does, in that the pass measures the argument list a join would produce rather than declining on the break it finds. Every measure reads the column a construct lands at once its parent settles, so a nested call that fits its destination row stays inline where measuring at its source column would have exploded it. The same reading covers the padding an alignment adds, wherein a keyword value in an exploded call answers the budget from the column

shifts it to rather than from the tight name=value the source wrote.

Where an exploded keyword's value is a dict, list, or comprehension an earlier pass already broke across lines,

re-indents that block to the keyword column rather than splicing it in at its old position, its body one step past the keyword and its closing bracket back at the keyword column. A value whose lines run through a multi-line string keeps its source shape, leaving the string's interior untouched.

Neither trigger reaches a call inside an f-string or t-string replacement field. A line break spliced into a single-quoted field is PEP 701 syntax that parses on Python 3.12 and later and fails everywhere earlier, so the literal is opaque to layout whatever its width and an over-wide interpolation is left for

to report.

The rule reshapes layout and nothing more, leaving argument order to

, which runs ahead of it, so disabling alphabetization leaves the exploded arguments in source order. The = spacing stays with , and whether the last argument carries a trailing comma stays with , which the explode carries through untouched.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueToggles the rule on or off.
max-argspositive int | false3Explodes a call to one keyword argument per line once its argument count exceeds the cap. false disables the count trigger and leaves every call inline.

The Canonical Case

A four-argument call to an in-module function explodes to one keyword argument per line, each positional argument named from the resolved signature and the closing ) dropping to the call's own indent.

def connect(host, port, timeout, user):
    return (host, port, timeout, user)


session = connect(
    host=h,
    port=p,
    timeout=t,
    user=u
)
python

More Examples

A multi-line collection argument re-indents to the keyword column, and the calls inside it explode in the same run, each measured from the row it lands on rather than the row it occupied in the source.

The enclosing call opens to one argument per line while the len(...) inside the f-string keeps its source shape, because an f-string literal is opaque to layout however wide the line it sits on runs. Splicing a break into a single-quoted replacement field parses only on Python 3.12 and later, so the interior is left for

to report.

An argument value that is not itself a call still carries its nested calls into the reshape, so a call sitting inside an operand answers the budget from the row it lands on.

The call carries three arguments, at max-args, so the count trigger leaves it inline. Its line still crosses code-line-length, so the width trigger explodes it to one keyword per line regardless of the count. The *-line-length cap is a hard constraint the count knob sits beneath.

A method chain settles its receiver before the link that carries it, so each link answers the budget from the column it lands at rather than the one it occupied in the source. The receiver that already fits stays inline, and so does the trailing link the explode drops back to the margin.

A call already broken across lines is measured by the argument list its join would produce, so a hand-wrapped continuation whose inline form crosses the budget explodes to one argument per line.

The length trigger reads the column a nested call reaches inside the exploded row rather than the column it occupied in the source, so a short call that crossed the budget where it sat stays inline where it lands.

An eligible call passed as an argument value explodes alongside its enclosing call, the recursion resolving both levels before the rule returns so the layout is stable in one pass.

The callee binds its leading arguments positionally, so the call cannot take full keyword form. The width trigger explodes it positionally rather than leaving it inline, one argument per line with the closing ) at the call's indent.

A call explodes to one keyword per line while one keyword holds a single-line dict value. The value stays inline rather than re-indenting, since a single-line collection block has no rows to shift.

A keyword value in an exploded call answers the budget from the column

pads it to, not from the column the tight name=value gave it. A nested call that fits its source column and crosses the padded one explodes here rather than leaving an over-budget line for the next run.

The callee resolves outside the module, so the call cannot take keyword form. The width trigger still answers it, exploding the positional arguments one per line with the closing ) at the call's indent, so no configuration leaves an over-cap call inline.

An all-keyword method call explodes the same as a free function, the attribute callee carrying its arguments without any signature resolution.

A four-argument all-keyword call explodes regardless of where its callee is defined, because every argument already names its parameter and needs no signature resolution.

max-args = false opts out of the count trigger, yet the width trigger stays live, so a call whose line crosses code-line-length still explodes. No configuration leaves an over-cap call inline.

A call explodes to one keyword per line while one keyword already carries a dict value spread one entry per line. That block re-indents so its entries sit at the keyword's item-indent plus a step and its } at the keyword column, rather than splicing in at the dict's original indentation.

The same re-indent a dict value takes carries a list value. A call explodes to one keyword per line while one keyword already holds a list spread across rows, and that block shifts so its rows sit at the keyword's item-indent plus a step and its ] at the keyword column.

No Change

A three-argument call sits at the default max-args cap and reads fine on one line, so the rule leaves it untouched.

No Change

A value that opens on a line after its = keeps the column the source gave it, because

sits out a continued row rather than shifting it. The length trigger reads that real column, so a call well inside the budget stays inline instead of answering to a shift the alignment never makes.

No Change

A from x import * can rebind the callee at runtime, so the rule declines to name the positional arguments against the source-visible def and leaves the call as written.