Skip to content

Commit 76273b5

Browse files
author
Guido van Rossum
committed
Some cleanup and fixup (not done).
- Add tests for Type[Any] and plain Type, and fix a bug that found. - Respond to easy review feedback (adding comments/docstrings mostly).
1 parent cac4de3 commit 76273b5

11 files changed

+115
-23
lines changed

mypy/checkexpr.py

+3
Original file line numberDiff line numberDiff line change
@@ -303,11 +303,14 @@ def analyze_type_type_callee(self, item: Type, context: Context) -> Type:
303303
# but better than AnyType...), but replace the return type
304304
# with typevar.
305305
callee = self.analyze_type_type_callee(item.upper_bound, context)
306+
# XXX What to do for a generic class? Maybe just reject.
306307
if isinstance(callee, CallableType):
307308
callee = callee.copy_modified(ret_type=item)
309+
# XXX What to do for Overloaded?
308310
return callee
309311

310312
# XXX Do we need to handle other forms?
313+
# XXX Or maybe those should be rejected during semantic analysis.
311314
# XXX Make a nicely formatted error message.
312315
self.msg.fail("XXX Bad arg to Type[]", context)
313316
return AnyType()

mypy/constraints.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,10 @@ def visit_overloaded(self, template: Overloaded) -> List[Constraint]:
357357
def visit_type_type(self, template: TypeType) -> List[Constraint]:
358358
if isinstance(self.actual, CallableType) and self.actual.is_type_obj():
359359
return infer_constraints(template.item, self.actual.ret_type, self.direction)
360-
print("XXX Don't know what to do with type %s for template %s" % (self.actual, template))
361-
return [] # XXX ???
360+
elif isinstance(self.actual, TypeType):
361+
return infer_constraints(template.item, self.actual.item, self.direction)
362+
else:
363+
return []
362364

363365

364366
def neg_op(op: int) -> int:

mypy/erasetype.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from mypy.types import (
44
Type, TypeVisitor, UnboundType, ErrorType, AnyType, Void, NoneTyp,
55
Instance, TypeVarType, CallableType, TupleType, UnionType, Overloaded, ErasedType,
6-
PartialType, DeletedType, TypeTranslator, TypeList
6+
PartialType, DeletedType, TypeTranslator, TypeList, TypeType
77
)
88

99

@@ -72,6 +72,10 @@ def visit_tuple_type(self, t: TupleType) -> Type:
7272
def visit_union_type(self, t: UnionType) -> Type:
7373
return AnyType() # XXX: return underlying type if only one?
7474

75+
def visit_type_type(self, t: TypeType) -> Type:
76+
# XXX No idea if this makes much sense.
77+
return TypeType(AnyType(), line=t.line)
78+
7579

7680
def erase_generic_types(t: Type) -> Type:
7781
"""Remove generic type arguments and type variables from a type.

mypy/expandtype.py

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ def visit_partial_type(self, t: PartialType) -> Type:
9595
return t
9696

9797
def visit_type_type(self, t: TypeType) -> Type:
98+
# TODO: Verify that the new item type is valid (instance or
99+
# union of instances or Any). Sadly we can't report errors
100+
# here yet.
98101
item = t.item.accept(self)
99102
return TypeType(item)
100103

mypy/join.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from mypy.types import (
66
Type, AnyType, NoneTyp, Void, TypeVisitor, Instance, UnboundType,
77
ErrorType, TypeVarType, CallableType, TupleType, ErasedType, TypeList,
8-
UnionType, FunctionLike, Overloaded, PartialType, DeletedType
8+
UnionType, FunctionLike, Overloaded, PartialType, DeletedType,
9+
TypeType
910
)
1011
from mypy.maptype import map_instance_to_supertype
1112
from mypy.subtypes import is_subtype, is_equivalent, is_subtype_ignoring_tvars
@@ -204,6 +205,13 @@ def visit_partial_type(self, t: PartialType) -> Type:
204205
# never get here.
205206
assert False, "Internal error"
206207

208+
def visit_type_type(self, t: TypeType) -> Type:
209+
if isinstance(self.s, TypeType):
210+
return TypeType(self.join(t.item, self.s.item), line=t.line)
211+
else:
212+
# XXX Should at least try to join Type[] with type
213+
return AnyType()
214+
207215
def join(self, s: Type, t: Type) -> Type:
208216
return join_types(s, t)
209217

mypy/meet.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from mypy.join import is_similar_callables, combine_similar_callables
44
from mypy.types import (
55
Type, AnyType, TypeVisitor, UnboundType, Void, ErrorType, NoneTyp, TypeVarType,
6-
Instance, CallableType, TupleType, ErasedType, TypeList, UnionType, PartialType, DeletedType
6+
Instance, CallableType, TupleType, ErasedType, TypeList,
7+
UnionType, PartialType, DeletedType, TypeType
78
)
89
from mypy.subtypes import is_subtype
910
from mypy.nodes import TypeInfo
@@ -207,6 +208,13 @@ def visit_partial_type(self, t: PartialType) -> Type:
207208
# We can't determine the meet of partial types. We should never get here.
208209
assert False, 'Internal error'
209210

211+
def visit_type_type(self, t: TypeType) -> Type:
212+
if isinstance(self.s, TypeType):
213+
return TypeType(self.meet(t.item, self.s.item), line=t.line)
214+
else:
215+
# XXX Other cases, e.g. type <--> Type[]?
216+
return NoneTyp()
217+
210218
def meet(self, s, t):
211219
return meet_types(s, t)
212220

mypy/sametypes.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22

33
from mypy.types import (
44
Type, UnboundType, ErrorType, AnyType, NoneTyp, Void, TupleType, UnionType, CallableType,
5-
TypeVarType, Instance, TypeVisitor, ErasedType, TypeList, Overloaded, PartialType, DeletedType
5+
TypeVarType, Instance, TypeVisitor, ErasedType, TypeList,
6+
Overloaded, PartialType, DeletedType, TypeType
67
)
78

89

@@ -121,3 +122,9 @@ def visit_partial_type(self, left: PartialType) -> bool:
121122
# A partial type is not fully defined, so the result is indeterminate. We shouldn't
122123
# get here.
123124
raise RuntimeError
125+
126+
def visit_type_type(self, left: TypeType) -> bool:
127+
if isinstance(self.right, TypeType):
128+
return is_same_type(left.item, self.right.item)
129+
else:
130+
return False

mypy/subtypes.py

+3
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ def visit_callable_type(self, left: CallableType) -> bool:
146146
elif isinstance(right, Instance):
147147
return is_subtype(left.fallback, right)
148148
elif isinstance(right, TypeType):
149+
# This is unsound, we don't check the __init__ signature.
149150
return left.is_type_obj() and is_subtype(left.ret_type, right.item)
150151
else:
151152
return False
@@ -203,6 +204,7 @@ def visit_overloaded(self, left: Overloaded) -> bool:
203204
elif isinstance(right, TypeType):
204205
# All the items must have the same type object status, so
205206
# it's sufficient to query only (any) one of them.
207+
# This is unsound, we don't check the __init__ signature.
206208
return left.is_type_obj() and is_subtype(left.items()[0].ret_type, right.item)
207209
else:
208210
return False
@@ -220,6 +222,7 @@ def visit_type_type(self, left: TypeType) -> bool:
220222
if isinstance(right, TypeType):
221223
return is_subtype(left.item, right.item)
222224
if isinstance(right, CallableType):
225+
# This is unsound, we don't check the __init__ signature.
223226
return right.is_type_obj() and is_subtype(left.item, right.ret_type)
224227
# XXX Others? Union, Any, TypeVar
225228
return False

mypy/test/data/check-classes.test

+40-12
Original file line numberDiff line numberDiff line change
@@ -1427,38 +1427,47 @@ C(arg=0)
14271427
from typing import Type
14281428
class User: pass
14291429
class ProUser(User): pass
1430-
class BasicUser(User): pass
14311430
def new_user(user_class: Type[User]) -> User:
14321431
return user_class()
1433-
reveal_type(new_user(User))
1434-
reveal_type(new_user(ProUser))
1432+
reveal_type(new_user(User)) # E: Revealed type is '__main__.User'
1433+
reveal_type(new_user(ProUser)) # E: Revealed type is '__main__.User'
14351434
[out]
1436-
main:7: error: Revealed type is '__main__.User'
1437-
main:8: error: Revealed type is '__main__.User'
14381435

14391436
[case testTypeUsingTypeCTypeVar]
14401437
from typing import Type, TypeVar
14411438
class User: pass
14421439
class ProUser(User): pass
1443-
class BasicUser(User): pass
14441440
U = TypeVar('U', bound=User)
14451441
def new_user(user_class: Type[U]) -> U:
14461442
user = user_class()
14471443
reveal_type(user)
14481444
return user
14491445
pro_user = new_user(ProUser)
1450-
reveal_type(pro_user) # XXX Why is this ProUser*?
1446+
reveal_type(pro_user)
14511447
[out]
14521448
main: note: In function "new_user":
1453-
main:8: error: Revealed type is 'U`-1'
1449+
main:7: error: Revealed type is 'U`-1'
14541450
main: note: At top level:
1455-
main:11: error: Revealed type is '__main__.ProUser*'
1451+
main:10: error: Revealed type is '__main__.ProUser*'
1452+
1453+
[case testTypeUsingTypeCTwoTypeVars]
1454+
from typing import Type, TypeVar
1455+
class User: pass
1456+
class ProUser(User): pass
1457+
class WizUser(ProUser): pass
1458+
U = TypeVar('U', bound=User)
1459+
def new_user(u_c: Type[U]) -> U: pass
1460+
P = TypeVar('P', bound=ProUser)
1461+
def new_pro(pro_c: Type[P]) -> P:
1462+
return new_user(pro_c)
1463+
wiz = new_pro(WizUser)
1464+
reveal_type(wiz) # E: Revealed type is '__main__.WizUser*'
1465+
[out]
14561466

14571467
[case testTypeUsingTypeCCovariance]
14581468
from typing import Type, TypeVar
14591469
class User: pass
14601470
class ProUser(User): pass
1461-
class BasicUser(User): pass
14621471
def new_user(user_class: Type[User]) -> User:
14631472
return user_class()
14641473
def new_pro_user(user_class: Type[ProUser]):
@@ -1471,10 +1480,9 @@ class User: pass
14711480
def new_user(user_class: Type[User]):
14721481
return user_class()
14731482
def foo(arg: Type[int]):
1474-
new_user(arg)
1483+
new_user(arg) # E: Argument 1 to "new_user" has incompatible type Type[int]; expected Type[User]
14751484
[out]
14761485
main: note: In function "foo":
1477-
main:6: error: Argument 1 to "new_user" has incompatible type Type[int]; expected Type[User]
14781486

14791487
[case testTypeUsingTypeCUnionOverload]
14801488
from typing import Type, Union, overload
@@ -1489,3 +1497,23 @@ def bar(o: Type[Union[X, Y]]): pass
14891497
bar(X)
14901498
bar(Y)
14911499
[out]
1500+
1501+
[case testTypeUsingTypeCTypeAny]
1502+
from typing import Type, Any
1503+
def foo(arg: Type[Any]):
1504+
x = arg()
1505+
reveal_type(x) # E: Revealed type is 'Any'
1506+
class X: pass
1507+
foo(X)
1508+
[out]
1509+
main: note: In function "foo":
1510+
1511+
[case testTypeUsingTypeCTypeNoArg]
1512+
from typing import Type
1513+
def foo(arg: Type):
1514+
x = arg()
1515+
reveal_type(x) # E: Revealed type is 'Any'
1516+
class X: pass
1517+
foo(X)
1518+
[out]
1519+
main: note: In function "foo":

mypy/typeanal.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,13 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
115115
elif fullname == 'typing.Callable':
116116
return self.analyze_callable_type(t)
117117
elif fullname == 'typing.Type':
118+
if len(t.args) == 0:
119+
return TypeType(AnyType(), line=t.line)
118120
if len(t.args) != 1:
119121
self.fail('Type[...] must have exactly one type argument', t)
120122
items = self.anal_array(t.args)
121123
item = items[0]
122-
return TypeType(item, t.line)
124+
return TypeType(item, line=t.line)
123125
elif sym.kind == TYPE_ALIAS:
124126
# TODO: Generic type aliases.
125127
return sym.type_override
@@ -214,7 +216,7 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type:
214216
return AnyType()
215217

216218
def visit_type_type(self, t: TypeType) -> Type:
217-
return TypeType(t.accept(self), t.line)
219+
return TypeType(t.accept(self), line=t.line)
218220

219221
def analyze_callable_type(self, t: UnboundType) -> Type:
220222
fallback = self.builtin_type('builtins.function')

mypy/types.py

+27-3
Original file line numberDiff line numberDiff line change
@@ -789,11 +789,34 @@ class TypeType(Type):
789789
790790
This annotates variables that are class objects, constrained by
791791
the type argument. See PEP 484 for more details.
792+
793+
We may encounter expressions whose values are specific classes;
794+
those are represented as callables (possibly overloaded)
795+
corresponding to the class's constructor's signature and returning
796+
an instance of that class. The difference with Type[C] is that
797+
those callables always represent the exact class given as the
798+
return type; Type[C] represents any class that's a subclass of C,
799+
and C may also be a type variable or a union (or Any).
800+
801+
Many questions around subtype relationships between Type[C1] and
802+
def(...) -> C2 are answered by looking at the subtype
803+
relationships between C1 and C2, since Type[] is considered
804+
covariant.
805+
806+
There's an unsolved problem with constructor signatures (also
807+
unsolved in PEP 484): calling a variable whose type is Type[C]
808+
assumes the constructor signature for C, even though a subclass of
809+
C might completely change the constructor signature. For now we
810+
just assume that users of Type[C] are careful not to do that (in
811+
the future we might detect when they are violating that
812+
assumption).
792813
"""
793814

815+
# This can't be everything, but it can be a class reference,
816+
# a generic class instance, a union, Any, a type variable...
794817
item = None # type: Type
795818

796-
def __init__(self, item: Type, line: int = -1) -> None:
819+
def __init__(self, item: Type, *, line: int = -1) -> None:
797820
super().__init__(line)
798821
self.item = item
799822

@@ -887,8 +910,9 @@ def visit_partial_type(self, t: PartialType) -> T:
887910
def visit_ellipsis_type(self, t: EllipsisType) -> T:
888911
raise self._notimplemented_helper('ellipsis_type')
889912

913+
@abstractmethod
890914
def visit_type_type(self, t: TypeType) -> T:
891-
raise self._notimplemented_helper('type_type')
915+
pass
892916

893917

894918
class TypeTranslator(TypeVisitor[Type]):
@@ -968,7 +992,7 @@ def visit_overloaded(self, t: Overloaded) -> Type:
968992
return Overloaded(items=items)
969993

970994
def visit_type_type(self, t: TypeType) -> Type:
971-
return t
995+
return TypeType(t.item.accept(self), line=t.line)
972996

973997

974998
class TypeStrVisitor(TypeVisitor[str]):

0 commit comments

Comments
 (0)