8000 Add support for method lookup from Type[]. · python/mypy@52bb2a6 · GitHub
[go: up one dir, main page]

Skip to content

Commit 52bb2a6

Browse files
author
Guido van Rossum
committed
Add support for method lookup from Type[].
We only support two special cases (hopefully the most common ones): - Type[<concrete_class>] - Type[<type_variable_with_concrete_class_upper_bound>]
1 parent 2c20f0a commit 52bb2a6

File tree

3 files changed

+92
-1
lines changed

3 files changed

+92
-1
lines changed

mypy/checkmember.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
from mypy.types import (
66
Type, Instance, AnyType, TupleType, CallableType, FunctionLike, TypeVarDef,
7-
Overloaded, TypeVarType, TypeTranslator, UnionType, PartialType, DeletedType, NoneTyp
7+
Overloaded, TypeVarType, TypeTranslator, UnionType, PartialType,
8+
DeletedType, NoneTyp, TypeType
89
)
910
from mypy.nodes import TypeInfo, FuncBase, Var, FuncDef, SymbolNode, Context
1011
from mypy.nodes import ARG_POS, ARG_STAR, ARG_STAR2, function_type, Decorator, OverloadedFuncDef
@@ -113,6 +114,23 @@ def analyze_member_access(name: str, typ: Type, node: Context, is_lvalue: bool,
113114
elif isinstance(typ, DeletedType):
114115
msg.deleted_as_rvalue(typ, node)
115116
return AnyType()
117+
elif isinstance(typ, TypeType):
118+
# Similar to FunctionLike + is_type_obj() above.
119+
item = None
120+
if isinstance(typ.item, Instance):
121+
item = typ.item
122+
elif isinstance(typ.item, TypeVarType):
123+
if isinstance(typ.item.upper_bound, Instance):
124+
item = typ.item.upper_bound
125+
if item:
126+
result = analyze_class_attribute_access(item, name, node, is_lvalue,
127+
builtin_type, not_ready_callback, msg)
128+
if result:
129+
return result
130+
fallback = builtin_type('builtins.type')
131+
return analyze_member_access(name, fallback, node, is_lvalue, is_super,
132+
builtin_type, not_ready_callback, msg,
133+
report_type=report_type)
116134
return msg.has_no_attr(report_type, name, node)
117135

118136

mypy/test/data/check-classes.test

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,3 +1527,75 @@ def bar(arg: Type[X]):
15271527
foo(X)
15281528
[builtins fixtures/tuple.py]
15291529
[out]
1530+
1531+
[case testTypeUsingTypeCClassMethod]
1532+
from typing import Type
1533+
class User:
1534+
@classmethod
1535+
def foo(cls) -> int: pass
1536+
def bar(self) -> int: pass
1537+
def process(cls: Type[User]):
1538+
reveal_type(cls.foo()) # E: Revealed type is 'builtins.int'
1539+
obj = cls()
1540+
reveal_type(cls.bar(obj)) # E: Revealed type is 'builtins.int'
1541+
cls.mro() # Defined in class type
1542+
cls.error # E: Type[User] has no attribute "error"
1543+
[builtins fixtures/classmethod.py]
1544+
[out]
1545+
main: note: In function "process":
1546+
1547+
[case testTypeUsingTypeCClassMethodUnion]
1548+
# Ideally this would work, but not worth the effort; just don't crash
1549+
from typing import Type, Union
1550+
class User:
1551+
@classmethod
1552+
def foo(cls) -> int: pass
1553+
def bar(self) -> int: pass
1554+
class ProUser(User): pass
1555+
class BasicUser(User): pass
1556+
def process(cls: Type[Union[BasicUser, ProUser]]):
1557+
cls.foo() # E: Type[Union[BasicUser, ProUser]] has no attribute "foo"
1558+
obj = cls()
1559+
cls.bar(obj) # E: Type[Union[BasicUser, ProUser]] has no attribute "bar"
1560+
cls.mro() # Defined in class type
1561+
cls.error # E: Type[Union[BasicUser, ProUser]] has no attribute "error"
1562+
[builtins fixtures/classmethod.py]
1563+
[out]
1564+
main: note: In function "process":
1565+
1566+
[case testTypeUsingTypeCClassMethodFromTypeVar]
1567+
from typing import Type, TypeVar
1568+
class User:
1569+
@classmethod
1570+
def foo(cls) -> int: pass
1571+
def bar(self) -> int: pass
1572+
U = TypeVar('U', bound=User)
1573+
def process(cls: Type[U]):
1574+
reveal_type(cls.foo()) # E: Revealed type is 'builtins.int'
1575+
obj = cls()
1576+
reveal_type(cls.bar(obj)) # E: Revealed type is 'builtins.int'
1577+
cls.mro() # Defined in class type
1578+
cls.error # E: Type[U] has no attribute "error"
1579+
[builtins fixtures/classmethod.py]
1580+
[out]
1581+
main: note: In function "process":
1582+
1583+
[case testTypeUsingTypeCClassMethodFromTypeVarUnionBound]
1584+
# Ideally this would work, but not worth the effort; just don't crash
1585+
from typing import Type, TypeVar, Union
1586+
class User:
1587+
@classmethod
1588+
def foo(cls) -> int: pass
1589+
def bar(self) -> int: pass
1590+
class ProUser(User): pass
1591+
class BasicUser(User): pass
1592+
U = TypeVar('U', bound=Union[ProUser, BasicUser])
1593+
def process(cls: Type[U]):
1594+
cls.foo() # E: Type[U] has no attribute "foo"
1595+
obj = cls()
1596+
cls.bar(obj) # E: Type[U] has no attribute "bar"
1597+
cls.mro() # Defined in class type
1598+
cls.error # E: Type[U] has no attribute "error"
1599+
[builtins fixtures/classmethod.py]
1600+
[out]
1601+
main: note: In function "process":

mypy/test/data/fixtures/classmethod.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ def __init__(self) -> None: pass
55

66
class type:
77
def __init__(self, x) -> None: pass
8+
def mro(self) -> typing.Any: pass
89

910
class function: pass
1011

0 commit comments

Comments
 (0)
0