Skip to content

align-comments

A trailing comment starts wherever its line of code happens to stop, so a block of annotated lines opens its notes at a different offset on every row even after the assignments beside them have settled into a column. The eye then tracks two ragged edges instead of one, and the notes read as a scatter rather than as the second column they are.

gathers a run of consecutive trailing comments onto one shared offset, so the annotations read straight down beside the code they describe.

The rule walks the trailing comments in source order and grows a run while each next comment sits on the line directly below the last. A line carrying no trailing comment, a blank line, and an own-line comment each end the run, and a run whose rows open at differing indents realizes no column at all, since a shared offset would land where neither row's code actually ends. The shared column sits

's two-space gap past the widest row in the run, which keeps every aligned row at or above the PEP 8 floor.

Three consecutive lines each carry a trailing comment, and each comment starts wherever its own code happened to end. The rule seats every # in the run two columns past the widest line, so the annotations read down one edge the way the assignments beside them already do.

attempts  = 1    # retries before the backoff widens
threshold = 200  # rows per flush
window    = 30   # seconds
python

A row that reaches no shared column takes the two-space floor instead, which is what settles a hand-set gap lining up with nothing. That covers a lone trailing comment, since a run of one has no column to answer to, and it covers a row partitioning out of a run. Two caps drive that partition, the max-shift spread budget the family shares and the code-line-length budget, wherein a row whose aligned line would cross the budget stays where it sits rather than manufacturing an overflow for

to flag.

A one-entry-per-line collection is the shape trailing comments crowd most, since each entry ends at a different width and every note starts somewhere new. The entries sit at one indent on consecutive lines, so the run settles on a single column and the notes read as a second column beside the values.

RETRY_DELAYS = [
    1,    # immediate
    30,   # after the first failure
    3600  # hourly from here on
]
python

The column resolves to two past the widest row whether that pulls a comment right or left, so a run whose notes already line up at a wider gutter tightens onto that same offset. The alignment the author drew survives, with the slack past it removed the way

removes padding that lines up with nothing.

Every row here is the same width, so the comments already line up and the run carries no spread at all. The shared column still resolves to two past the widest row, which pulls the hand-set gutter in without disturbing the alignment it was drawn to create.

DECODING_TABLE = {
    0x0080 : 0x00C7,  # LATIN CAPITAL LETTER C WITH CEDILLA
    0x0081 : 0x00FC,  # LATIN SMALL LETTER U WITH DIAERESIS
    0x0082 : 0x00E9   # LATIN SMALL LETTER E WITH ACUTE
}
python

The first two rows settle on a shared column, and the third would need its already-long line pushed past code-line-length to reach that same column. It partitions out of the run and keeps the two-space floor instead, so alignment never manufactures an overflow.

warm_the_cache_now()      # cheap
reconcile_open_charges()  # slow
flush_writes()  # padding this one out to the shared column would cross the budget
python

A # prose: skip on a row holds it out of the column math without ending the run, so the rows above and below it reach across and settle together. The directive is itself a trailing comment, which is what lets a single annotation both name the exception and carry it.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueToggles the rule on or off.
max-shiftpositive int | 0 | false16The width-spread budget a contiguous run may shift to reach the shared column. A positive N caps the spread, 0 forbids any shift so every row sits flush, and false lifts the cap so a contiguous run folds into one column. To hold one row out of an otherwise-aligned group, mark it with # prose: skip.

max-shift bounds how far a comment may shift to reach the shared column. The rule walks each run in source order and grows a column while its width spread stays within the cap, cutting a fresh column at the first row that would exceed it. A max-shift of false lifts the cap so a contiguous run folds into one column, and 0 forbids any shift, leaving every trailing comment at the two-space floor. The per-rule facets reference covers the full semantics.

The Canonical Case

Three consecutive lines each carry a trailing comment, and each comment starts wherever its own code happened to end. The rule seats every # in the run two columns past the widest line, so the annotations read down one edge the way the assignments beside them already do.

attempts  = 1    # retries before the backoff widens
threshold = 200  # rows per flush
window    = 30   # seconds
python

More Examples

Setting max-shift to 0 forbids any shift, so no row may travel to meet another and each stands alone at the floor. The rule reduces to a settle pass, taking the hand-set column back down to the two spaces PEP 8 prescribes.

Setting max-shift to false lifts the spread budget, so a run the default cap would split folds into one column measured at its widest row. The narrow row reaches all the way across rather than falling back to the two-space floor.

A comment carrying no text after its hash is still a trailing comment, so it travels to the shared column rather than sitting out. The rule measures the code ahead of the # and never reads what follows it.

The blank line ends the first run, so the pair above it and the pair below it each settle on their own column rather than reaching across the gap for one shared offset. Each column measures only the lines it actually spans.

One annotated line is not a run, so the wide gap has no column to answer to and it settles back to the two-space floor. A row reaching no shared column ends up flush the same way whether it stood alone or partitioned out of one.

The first two rows settle on a shared column, and the third would need its already-long line pushed past code-line-length to reach that same column. It partitions out of the run and keeps the two-space floor instead, so alignment never manufactures an overflow.

The middle row carries a # prose: skip directive naming this rule, so it neither moves nor ends the run. The rows above and below reach across it and settle on one column measured from their own widths alone.

The two rows differ by more than max-shift allows, so neither can reach the other's column and each stands alone. A row standing alone takes the two-space floor, which strips the hand-set gap on the narrow row down to the width it would carry with nothing beside it.

Every row here is the same width, so the comments already line up and the run carries no spread at all. The shared column still resolves to two past the widest row, which pulls the hand-set gutter in without disturbing the alignment it was drawn to create.

leaves a #: or #! opener untouched, and that exemption covers what a comment opens with rather than where it sits. A Sphinx attribute doc and a type pragma therefore travel to the shared column alongside any other trailing comment.

A comment on its own line is a heading for what follows rather than an annotation on a line of code, so it never joins a column and it ends the run above it. The pairs on either side settle independently, leaving the heading flush at the indent it was written on.

A one-entry-per-line collection is the shape trailing comments crowd most, since each entry ends at a different width and every note starts somewhere new. The entries sit at one indent on consecutive lines, so the run settles on a single column and the notes read as a second column beside the values.

No Change

The pair qualifies as a column and resolves to the offset it already occupies, so every row's target matches what it carries and no edit is produced. The rule reports nothing rather than reporting a fix that would change no bytes.

No Change

The two annotated lines sit on adjacent rows yet open at different indents, so a shared column would land where neither row's code actually ends. The run realizes no column and both comments keep the two-space gap they carry.