Skip to content

collection-layout

A dictionary, list, or set with five non-trivial entries on one line reads as a single chunky token, and the reader's eye flicks across to find the entry it wants. The same data on five separate lines reads as a column of entries, each one a unit.

expands multi-entry collections to the one-per-line shape whenever the entries cross the atomicity threshold, and it leaves short single-line collections alone when each entry is already small enough to skim.

The rule fires on dictionary, list, set, and tuple literals. A literal expands when any entry is non-atomic (a function call, a nested collection, a computed expression) or when the entry count exceeds max-atomics-per-line. Single-line collections of atomic literals (ints, floats, strings, single-name identifiers) inside the cap stay on one line. Pair with

for the dict-key alignment after the expansion, with for sibling sorting where ordering doesn't matter, and with for the trailing-comma sweep on the multi-line form.

The rule runs the inverse move as well, joining a multi-line construct whose single-line form fits the budget back onto one line. The join reaches the four literals, a multi-line subscript such as data[key] or matrix[row + step], and a multi-line collection key inside a dict, so a tuple key split across lines rejoins and reads as the single clean member

and fold into their run. A construct that would overflow once joined, or whose subscript index carries a call or other member the single-line form cannot rejoin, keeps its source break for the cross-line guard.

A dict also expands once it holds more than max-inline-dict-entries entries, whatever its width, taking any enclosing collection with it. It mirrors

's max-inline-params, the same count-gate shape applied to parameters. The trigger is dict-only, since a list or set reads acceptably as a packed run while a dict's key-value pairs earn the vertical layout. Set the facet to false to leave width as the only dict gate.

A dict entry whose key: value width overflows the budget at the item-indent column breaks at : and hangs the value at item_indent + INDENT_STEP. The hang applies per-row, so a multi-item dict hangs only the rows that need it. A single-entry dict whose entry overflows enters the expand path and applies the same break. Tuples, lists, and sets stay out of the hang shape because their elements carry no : separator.

Each shape move sits behind its own facet, so a project can switch one off without disturbing the others. collapse governs the inverse-join back to one line, explode governs every expansion (the width-driven spread and the max-inline-dict-entries count trigger alike, so false leaves the cap inert), and wrap-dict-entries governs the over-wide-entry break at :. Each defaults on, preserving the combined behavior above, and clearing one freezes that move while the others keep running.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueToggle the rule on or off
collapsebooltrueJoin a fitting multi-line literal, subscript, or dict key back to one line. Setting false freezes those shapes where they sit
explodebooltrueExpand an overflowing or over-count collection to one entry per line. Setting false suppresses every expansion and leaves max-inline-dict-entries inert
max-atomics-per-linepositive int | false8Keep short collections on one line when each entry is an atomic literal and the run fits the cap. Setting false removes the cap and packs each line by width alone
max-inline-dict-entriespositive int | false3Expand a dict once its entry count exceeds the cap, whatever its width. Setting false disables the count trigger and leaves width as the only dict gate
wrap-dict-entriesbooltrueBreak an over-wide key: value at its : and hang the value beneath. Setting false leaves the oversized entry on one line

A short tuple inside a function-call argument list, like numpy.zeros((3, 4)), stays inline at the default cap. A dict literal with eight non-atomic entries expands regardless of length. A four-entry dict expands at the default max-inline-dict-entries of 3 even when it fits the line.

The Canonical Case

A dict literal with non-atomic entries expands to one entry per line, and the reader reads the entries as a column of key-value pairs.

python
short_dict = {"alpha": 1, "beta": 2, "gamma": 3}
long_dict = {
    "alpha": 1,
    "beta": 2,
    "gamma": 3,
    "delta": 4,
    "epsilon": 5,
    "zeta": 6,
    "eta": 7
}
single_entry_dict = {"default_action": "noop"}
collapsing_dict = {"alpha": 1, "beta": 2, "gamma": 3}

More Examples

Literals, names, attribute chains, and unary operands are all atomic and flow together across balanced lines. A non-atomic call expression in the middle breaks its surrounding run into two independent flow segments, each balancing on its own.

With collapse off, a fitting multi-line literal stays put while an overflowing single-line literal still explodes to its packed rows. The collapse and explode facets move independently, so clearing one leaves the other live.

Setting wrap-dict-entries to false keeps an over-wide key: value on a single line as the dict expands, rather than breaking at : and hanging the value beneath. The width and count caps keep their meaning.

A nested matrix stays fully inline within the 88-character budget. When the outer list overflows, it expands row by row, each inner row checking fit at its own column and staying inline when it fits, producing the matrix-by-row layout rather than one cell per line.

A dict row whose key: value width overflows at the item-indent column breaks at : and hangs the value at item_indent + INDENT_STEP. Rows that fit stay on one line, and an already-multi-line nested value runs its own expansion without triggering the hang.

A dict carrying more entries than max-inline-dict-entries breaks to one entry per line even when it fits the line budget, while a dict at or under the cap stays inline and an already-expanded dict past the cap stays expanded.

A list stays inline within the 88-character budget and expands when it overflows. Inside the expanded form, atomic items flow across as few balanced lines as fit under code-line-length and the max_atomics_per_line cap. A multi-line list that fits collapses back inline.

A multi-line collection key joins onto one line so it reads as the single clean member the alignment and ordering rules fold into their run rather than one stranded across a break. A key that would overflow once joined keeps its source break.

Each nested collection runs its own layout decision at its own column. An expanding outer literal drags its inners along, whereas a pinned outer leaves each inner to its own pass. Dict-value nesting folds the key-text offset into the inner's column, so a long key can push its value past the budget even when the outer fits.

No Change

Setting collapse to false freezes the collapse targets in place. A fitting multi-line literal, a multi-line subscript, and a multi-line collection dict key each stay spread across the lines they were written on rather than joining back to one.

No Change

Setting explode to false suppresses every expansion. An overflowing literal keeps its single line, an over-count dict that fits stays inline because the count cap goes inert, and a literal already spread across lines is left untouched.

No Change

With explode off, a dict whose key: value entry overflows the budget keeps its whole single line rather than expanding and hanging the value beneath. The wrap is a facet of the expansion explode gates, so clearing explode freezes it too.

No Change

An inline literal that already fits and a multi-line literal whose inline form would overflow both round-trip with no edit. A dict already hanging an oversized entry's value at the post-: column also passes through unchanged.

No Change

List, dict, set, and generator comprehensions are not literals, so the rule leaves them inline and never expands them. The visitor returns no rewrite for these shapes and keeps descending through their bodies.