Skip to content

Commit 317533c

Browse files
Fix crash on TypedDict unpacking for ParamSpec (#17358)
Fixes #17345 Fixes #17112 Fixes #16616 Oh well, I clearly remember I have put those lines before `if` only because otherwise the line would be 101 chars long, and I didn't want to wrap arguments. Now I see it was a bad idea, LOL. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 4fa4657 commit 317533c

File tree

2 files changed

+31
-3
lines changed

2 files changed

+31
-3
lines changed

mypy/constraints.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,6 @@ def infer_constraints_for_callable(
223223
if actual_arg_type is None:
224224
continue
225225

226-
actual_type = mapper.expand_actual_type(
227-
actual_arg_type, arg_kinds[actual], callee.arg_names[i], callee.arg_kinds[i]
228-
)
229226
if param_spec and callee.arg_kinds[i] in (ARG_STAR, ARG_STAR2):
230227
# If actual arguments are mapped to ParamSpec type, we can't infer individual
231228
# constraints, instead store them and infer single constraint at the end.
@@ -243,6 +240,12 @@ def infer_constraints_for_callable(
243240
)
244241
param_spec_arg_names.append(arg_names[actual] if arg_names else None)
245242
else:
243+
actual_type = mapper.expand_actual_type(
244+
actual_arg_type,
245+
arg_kinds[actual],
246+
callee.arg_names[i],
247+
callee.arg_kinds[i],
248+
)
246249
c = infer_constraints(callee.arg_types[i], actual_type, SUPERTYPE_OF)
247250
constraints.extend(c)
248251
if (

test-data/unit/check-typeddict.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,3 +3525,28 @@ class B(A):
35253525
reveal_type(B.f) # N: Revealed type is "def (self: __main__.B, **kwargs: Unpack[TypedDict('__main__.TD', {'x'?: builtins.int, 'y'?: builtins.str})])"
35263526
B().f(x=1.0) # E: Argument "x" to "f" of "B" has incompatible type "float"; expected "int"
35273527
[builtins fixtures/primitives.pyi]
3528+
3529+
[case testTypedDictUnpackWithParamSpecInference]
3530+
from typing import TypeVar, ParamSpec, Callable
3531+
from typing_extensions import TypedDict, Unpack
3532+
3533+
P = ParamSpec("P")
3534+
R = TypeVar("R")
3535+
3536+
def run(func: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R: ...
3537+
3538+
class Params(TypedDict):
3539+
temperature: float
3540+
3541+
def test(temperature: int) -> None: ...
3542+
def test2(temperature: float, other: str) -> None: ...
3543+
3544+
class Test:
3545+
def f(self, c: Callable[..., None], **params: Unpack[Params]) -> None:
3546+
run(c, **params)
3547+
def g(self, **params: Unpack[Params]) -> None:
3548+
run(test, **params) # E: Argument "temperature" to "run" has incompatible type "float"; expected "int"
3549+
def h(self, **params: Unpack[Params]) -> None:
3550+
run(test2, other="yes", **params)
3551+
run(test2, other=0, **params) # E: Argument "other" to "run" has incompatible type "int"; expected "str"
3552+
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)