[red-knot] Understand typing.Tuple (#14927)
Some checks are pending
CI / cargo fmt (push) Waiting to run
CI / cargo build (release) (push) Waiting to run
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / Determine changes (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / benchmarks (push) Blocked by required conditions

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
InSync 2024-12-12 07:58:06 +07:00 committed by GitHub
parent a7e5e42b88
commit e4885a2fb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 66 additions and 2 deletions

View file

@ -72,3 +72,55 @@ def _(m: int, n: int):
# TODO: Support overloads... Should be `tuple[Literal[1, 'a', b"b"] | None, ...]`
reveal_type(tuple_slice) # revealed: @Todo(return type)
```
## Inheritance
```toml
[environment]
target-version = "3.9"
```
```py
# TODO:
# * `tuple.__class_getitem__` is always bound on 3.9 (`sys.version_info`)
# * `tuple[int, str]` is a valid base (generics)
# error: [call-possibly-unbound-method] "Method `__class_getitem__` of type `Literal[tuple]` is possibly unbound"
# error: [invalid-base] "Invalid class base with type `GenericAlias` (all bases must be a class, `Any`, `Unknown` or `Todo`)"
class A(tuple[int, str]): ...
# Runtime value: `(A, tuple, object)`
# TODO: Generics
reveal_type(A.__mro__) # revealed: tuple[Literal[A], Unknown, Literal[object]]
```
## `typing.Tuple`
### Correspondence with `tuple`
`typing.Tuple` can be used interchangeably with `tuple`:
```py
from typing import Tuple
class A: ...
def _(c: Tuple, d: Tuple[int, A], e: Tuple[Any, ...]):
reveal_type(c) # revealed: tuple
reveal_type(d) # revealed: tuple[int, A]
reveal_type(e) # revealed: @Todo(full tuple[...] support)
```
### Inheritance
Inheriting from `Tuple` results in a MRO with `builtins.tuple` and `typing.Generic`. `Tuple` itself
is not a class.
```py
from typing import Tuple
class C(Tuple): ...
# Runtime value: `(C, tuple, typing.Generic, object)`
# TODO: Add `Generic` to the MRO
reveal_type(C.__mro__) # revealed: tuple[Literal[C], Literal[tuple], Unknown, Literal[object]]
```

View file

@ -1758,6 +1758,7 @@ impl<'db> Type<'db> {
Type::ClassLiteral(_) | Type::SubclassOf(_) => self.to_instance(db),
// We treat `typing.Type` exactly the same as `builtins.type`:
Type::KnownInstance(KnownInstanceType::Type) => KnownClass::Type.to_instance(db),
Type::KnownInstance(KnownInstanceType::Tuple) => KnownClass::Tuple.to_instance(db),
Type::Union(union) => union.map(db, |element| element.in_type_expression(db)),
Type::Unknown => Type::Unknown,
// TODO map this to a new `Type::TypeVar` variant
@ -2137,6 +2138,8 @@ pub enum KnownInstanceType<'db> {
Never,
/// The symbol `typing.Any` (which can also be found as `typing_extensions.Any`)
Any,
/// The symbol `typing.Tuple` (which can also be found as `typing_extensions.Tuple`)
Tuple,
/// The symbol `typing.Type` (which can also be found as `typing_extensions.Type`)
Type,
/// A single instance of `typing.TypeVar`
@ -2157,6 +2160,7 @@ impl<'db> KnownInstanceType<'db> {
Self::NoReturn => "NoReturn",
Self::Never => "Never",
Self::Any => "Any",
Self::Tuple => "Tuple",
Self::Type => "Type",
Self::TypeAliasType(_) => "TypeAliasType",
}
@ -2173,6 +2177,7 @@ impl<'db> KnownInstanceType<'db> {
| Self::NoReturn
| Self::Never
| Self::Any
| Self::Tuple
| Self::Type
| Self::TypeAliasType(_) => Truthiness::AlwaysTrue,
}
@ -2188,6 +2193,7 @@ impl<'db> KnownInstanceType<'db> {
Self::NoReturn => "typing.NoReturn",
Self::Never => "typing.Never",
Self::Any => "typing.Any",
Self::Tuple => "typing.Tuple",
Self::Type => "typing.Type",
Self::TypeVar(typevar) => typevar.name(db),
Self::TypeAliasType(_) => "typing.TypeAliasType",
@ -2204,7 +2210,8 @@ impl<'db> KnownInstanceType<'db> {
Self::NoReturn => KnownClass::SpecialForm,
Self::Never => KnownClass::SpecialForm,
Self::Any => KnownClass::Object,
Self::Type => KnownClass::Object,
Self::Tuple => KnownClass::SpecialForm,
Self::Type => KnownClass::SpecialForm,
Self::TypeVar(_) => KnownClass::TypeVar,
Self::TypeAliasType(_) => KnownClass::TypeAliasType,
}
@ -2231,6 +2238,7 @@ impl<'db> KnownInstanceType<'db> {
("typing" | "typing_extensions", "Union") => Some(Self::Union),
("typing" | "typing_extensions", "NoReturn") => Some(Self::NoReturn),
("typing" | "typing_extensions", "Never") => Some(Self::Never),
("typing" | "typing_extensions", "Tuple") => Some(Self::Tuple),
("typing" | "typing_extensions", "Type") => Some(Self::Type),
_ => None,
}

View file

@ -4862,6 +4862,7 @@ impl<'db> TypeInferenceBuilder<'db> {
Type::Unknown
}
KnownInstanceType::Type => self.infer_subclass_of_type_expression(parameters),
KnownInstanceType::Tuple => self.infer_tuple_type_expression(parameters),
KnownInstanceType::Any => Type::Any,
}
}

View file

@ -364,10 +364,13 @@ impl<'db> ClassBase<'db> {
| KnownInstanceType::Never
| KnownInstanceType::Optional => None,
KnownInstanceType::Any => Some(Self::Any),
// TODO: classes inheriting from `typing.Type` also have `Generic` in their MRO
// TODO: Classes inheriting from `typing.Type` et al. also have `Generic` in their MRO
KnownInstanceType::Type => {
ClassBase::try_from_ty(db, KnownClass::Type.to_class_literal(db))
}
KnownInstanceType::Tuple => {
ClassBase::try_from_ty(db, KnownClass::Tuple.to_class_literal(db))
}
},
}
}