mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-27 12:29:28 +00:00
[red-knot] Fixes to Type::to_meta_type
(#14942)
This commit is contained in:
parent
d2712c7669
commit
dbc191d2d6
5 changed files with 65 additions and 18 deletions
|
@ -194,3 +194,26 @@ class A[T: str](metaclass=M): ...
|
||||||
|
|
||||||
reveal_type(A.__class__) # revealed: Literal[M]
|
reveal_type(A.__class__) # revealed: Literal[M]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Metaclasses of metaclasses
|
||||||
|
|
||||||
|
```py
|
||||||
|
class Foo(type): ...
|
||||||
|
class Bar(type, metaclass=Foo): ...
|
||||||
|
class Baz(type, metaclass=Bar): ...
|
||||||
|
class Spam(metaclass=Baz): ...
|
||||||
|
|
||||||
|
reveal_type(Spam.__class__) # revealed: Literal[Baz]
|
||||||
|
reveal_type(Spam.__class__.__class__) # revealed: Literal[Bar]
|
||||||
|
reveal_type(Spam.__class__.__class__.__class__) # revealed: Literal[Foo]
|
||||||
|
|
||||||
|
def test(x: Spam):
|
||||||
|
reveal_type(x.__class__) # revealed: type[Spam]
|
||||||
|
reveal_type(x.__class__.__class__) # revealed: type[Baz]
|
||||||
|
reveal_type(x.__class__.__class__.__class__) # revealed: type[Bar]
|
||||||
|
reveal_type(x.__class__.__class__.__class__.__class__) # revealed: type[Foo]
|
||||||
|
reveal_type(x.__class__.__class__.__class__.__class__.__class__) # revealed: type[type]
|
||||||
|
|
||||||
|
# revealed: type[type]
|
||||||
|
reveal_type(x.__class__.__class__.__class__.__class__.__class__.__class__.__class__.__class__)
|
||||||
|
```
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# type[Any]
|
# `type[Any]`
|
||||||
|
|
||||||
## Simple
|
## Simple
|
||||||
|
|
||||||
|
@ -51,3 +51,22 @@ x: type[object] = type
|
||||||
x: type[object] = A
|
x: type[object] = A
|
||||||
x: type[object] = A() # error: [invalid-assignment]
|
x: type[object] = A() # error: [invalid-assignment]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## The type of `Any` is `type[Any]`
|
||||||
|
|
||||||
|
`Any` represents an unknown set of possible runtime values. If `x` is of type `Any`, the type of
|
||||||
|
`x.__class__` is also unknown and remains dynamic, *except* that we know it must be a class object
|
||||||
|
of some kind. As such, the type of `x.__class__` is `type[Any]` rather than `Any`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Any
|
||||||
|
from does_not_exist import SomethingUnknown # error: [unresolved-import]
|
||||||
|
|
||||||
|
reveal_type(SomethingUnknown) # revealed: Unknown
|
||||||
|
|
||||||
|
def test(x: Any, y: SomethingUnknown):
|
||||||
|
reveal_type(x.__class__) # revealed: type[Any]
|
||||||
|
reveal_type(x.__class__.__class__.__class__.__class__) # revealed: type[Any]
|
||||||
|
reveal_type(y.__class__) # revealed: type[Unknown]
|
||||||
|
reveal_type(y.__class__.__class__.__class__.__class__) # revealed: type[Unknown]
|
||||||
|
```
|
||||||
|
|
|
@ -9,7 +9,7 @@ from typing import Type
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
|
|
||||||
def _(c: Type, d: Type[A], e: Type[A]):
|
def _(c: Type, d: Type[A]):
|
||||||
reveal_type(c) # revealed: type
|
reveal_type(c) # revealed: type
|
||||||
reveal_type(d) # revealed: type[A]
|
reveal_type(d) # revealed: type[A]
|
||||||
c = d # fine
|
c = d # fine
|
||||||
|
|
|
@ -1844,23 +1844,22 @@ impl<'db> Type<'db> {
|
||||||
Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_literal(db),
|
Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_literal(db),
|
||||||
Type::Tuple(_) => KnownClass::Tuple.to_class_literal(db),
|
Type::Tuple(_) => KnownClass::Tuple.to_class_literal(db),
|
||||||
Type::ClassLiteral(ClassLiteralType { class }) => class.metaclass(db),
|
Type::ClassLiteral(ClassLiteralType { class }) => class.metaclass(db),
|
||||||
Type::SubclassOf(SubclassOfType {
|
Type::SubclassOf(SubclassOfType { base }) => match base {
|
||||||
base: ClassBase::Class(class),
|
ClassBase::Any | ClassBase::Unknown | ClassBase::Todo(_) => *self,
|
||||||
}) => Type::subclass_of(
|
ClassBase::Class(class) => Type::subclass_of_base(
|
||||||
class
|
ClassBase::try_from_ty(db, class.metaclass(db)).unwrap_or(ClassBase::Unknown),
|
||||||
.try_metaclass(db)
|
|
||||||
.ok()
|
|
||||||
.and_then(Type::into_class_literal)
|
|
||||||
.unwrap_or_else(|| KnownClass::Type.to_class_literal(db).expect_class_literal())
|
|
||||||
.class,
|
|
||||||
),
|
),
|
||||||
Type::SubclassOf(_) => Type::Any,
|
},
|
||||||
|
|
||||||
Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db),
|
Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db),
|
||||||
Type::Any => Type::Any,
|
Type::Any => Type::subclass_of_base(ClassBase::Any),
|
||||||
Type::Unknown => Type::Unknown,
|
Type::Unknown => Type::subclass_of_base(ClassBase::Unknown),
|
||||||
// TODO intersections
|
// TODO intersections
|
||||||
Type::Intersection(_) => todo_type!(),
|
Type::Intersection(_) => Type::subclass_of_base(
|
||||||
todo @ Type::Todo(_) => *todo,
|
ClassBase::try_from_ty(db, todo_type!("Intersection meta-type"))
|
||||||
|
.expect("Type::Todo should be a valid ClassBase"),
|
||||||
|
),
|
||||||
|
Type::Todo(todo) => Type::subclass_of_base(ClassBase::Todo(*todo)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,7 +334,7 @@ impl<'db> ClassBase<'db> {
|
||||||
/// Attempt to resolve `ty` into a `ClassBase`.
|
/// Attempt to resolve `ty` into a `ClassBase`.
|
||||||
///
|
///
|
||||||
/// Return `None` if `ty` is not an acceptable type for a class base.
|
/// Return `None` if `ty` is not an acceptable type for a class base.
|
||||||
fn try_from_ty(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
|
pub(super) fn try_from_ty(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
|
||||||
match ty {
|
match ty {
|
||||||
Type::Any => Some(Self::Any),
|
Type::Any => Some(Self::Any),
|
||||||
Type::Unknown => Some(Self::Unknown),
|
Type::Unknown => Some(Self::Unknown),
|
||||||
|
@ -449,6 +449,12 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'db> From<&ClassBase<'db>> for Type<'db> {
|
||||||
|
fn from(value: &ClassBase<'db>) -> Self {
|
||||||
|
Self::from(*value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Implementation of the [C3-merge algorithm] for calculating a Python class's
|
/// Implementation of the [C3-merge algorithm] for calculating a Python class's
|
||||||
/// [method resolution order].
|
/// [method resolution order].
|
||||||
///
|
///
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue