[red-knot] Fix MRO inference for protocol classes; allow inheritance from subscripted Generic[]; forbid subclassing unsubscripted Generic (#17452)
This commit is contained in:
@@ -67,21 +67,24 @@ import typing
|
||||
|
||||
####################
|
||||
### Built-ins
|
||||
####################
|
||||
|
||||
class ListSubclass(typing.List): ...
|
||||
|
||||
# revealed: tuple[Literal[ListSubclass], Literal[list], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[ListSubclass], Literal[list], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(ListSubclass.__mro__)
|
||||
|
||||
class DictSubclass(typing.Dict): ...
|
||||
|
||||
# TODO: should have `Generic`, should not have `Unknown`
|
||||
# revealed: tuple[Literal[DictSubclass], Literal[dict], Unknown, Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[DictSubclass], Literal[dict], Literal[MutableMapping], Literal[Mapping], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(DictSubclass.__mro__)
|
||||
|
||||
class SetSubclass(typing.Set): ...
|
||||
|
||||
# revealed: tuple[Literal[SetSubclass], Literal[set], Literal[MutableSet], Literal[AbstractSet], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[SetSubclass], Literal[set], Literal[MutableSet], Literal[AbstractSet], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(SetSubclass.__mro__)
|
||||
|
||||
class FrozenSetSubclass(typing.FrozenSet): ...
|
||||
@@ -92,11 +95,12 @@ reveal_type(FrozenSetSubclass.__mro__)
|
||||
|
||||
####################
|
||||
### `collections`
|
||||
####################
|
||||
|
||||
class ChainMapSubclass(typing.ChainMap): ...
|
||||
|
||||
# TODO: Should be (ChainMapSubclass, ChainMap, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[ChainMapSubclass], Literal[ChainMap], Unknown, Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[ChainMapSubclass], Literal[ChainMap], Literal[MutableMapping], Literal[Mapping], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(ChainMapSubclass.__mro__)
|
||||
|
||||
class CounterSubclass(typing.Counter): ...
|
||||
@@ -113,7 +117,8 @@ reveal_type(DefaultDictSubclass.__mro__)
|
||||
|
||||
class DequeSubclass(typing.Deque): ...
|
||||
|
||||
# revealed: tuple[Literal[DequeSubclass], Literal[deque], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[DequeSubclass], Literal[deque], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(DequeSubclass.__mro__)
|
||||
|
||||
class OrderedDictSubclass(typing.OrderedDict): ...
|
||||
|
||||
@@ -41,7 +41,7 @@ class Foo:
|
||||
One thing that is supported is error messages for using special forms in type expressions.
|
||||
|
||||
```py
|
||||
from typing_extensions import Unpack, TypeGuard, TypeIs, Concatenate, ParamSpec
|
||||
from typing_extensions import Unpack, TypeGuard, TypeIs, Concatenate, ParamSpec, Generic
|
||||
|
||||
def _(
|
||||
a: Unpack, # error: [invalid-type-form] "`typing.Unpack` requires exactly one argument when used in a type expression"
|
||||
@@ -49,6 +49,7 @@ def _(
|
||||
c: TypeIs, # error: [invalid-type-form] "`typing.TypeIs` requires exactly one argument when used in a type expression"
|
||||
d: Concatenate, # error: [invalid-type-form] "`typing.Concatenate` requires at least two arguments when used in a type expression"
|
||||
e: ParamSpec,
|
||||
f: Generic, # error: [invalid-type-form] "`typing.Generic` is not allowed in type expressions"
|
||||
) -> None:
|
||||
reveal_type(a) # revealed: Unknown
|
||||
reveal_type(b) # revealed: Unknown
|
||||
@@ -65,7 +66,7 @@ You can't inherit from most of these. `typing.Callable` is an exception.
|
||||
|
||||
```py
|
||||
from typing import Callable
|
||||
from typing_extensions import Self, Unpack, TypeGuard, TypeIs, Concatenate
|
||||
from typing_extensions import Self, Unpack, TypeGuard, TypeIs, Concatenate, Generic
|
||||
|
||||
class A(Self): ... # error: [invalid-base]
|
||||
class B(Unpack): ... # error: [invalid-base]
|
||||
@@ -73,6 +74,7 @@ class C(TypeGuard): ... # error: [invalid-base]
|
||||
class D(TypeIs): ... # error: [invalid-base]
|
||||
class E(Concatenate): ... # error: [invalid-base]
|
||||
class F(Callable): ...
|
||||
class G(Generic): ... # error: [invalid-base] "Cannot inherit from plain `Generic`"
|
||||
|
||||
reveal_type(F.__mro__) # revealed: tuple[Literal[F], @Todo(Support for Callable as a base class), Literal[object]]
|
||||
```
|
||||
|
||||
@@ -45,8 +45,6 @@ from typing import Generic, TypeVar
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
# TODO: no error
|
||||
# error: [invalid-base]
|
||||
class C(Generic[T]): ...
|
||||
```
|
||||
|
||||
|
||||
@@ -137,8 +137,6 @@ from typing import TypeVar, Generic
|
||||
T = TypeVar("T")
|
||||
S = TypeVar("S")
|
||||
|
||||
# TODO: no error
|
||||
# error: [invalid-base]
|
||||
class Legacy(Generic[T]):
|
||||
def m(self, x: T, y: S) -> S:
|
||||
return y
|
||||
@@ -177,8 +175,6 @@ def f(x: T) -> None:
|
||||
# TODO: invalid-assignment error
|
||||
y: list[S] = []
|
||||
|
||||
# TODO: no error
|
||||
# error: [invalid-base]
|
||||
class C(Generic[T]):
|
||||
# TODO: error: cannot use S if it's not in the current generic context
|
||||
x: list[S] = []
|
||||
@@ -259,8 +255,7 @@ def f[T](x: T, y: T) -> None:
|
||||
class Ok[S]: ...
|
||||
# TODO: error for reuse of typevar
|
||||
class Bad1[T]: ...
|
||||
# TODO: no non-subscriptable error, error for reuse of typevar
|
||||
# error: [non-subscriptable]
|
||||
# TODO: error for reuse of typevar
|
||||
class Bad2(Iterable[T]): ...
|
||||
```
|
||||
|
||||
@@ -273,8 +268,7 @@ class C[T]:
|
||||
class Ok1[S]: ...
|
||||
# TODO: error for reuse of typevar
|
||||
class Bad1[T]: ...
|
||||
# TODO: no non-subscriptable error, error for reuse of typevar
|
||||
# error: [non-subscriptable]
|
||||
# TODO: error for reuse of typevar
|
||||
class Bad2(Iterable[T]): ...
|
||||
```
|
||||
|
||||
|
||||
@@ -28,8 +28,7 @@ from typing import Protocol
|
||||
|
||||
class MyProtocol(Protocol): ...
|
||||
|
||||
# TODO: at runtime this is `(<class '__main__.MyProtocol'>, <class 'typing.Protocol'>, <class 'typing.Generic'>, <class 'object'>)`
|
||||
reveal_type(MyProtocol.__mro__) # revealed: tuple[Literal[MyProtocol], @Todo(protocol), Literal[object]]
|
||||
reveal_type(MyProtocol.__mro__) # revealed: tuple[Literal[MyProtocol], typing.Protocol, typing.Generic, Literal[object]]
|
||||
```
|
||||
|
||||
Just like for any other class base, it is an error for `Protocol` to appear multiple times in a
|
||||
@@ -72,8 +71,7 @@ it is not sufficient for it to have `Protocol` in its MRO.
|
||||
```py
|
||||
class SubclassOfMyProtocol(MyProtocol): ...
|
||||
|
||||
# TODO
|
||||
# revealed: tuple[Literal[SubclassOfMyProtocol], Literal[MyProtocol], @Todo(protocol), Literal[object]]
|
||||
# revealed: tuple[Literal[SubclassOfMyProtocol], Literal[MyProtocol], typing.Protocol, typing.Generic, Literal[object]]
|
||||
reveal_type(SubclassOfMyProtocol.__mro__)
|
||||
|
||||
# TODO: should be `Literal[False]`
|
||||
@@ -94,8 +92,7 @@ class OtherProtocol(Protocol):
|
||||
|
||||
class ComplexInheritance(SubProtocol, OtherProtocol, Protocol): ...
|
||||
|
||||
# TODO
|
||||
# revealed: tuple[Literal[ComplexInheritance], Literal[SubProtocol], Literal[MyProtocol], Literal[OtherProtocol], @Todo(protocol), Literal[object]]
|
||||
# revealed: tuple[Literal[ComplexInheritance], Literal[SubProtocol], Literal[MyProtocol], Literal[OtherProtocol], typing.Protocol, typing.Generic, Literal[object]]
|
||||
reveal_type(ComplexInheritance.__mro__)
|
||||
|
||||
# TODO: should be `Literal[True]`
|
||||
@@ -109,15 +106,13 @@ or `TypeError` is raised at runtime when the class is created.
|
||||
# TODO: should emit `[invalid-protocol]`
|
||||
class Invalid(NotAProtocol, Protocol): ...
|
||||
|
||||
# TODO
|
||||
# revealed: tuple[Literal[Invalid], Literal[NotAProtocol], @Todo(protocol), Literal[object]]
|
||||
# revealed: tuple[Literal[Invalid], Literal[NotAProtocol], typing.Protocol, typing.Generic, Literal[object]]
|
||||
reveal_type(Invalid.__mro__)
|
||||
|
||||
# TODO: should emit an `[invalid-protocol`] error
|
||||
class AlsoInvalid(MyProtocol, OtherProtocol, NotAProtocol, Protocol): ...
|
||||
|
||||
# TODO
|
||||
# revealed: tuple[Literal[AlsoInvalid], Literal[MyProtocol], Literal[OtherProtocol], Literal[NotAProtocol], @Todo(protocol), Literal[object]]
|
||||
# revealed: tuple[Literal[AlsoInvalid], Literal[MyProtocol], Literal[OtherProtocol], Literal[NotAProtocol], typing.Protocol, typing.Generic, Literal[object]]
|
||||
reveal_type(AlsoInvalid.__mro__)
|
||||
```
|
||||
|
||||
@@ -135,11 +130,9 @@ T = TypeVar("T")
|
||||
# type checkers.
|
||||
class Fine(Protocol, object): ...
|
||||
|
||||
# TODO
|
||||
reveal_type(Fine.__mro__) # revealed: tuple[Literal[Fine], @Todo(protocol), Literal[object]]
|
||||
reveal_type(Fine.__mro__) # revealed: tuple[Literal[Fine], typing.Protocol, typing.Generic, Literal[object]]
|
||||
|
||||
# TODO: should not error
|
||||
class StillFine(Protocol, Generic[T], object): ... # error: [invalid-base]
|
||||
class StillFine(Protocol, Generic[T], object): ...
|
||||
class EvenThis[T](Protocol, object): ...
|
||||
```
|
||||
|
||||
@@ -149,8 +142,7 @@ And multiple inheritance from a mix of protocol and non-protocol classes is fine
|
||||
```py
|
||||
class FineAndDandy(MyProtocol, OtherProtocol, NotAProtocol): ...
|
||||
|
||||
# TODO
|
||||
# revealed: tuple[Literal[FineAndDandy], Literal[MyProtocol], Literal[OtherProtocol], @Todo(protocol), Literal[NotAProtocol], Literal[object]]
|
||||
# revealed: tuple[Literal[FineAndDandy], Literal[MyProtocol], Literal[OtherProtocol], typing.Protocol, typing.Generic, Literal[NotAProtocol], Literal[object]]
|
||||
reveal_type(FineAndDandy.__mro__)
|
||||
```
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ from typing import Tuple
|
||||
|
||||
class C(Tuple): ...
|
||||
|
||||
# revealed: tuple[Literal[C], Literal[tuple], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
|
||||
# TODO: generic protocols
|
||||
# revealed: tuple[Literal[C], Literal[tuple], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(`Protocol[]` subscript), @Todo(`Generic[]` subscript), Literal[object]]
|
||||
reveal_type(C.__mro__)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user