From 85f0682eab7f00179ae78e56cbf7b0a11522addc Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sat, 10 Aug 2024 12:39:54 -0700 Subject: [PATCH 1/3] Resolve TypeVar upper bounds in functools.partial Mostly fixes #17646 --- mypy/checker.py | 2 ++ test-data/unit/check-functools.test | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 59de599006a8..9f41e2021853 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -682,6 +682,8 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab inner_type = get_proper_type(inner_type) outer_type: CallableType | None = None if inner_type is not None and not isinstance(inner_type, AnyType): + if isinstance(inner_type, TypeVarLikeType): + inner_type = inner_type.upper_bound if isinstance(inner_type, TypeType): if isinstance(inner_type.item, Instance): inner_type = expand_type_by_instance( diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 710d3e66dfad..2b67f35c2179 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -541,3 +541,14 @@ p(1, "no") # E: Argument 2 to "A" has incompatible type "str"; expected "int" q: partial[A] = partial(A, 1) # OK [builtins fixtures/tuple.pyi] + +[case testFunctoolsPartialTypeVarBound] +from typing import Callable, TypeVar +import functools + +T = TypeVar("T", bound=Callable[[str, int], str]) + +def foo(f: T) -> T: + g = functools.partial(f, "foo") + return f +[builtins fixtures/tuple.pyi] From d2fd2543a2e3332b0dd00b3615cbdba64a142ad2 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sat, 10 Aug 2024 13:07:11 -0700 Subject: [PATCH 2/3] . --- mypy/checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 9f41e2021853..4a0f0fd4a151 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -683,7 +683,7 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab outer_type: CallableType | None = None if inner_type is not None and not isinstance(inner_type, AnyType): if isinstance(inner_type, TypeVarLikeType): - inner_type = inner_type.upper_bound + inner_type = get_proper_type(inner_type.upper_bound) if isinstance(inner_type, TypeType): if isinstance(inner_type.item, Instance): inner_type = expand_type_by_instance( From 24e4d3defcadd34831912c2cf09e80719fefaba7 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Sat, 10 Aug 2024 13:11:49 -0700 Subject: [PATCH 3/3] . --- test-data/unit/check-functools.test | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 2b67f35c2179..9f8fbd42440b 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -543,12 +543,17 @@ q: partial[A] = partial(A, 1) # OK [builtins fixtures/tuple.pyi] [case testFunctoolsPartialTypeVarBound] -from typing import Callable, TypeVar +from typing import Callable, TypeVar, Type import functools T = TypeVar("T", bound=Callable[[str, int], str]) +S = TypeVar("S", bound=Type[int]) def foo(f: T) -> T: g = functools.partial(f, "foo") return f -[builtins fixtures/tuple.pyi] + +def bar(f: S) -> S: + g = functools.partial(f, "foo") + return f +[builtins fixtures/primitives.pyi]