From 935e9ffc8bba0624678447af97cd455788a98f27 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 5 Sep 2019 18:49:23 +0100 Subject: [PATCH 1/2] Bind self correctly when mapping class methods from supertype --- mypy/checker.py | 8 ++++- test-data/unit/check-generics.test | 49 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 4dabdc6fdfcd..5eb36c81b38f 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1530,7 +1530,13 @@ def bind_and_map_method(self, sym: SymbolTableNode, typ: FunctionLike, """ if (isinstance(sym.node, (FuncDef, OverloadedFuncDef, Decorator)) and not is_static(sym.node)): - bound = bind_self(typ, self.scope.active_self_type()) + if isinstance(sym.node, Decorator): + is_class_method = sym.node.func.is_class + elif isinstance(sym.node, OverloadedFuncDef): + is_class_method = sym.node.is_class + else: + is_class_method = False + bound = bind_self(typ, self.scope.active_self_type(), is_class_method) else: bound = typ return cast(FunctionLike, map_type_from_supertype(bound, sub_info, super_info)) diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index c7866dbbedee..4a3829200f65 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -2062,3 +2062,52 @@ class A: x, y = A() reveal_type(x) # N: Revealed type is 'Any' reveal_type(y) # N: Revealed type is 'Any' + +[case testSubclassingGenericSelfClassMethod] +from typing import TypeVar, Type + +AT = TypeVar('AT', bound='A') + +class A: + @classmethod + def from_config(cls: Type[AT]) -> AT: + ... + +class B(A): + @classmethod + def from_config(cls: Type[B]) -> B: + return B() +[builtins fixtures/classmethod.pyi] + +[case testSubclassingGenericSelfClassMethodOptional] +# flags: --strict-optional +from typing import TypeVar, Type, Optional + +AT = TypeVar('AT', bound='A') + +class A: + @classmethod + def from_config(cls: Type[AT]) -> Optional[AT]: + return None + +class B(A): + @classmethod + def from_config(cls: Type[B]) -> Optional[B]: + return B() +[builtins fixtures/classmethod.pyi] + +[case testSubclassingGenericSelfClassMethodNonAnnotated] +from typing import TypeVar, Type + +AT = TypeVar('AT', bound='A') + +class A: + @classmethod + def from_config(cls: Type[AT]) -> AT: + ... + +class B(A): + @classmethod + def from_config(cls) -> B: + return B() +[builtins fixtures/classmethod.pyi] From 7de627c514f65272076321a8b99561a031fdbe76 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 5 Sep 2019 21:46:51 +0100 Subject: [PATCH 2/2] Address CR --- mypy/checker.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mypy/checker.py b/mypy/checker.py index 5eb36c81b38f..b758af4ad5e6 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1532,10 +1532,8 @@ def bind_and_map_method(self, sym: SymbolTableNode, typ: FunctionLike, and not is_static(sym.node)): if isinstance(sym.node, Decorator): is_class_method = sym.node.func.is_class - elif isinstance(sym.node, OverloadedFuncDef): - is_class_method = sym.node.is_class else: - is_class_method = False + is_class_method = sym.node.is_class bound = bind_self(typ, self.scope.active_self_type(), is_class_method) else: bound = typ