Skip to content

Fix TypeVarTuple with defaulted ParamSpec - #22378

Draft
Udit-Agarwal20 wants to merge 1 commit into
python:masterfrom
Udit-Agarwal20:fix-22375-typevartuple-defaulted-paramspec
Draft

Udit-Agarwal20 wants to merge 1 commit into
python:masterfrom
Udit-Agarwal20:fix-22375-typevartuple-defaulted-paramspec

Conversation

@Udit-Agarwal20

Copy link
Copy Markdown

Fixes #22375.

Summary

Fix incorrect type argument binding when a TypeVarTuple is followed by a defaulted ParamSpec, as permitted by PEP 696.

Previously, mypy incorrectly assigned arguments intended for TypeVarTuple to the trailing ParamSpec. This produced invalid substitution errors and caused inferred types to degrade to Any.

Changes

  • Correct argument partitioning for variadic generics followed by defaulted ParamSpec parameters.
  • Handle omitted ParamSpec defaults during generic instance and type alias normalization.
  • Move ParamSpec argument validation after variadic argument grouping.
  • Preserve type inference for both explicit and defaulted ParamSpec arguments.
  • Add regression tests for PEP 695 syntax, legacy generic syntax, type aliases, inheritance, and invalid substitutions.

Example

from collections.abc import Callable
from typing import assert_type

class Foo[*Ts, **P = [float, bool]]:
    def args(self) -> tuple[*Ts]: ...
    def fn(self) -> Callable[P, None]: ...

foo = Foo[int, str]()
assert_type(foo.args(), tuple[int, str])
assert_type(foo.fn(), Callable[[float, bool], None])

bar = Foo[int, str, [bytes]]()
assert_type(bar.args(), tuple[int, str])
assert_type(bar.fn(), Callable[[bytes], None])

Both specializations now preserve the expected types without erroneous ParamSpec substitution errors.

Testing

Local verification on macOS with Python 3.13:

  • Full type-checking suite: 8,363 passed.
  • Semantic, subtype, and types tests: 725 passed.
  • Command-line tests: 608 passed.
  • New regression tests: 5 passed.
  • Additional adversarial and combinatorial tests passed.
  • Incremental and daemon tests passed.
  • Mypy self-check passed.
  • Full lint workflow and pre-commit checks passed.
  • git diff --check passed.

Linux, Windows, and mypyc-compiled configurations remain to be validated by GitHub Actions.

@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

subscripting a class with a TypeVarTuple followed by a defaulted ParamSpec reports "Can only replace ParamSpec" and infers Any

1 participant