Skip to content

strip-trailing-commas

strip-trailing-commas removes the comma after the last entry of any bracketed container that carries one, and leaves tuples alone, because Python uses the trailing comma to tell a single-element tuple from a parenthesized expression. A trailing comma on the last entry of a multi-line collection adds a small visual hiccup at every block boundary without earning its keep, in that each entry already has its own line, so a new entry adds a new line of its own and the comma on the previous last entry brings no diff-stability win.

The rule reads every bracketed container (dictionaries, lists, sets, function signatures, function calls, class base lists, parenthesized argument lists, and the type-parameter list on a def, a class, or a type alias) and removes the comma after the last entry when one is present. The strip applies whether the container spans one line or many, in that f(a, b, c,) loses its comma the same way a multi-line call does, though a single-line container rarely carries one in idiomatic Python. A comment between the last entry and the closing bracket is trivia the rule reads past, so the comma goes and the comment stays. Pair with reflow-collections for the multi-line expansion that puts the trailing comma in reach in the first place.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueTurns the rule on or off.

The strip is unconditional within the contexts named above, so the rule carries enabled as its only facet. Tuple literals are exempt by construction, because Python uses the trailing comma to tell a single-element tuple from a parenthesized expression, leaving no project-level switch on the tuple carve-out.

The Canonical Case

config spreads four entries over one line each, and the last, "threshold": 0.7,, carries a , before the closing }. That comma is removed, so the last entry reads "threshold": 0.7 and the dict ends on its value.

config = {
    "linkage": "ward",
    "metric": "euclidean",
    "n_clusters": None,
    "threshold": 0.7
}
python

More Examples

Posting(...) spreads three keyword arguments over one line each, and the last, title="Electrician",, carries a , before the closing ). That comma is removed, so the call ends on its last argument rather than on a dangling separator.

Loader lists its bases BaseLoader and CacheMixin one per line, with a , after CacheMixin before the closing ). That comma is removed, because a base list is bracketed the same way a call's argument list is and the rule reads it the same way.

retired spreads three strings over one line each, and the last, "INST_2021_0044",, carries a , before the closing ]. That comma is removed, by the same read from the closing bracket that strips a dict's last entry.

compute takes (a + b) and (c * d), each wrapped in its own parentheses, so an inner ) sits between the last expression and the call's trailing ,. The comma after (c * d) is removed, because the rule reads backward from the call's outer ) and reaches the comma directly, with the inner ) never in its way.

load_lexicon spreads three parameters over one line each, and the last, fallback: dict[str, str],, carries a , before the closing ). That comma is removed, because a signature's parameter list is read the same way as any other argument list.

Posting(...), its keywords list, its metadata dict, and the tags list inside metadata each carry a , before their own closing bracket. Each container's trailing comma is removed, one deletion edit per container, so nesting the four inside one another changes nothing about how each is stripped.

No Change

Posting(company="Cianbro", title="Electrician") spreads its two arguments over one line each and already ends without a , before the closing ). The rule emits no edit, because the token before the ) is "Electrician" rather than a comma, so the source is written back exactly as it came.

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

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

For block-level opt-outs (a project that keeps the trailing comma for diff stability even on multi-line forms), Suppression covers the # fmt: off / # fmt: on block markers.