Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/ty_python_semantic/resources/mdtest/mro.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ if not isinstance(DoesNotExist, type):
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

If the class's `__bases__` cause an exception to be raised at runtime and therefore the class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ info[revealed-type]: Revealed type
```

```
error[duplicate-base]: Duplicate base class `Spam`
error[duplicate-base]: Duplicate base class `Eggs`
--> src/mdtest_snippet.py:16:7
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
Expand All @@ -160,26 +160,25 @@ error[duplicate-base]: Duplicate base class `Spam`
25 | # fmt: on
|
info: The definition of class `Ham` will raise `TypeError` at runtime
--> src/mdtest_snippet.py:17:5
--> src/mdtest_snippet.py:18:5
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
16 | class Ham(
17 | Spam,
| ---- Class `Spam` first included in bases list here
18 | Eggs,
| ---- Class `Eggs` first included in bases list here
19 | Bar,
20 | Baz,
21 | Spam,
| ^^^^ Class `Spam` later repeated here
22 | Eggs,
| ^^^^ Class `Eggs` later repeated here
23 | ): ...
|
info: rule `duplicate-base` is enabled by default

```

```
error[duplicate-base]: Duplicate base class `Eggs`
error[duplicate-base]: Duplicate base class `Spam`
--> src/mdtest_snippet.py:16:7
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
Expand All @@ -198,17 +197,18 @@ error[duplicate-base]: Duplicate base class `Eggs`
25 | # fmt: on
|
info: The definition of class `Ham` will raise `TypeError` at runtime
--> src/mdtest_snippet.py:18:5
--> src/mdtest_snippet.py:17:5
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
16 | class Ham(
17 | Spam,
| ---- Class `Spam` first included in bases list here
18 | Eggs,
| ---- Class `Eggs` first included in bases list here
19 | Bar,
20 | Baz,
21 | Spam,
| ^^^^ Class `Spam` later repeated here
22 | Eggs,
| ^^^^ Class `Eggs` later repeated here
23 | ): ...
|
info: rule `duplicate-base` is enabled by default
Expand Down
7 changes: 0 additions & 7 deletions crates/ty_python_semantic/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -543,13 +543,6 @@ impl<'db> Type<'db> {
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 {
KnownClass::Object.to_instance(db)
}
Expand Down
11 changes: 7 additions & 4 deletions crates/ty_python_semantic/src/types/class_base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,16 +107,20 @@ impl<'db> ClassBase<'db> {
{
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) => {
let dynamic_element = inter
let valid_element = inter
.positive(db)
.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)) {
None
} else {
Some(ClassBase::Dynamic(dynamic_element))
Some(valid_element)
}
}
Type::Union(_) => None, // TODO -- forces consideration of multiple possible MROs?
Expand All @@ -137,7 +141,6 @@ impl<'db> ClassBase<'db> {
| Type::LiteralString
| Type::Tuple(_)
| Type::ModuleLiteral(_)
| Type::SubclassOf(_)
| Type::TypeVar(_)
| Type::BoundSuper(_)
| Type::ProtocolInstance(_)
Expand Down
7 changes: 7 additions & 0 deletions crates/ty_python_semantic/src/types/subclass_of.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Expand Down
Loading