Skip to content

Rule Composition

Each case on this page pairs a small Python source with the rule set it turns on and shows what those rules write when they run together in Pipeline Order. A rule page shows the cases its own rule takes part in, whereas this page runs each case through the whole listed set. The cards below render the previewable cases from crate/tests/fixtures/composition/, and the binary's integration tests run every case in that directory.

The Canonical Case

One module-level constant exercises a rule from each family:

  • Its values use the legacy Union[…] form, so modernize-annotations rewrites them to the | operator and removes the typing import they read through.
  • The literal overflows code-line-length on one line, so reflow-collections writes it one entry per line.
  • The entries sit in the order the author wrote them rather than alphabetical, so alphabetize-siblings sorts them.
  • The keys then sit one per line in a column, so align-colons computes the padding from the widths the rewrite leaves.
  • The removed import leaves a gap where it stood, so space-statements closes it.

The rules run against the same block, re-parsing between each so every later rule measures the rewritten source.

PRIMARY overflows its line with values written in the older Optional and Union forms, and target-version = "3.10" allows the | syntax. modernize-annotations rewrites each value to | form first, so every rule downstream measures the shorter text, and the dict then explodes, its keys sort so "delta_long" moves ahead of "gamma", and align-colons pads the : column against the rewritten widths. With nothing left reading either name, from typing import Optional, Union is removed.

PRIMARY = {
    "alpha"      : str | None,
    "beta"       : int | str,
    "delta_long" : bool | None,
    "gamma"      : int | None
}
SECONDARY = "fallback"
python

The Cases

  1. _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.

  2. The "alpha" and "beta" arms of dispatch each return a long inline dict, and the wildcard arm returns None. Each dict explodes to one entry per line, its keys sort so "comment_text" leads, and the : of each entry pads into one column within its arm, while align-match-case joins the wildcard's return None onto its case _: line.

  3. 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.

  4. 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.

  5. from .pgen2 import token is unread, and it is the line the # Local imports comment sits above, with from ..pgen2 import driver below it. band-constants sorts the import band and moves the comment onto whichever import ends up first, so when prune-inert-imports removes token, from ..pgen2 import driver moves up onto the vacated line with the heading still above it, and the comment is never left over a gap.

  6. visit names Node in its annotations and sits above class Node, under from __future__ import annotations. alphabetize-siblings moves the class above visit, and the output reads as if the directive were no longer needed. from __future__ import annotations stays, because prune-inert-imports runs before the sort, reads Node as a binding the sort is free to move either way, and keeps the directive rather than depending on where the sort puts the class.

  7. import sys, import os, and import json arrive out of order, and each module is used once below through a single attribute, os.getcwd(), sys.argv, and json.loads. alphabetize-siblings sorts the statements so json leads, and bare-imports reports all three, each finding landing on the import's row in the sorted output, so json is reported on row 1 and sys on row 3.

  8. 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.

  9. 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.

  10. 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.

  11. 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.

  12. 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.

  13. 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.

  14. 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.

  15. 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.

  16. One key of _hash_action carries a stranded space before its closing parenthesis, (False, False, False, True ), and strip-stranded-padding removes it. align-colons pads the run onto one : column, because it measures each row at the width it has once that removal is written, whereas the padded width would have read as a breach of the 40-column budget and the run would have skipped its column over a breach that never happens.

  17. import sys, os and import myapp.core, abc each name two modules in one statement, with myapp in first-party. reflow-imports splits each into one module per line ahead of group-imports and alphabetize-siblings, so every split-off module reaches its own group and its sorted position, and myapp.core ends up in the local section rather than beside the stdlib abc it was joined to.

  18. _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.

  19. from pkg import a is unread and sits directly under the # Local imports comment, with a blank line between it and from pkg import b. reflow-imports merges the two statements across that blank line, so when prune-inert-imports removes a, from pkg import b moves up onto the line a had, the blank line goes with b's old position, and the comment ends up heading the import that survives.

  20. from .pgen2 import token is the first line under the # Local imports comment, and nothing in the module reads it. reflow-imports merges that line into its .pgen2 sibling, so once it goes the comment heads from .pgen2 import driver. prune-inert-imports moves the sibling onto the vacated line rather than leaving the move to the merge on a later pass, which keeps a real import under the comment at every step.

  21. 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.

  22. 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.

  23. CONFIG is written as one overlong line with its keys out of order. The dict explodes to one entry per row, the keys sort, and every : pads into one column one space past the widest key, "beta_extended", so even the short "zeta" row takes the full padding to reach that column.

  24. tagdefs writes two spaces after each key's :, and alphabetize-siblings moves "stderr" ahead of "stdout", the comment above "stdout" moving with it. The "stdout" value, last after the sort, stays on one row at exactly the 40-column budget, because reflow-collections measures a value from the plain ": " past its key rather than from the two spaces the source wrote, and the row is written back at that width, whereas the "stderr" value explodes.

  25. The module top carries from __future__ import annotations, three aliased imports out of order, and three blank lines before def add. The directive is removed, collections, numpy, and requests sort, their as keywords pad into one column, and the gap closes to two blank lines before the function.

  26. __all__ lists six entries on one overlong line, three short names interleaved with three thirty-character ones. alphabetize-siblings sorts them so the short names lead, and reflow-collections packs them onto rows within the 88-column budget. The packing measures the widths the sort leaves, not the widths as written, which is why "cc" shares a row with the first long name rather than joining the two short entries above it, and the six entries settle onto three rows of two with no row widening afterward.

  27. 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.

  28. 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.

  29. 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.

  30. compose arrives exploded, one parameter per line, with a trailing , after verbose: bool. strip-trailing-commas removes that comma and neither rule changes anything else, because the stripped form is the layout reflow-signatures writes itself.

  31. 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.

  32. self.loggerMap is assigned a dict with spaces just inside its braces, and m a dict with no space after any :. strip-stranded-padding removes the spaces from the first and writes a space after each : in the second, and reflow-collections measures each literal at the width those edits leave, so the first stays on its row and only the second, now past the 40-column budget, explodes.

  33. The first element of the set s is written across two lines as (1, and 2). alphabetize-siblings sorts each element by the text it has once joined onto one row, not by the broken text in the source, so (1, 2) sorts after the long tuple.

    Sorting puts the long tuple first, which gives it a trailing comma, and measuring it with that comma takes it one column past the 40-column budget, so it explodes rather than staying on one row.

  34. 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.

  35. 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.

  36. convert names Alias in its annotations while Alias = int is written below it, under from __future__ import annotations. band-constants moves the assignment into the leading band, and prune-inert-imports removes the directive, because it counts the hoisted binding as written ahead of the annotations that name it.

  37. convert names Sequence in its annotations while from collections.abc import Sequence is written below it, the case the directive exists for. band-constants moves the import above the definition, and from __future__ import annotations is removed, because the relocated binding counts as written ahead of the annotations that name it and they resolve without the directive.

  38. render's docstring starts its summary on the """ line and carries an Args: section. frame-docstrings moves the summary off the opener line, align-colons pads the : after template, context_map, and escape_html into one column, and wrap-docstrings wraps the summary to the docstring budget across two lines.

  39. 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.

  40. d is a dict with # prose: keep at the end of its line, written with "b" before "a" and too wide for the 40-column budget. reflow-collections explodes the dict, which moves the marker onto the closing } line, and the marker still keeps the dict out of alphabetize-siblings, so "b" stays ahead of "a" and the entries keep their order and their separators, because the marker applies from the line of either the { or the }.

  41. 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.

  42. 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.

  43. _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.

  44. _FORMATS lists FMT_BINARY and FMT_XML in the order alphabetize-siblings leaves them, so the last entry gains no separator, and align-colons pads FMT_XML onto a : column that puts its row exactly at the 45-column budget. The column stays, because reflow-collections measures that row at exactly the budget rather than past it, and the padding is not stripped back.

  45. non_adjacent.append( (la, lb, 0) ) carries a space after append( and a space before its closing ), and the row crosses the 40-column budget by exactly those two spaces. strip-stranded-padding removes both, and reflow-collections measures the tuple at the width that removal leaves, so the tuple stays on its row, the first space narrowing the column the tuple starts at and the second narrowing the text trailing it.

  46. LIMIT = 3 sits between from pkg import alpha and from pkg import beta, which would ordinarily keep the two apart. band-constants moves the constant below the imports, and reflow-imports merges the two pkg statements onto one line in the same run, because it measures against the order the move leaves rather than the order as written, so no second pass is needed once the constant has moved.

  47. Three methods of Dispatcher sit out of order, and handle_alpha contains a match whose arms each return a single value. The methods sort so handle_alpha leads, align-match-case joins each return onto its case line and pads the : into one column, and space-statements writes one blank line between the methods.

  48. zeta, alpha, and beta arrive out of order, alpha with a two-line docstring and the other two with one-line docstrings. alphabetize-siblings sorts the methods, expand-docstrings rewrites the one-line docstrings of beta and zeta to multi-line form, and frame-docstrings puts every """ on its own line, with space-statements writing one blank line between the methods.

  49. paired lists three entries with uneven spacing before their :, and single lists one entry written "only_key" : 1. align-colons pads paired's : into one column one space past "second", and strip-stranded-padding removes the stray space in single, leaving "only_key": 1, because a lone entry forms no alignment group.

  50. 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.

  51. PRIMARY overflows its line with values written in the older Optional and Union forms, and target-version = "3.10" allows the | syntax. modernize-annotations rewrites each value to | form first, so every rule downstream measures the shorter text, and the dict then explodes, its keys sort so "delta_long" moves ahead of "gamma", and align-colons pads the : column against the rewritten widths. With nothing left reading either name, from typing import Optional, Union is removed.

  52. d opens with { # note and lists "zz" before "a": 1, "b": 2 packed on one row. The comment on the { row keeps reflow-collections from splitting that row, and a packed row is never reordered, so the dict keeps its source order under alphabetize-siblings. The "zz" entry is measured with the comma it carries as written rather than one a sort would have moved, its value is too wide for the 40-column budget, so that value explodes while "a": 1, "b": 2 stay packed on their shared row.

  53. s is a set packed across three rows with # note at the end of the first. The comment inside the set keeps its order fixed under alphabetize-siblings, so no element moves, and reflow-collections measures each element with the comma it carries as written rather than the one a sort would have left. The one element too wide for the 40-column budget explodes onto rows of its own, and the packed rows around it keep their commas and their places.

  54. Both Attributes: entries of Process run past the docstring budget before anything wraps them. align-colons pads both : to one column past command_message with no line cap of its own, and wrap-docstrings then reflows each description within docstring-line-length and hangs the wrapped lines from that padded column, so both entries reach their final layout in one pass rather than drifting across several.

  55. 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.

  56. _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.

  57. total is assigned a % template whose tuple arrives exploded, first and second each on their own line. reflow-collections condenses the tuple onto one line first, and prefer-fstring then measures the conversion against the budget and rewrites "%s and %s" % (first, second) to `f"{first} and {second}", each member inline in the template.

  58. 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".

  59. 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.

  60. 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.

  61. 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.

  62. 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.

  63. handler_map's return annotation crosses the 50-column budget, and the only collection literal on that row is the [Values, list[str]] list inside Callable. reflow-collections explodes that literal, and reflow-signatures keeps self inline, because it reads the opening row as ending at the literal's bracket rather than exploding a lone parameter to recover width the explode is about to give back.

  64. Bare, external from, and local imports sit scrambled together, with myapp named in first-party. group-imports partitions them into bare, external, and local sections, alphabetize-siblings sorts the names within each section, space-statements writes one blank line between sections, and align-imports pads the import keyword into a column within each section.

  65. y is a four-element set on one line past the 30-column budget. reflow-collections packs the rows at the widths alphabetize-siblings leaves in each slot, not the widths the source wrote, so the rows fill the budget as densely as a list's would and the sort is written without widening any row.

  66. 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.

  67. render's parameters template, context_map, and escape_html already list in the same order as the docstring's Args: entries. alphabetize-siblings leaves that order as written, align-colons pads the three entries' : into one column, and wrap-docstrings wraps each description with its continuation lines hanging under that column, because Args: entries mirror the signature rather than sorting alphabetically.

  68. store writes ttl : int with a space before the :, and its one-line signature crosses the 40-column budget by exactly that space. strip-stranded-padding removes it, and reflow-signatures measures the signature at the width that removal leaves, so it stays on one line rather than exploding and joining back on a later pass.

  69. The one Args: entry of fetch is written timeout : Maximum seconds to wait before giving up., with a space before its :, and its description runs past the 40-column docstring budget. strip-stranded-padding removes the space, because a lone entry has no sibling column to share, and wrap-docstrings breaks the description at the budget and hangs the continuation under the start of the text as the strip leaves it, one column narrower than the source, so both rules agree on where the continuation sits.

  70. table carries # prose: skip[alphabetize-siblings] on its closing line, listing "bb" before "aa". reflow-collections ordinarily measures each entry with the comma the sort is about to write, and the skip directive turns the sort off, and with it the measurement against the sorted order, so every entry is measured with the comma it already carries, and "bb" counts a trailing comma and breaks after its : rather than being measured as the last entry.

  71. 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.

  72. 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.

  73. 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.

  74. zebra, alpha, and mango sit out of alphabetical order at module level with no spacing between them. alphabetize-siblings sorts the definitions and space-statements writes two blank lines between each pair of top-level functions.

  75. 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.

  76. 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.

  77. from pkg import a and from pkg import b are both unread, and a is the line the # Local imports comment heads. from pkg import b carries no comment and is removed, and with it gone there is no sibling left for reflow-imports to merge a into, so from pkg import a stays under its comment rather than being removed too, because removing it would leave the comment over nothing.

  78. from pkg import a is never read and is the line the # Local imports comment heads, with X = 1 between it and from pkg import b. band-constants moves X = 1 below the imports, which brings the two pkg statements together, and with them adjacent from pkg import b moves up onto the line a vacates as a is removed, leaving the comment heading a real import rather than a gap.

  79. 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.

How Composition Resolves

Each case runs its listed rules in canonical order, and the sections below cover the common interactions.

Layout Before Alignment

reflow-collections runs before align-colons and writes the one-entry-per-line layout the alignment columns are computed from.

Reorder Before Align

alphabetize-siblings runs before align-equals and settles the entry order, so the alignment math measures the final column positions rather than the source ones.

Docstring Discipline Before Wrap

expand-docstrings and frame-docstrings run before wrap-docstrings and settle the quote placement before the body rewrap measures its budget.

Module Reorder Around a Block Marker

band-constants sorts the constants above a # fmt: off block while the lines inside the block stay as written, so both it and align-equals act freely outside the marked region.

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.

bar_baz = 3
foo     = 2
zebra   = 1

# fmt: off
matrix = [[0.7, 0.1, 0.1],
          [0.1, 0.7, 0.1],
          [0.1, 0.1, 0.7]]
# fmt: on
python

Click any rule chip above for its canonical case. The Pipeline Order reference lists the order the pipeline runs in, the Pipeline primitive covers the runner, and the Rules catalog lists the rest.