Skip to content

Commit 7e7b1e4

Browse files
authored
Use union component as a self-type in class methods (#7822)
1 parent 2f72de9 commit 7e7b1e4

File tree

2 files changed

+51
-2
lines changed

2 files changed

+51
-2
lines changed

mypy/checkmember.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,7 @@ def analyze_class_attribute_access(itype: Instance,
703703
is_classmethod = ((is_decorated and cast(Decorator, node.node).func.is_class)
704704
or (isinstance(node.node, FuncBase) and node.node.is_class))
705705
result = add_class_tvars(get_proper_type(t), itype, isuper, is_classmethod,
706-
mx.builtin_type, mx.original_type)
706+
mx.builtin_type, mx.self_type)
707707
if not mx.is_lvalue:
708708
result = analyze_descriptor_access(mx.original_type, result, mx.builtin_type,
709709
mx.msg, mx.context, chk=mx.chk)
@@ -761,7 +761,8 @@ class B(A[str]): pass
761761
762762
B.foo()
763763
764-
original_type is the value of the type B in the expression B.foo()
764+
original_type is the value of the type B in the expression B.foo() or the corresponding
765+
component in case if a union (this is used to bind the self-types).
765766
"""
766767
# TODO: verify consistency between Q and T
767768
info = itype.type # type: TypeInfo

test-data/unit/check-selftype.test

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,3 +569,51 @@ class Base:
569569
@classmethod
570570
def make(cls: Type[T], num: int) -> Tuple[T, ...]: ...
571571
[builtins fixtures/classmethod.pyi]
572+
573+
[case testSelfTypeClassMethodOnUnion]
574+
from typing import Type, Union, TypeVar
575+
576+
T = TypeVar('T')
577+
578+
class A:
579+
@classmethod
580+
def meth(cls: Type[T]) -> T: ...
581+
class B(A): ...
582+
class C(A): ...
583+
584+
t: Type[Union[B, C]]
585+
reveal_type(t.meth) # N: Revealed type is 'Union[def () -> __main__.B*, def () -> __main__.C*]'
586+
x = t.meth()
587+
reveal_type(x) # N: Revealed type is 'Union[__main__.B*, __main__.C*]'
588+
[builtins fixtures/classmethod.pyi]
589+
590+
[case testSelfTypeClassMethodOnUnionGeneric]
591+
from typing import Type, Union, TypeVar, Generic
592+
593+
T = TypeVar('T')
594+
S = TypeVar('S')
595+
596+
class A(Generic[T]):
597+
@classmethod
598+
def meth(cls: Type[S]) -> S: ...
599+
600+
t: Type[Union[A[int], A[str]]]
601+
x = t.meth()
602+
reveal_type(x) # N: Revealed type is 'Union[__main__.A*[builtins.int], __main__.A*[builtins.str]]'
603+
[builtins fixtures/classmethod.pyi]
604+
605+
[case testSelfTypeClassMethodOnUnionList]
606+
from typing import Type, Union, TypeVar, List
607+
608+
T = TypeVar('T')
609+
610+
class A:
611+
@classmethod
612+
def meth(cls: Type[T]) -> List[T]: ...
613+
class B(A): ...
614+
class C(A): ...
615+
616+
t: Type[Union[B, C]]
617+
x = t.meth()[0]
618+
reveal_type(x) # N: Revealed type is 'Union[__main__.B*, __main__.C*]'
619+
[builtins fixtures/isinstancelist.pyi]

0 commit comments

Comments
 (0)