mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-01 06:11:21 +00:00
[ty]: Consider a class with a dynamic element in its MRO assignable to any subtype of type
(#18205)
This commit is contained in:
parent
4fad15805b
commit
fb589730ef
4 changed files with 54 additions and 0 deletions
|
@ -62,6 +62,20 @@ def foo(
|
||||||
reveal_type(i) # revealed: BaseException
|
reveal_type(i) # revealed: BaseException
|
||||||
```
|
```
|
||||||
|
|
||||||
|
We do not emit an `invalid-exception-caught` if a class is caught that has `Any` or `Unknown` in its
|
||||||
|
MRO, as the dynamic element in the MRO could materialize to some subclass of `BaseException`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from compat import BASE_EXCEPTION_CLASS # error: [unresolved-import] "Cannot resolve imported module `compat`"
|
||||||
|
|
||||||
|
class Error(BASE_EXCEPTION_CLASS): ...
|
||||||
|
|
||||||
|
try:
|
||||||
|
...
|
||||||
|
except Error as err:
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
## Invalid exception handlers
|
## Invalid exception handlers
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
|
@ -204,6 +204,34 @@ static_assert(is_assignable_to(type[AnyMeta], type[object]))
|
||||||
static_assert(is_assignable_to(type[AnyMeta], type[Any]))
|
static_assert(is_assignable_to(type[AnyMeta], type[Any]))
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Class-literals that inherit from `Any`
|
||||||
|
|
||||||
|
Class-literal types that inherit from `Any` are assignable to any type `T` where `T` is assignable
|
||||||
|
to `type`:
|
||||||
|
|
||||||
|
```py
|
||||||
|
from typing import Any
|
||||||
|
from ty_extensions import is_assignable_to, static_assert, TypeOf
|
||||||
|
|
||||||
|
def test(x: Any):
|
||||||
|
class Foo(x): ...
|
||||||
|
class Bar(Any): ...
|
||||||
|
static_assert(is_assignable_to(TypeOf[Foo], Any))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Foo], type))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Foo], type[int]))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Foo], type[Any]))
|
||||||
|
|
||||||
|
static_assert(is_assignable_to(TypeOf[Bar], Any))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Bar], type))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Bar], type[int]))
|
||||||
|
static_assert(is_assignable_to(TypeOf[Bar], type[Any]))
|
||||||
|
|
||||||
|
static_assert(not is_assignable_to(TypeOf[Foo], int))
|
||||||
|
static_assert(not is_assignable_to(TypeOf[Bar], int))
|
||||||
|
```
|
||||||
|
|
||||||
|
This is because the `Any` element in the MRO could materialize to any subtype of `type`.
|
||||||
|
|
||||||
## Heterogeneous tuple types
|
## Heterogeneous tuple types
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
|
|
@ -1542,6 +1542,14 @@ impl<'db> Type<'db> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Type::ClassLiteral(class), Type::SubclassOf(_))
|
||||||
|
if class
|
||||||
|
.iter_mro(db, None)
|
||||||
|
.any(class_base::ClassBase::is_dynamic) =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
// Every `type[...]` is assignable to `type`
|
// Every `type[...]` is assignable to `type`
|
||||||
(Type::SubclassOf(_), _)
|
(Type::SubclassOf(_), _)
|
||||||
if KnownClass::Type
|
if KnownClass::Type
|
||||||
|
|
|
@ -303,6 +303,10 @@ impl<'db> ClassBase<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn is_dynamic(self) -> bool {
|
||||||
|
matches!(self, Self::Dynamic(_))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue