mirror of
https://github.com/astral-sh/ruff
synced 2026-01-20 21:10:48 -05:00
[red-knot] Basic support for other legacy typing aliases (#14998)
## Summary Resolves #14997. ## Test Plan Markdown tests.
This commit is contained in:
@@ -3,43 +3,59 @@
|
||||
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
|
||||
## Correspondence
|
||||
|
||||
Support for most of these symbols is currently a TODO:
|
||||
All of the following symbols can be mapped one-to-one with the actual type:
|
||||
|
||||
```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],
|
||||
list_bare: typing.List,
|
||||
list_parametrized: typing.List[int],
|
||||
dict_bare: typing.Dict,
|
||||
dict_parametrized: typing.Dict[int, str],
|
||||
set_bare: typing.Set,
|
||||
set_parametrized: typing.Set[int],
|
||||
frozen_set_bare: typing.FrozenSet,
|
||||
frozen_set_parametrized: typing.FrozenSet[str],
|
||||
chain_map_bare: typing.ChainMap,
|
||||
chain_map_parametrized: typing.ChainMap[int],
|
||||
counter_bare: typing.Counter,
|
||||
counter_parametrized: typing.Counter[int],
|
||||
default_dict_bare: typing.DefaultDict,
|
||||
default_dict_parametrized: typing.DefaultDict[str, int],
|
||||
deque_bare: typing.Deque,
|
||||
deque_parametrized: typing.Deque[str],
|
||||
ordered_dict_bare: typing.OrderedDict,
|
||||
ordered_dict_parametrized: typing.OrderedDict[int, str],
|
||||
):
|
||||
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)
|
||||
reveal_type(list_bare) # revealed: list
|
||||
reveal_type(list_parametrized) # revealed: list
|
||||
|
||||
reveal_type(dict_bare) # revealed: dict
|
||||
reveal_type(dict_parametrized) # revealed: dict
|
||||
|
||||
reveal_type(set_bare) # revealed: set
|
||||
reveal_type(set_parametrized) # revealed: set
|
||||
|
||||
reveal_type(frozen_set_bare) # revealed: frozenset
|
||||
reveal_type(frozen_set_parametrized) # revealed: frozenset
|
||||
|
||||
reveal_type(chain_map_bare) # revealed: ChainMap
|
||||
reveal_type(chain_map_parametrized) # revealed: ChainMap
|
||||
|
||||
reveal_type(counter_bare) # revealed: Counter
|
||||
reveal_type(counter_parametrized) # revealed: Counter
|
||||
|
||||
reveal_type(default_dict_bare) # revealed: defaultdict
|
||||
reveal_type(default_dict_parametrized) # revealed: defaultdict
|
||||
|
||||
reveal_type(deque_bare) # revealed: deque
|
||||
reveal_type(deque_parametrized) # revealed: deque
|
||||
|
||||
reveal_type(ordered_dict_bare) # revealed: OrderedDict
|
||||
reveal_type(ordered_dict_parametrized) # revealed: OrderedDict
|
||||
```
|
||||
|
||||
## Inheritance
|
||||
@@ -49,35 +65,63 @@ The aliases can be inherited from. Some of these are still partially or wholly T
|
||||
```py
|
||||
import typing
|
||||
|
||||
class A(typing.Dict): ...
|
||||
####################
|
||||
### Built-ins
|
||||
|
||||
class ListSubclass(typing.List): ...
|
||||
|
||||
# TODO: should have `Generic`, should not have `Unknown`
|
||||
reveal_type(A.__mro__) # revealed: tuple[Literal[A], Literal[dict], Unknown, Literal[object]]
|
||||
# revealed: tuple[Literal[ListSubclass], Literal[list], Unknown, Literal[object]]
|
||||
reveal_type(ListSubclass.__mro__)
|
||||
|
||||
class B(typing.List): ...
|
||||
class DictSubclass(typing.Dict): ...
|
||||
|
||||
# TODO: should have `Generic`, should not have `Unknown`
|
||||
reveal_type(B.__mro__) # revealed: tuple[Literal[B], Literal[list], Unknown, Literal[object]]
|
||||
# revealed: tuple[Literal[DictSubclass], Literal[dict], Unknown, Literal[object]]
|
||||
reveal_type(DictSubclass.__mro__)
|
||||
|
||||
class C(typing.Set): ...
|
||||
class SetSubclass(typing.Set): ...
|
||||
|
||||
# TODO: should have `Generic`, should not have `Unknown`
|
||||
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[set], Unknown, Literal[object]]
|
||||
# revealed: tuple[Literal[SetSubclass], Literal[set], Unknown, Literal[object]]
|
||||
reveal_type(SetSubclass.__mro__)
|
||||
|
||||
class D(typing.FrozenSet): ...
|
||||
class FrozenSetSubclass(typing.FrozenSet): ...
|
||||
|
||||
# TODO: should have `Generic`, should not have `Unknown`
|
||||
reveal_type(D.__mro__) # revealed: tuple[Literal[D], Literal[frozenset], Unknown, Literal[object]]
|
||||
# revealed: tuple[Literal[FrozenSetSubclass], Literal[frozenset], Unknown, Literal[object]]
|
||||
reveal_type(FrozenSetSubclass.__mro__)
|
||||
|
||||
class E(typing.DefaultDict): ...
|
||||
####################
|
||||
### `collections`
|
||||
|
||||
reveal_type(E.__mro__) # revealed: tuple[Literal[E], @Todo(Support for more typing aliases as base classes), Literal[object]]
|
||||
class ChainMapSubclass(typing.ChainMap): ...
|
||||
|
||||
class F(typing.OrderedDict): ...
|
||||
# TODO: Should be (ChainMapSubclass, ChainMap, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[ChainMapSubclass], Literal[ChainMap], Unknown, Literal[object]]
|
||||
reveal_type(ChainMapSubclass.__mro__)
|
||||
|
||||
reveal_type(F.__mro__) # revealed: tuple[Literal[F], @Todo(Support for more typing aliases as base classes), Literal[object]]
|
||||
class CounterSubclass(typing.Counter): ...
|
||||
|
||||
class G(typing.Counter): ...
|
||||
# TODO: Should be (CounterSubclass, Counter, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[CounterSubclass], Literal[Counter], Unknown, Literal[object]]
|
||||
reveal_type(CounterSubclass.__mro__)
|
||||
|
||||
reveal_type(G.__mro__) # revealed: tuple[Literal[G], @Todo(Support for more typing aliases as base classes), Literal[object]]
|
||||
class DefaultDictSubclass(typing.DefaultDict): ...
|
||||
|
||||
# TODO: Should be (DefaultDictSubclass, defaultdict, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[DefaultDictSubclass], Literal[defaultdict], Unknown, Literal[object]]
|
||||
reveal_type(DefaultDictSubclass.__mro__)
|
||||
|
||||
class DequeSubclass(typing.Deque): ...
|
||||
|
||||
# TODO: Should be (DequeSubclass, deque, MutableSequence, Sequence, Reversible, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[DequeSubclass], Literal[deque], Unknown, Literal[object]]
|
||||
reveal_type(DequeSubclass.__mro__)
|
||||
|
||||
class OrderedDictSubclass(typing.OrderedDict): ...
|
||||
|
||||
# TODO: Should be (OrderedDictSubclass, OrderedDict, dict, MutableMapping, Mapping, Collection, Sized, Iterable, Container, Generic, object)
|
||||
# revealed: tuple[Literal[OrderedDictSubclass], Literal[OrderedDict], Unknown, Literal[object]]
|
||||
reveal_type(OrderedDictSubclass.__mro__)
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ 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]]
|
||||
reveal_type(F.__mro__) # revealed: tuple[Literal[F], @Todo(Support for Callable as a base class), Literal[object]]
|
||||
```
|
||||
|
||||
## Subscriptability
|
||||
|
||||
@@ -17,6 +17,7 @@ pub(crate) enum CoreStdlibModule {
|
||||
Sys,
|
||||
#[allow(dead_code)]
|
||||
Abc, // currently only used in tests
|
||||
Collections,
|
||||
}
|
||||
|
||||
impl CoreStdlibModule {
|
||||
@@ -29,6 +30,7 @@ impl CoreStdlibModule {
|
||||
Self::TypingExtensions => "typing_extensions",
|
||||
Self::Sys => "sys",
|
||||
Self::Abc => "abc",
|
||||
Self::Collections => "collections",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1393,6 +1393,11 @@ impl<'db> Type<'db> {
|
||||
| KnownClass::ModuleType
|
||||
| KnownClass::FunctionType
|
||||
| KnownClass::SpecialForm
|
||||
| KnownClass::ChainMap
|
||||
| KnownClass::Counter
|
||||
| KnownClass::DefaultDict
|
||||
| KnownClass::Deque
|
||||
| KnownClass::OrderedDict
|
||||
| KnownClass::StdlibAlias
|
||||
| KnownClass::TypeVar,
|
||||
) => false,
|
||||
@@ -1928,6 +1933,28 @@ impl<'db> Type<'db> {
|
||||
// We treat `typing.Type` exactly the same as `builtins.type`:
|
||||
Type::KnownInstance(KnownInstanceType::Type) => Ok(KnownClass::Type.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::Tuple) => Ok(KnownClass::Tuple.to_instance(db)),
|
||||
|
||||
// Legacy `typing` aliases
|
||||
Type::KnownInstance(KnownInstanceType::List) => Ok(KnownClass::List.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::Dict) => Ok(KnownClass::Dict.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::Set) => Ok(KnownClass::Set.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::FrozenSet) => {
|
||||
Ok(KnownClass::FrozenSet.to_instance(db))
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::ChainMap) => {
|
||||
Ok(KnownClass::ChainMap.to_instance(db))
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::Counter) => {
|
||||
Ok(KnownClass::Counter.to_instance(db))
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::DefaultDict) => {
|
||||
Ok(KnownClass::DefaultDict.to_instance(db))
|
||||
}
|
||||
Type::KnownInstance(KnownInstanceType::Deque) => Ok(KnownClass::Deque.to_instance(db)),
|
||||
Type::KnownInstance(KnownInstanceType::OrderedDict) => {
|
||||
Ok(KnownClass::OrderedDict.to_instance(db))
|
||||
}
|
||||
|
||||
Type::Union(union) => {
|
||||
let mut builder = UnionBuilder::new(db);
|
||||
let mut invalid_expressions = smallvec::SmallVec::default();
|
||||
@@ -2189,6 +2216,12 @@ pub enum KnownClass {
|
||||
TypeVar,
|
||||
TypeAliasType,
|
||||
NoDefaultType,
|
||||
// Collections
|
||||
ChainMap,
|
||||
Counter,
|
||||
DefaultDict,
|
||||
Deque,
|
||||
OrderedDict,
|
||||
// sys
|
||||
VersionInfo,
|
||||
}
|
||||
@@ -2219,6 +2252,11 @@ impl<'db> KnownClass {
|
||||
Self::TypeVar => "TypeVar",
|
||||
Self::TypeAliasType => "TypeAliasType",
|
||||
Self::NoDefaultType => "_NoDefaultType",
|
||||
Self::ChainMap => "ChainMap",
|
||||
Self::Counter => "Counter",
|
||||
Self::DefaultDict => "defaultdict",
|
||||
Self::Deque => "deque",
|
||||
Self::OrderedDict => "OrderedDict",
|
||||
// For example, `typing.List` is defined as `List = _Alias()` in typeshed
|
||||
Self::StdlibAlias => "_Alias",
|
||||
// This is the name the type of `sys.version_info` has in typeshed,
|
||||
@@ -2282,6 +2320,11 @@ impl<'db> KnownClass {
|
||||
CoreStdlibModule::TypingExtensions
|
||||
}
|
||||
}
|
||||
Self::ChainMap
|
||||
| Self::Counter
|
||||
| Self::DefaultDict
|
||||
| Self::Deque
|
||||
| Self::OrderedDict => CoreStdlibModule::Collections,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2309,6 +2352,11 @@ impl<'db> KnownClass {
|
||||
| Self::ModuleType
|
||||
| Self::FunctionType
|
||||
| Self::SpecialForm
|
||||
| Self::ChainMap
|
||||
| Self::Counter
|
||||
| Self::DefaultDict
|
||||
| Self::Deque
|
||||
| Self::OrderedDict
|
||||
| Self::StdlibAlias
|
||||
| Self::BaseException
|
||||
| Self::BaseExceptionGroup
|
||||
@@ -2341,6 +2389,11 @@ impl<'db> KnownClass {
|
||||
"ModuleType" => Self::ModuleType,
|
||||
"FunctionType" => Self::FunctionType,
|
||||
"TypeAliasType" => Self::TypeAliasType,
|
||||
"ChainMap" => Self::ChainMap,
|
||||
"Counter" => Self::Counter,
|
||||
"defaultdict" => Self::DefaultDict,
|
||||
"deque" => Self::Deque,
|
||||
"OrderedDict" => Self::OrderedDict,
|
||||
"_Alias" => Self::StdlibAlias,
|
||||
"_SpecialForm" => Self::SpecialForm,
|
||||
"_NoDefaultType" => Self::NoDefaultType,
|
||||
@@ -2372,6 +2425,11 @@ impl<'db> KnownClass {
|
||||
| Self::Dict
|
||||
| Self::Slice
|
||||
| Self::GenericAlias
|
||||
| Self::ChainMap
|
||||
| Self::Counter
|
||||
| Self::DefaultDict
|
||||
| Self::Deque
|
||||
| Self::OrderedDict
|
||||
| Self::StdlibAlias // no equivalent class exists in typing_extensions, nor ever will
|
||||
| Self::ModuleType
|
||||
| Self::VersionInfo
|
||||
@@ -2407,6 +2465,24 @@ pub enum KnownInstanceType<'db> {
|
||||
Any,
|
||||
/// The symbol `typing.Tuple` (which can also be found as `typing_extensions.Tuple`)
|
||||
Tuple,
|
||||
/// The symbol `typing.List` (which can also be found as `typing_extensions.List`)
|
||||
List,
|
||||
/// The symbol `typing.Dict` (which can also be found as `typing_extensions.Dict`)
|
||||
Dict,
|
||||
/// The symbol `typing.Set` (which can also be found as `typing_extensions.Set`)
|
||||
Set,
|
||||
/// The symbol `typing.FrozenSet` (which can also be found as `typing_extensions.FrozenSet`)
|
||||
FrozenSet,
|
||||
/// The symbol `typing.ChainMap` (which can also be found as `typing_extensions.ChainMap`)
|
||||
ChainMap,
|
||||
/// The symbol `typing.Counter` (which can also be found as `typing_extensions.Counter`)
|
||||
Counter,
|
||||
/// The symbol `typing.DefaultDict` (which can also be found as `typing_extensions.DefaultDict`)
|
||||
DefaultDict,
|
||||
/// The symbol `typing.Deque` (which can also be found as `typing_extensions.Deque`)
|
||||
Deque,
|
||||
/// The symbol `typing.OrderedDict` (which can also be found as `typing_extensions.OrderedDict`)
|
||||
OrderedDict,
|
||||
/// The symbol `typing.Type` (which can also be found as `typing_extensions.Type`)
|
||||
Type,
|
||||
/// A single instance of `typing.TypeVar`
|
||||
@@ -2427,15 +2503,6 @@ pub enum KnownInstanceType<'db> {
|
||||
TypeAlias,
|
||||
TypeGuard,
|
||||
TypeIs,
|
||||
List,
|
||||
Dict,
|
||||
DefaultDict,
|
||||
Set,
|
||||
FrozenSet,
|
||||
Counter,
|
||||
Deque,
|
||||
ChainMap,
|
||||
OrderedDict,
|
||||
ReadOnly,
|
||||
// TODO: fill this enum out with more special forms, etc.
|
||||
}
|
||||
|
||||
@@ -112,15 +112,24 @@ impl<'db> ClassBase<'db> {
|
||||
KnownInstanceType::FrozenSet => {
|
||||
Self::try_from_ty(db, KnownClass::FrozenSet.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::Callable
|
||||
| KnownInstanceType::ChainMap
|
||||
| KnownInstanceType::Counter
|
||||
| KnownInstanceType::DefaultDict
|
||||
| KnownInstanceType::Deque
|
||||
| KnownInstanceType::OrderedDict => Self::try_from_ty(
|
||||
db,
|
||||
todo_type!("Support for more typing aliases as base classes"),
|
||||
),
|
||||
KnownInstanceType::ChainMap => {
|
||||
Self::try_from_ty(db, KnownClass::ChainMap.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::Counter => {
|
||||
Self::try_from_ty(db, KnownClass::Counter.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::DefaultDict => {
|
||||
Self::try_from_ty(db, KnownClass::DefaultDict.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::Deque => {
|
||||
Self::try_from_ty(db, KnownClass::Deque.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::OrderedDict => {
|
||||
Self::try_from_ty(db, KnownClass::OrderedDict.to_class_literal(db))
|
||||
}
|
||||
KnownInstanceType::Callable => {
|
||||
Self::try_from_ty(db, todo_type!("Support for Callable as a base class"))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4938,42 +4938,45 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("Callable types")
|
||||
}
|
||||
|
||||
// TODO: Generics
|
||||
KnownInstanceType::ChainMap => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.ChainMap alias")
|
||||
KnownClass::ChainMap.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::OrderedDict => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.OrderedDict alias")
|
||||
KnownClass::OrderedDict.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::Dict => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.Dict alias")
|
||||
KnownClass::Dict.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::List => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.List alias")
|
||||
KnownClass::List.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::DefaultDict => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.DefaultDict[] alias")
|
||||
KnownClass::DefaultDict.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::Counter => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.Counter[] alias")
|
||||
KnownClass::Counter.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::Set => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.Set alias")
|
||||
KnownClass::Set.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::FrozenSet => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.FrozenSet alias")
|
||||
KnownClass::FrozenSet.to_instance(self.db)
|
||||
}
|
||||
KnownInstanceType::Deque => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("typing.Deque alias")
|
||||
KnownClass::Deque.to_instance(self.db)
|
||||
}
|
||||
|
||||
KnownInstanceType::ReadOnly => {
|
||||
self.infer_type_expression(arguments_slice);
|
||||
todo_type!("Required[] type qualifier")
|
||||
|
||||
Reference in New Issue
Block a user