Skip to content

Respect the magic trailing comma in starred type parameter lists - #5244

Merged
cobaltt7 merged 2 commits into
psf:mainfrom
dchaudhari7177:fix/magic-trailing-comma-starred-type-params
Jul 21, 2026
Merged

Respect the magic trailing comma in starred type parameter lists#5244
cobaltt7 merged 2 commits into
psf:mainfrom
dchaudhari7177:fix/magic-trailing-comma-starred-type-params

Conversation

@dchaudhari7177

Copy link
Copy Markdown
Contributor

Summary

Fixes #5243. A magic trailing comma explodes a PEP 695 type-parameter list of plain type vars, but was silently ignored once the list contained a *TypeVarTuple or **ParamSpec:

class C[
    T,
    *Ts,
    **P,
]:
    pass

collapsed back to

class C[
    T, *Ts, **P,
]:
    pass

Root cause

is_split_before_delimiter / is_split_after_delimiter deliberately skip a star that is_vararg() recognises, with the comment:

# * and ** might also be MATH_OPERATORS but in this case they are not.
# Don't treat them as a delimiter.

is_vararg() decides that by checking the star's parent against VARARGS_PARENTS, which listed the argument/lambda nodes but not the PEP 695 typevartuple and paramspec nodes:

leaf='*'  parent_sym=typevartuple   is_vararg=False
leaf='**' parent_sym=paramspec      is_vararg=False

So the stars in a type-parameter list were scored as * and ** math operators, and that delimiter outranked the comma when the line was split — which is why the trailing comma stopped mattering.

Adding both nodes to VARARGS_PARENTS restores the intended "not a delimiter" treatment.

Scope

Fixes def, async def, class and type aliases alike, matching the issue. The non-magic case is unchanged — def all_in[T: int, U: (bytes, str), *Ts, **P]() still stays on one line (pinned by the existing fixture case).

Tests

Added magic_starred (def), MagicStarred (class) and MagicStarredAlias (type alias) to tests/data/cases/type_params.py, right beside the existing plain-type-var magic[Trailing, Comma,] case.

Reverting only src/ fails the fixture with exactly the reported collapse, on all three constructs:

-    *Ts,
+    T, *Ts, **P,
-    *Ts,
+    T, *Ts, **P,
-    *Ts,
+    T, *Ts, **P,

Verification

  • pytest tests/ -k "not symlink"464 passed, 3 skipped, 8 subtests passed — no formatting regressions across the fixture corpus
  • The 6 deselected symlink tests fail on a clean tree on my machine (OSError: WinError 1314 A required privilege is not held by the client — Windows needs elevation to create symlinks), verified by stashing; unrelated to this change

I'll add the CHANGES.md entry under Stable style in a follow-up commit on this branch, since the file asks for the PR number rather than the issue number and I only get that once this is opened.

A magic trailing comma exploded a PEP 695 type parameter list of plain
type vars, but was silently ignored once the list contained a
*TypeVarTuple or **ParamSpec, collapsing the params back onto one line.

is_split_before_delimiter/is_split_after_delimiter skip a star that
is_vararg() recognises, so it is not mistaken for a MATH_OPERATORS
delimiter. is_vararg() checks the star's parent against
VARARGS_PARENTS, which listed the argument and lambda nodes but not the
PEP 695 typevartuple and paramspec ones. The stars in a type parameter
list were therefore scored as multiplication and power operators, and
that delimiter beat the comma when the line was split.

Add both nodes to VARARGS_PARENTS. This affects def, async def, class
and type aliases alike.

Closes psf#5243
@github-actions

Copy link
Copy Markdown
Contributor

diff-shades results comparing this PR (94cfff8) to main (51abf53):

--preview style: no changes

--stable style: no changes


What is this? | Workflow run | diff-shades documentation

@cobaltt7
cobaltt7 merged commit a20a016 into psf:main Jul 21, 2026
57 checks passed
@cobaltt7

Copy link
Copy Markdown
Collaborator

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants