@@ -656,3 +656,73 @@ def f(x: P):
656
656
# TODO: but this is incorrect, predating the functools.partial plugin
657
657
reveal_type(partial(x, "a")()) # N: Revealed type is "builtins.int"
658
658
[builtins fixtures/tuple.pyi]
659
+
660
+ [case testFunctoolsPartialTypeVarErasure]
661
+ from typing import Callable, TypeVar, Union
662
+ from typing_extensions import ParamSpec, TypeVarTuple, Unpack
663
+ from functools import partial
664
+
665
+ def use_int_callable(x: Callable[[int], int]) -> None:
666
+ pass
667
+ def use_func_callable(
668
+ x: Callable[
669
+ [Callable[[int], None]],
670
+ Callable[[int], None],
671
+ ],
672
+ ) -> None:
673
+ pass
674
+
675
+ Tc = TypeVar("Tc", int, str)
676
+ Tb = TypeVar("Tb", bound=Union[int, str])
677
+ P = ParamSpec("P")
678
+ Ts = TypeVarTuple("Ts")
679
+
680
+ def func_b(a: Tb, b: str) -> Tb:
681
+ return a
682
+ def func_c(a: Tc, b: str) -> Tc:
683
+ return a
684
+
685
+ def func_fn(fn: Callable[P, Tc], b: str) -> Callable[P, Tc]:
686
+ return fn
687
+ def func_fn_unpack(fn: Callable[[Unpack[Ts]], Tc], b: str) -> Callable[[Unpack[Ts]], Tc]:
688
+ return fn
689
+
690
+ # We should not leak stray typevars that aren't in scope:
691
+ reveal_type(partial(func_b, b="")) # N: Revealed type is "functools.partial[Any]"
692
+ reveal_type(partial(func_c, b="")) # N: Revealed type is "functools.partial[Any]"
693
+ reveal_type(partial(func_fn, b="")) # N: Revealed type is "functools.partial[def (*Any, **Any) -> Any]"
694
+ reveal_type(partial(func_fn_unpack, b="")) # N: Revealed type is "functools.partial[def (*Any) -> Any]"
695
+
696
+ use_int_callable(partial(func_b, b=""))
697
+ use_func_callable(partial(func_b, b=""))
698
+ use_int_callable(partial(func_c, b=""))
699
+ use_func_callable(partial(func_c, b=""))
700
+ use_int_callable(partial(func_fn, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[Callable[[VarArg(Any), KwArg(Any)], Any]]"; expected "Callable[[int], int]" \
701
+ # N: "partial[Callable[[VarArg(Any), KwArg(Any)], Any]].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Callable[[VarArg(Any), KwArg(Any)], Any]]"
702
+ use_func_callable(partial(func_fn, b=""))
703
+ use_int_callable(partial(func_fn_unpack, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[Callable[[VarArg(Any)], Any]]"; expected "Callable[[int], int]" \
704
+ # N: "partial[Callable[[VarArg(Any)], Any]].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Callable[[VarArg(Any)], Any]]"
705
+ use_func_callable(partial(func_fn_unpack, b=""))
706
+
707
+ # But we should not erase typevars that aren't bound by function
708
+ # passed to `partial`:
709
+
710
+ def outer_b(arg: Tb) -> None:
711
+
712
+ def inner(a: Tb, b: str) -> Tb:
713
+ return a
714
+
715
+ reveal_type(partial(inner, b="")) # N: Revealed type is "functools.partial[Tb`-1]"
716
+ use_int_callable(partial(inner, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[Tb]"; expected "Callable[[int], int]" \
717
+ # N: "partial[Tb].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], Tb]"
718
+
719
+ def outer_c(arg: Tc) -> None:
720
+
721
+ def inner(a: Tc, b: str) -> Tc:
722
+ return a
723
+
724
+ reveal_type(partial(inner, b="")) # N: Revealed type is "functools.partial[builtins.int]" \
725
+ # N: Revealed type is "functools.partial[builtins.str]"
726
+ use_int_callable(partial(inner, b="")) # E: Argument 1 to "use_int_callable" has incompatible type "partial[str]"; expected "Callable[[int], int]" \
727
+ # N: "partial[str].__call__" has type "Callable[[VarArg(Any), KwArg(Any)], str]"
728
+ [builtins fixtures/tuple.pyi]
0 commit comments