@@ -191,6 +191,7 @@ functools.partial(1) # E: "int" not callable \
191191
192192[case testFunctoolsPartialStar]
193193import functools
194+ from typing import List
194195
195196def foo(a: int, b: str, *args: int, d: str, **kwargs: int) -> int: ...
196197
@@ -215,6 +216,13 @@ def bar(*a: bytes, **k: int):
215216 p1("a", **k) # E: Argument 2 to "foo" has incompatible type "**Dict[str, int]"; expected "str"
216217 p1(**k) # E: Argument 1 to "foo" has incompatible type "**Dict[str, int]"; expected "str"
217218 p1(*a) # E: List or tuple expected as variadic arguments
219+
220+
221+ def baz(a: int, b: int) -> int: ...
222+ def test_baz(xs: List[int]):
223+ p3 = functools.partial(baz, *xs)
224+ p3()
225+ p3(1) # E: Too many arguments for "baz"
218226[builtins fixtures/dict.pyi]
219227
220228[case testFunctoolsPartialGeneric]
@@ -408,33 +416,83 @@ def foo(cls3: Type[B[T]]):
408416from typing_extensions import TypedDict, Unpack
409417from functools import partial
410418
411- class Data(TypedDict, total=False):
412- x: int
413-
414- def f(**kwargs: Unpack[Data]) -> None: ...
415- def g(**kwargs: Unpack[Data]) -> None:
416- partial(f, **kwargs)()
417-
418- class MoreData(TypedDict, total=False):
419- x: int
420- y: int
419+ class D1(TypedDict, total=False):
420+ a1: int
421+
422+ def fn1(a1: int) -> None: ... # N: "fn1" defined here
423+ def main1(**d1: Unpack[D1]) -> None:
424+ partial(fn1, **d1)()
425+ partial(fn1, **d1)(**d1)
426+ partial(fn1, **d1)(a1=1)
427+ partial(fn1, **d1)(a1="asdf") # E: Argument "a1" to "fn1" has incompatible type "str"; expected "int"
428+ partial(fn1, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn1"
429+
430+ def fn2(**kwargs: Unpack[D1]) -> None: ... # N: "fn2" defined here
431+ def main2(**d1: Unpack[D1]) -> None:
432+ partial(fn2, **d1)()
433+ partial(fn2, **d1)(**d1)
434+ partial(fn2, **d1)(a1=1)
435+ partial(fn2, **d1)(a1="asdf") # E: Argument "a1" to "fn2" has incompatible type "str"; expected "int"
436+ partial(fn2, **d1)(oops=1) # E: Unexpected keyword argument "oops" for "fn2"
437+
438+ class D2(TypedDict, total=False):
439+ a1: int
440+ a2: str
441+
442+ class A2Good(TypedDict, total=False):
443+ a2: str
444+ class A2Bad(TypedDict, total=False):
445+ a2: int
446+
447+ def fn3(a1: int, a2: str) -> None: ... # N: "fn3" defined here
448+ def main3(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None:
449+ partial(fn3, **d2)()
450+ partial(fn3, **d2)(a1=1, a2="asdf")
451+
452+ partial(fn3, **d2)(**d2)
453+
454+ partial(fn3, **d2)(a1="asdf") # E: Argument "a1" to "fn3" has incompatible type "str"; expected "int"
455+ partial(fn3, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn3"
456+
457+ partial(fn3, **d2)(**a2good)
458+ partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str"
459+
460+ def fn4(**kwargs: Unpack[D2]) -> None: ... # N: "fn4" defined here
461+ def main4(a2good: A2Good, a2bad: A2Bad, **d2: Unpack[D2]) -> None:
462+ partial(fn4, **d2)()
463+ partial(fn4, **d2)(a1=1, a2="asdf")
464+
465+ partial(fn4, **d2)(**d2)
466+
467+ partial(fn4, **d2)(a1="asdf") # E: Argument "a1" to "fn4" has incompatible type "str"; expected "int"
468+ partial(fn4, **d2)(a1=1, a2="asdf", oops=1) # E: Unexpected keyword argument "oops" for "fn4"
469+
470+ partial(fn3, **d2)(**a2good)
471+ partial(fn3, **d2)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str"
472+
473+ def main5(**d2: Unpack[D2]) -> None:
474+ partial(fn1, **d2)() # E: Extra argument "a2" from **args for "fn1"
475+ partial(fn2, **d2)() # E: Extra argument "a2" from **args for "fn2"
476+
477+ def main6(a2good: A2Good, a2bad: A2Bad, **d1: Unpack[D1]) -> None:
478+ partial(fn3, **d1)() # E: Missing positional argument "a1" in call to "fn3"
479+ partial(fn3, **d1)("asdf") # E: Too many positional arguments for "fn3" \
480+ # E: Too few arguments for "fn3" \
481+ # E: Argument 1 to "fn3" has incompatible type "str"; expected "int"
482+ partial(fn3, **d1)(a2="asdf")
483+ partial(fn3, **d1)(**a2good)
484+ partial(fn3, **d1)(**a2bad) # E: Argument "a2" to "fn3" has incompatible type "int"; expected "str"
485+
486+ partial(fn4, **d1)()
487+ partial(fn4, **d1)("asdf") # E: Too many positional arguments for "fn4" \
488+ # E: Argument 1 to "fn4" has incompatible type "str"; expected "int"
489+ partial(fn4, **d1)(a2="asdf")
490+ partial(fn4, **d1)(**a2good)
491+ partial(fn4, **d1)(**a2bad) # E: Argument "a2" to "fn4" has incompatible type "int"; expected "str"
421492
422- def f_more(**kwargs: Unpack[MoreData]) -> None: ...
423- def g_more(**kwargs: Unpack[MoreData]) -> None:
424- partial(f_more, **kwargs)()
425-
426- class Good(TypedDict, total=False):
427- y: int
428- class Bad(TypedDict, total=False):
429- y: str
430-
431- def h(**kwargs: Unpack[Data]) -> None:
432- bad: Bad
433- partial(f_more, **kwargs)(**bad) # E: Argument "y" to "f_more" has incompatible type "str"; expected "int"
434- good: Good
435- partial(f_more, **kwargs)(**good)
436493[builtins fixtures/dict.pyi]
437494
495+
438496[case testFunctoolsPartialNestedGeneric]
439497from functools import partial
440498from typing import Generic, TypeVar, List
@@ -456,6 +514,21 @@ first_kw([1]) # E: Too many positional arguments for "get" \
456514 # E: Argument 1 to "get" has incompatible type "List[int]"; expected "int"
457515[builtins fixtures/list.pyi]
458516
517+ [case testFunctoolsPartialHigherOrder]
518+ from functools import partial
519+ from typing import Callable
520+
521+ def fn(a: int, b: str, c: bytes) -> int: ...
522+
523+ def callback1(fn: Callable[[str, bytes], int]) -> None: ...
524+ def callback2(fn: Callable[[str, int], int]) -> None: ...
525+
526+ callback1(partial(fn, 1))
527+ # TODO: false negative
528+ # https://github.com/python/mypy/issues/17461
529+ callback2(partial(fn, 1))
530+ [builtins fixtures/tuple.pyi]
531+
459532[case testFunctoolsPartialClassObjectMatchingPartial]
460533from functools import partial
461534
0 commit comments