From 58172d24d393173c456dfde4eadddb15096189f5 Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Tue, 8 Oct 2024 11:45:08 -0700 Subject: [PATCH 1/3] Handle partial of abstract types better Fixes https://github.com/python/mypy/issues/17556 --- mypy/checker.py | 3 +++ test-data/unit/check-functools.test | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/mypy/checker.py b/mypy/checker.py index 4bbd49cd7198..09dc8af36f88 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -690,6 +690,9 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab inner_type = expand_type_by_instance( type_object_type(inner_type.item.type, self.named_type), inner_type.item ) + if isinstance(inner_type, CallableType): + inner_type.from_type_type = True + if isinstance(inner_type, CallableType): outer_type = inner_type elif isinstance(inner_type, Instance): diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 9f8fbd42440b..077881d819cd 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -557,3 +557,25 @@ def bar(f: S) -> S: g = functools.partial(f, "foo") return f [builtins fixtures/primitives.pyi] + + +[case testFunctoolsPartialAbstractType] +# flags: --python-version 3.9 +from abc import ABC, abstractmethod +from functools import partial + +class A(ABC): + def __init__(self) -> None: ... + @abstractmethod + def method(self) -> None: ... + +def f1(cls: type[A]) -> None: + cls() + partial_cls = partial(cls) + partial_cls() + +def f2() -> None: + A() # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls = partial(A) # E: Cannot instantiate abstract class "A" with abstract attribute "method" + partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method" +[builtins fixtures/tuple.pyi] From a8f228245d29f853c3d56f66e95f7aa7280ed09e Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Tue, 8 Oct 2024 12:29:54 -0700 Subject: [PATCH 2/3] better fix --- mypy/checker.py | 7 +------ test-data/unit/check-functools.test | 14 ++++++++++++++ test-data/unit/lib-stub/typing_extensions.pyi | 2 ++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 09dc8af36f88..5a1a78c3e25f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -686,12 +686,7 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab if isinstance(inner_type, TypeVarLikeType): 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( - type_object_type(inner_type.item.type, self.named_type), inner_type.item - ) - if isinstance(inner_type, CallableType): - inner_type.from_type_type = True + inner_type = self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) if isinstance(inner_type, CallableType): outer_type = inner_type diff --git a/test-data/unit/check-functools.test b/test-data/unit/check-functools.test index 077881d819cd..50de3789ebd2 100644 --- a/test-data/unit/check-functools.test +++ b/test-data/unit/check-functools.test @@ -579,3 +579,17 @@ def f2() -> None: partial_cls = partial(A) # E: Cannot instantiate abstract class "A" with abstract attribute "method" partial_cls() # E: Cannot instantiate abstract class "A" with abstract attribute "method" [builtins fixtures/tuple.pyi] + + +[case testFunctoolsPartialSelfType] +from functools import partial +from typing_extensions import Self + +class A: + def __init__(self, ts: float, msg: str) -> None: ... + + @classmethod + def from_msg(cls, msg: str) -> Self: + factory = partial(cls, ts=0) + return factory(msg=msg) +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/lib-stub/typing_extensions.pyi b/test-data/unit/lib-stub/typing_extensions.pyi index d9d7067efe0f..cb054b0e6b4f 100644 --- a/test-data/unit/lib-stub/typing_extensions.pyi +++ b/test-data/unit/lib-stub/typing_extensions.pyi @@ -43,6 +43,8 @@ Required: _SpecialForm NotRequired: _SpecialForm ReadOnly: _SpecialForm +Self: _SpecialForm + @final class TypeAliasType: def __init__( From d03b830876f2c80836ad7030a5637c3683eb7fbe Mon Sep 17 00:00:00 2001 From: Shantanu Jain Date: Tue, 8 Oct 2024 12:34:26 -0700 Subject: [PATCH 3/3] proper --- mypy/checker.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5a1a78c3e25f..e0c2578df095 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -686,7 +686,9 @@ def extract_callable_type(self, inner_type: Type | None, ctx: Context) -> Callab if isinstance(inner_type, TypeVarLikeType): inner_type = get_proper_type(inner_type.upper_bound) if isinstance(inner_type, TypeType): - inner_type = self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + inner_type = get_proper_type( + self.expr_checker.analyze_type_type_callee(inner_type.item, ctx) + ) if isinstance(inner_type, CallableType): outer_type = inner_type