[red-knot] Add explicit TODO branches for many typing special forms and qualifiers (#14936)

This commit is contained in:
Alex Waygood
2024-12-12 17:57:26 +00:00
committed by GitHub
parent 58930905eb
commit 71239f248e
6 changed files with 471 additions and 22 deletions

View File

@@ -0,0 +1,83 @@
# Typing-module aliases to other stdlib classes
The `typing` module has various aliases to other stdlib classes. These are a legacy feature, but
still need to be supported by a type checker.
## Currently unsupported
Support for most of these symbols is currently a TODO:
```py
import typing
def f(
a: typing.List,
b: typing.List[int],
c: typing.Dict,
d: typing.Dict[int, str],
e: typing.DefaultDict,
f: typing.DefaultDict[str, int],
g: typing.Set,
h: typing.Set[int],
i: typing.FrozenSet,
j: typing.FrozenSet[str],
k: typing.OrderedDict,
l: typing.OrderedDict[int, str],
m: typing.Counter,
n: typing.Counter[int],
):
reveal_type(a) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(b) # revealed: @Todo(typing.List alias)
reveal_type(c) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(d) # revealed: @Todo(typing.Dict alias)
reveal_type(e) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(f) # revealed: @Todo(typing.DefaultDict[] alias)
reveal_type(g) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(h) # revealed: @Todo(typing.Set alias)
reveal_type(i) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(j) # revealed: @Todo(typing.FrozenSet alias)
reveal_type(k) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(l) # revealed: @Todo(typing.OrderedDict alias)
reveal_type(m) # revealed: @Todo(Unsupported or invalid type in a type expression)
reveal_type(n) # revealed: @Todo(typing.Counter[] alias)
```
## Inheritance
The aliases can be inherited from. Some of these are still partially or wholly TODOs.
```py
import typing
class A(typing.Dict): ...
# TODO: should have `Generic`, should not have `Unknown`
reveal_type(A.__mro__) # revealed: tuple[Literal[A], Literal[dict], Unknown, Literal[object]]
class B(typing.List): ...
# TODO: should have `Generic`, should not have `Unknown`
reveal_type(B.__mro__) # revealed: tuple[Literal[B], Literal[list], Unknown, Literal[object]]
class C(typing.Set): ...
# TODO: should have `Generic`, should not have `Unknown`
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[set], Unknown, Literal[object]]
class D(typing.FrozenSet): ...
# TODO: should have `Generic`, should not have `Unknown`
reveal_type(D.__mro__) # revealed: tuple[Literal[D], Literal[frozenset], Unknown, Literal[object]]
class E(typing.DefaultDict): ...
reveal_type(E.__mro__) # revealed: tuple[Literal[E], @Todo(Support for more typing aliases as base classes), Literal[object]]
class F(typing.OrderedDict): ...
reveal_type(F.__mro__) # revealed: tuple[Literal[F], @Todo(Support for more typing aliases as base classes), Literal[object]]
class G(typing.Counter): ...
reveal_type(G.__mro__) # revealed: tuple[Literal[G], @Todo(Support for more typing aliases as base classes), Literal[object]]
```

View File

@@ -0,0 +1,71 @@
# Unsupported special forms
## Not yet supported
Several special forms are unsupported by red-knot currently. However, we also don't emit
false-positive errors if you use one in an annotation:
```py
from typing_extensions import Self, TypeVarTuple, Unpack, TypeGuard, TypeIs, Concatenate, ParamSpec, TypeAlias, Callable, TypeVar
P = ParamSpec("P")
Ts = TypeVarTuple("Ts")
R_co = TypeVar("R_co", covariant=True)
Alias: TypeAlias = int
def f(*args: Unpack[Ts]) -> tuple[Unpack[Ts]]:
# TODO: should understand the annotation
reveal_type(args) # revealed: tuple
reveal_type(Alias) # revealed: @Todo(Unsupported or invalid type in a type expression)
def g() -> TypeGuard[int]: ...
def h() -> TypeIs[int]: ...
def i(callback: Callable[Concatenate[int, P], R_co], *args: P.args, **kwargs: P.kwargs) -> R_co:
# TODO: should understand the annotation
reveal_type(args) # revealed: tuple
# TODO: should understand the annotation
reveal_type(kwargs) # revealed: dict
return callback(42, *args, **kwargs)
class Foo:
def method(self, x: Self):
reveal_type(x) # revealed: @Todo(Unsupported or invalid type in a type expression)
```
## Inheritance
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
class A(Self): ... # error: [invalid-base]
class B(Unpack): ... # error: [invalid-base]
class C(TypeGuard): ... # error: [invalid-base]
class D(TypeIs): ... # error: [invalid-base]
class E(Concatenate): ... # error: [invalid-base]
class F(Callable): ...
reveal_type(F.__mro__) # revealed: tuple[Literal[F], @Todo(Support for more typing aliases as base classes), Literal[object]]
```
## Subscriptability
Some of these are not subscriptable:
```py
from typing_extensions import Self, TypeAlias
X: TypeAlias[T] = int # error: [invalid-type-parameter]
class Foo[T]:
# error: [invalid-type-parameter] "Special form `typing.Self` expected no type parameter"
# error: [invalid-type-parameter] "Special form `typing.Self` expected no type parameter"
def method(self: Self[int]) -> Self[int]:
reveal_type(self) # revealed: Unknown
```

View File

@@ -0,0 +1,37 @@
# Unsupported type qualifiers
## Not yet supported
Several type qualifiers are unsupported by red-knot currently. However, we also don't emit
false-positive errors if you use one in an annotation:
```py
from typing_extensions import Final, ClassVar, Required, NotRequired, ReadOnly, TypedDict
X: Final = 42
Y: Final[int] = 42
class Foo:
A: ClassVar[int] = 42
# TODO: `TypedDict` is actually valid as a base
# error: [invalid-base]
class Bar(TypedDict):
x: Required[int]
y: NotRequired[str]
z: ReadOnly[bytes]
```
## Inheritance
You can't inherit from a type qualifier.
```py
from typing_extensions import Final, ClassVar, Required, NotRequired, ReadOnly
class A(Final): ... # error: [invalid-base]
class B(ClassVar): ... # error: [invalid-base]
class C(Required): ... # error: [invalid-base]
class D(NotRequired): ... # error: [invalid-base]
class E(ReadOnly): ... # error: [invalid-base]
```