diff --git a/mypy/typeanal.py b/mypy/typeanal.py index b22e1f80be59..3c36ac80aa80 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -903,8 +903,12 @@ def analyze_type_with_type_info( if info.special_alias: return instantiate_type_alias( info.special_alias, - # TODO: should we allow NamedTuples generic in ParamSpec? - self.anal_array(args, allow_unpack=True), + self.anal_array( + args, + allow_unpack=True, + allow_param_spec=True, + allow_param_spec_literals=True, + ), self.fail, False, ctx, @@ -921,8 +925,12 @@ def analyze_type_with_type_info( if info.special_alias: return instantiate_type_alias( info.special_alias, - # TODO: should we allow TypedDicts generic in ParamSpec? - self.anal_array(args, allow_unpack=True), + self.anal_array( + args, + allow_unpack=True, + allow_param_spec=True, + allow_param_spec_literals=True, + ), self.fail, False, ctx, diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index b0808105a385..6be1d7983a0e 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2756,3 +2756,35 @@ reveal_type(Sneaky(f8, 1, y='').kwargs) # N: Revealed type is "builtins.dict[bu reveal_type(Sneaky(f9, 1, y=0).kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})" reveal_type(Sneaky(f9, 1, y=0, z='').kwargs) # N: Revealed type is "TypedDict('builtins.dict', {'x'?: builtins.int, 'y': builtins.int, 'z'?: builtins.str})" [builtins fixtures/paramspec.pyi] + +[case testNamedTupleAndTypedDictParamSpecInteraction] +# https://github.com/python/mypy/issues/21039 +from typing import Callable, NamedTuple, TypedDict, ParamSpec, Generic + +P = ParamSpec("P") + +class X(TypedDict, Generic[P]): + c: Callable[P, None] + +def f1(y: X[P]) -> None: ... +def f2(y: X[[int]]) -> None: ... +def f3(y: X[...]) -> None: ... + +x1: X[[str]] +f1(x1) +f2(x1) # E: Argument 1 to "f2" has incompatible type "X[[str]]"; expected "X[[int]]" +f3(x1) + +class Y(NamedTuple, Generic[P]): + c: Callable[P, None] + +def g1(y: Y[P]) -> None: ... +def g2(y: Y[[int]]) -> None: ... +def g3(y: Y[...]) -> None: ... + +y1: Y[[str]] +g1(y1) +g2(y1) # E: Argument 1 to "g2" has incompatible type "Y[[str]]"; expected "Y[[int]]" +g3(y1) +[builtins fixtures/paramspec.pyi] +[typing fixtures/typing-full.pyi] diff --git a/test-data/unit/fixtures/paramspec.pyi b/test-data/unit/fixtures/paramspec.pyi index a61e5b66ae24..af2331dbdc5d 100644 --- a/test-data/unit/fixtures/paramspec.pyi +++ b/test-data/unit/fixtures/paramspec.pyi @@ -51,8 +51,6 @@ class tuple(Sequence[T_co], Generic[T_co]): def __len__(self) -> int: ... def count(self, obj: object) -> int: ... -class _ItemsView(Iterable[Tuple[KT, VT]]): ... - class dict(Mapping[KT, VT]): @overload def __init__(self, **kwargs: VT) -> None: ... @@ -71,7 +69,7 @@ class dict(Mapping[KT, VT]): def get(self, key: KT, default: T, /) -> Union[VT, T]: ... def __len__(self) -> int: ... def pop(self, k: KT) -> VT: ... - def items(self) -> _ItemsView[KT, VT]: ... + def items(self) -> Iterable[Tuple[KT, VT]]: ... def isinstance(x: object, t: type) -> bool: ...