Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix crash when passing too many type arguments to generic base class accepting single ParamSpec #17770

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

brianschubert
Copy link

Fixes #17765

The offender for this crash appears to be this snippet:

mypy/mypy/semanal.py

Lines 5905 to 5910 in 72c413d

if has_param_spec and num_args == 1 and types:
first_arg = get_proper_type(types[0])
if not (
len(types) == 1 and isinstance(first_arg, (Parameters, ParamSpecType, AnyType))
):
types = [Parameters(types, [ARG_POS] * len(types), [None] * len(types))]

This branch triggers when applying type args to a type that is generic with respect to a single ParamSpec. It allows double brackets to be omitted when providing a parameter specification by wrapping all of the provided type arguments into a single parameter specification argument (i.e. equating Foo[int, int] to Foo[[int, int]]). This wrapping occurs unless:

  • there is only a single type argument, and it resolves to Any (e.g. Foo[Any])
  • there is only a single type argument, and it's a bracketed parameter specification or a ParamSpec (e.g. Foo[[int, int]])

The problem occurs when multiple type arguments provided and at least one of them is a bracketed parameter specification, as in Foo[[int, int], str].

Per the rules above, since there is more than 1 type argument, mypy attempts to wrap the arguments into a single parameter specification. This results in the attempted creation of a Parameters instance that contains another Parameters instance, which triggers this assert inside Parameters.__init__:

assert not any(isinstance(t, Parameters) for t in arg_types)

I think a reasonable solution is to forgo wrapping the type arguments into a single Parameters if any of the provided type arguments are a Parameters/ParamSpecType. That is, don't transform Foo[A1, A2, ...] to Foo[[A1, A2, ...]] if any of A1, A2, ... are a parameter specification.

This change brings the crash case inline with mypy's current behavior for a similar case:

# Current behavior
P = ParamSpec("P")
class C(Generic[P]): ...
c: C[int, [int, str], str]  # E: Nested parameter specifications are not allowed

Before this change:

P = ParamSpec("P")
class C(Generic[P]): ...
class D(C[int, [int, str], str]): ... # !!! CRASH !!!

After this change:

P = ParamSpec("P")
class C(Generic[P]): ...
class D(C[int, [int, str], str]): ... # E: Nested parameter specifications are not allowed

Copy link
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.

Crash when passing too many type arguments to generic base class accepting single ParamSpec
1 participant