mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 04:45:01 +00:00
[ty] Allow classes to inherit from type[Any]
or type[Unknown]
(#18060)
This commit is contained in:
parent
41fa082414
commit
7e9b0df18a
5 changed files with 68 additions and 48 deletions
|
@ -177,6 +177,23 @@ if not isinstance(DoesNotExist, type):
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Inheritance from `type[Any]` and `type[Unknown]`
|
||||||
|
|
||||||
|
Inheritance from `type[Any]` and `type[Unknown]` is also permitted, in keeping with the gradual
|
||||||
|
guarantee:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Any
|
||||||
|
from ty_extensions import Unknown, Intersection
|
||||||
|
|
||||||
|
def f(x: type[Any], y: Intersection[Unknown, type[Any]]):
|
||||||
|
class Foo(x): ...
|
||||||
|
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Any, <class 'object'>]
|
||||||
|
|
||||||
|
class Bar(y): ...
|
||||||
|
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Unknown, <class 'object'>]
|
||||||
|
```
|
||||||
|
|
||||||
## `__bases__` lists that cause errors at runtime
|
## `__bases__` lists that cause errors at runtime
|
||||||
|
|
||||||
If the class's `__bases__` cause an exception to be raised at runtime and therefore the class
|
If the class's `__bases__` cause an exception to be raised at runtime and therefore the class
|
||||||
|
|
|
@ -140,6 +140,43 @@ info[revealed-type]: Revealed type
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
error[duplicate-base]: Duplicate base class `Eggs`
|
||||||
|
--> src/mdtest_snippet.py:16:7
|
||||||
|
|
|
||||||
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
||||||
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
|
16 | class Ham(
|
||||||
|
| _______^
|
||||||
|
17 | | Spam,
|
||||||
|
18 | | Eggs,
|
||||||
|
19 | | Bar,
|
||||||
|
20 | | Baz,
|
||||||
|
21 | | Spam,
|
||||||
|
22 | | Eggs,
|
||||||
|
23 | | ): ...
|
||||||
|
| |_^
|
||||||
|
24 |
|
||||||
|
25 | # fmt: on
|
||||||
|
|
|
||||||
|
info: The definition of class `Ham` will raise `TypeError` at runtime
|
||||||
|
--> src/mdtest_snippet.py:18:5
|
||||||
|
|
|
||||||
|
16 | class Ham(
|
||||||
|
17 | Spam,
|
||||||
|
18 | Eggs,
|
||||||
|
| ---- Class `Eggs` first included in bases list here
|
||||||
|
19 | Bar,
|
||||||
|
20 | Baz,
|
||||||
|
21 | Spam,
|
||||||
|
22 | Eggs,
|
||||||
|
| ^^^^ Class `Eggs` later repeated here
|
||||||
|
23 | ): ...
|
||||||
|
|
|
||||||
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `Spam`
|
error[duplicate-base]: Duplicate base class `Spam`
|
||||||
--> src/mdtest_snippet.py:16:7
|
--> src/mdtest_snippet.py:16:7
|
||||||
|
@ -178,43 +215,6 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
error[duplicate-base]: Duplicate base class `Eggs`
|
|
||||||
--> src/mdtest_snippet.py:16:7
|
|
||||||
|
|
|
||||||
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
|
||||||
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
|
||||||
16 | class Ham(
|
|
||||||
| _______^
|
|
||||||
17 | | Spam,
|
|
||||||
18 | | Eggs,
|
|
||||||
19 | | Bar,
|
|
||||||
20 | | Baz,
|
|
||||||
21 | | Spam,
|
|
||||||
22 | | Eggs,
|
|
||||||
23 | | ): ...
|
|
||||||
| |_^
|
|
||||||
24 |
|
|
||||||
25 | # fmt: on
|
|
||||||
|
|
|
||||||
info: The definition of class `Ham` will raise `TypeError` at runtime
|
|
||||||
--> src/mdtest_snippet.py:18:5
|
|
||||||
|
|
|
||||||
16 | class Ham(
|
|
||||||
17 | Spam,
|
|
||||||
18 | Eggs,
|
|
||||||
| ---- Class `Eggs` first included in bases list here
|
|
||||||
19 | Bar,
|
|
||||||
20 | Baz,
|
|
||||||
21 | Spam,
|
|
||||||
22 | Eggs,
|
|
||||||
| ^^^^ Class `Eggs` later repeated here
|
|
||||||
23 | ): ...
|
|
||||||
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
```
|
||||||
info[revealed-type]: Revealed type
|
info[revealed-type]: Revealed type
|
||||||
--> src/mdtest_snippet.py:27:13
|
--> src/mdtest_snippet.py:27:13
|
||||||
|
|
|
@ -543,13 +543,6 @@ impl<'db> Type<'db> {
|
||||||
Self::Dynamic(DynamicType::Unknown)
|
Self::Dynamic(DynamicType::Unknown)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn into_dynamic(self) -> Option<DynamicType> {
|
|
||||||
match self {
|
|
||||||
Type::Dynamic(dynamic_type) => Some(dynamic_type),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn object(db: &'db dyn Db) -> Self {
|
pub fn object(db: &'db dyn Db) -> Self {
|
||||||
KnownClass::Object.to_instance(db)
|
KnownClass::Object.to_instance(db)
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,16 +107,20 @@ impl<'db> ClassBase<'db> {
|
||||||
{
|
{
|
||||||
Self::try_from_type(db, todo_type!("GenericAlias instance"))
|
Self::try_from_type(db, todo_type!("GenericAlias instance"))
|
||||||
}
|
}
|
||||||
|
Type::SubclassOf(subclass_of) => subclass_of
|
||||||
|
.subclass_of()
|
||||||
|
.into_dynamic()
|
||||||
|
.map(ClassBase::Dynamic),
|
||||||
Type::Intersection(inter) => {
|
Type::Intersection(inter) => {
|
||||||
let dynamic_element = inter
|
let valid_element = inter
|
||||||
.positive(db)
|
.positive(db)
|
||||||
.iter()
|
.iter()
|
||||||
.find_map(|elem| elem.into_dynamic())?;
|
.find_map(|elem| ClassBase::try_from_type(db, *elem))?;
|
||||||
|
|
||||||
if ty.is_disjoint_from(db, KnownClass::Type.to_instance(db)) {
|
if ty.is_disjoint_from(db, KnownClass::Type.to_instance(db)) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(ClassBase::Dynamic(dynamic_element))
|
Some(valid_element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs?
|
Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs?
|
||||||
|
@ -137,7 +141,6 @@ impl<'db> ClassBase<'db> {
|
||||||
| Type::LiteralString
|
| Type::LiteralString
|
||||||
| Type::Tuple(_)
|
| Type::Tuple(_)
|
||||||
| Type::ModuleLiteral(_)
|
| Type::ModuleLiteral(_)
|
||||||
| Type::SubclassOf(_)
|
|
||||||
| Type::TypeVar(_)
|
| Type::TypeVar(_)
|
||||||
| Type::BoundSuper(_)
|
| Type::BoundSuper(_)
|
||||||
| Type::ProtocolInstance(_)
|
| Type::ProtocolInstance(_)
|
||||||
|
|
|
@ -138,6 +138,13 @@ impl<'db> SubclassOfInner<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn into_dynamic(self) -> Option<DynamicType> {
|
||||||
|
match self {
|
||||||
|
Self::Class(_) => None,
|
||||||
|
Self::Dynamic(dynamic) => Some(dynamic),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
|
pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
|
||||||
match ty {
|
match ty {
|
||||||
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
|
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue