The types typing.Type
and builtins.type
are closely related. However, mypy and pyright treat them somewhat inconsistently (both internally to each type checker and across the two type checkers).
I’d like to fix the internal inconsistency in pyright, but before I make the change, I want to make sure my understanding of the spec is correct here.
PEP 484 says:
When
Type
is parameterized it requires exactly one parameter. PlainType
without brackets is equivalent toType[Any]
and this in turn is equivalent totype
(the root of Python’s metaclass hierarchy). This equivalence also motivates the name,Type
, as opposed to alternatives likeClass
orSubType
, which were proposed while this feature was under discussion; this is similar to the relationship between e.g.List
andlist
.Regarding the behavior of
Type[Any]
(orType
ortype
), accessing attributes of a variable with this type only provides attributes and methods defined bytype
(for example,__repr__()
and__mro__
).
If I’m interpreting this correctly, type checkers should treat all of the following type annotations the same with regard to attribute access: Type
, Type[Any]
, type
, and type[Any]
.
Code sample in pyright playground
Code sample in mypy playground
from typing import Any, Type
def func1(t1: Type, t2: Type[Any], t3: type, t4: type[Any]):
v1 = t1.x # No Error
v2 = t2.x # Mypy: No Error, Pyright: Error *** Mismatch ***
v3 = t3.x # Error
v4 = t4.x # Mypy: No Error, Pyright: Error *** Mismatch ***
Pyright currently emits errors for v2, v3, v4
Mypy currently emits an error only for v3
Pyre currently emits errors for none of these cases
If I’m reading the spec correctly, an error should be emitted for all four. Does that make sense?
Mypy was the reference implementation for PEP 484, so I’m always cautious when my understanding of PEP 484 appears to differ from mypy’s behavior.