Skip to content

Commit bd1b3d7

Browse files
ilevkivskyiJukkaL
authored andcommitted
Fix --incremental crash on Type[...] (#4038)
This fixes the second traceback reported in #3852. This also improves member lookup on `Type[...]` by moving it to a later stage (`checkmember.py` instead of some manipulations in `TypeType` constructor that are dangerous during de-serialization). Also fixes the first case in #4058.
1 parent e5816bc commit bd1b3d7

File tree

5 files changed

+79
-12
lines changed

5 files changed

+79
-12
lines changed

mypy/checkmember.py

+14-5
Original file line numberDiff line numberDiff line change
@@ -177,26 +177,35 @@ def analyze_member_access(name: str,
177177
elif isinstance(typ, TypeType):
178178
# Similar to FunctionLike + is_type_obj() above.
179179
item = None
180+
fallback = builtin_type('builtins.type')
181+
ignore_messages = msg.copy()
182+
ignore_messages.disable_errors()
180183
if isinstance(typ.item, Instance):
181184
item = typ.item
182185
elif isinstance(typ.item, AnyType):
183-
fallback = builtin_type('builtins.type')
184-
ignore_messages = msg.copy()
185-
ignore_messages.disable_errors()
186186
return analyze_member_access(name, fallback, node, is_lvalue, is_super,
187187
is_operator, builtin_type, not_ready_callback,
188188
ignore_messages, original_type=original_type, chk=chk)
189189
elif isinstance(typ.item, TypeVarType):
190190
if isinstance(typ.item.upper_bound, Instance):
191191
item = typ.item.upper_bound
192+
elif isinstance(typ.item, FunctionLike) and typ.item.is_type_obj():
193+
item = typ.item.fallback
194+
elif isinstance(typ.item, TypeType):
195+
# Access member on metaclass object via Type[Type[C]]
196+
if isinstance(typ.item.item, Instance):
197+
item = typ.item.item.type.metaclass_type
192198
if item and not is_operator:
193199
# See comment above for why operators are skipped
194200
result = analyze_class_attribute_access(item, name, node, is_lvalue,
195201
builtin_type, not_ready_callback, msg,
196202
original_type=original_type)
197203
if result:
198-
return result
199-
fallback = builtin_type('builtins.type')
204+
if not (isinstance(result, AnyType) and item.type.fallback_to_any):
205+
return result
206+
else:
207+
# We don't want errors on metaclass lookup for classes with Any fallback
208+
msg = ignore_messages
200209
if item is not None:
201210
fallback = item.type.metaclass_type or fallback
202211
return analyze_member_access(name, fallback, node, is_lvalue, is_super,

mypy/types.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -717,8 +717,7 @@ def copy_modified(self,
717717
)
718718

719719
def is_type_obj(self) -> bool:
720-
t = self.fallback.type
721-
return t is not None and t.is_metaclass()
720+
return self.fallback.type.is_metaclass()
722721

723722
def is_concrete_type_obj(self) -> bool:
724723
return self.is_type_obj() and self.is_classmethod_class
@@ -1341,10 +1340,7 @@ def __init__(self, item: Union[Instance, AnyType, TypeVarType, TupleType, NoneTy
13411340
type UnionType must be handled through make_normalized static method.
13421341
"""
13431342
super().__init__(line, column)
1344-
if isinstance(item, CallableType) and item.is_type_obj():
1345-
self.item = item.fallback
1346-
else:
1347-
self.item = item
1343+
self.item = item
13481344

13491345
@staticmethod
13501346
def make_normalized(item: Type, *, line: int = -1, column: int = -1) -> Type:

test-data/unit/check-classes.test

+41
Original file line numberDiff line numberDiff line change
@@ -3995,3 +3995,44 @@ class E(metaclass=t.M): pass
39953995
class F(six.with_metaclass(t.M)): pass
39963996
@six.add_metaclass(t.M)
39973997
class G: pass
3998+
3999+
[case testMetaclassMemberAccessViaType]
4000+
from typing import Type
4001+
class M(type):
4002+
def m(cls, x: int) -> int:
4003+
pass
4004+
4005+
class C(metaclass=M):
4006+
pass
4007+
x = C
4008+
y: Type[C] = C
4009+
4010+
reveal_type(type(C).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
4011+
reveal_type(type(x).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
4012+
reveal_type(type(y).m) # E: Revealed type is 'def (cls: __main__.M, x: builtins.int) -> builtins.int'
4013+
[out]
4014+
4015+
[case testMetaclassMemberAccessViaType2]
4016+
from typing import Any, Type
4017+
class M(type):
4018+
def m(cls, x: int) -> int:
4019+
pass
4020+
B: Any
4021+
class C(B, metaclass=M):
4022+
pass
4023+
4024+
x: Type[C]
4025+
reveal_type(x.m) # E: Revealed type is 'def (x: builtins.int) -> builtins.int'
4026+
reveal_type(x.whatever) # E: Revealed type is 'Any'
4027+
[out]
4028+
4029+
[case testMetaclassMemberAccessViaType3]
4030+
from typing import Any, Type, TypeVar
4031+
T = TypeVar('T')
4032+
class C(Any):
4033+
def bar(self: T) -> Type[T]: pass
4034+
def foo(self) -> None:
4035+
reveal_type(self.bar()) # E: Revealed type is 'Type[__main__.C*]'
4036+
reveal_type(self.bar().__name__) # E: Revealed type is 'builtins.str'
4037+
[builtins fixtures/type.pyi]
4038+
[out]

test-data/unit/check-incremental.test

+20
Original file line numberDiff line numberDiff line change
@@ -3246,3 +3246,23 @@ import foo
32463246
external_list = [0]
32473247

32483248
[builtins fixtures/dict.pyi]
3249+
3250+
[case testIncrementalCrashOnTypeWithFunction]
3251+
import a
3252+
[file a.py]
3253+
import b
3254+
[file a.py.2]
3255+
from b import x
3256+
3257+
[file b.py]
3258+
from typing import TypeVar, Type
3259+
T = TypeVar('T')
3260+
3261+
def tp(arg: T) -> Type[T]:
3262+
pass
3263+
def func(x: int) -> int:
3264+
pass
3265+
3266+
x = tp(func)
3267+
[out]
3268+
[out2]

test-data/unit/fixtures/type.pyi

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ class object:
1111
class list(Generic[T]): pass
1212

1313
class type:
14+
__name__: str
1415
def mro(self) -> List['type']: pass
1516

1617
class tuple(Generic[T]): pass
1718
class function: pass
1819
class bool: pass
1920
class int: pass
2021
class str: pass
21-
class unicode: pass
22+
class unicode: pass

0 commit comments

Comments
 (0)