mirror of https://github.com/astral-sh/ruff
[ty] Fix bug where ty would think all types had an `__mro__` attribute (#20995)
This commit is contained in:
parent
3c7f56f582
commit
db0e921db1
|
|
@ -20,6 +20,6 @@ scope-existing-over-new-import,main.py,0,474
|
||||||
scope-prioritize-closer,main.py,0,2
|
scope-prioritize-closer,main.py,0,2
|
||||||
scope-simple-long-identifier,main.py,0,1
|
scope-simple-long-identifier,main.py,0,1
|
||||||
tstring-completions,main.py,0,1
|
tstring-completions,main.py,0,1
|
||||||
ty-extensions-lower-stdlib,main.py,0,7
|
ty-extensions-lower-stdlib,main.py,0,8
|
||||||
type-var-typing-over-ast,main.py,0,3
|
type-var-typing-over-ast,main.py,0,3
|
||||||
type-var-typing-over-ast,main.py,1,270
|
type-var-typing-over-ast,main.py,1,270
|
||||||
|
|
|
||||||
|
|
|
@ -1864,7 +1864,7 @@ C.<CURSOR>
|
||||||
__instancecheck__ :: bound method <class 'C'>.__instancecheck__(instance: Any, /) -> bool
|
__instancecheck__ :: bound method <class 'C'>.__instancecheck__(instance: Any, /) -> bool
|
||||||
__itemsize__ :: int
|
__itemsize__ :: int
|
||||||
__module__ :: str
|
__module__ :: str
|
||||||
__mro__ :: tuple[<class 'C'>, <class 'object'>]
|
__mro__ :: tuple[type, ...]
|
||||||
__name__ :: str
|
__name__ :: str
|
||||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||||
__new__ :: def __new__(cls) -> Self@__new__
|
__new__ :: def __new__(cls) -> Self@__new__
|
||||||
|
|
@ -1933,7 +1933,7 @@ Meta.<CURSOR>
|
||||||
__instancecheck__ :: def __instancecheck__(self, instance: Any, /) -> bool
|
__instancecheck__ :: def __instancecheck__(self, instance: Any, /) -> bool
|
||||||
__itemsize__ :: int
|
__itemsize__ :: int
|
||||||
__module__ :: str
|
__module__ :: str
|
||||||
__mro__ :: tuple[<class 'Meta'>, <class 'type'>, <class 'object'>]
|
__mro__ :: tuple[type, ...]
|
||||||
__name__ :: str
|
__name__ :: str
|
||||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||||
__or__ :: def __or__[Self](self: Self@__or__, value: Any, /) -> UnionType | Self@__or__
|
__or__ :: def __or__[Self](self: Self@__or__, value: Any, /) -> UnionType | Self@__or__
|
||||||
|
|
@ -2061,7 +2061,7 @@ Quux.<CURSOR>
|
||||||
__instancecheck__ :: bound method <class 'Quux'>.__instancecheck__(instance: Any, /) -> bool
|
__instancecheck__ :: bound method <class 'Quux'>.__instancecheck__(instance: Any, /) -> bool
|
||||||
__itemsize__ :: int
|
__itemsize__ :: int
|
||||||
__module__ :: str
|
__module__ :: str
|
||||||
__mro__ :: tuple[<class 'Quux'>, <class 'object'>]
|
__mro__ :: tuple[type, ...]
|
||||||
__name__ :: str
|
__name__ :: str
|
||||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||||
__new__ :: def __new__(cls) -> Self@__new__
|
__new__ :: def __new__(cls) -> Self@__new__
|
||||||
|
|
@ -2138,7 +2138,7 @@ Answer.<CURSOR>
|
||||||
__len__ :: bound method <class 'Answer'>.__len__() -> int
|
__len__ :: bound method <class 'Answer'>.__len__() -> int
|
||||||
__members__ :: MappingProxyType[str, Unknown]
|
__members__ :: MappingProxyType[str, Unknown]
|
||||||
__module__ :: str
|
__module__ :: str
|
||||||
__mro__ :: tuple[<class 'Answer'>, <class 'Enum'>, <class 'object'>]
|
__mro__ :: tuple[type, ...]
|
||||||
__name__ :: str
|
__name__ :: str
|
||||||
__ne__ :: def __ne__(self, value: object, /) -> bool
|
__ne__ :: def __ne__(self, value: object, /) -> bool
|
||||||
__new__ :: def __new__(cls, value: object) -> Self@__new__
|
__new__ :: def __new__(cls, value: object) -> Self@__new__
|
||||||
|
|
|
||||||
|
|
@ -72,21 +72,23 @@ Inheriting from `Annotated[T, ...]` is equivalent to inheriting from `T` itself.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import Annotated
|
from typing_extensions import Annotated
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class C(Annotated[int, "foo"]): ...
|
class C(Annotated[int, "foo"]): ...
|
||||||
|
|
||||||
# TODO: Should be `tuple[Literal[C], Literal[int], Literal[object]]`
|
# TODO: Should be `(<class 'C'>, <class 'int'>, <class 'object'>)`
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, @Todo(Inference of subscript on special form), <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, @Todo(Inference of subscript on special form), <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Not parameterized
|
### Not parameterized
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing_extensions import Annotated
|
from typing_extensions import Annotated
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
# At runtime, this is an error.
|
# At runtime, this is an error.
|
||||||
# error: [invalid-base]
|
# error: [invalid-base]
|
||||||
class C(Annotated): ...
|
class C(Annotated): ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, Unknown, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,11 @@ allowed, even when the unknown superclass is `int`. The assignment to `y` should
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class SubclassOfAny(Any): ...
|
class SubclassOfAny(Any): ...
|
||||||
|
|
||||||
reveal_type(SubclassOfAny.__mro__) # revealed: tuple[<class 'SubclassOfAny'>, Any, <class 'object'>]
|
reveal_mro(SubclassOfAny) # revealed: (<class 'SubclassOfAny'>, Any, <class 'object'>)
|
||||||
|
|
||||||
x: SubclassOfAny = 1 # error: [invalid-assignment]
|
x: SubclassOfAny = 1 # error: [invalid-assignment]
|
||||||
y: int = SubclassOfAny()
|
y: int = SubclassOfAny()
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ The aliases can be inherited from. Some of these are still partially or wholly T
|
||||||
|
|
||||||
```py
|
```py
|
||||||
import typing
|
import typing
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
####################
|
####################
|
||||||
### Built-ins
|
### Built-ins
|
||||||
|
|
@ -121,23 +122,23 @@ import typing
|
||||||
|
|
||||||
class ListSubclass(typing.List): ...
|
class ListSubclass(typing.List): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'ListSubclass'>, <class 'list[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(ListSubclass.__mro__)
|
reveal_mro(ListSubclass)
|
||||||
|
|
||||||
class DictSubclass(typing.Dict): ...
|
class DictSubclass(typing.Dict): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'DictSubclass'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(DictSubclass.__mro__)
|
reveal_mro(DictSubclass)
|
||||||
|
|
||||||
class SetSubclass(typing.Set): ...
|
class SetSubclass(typing.Set): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'SetSubclass'>, <class 'set[Unknown]'>, <class 'MutableSet[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(SetSubclass.__mro__)
|
reveal_mro(SetSubclass)
|
||||||
|
|
||||||
class FrozenSetSubclass(typing.FrozenSet): ...
|
class FrozenSetSubclass(typing.FrozenSet): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'FrozenSetSubclass'>, <class 'frozenset[Unknown]'>, <class 'AbstractSet[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(FrozenSetSubclass.__mro__)
|
reveal_mro(FrozenSetSubclass)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
### `collections`
|
### `collections`
|
||||||
|
|
@ -145,26 +146,26 @@ reveal_type(FrozenSetSubclass.__mro__)
|
||||||
|
|
||||||
class ChainMapSubclass(typing.ChainMap): ...
|
class ChainMapSubclass(typing.ChainMap): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'ChainMapSubclass'>, <class 'ChainMap[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(ChainMapSubclass.__mro__)
|
reveal_mro(ChainMapSubclass)
|
||||||
|
|
||||||
class CounterSubclass(typing.Counter): ...
|
class CounterSubclass(typing.Counter): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'CounterSubclass'>, <class 'Counter[Unknown]'>, <class 'dict[Unknown, int]'>, <class 'MutableMapping[Unknown, int]'>, <class 'Mapping[Unknown, int]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(CounterSubclass.__mro__)
|
reveal_mro(CounterSubclass)
|
||||||
|
|
||||||
class DefaultDictSubclass(typing.DefaultDict): ...
|
class DefaultDictSubclass(typing.DefaultDict): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'DefaultDictSubclass'>, <class 'defaultdict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(DefaultDictSubclass.__mro__)
|
reveal_mro(DefaultDictSubclass)
|
||||||
|
|
||||||
class DequeSubclass(typing.Deque): ...
|
class DequeSubclass(typing.Deque): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'DequeSubclass'>, <class 'deque[Unknown]'>, <class 'MutableSequence[Unknown]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(DequeSubclass.__mro__)
|
reveal_mro(DequeSubclass)
|
||||||
|
|
||||||
class OrderedDictSubclass(typing.OrderedDict): ...
|
class OrderedDictSubclass(typing.OrderedDict): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'OrderedDictSubclass'>, <class 'OrderedDict[Unknown, Unknown]'>, <class 'dict[Unknown, Unknown]'>, <class 'MutableMapping[Unknown, Unknown]'>, <class 'Mapping[Unknown, Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(OrderedDictSubclass.__mro__)
|
reveal_mro(OrderedDictSubclass)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ You can't inherit from most of these. `typing.Callable` is an exception.
|
||||||
```py
|
```py
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing_extensions import Self, Unpack, TypeGuard, TypeIs, Concatenate, Generic
|
from typing_extensions import Self, Unpack, TypeGuard, TypeIs, Concatenate, Generic
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A(Self): ... # error: [invalid-base]
|
class A(Self): ... # error: [invalid-base]
|
||||||
class B(Unpack): ... # error: [invalid-base]
|
class B(Unpack): ... # error: [invalid-base]
|
||||||
|
|
@ -87,7 +88,7 @@ class E(Concatenate): ... # error: [invalid-base]
|
||||||
class F(Callable): ...
|
class F(Callable): ...
|
||||||
class G(Generic): ... # error: [invalid-base] "Cannot inherit from plain `Generic`"
|
class G(Generic): ... # error: [invalid-base] "Cannot inherit from plain `Generic`"
|
||||||
|
|
||||||
reveal_type(F.__mro__) # revealed: tuple[<class 'F'>, @Todo(Support for Callable as a base class), <class 'object'>]
|
reveal_mro(F) # revealed: (<class 'F'>, @Todo(Support for Callable as a base class), <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Subscriptability
|
## Subscriptability
|
||||||
|
|
|
||||||
|
|
@ -1468,6 +1468,8 @@ C.X = "bar"
|
||||||
### Multiple inheritance
|
### Multiple inheritance
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class O: ...
|
class O: ...
|
||||||
|
|
||||||
class F(O):
|
class F(O):
|
||||||
|
|
@ -1481,8 +1483,8 @@ class C(D, F): ...
|
||||||
class B(E, D): ...
|
class B(E, D): ...
|
||||||
class A(B, C): ...
|
class A(B, C): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'A'>, <class 'B'>, <class 'E'>, <class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'A'>, <class 'B'>, <class 'E'>, <class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(A.__mro__)
|
reveal_mro(A)
|
||||||
|
|
||||||
# `E` is earlier in the MRO than `F`, so we should use the type of `E.X`
|
# `E` is earlier in the MRO than `F`, so we should use the type of `E.X`
|
||||||
reveal_type(A.X) # revealed: Unknown | Literal[42]
|
reveal_type(A.X) # revealed: Unknown | Literal[42]
|
||||||
|
|
@ -1682,6 +1684,7 @@ Similar principles apply if `Any` appears in the middle of an inheritance hierar
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import ClassVar, Literal
|
from typing import ClassVar, Literal
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
x: ClassVar[Literal[1]] = 1
|
x: ClassVar[Literal[1]] = 1
|
||||||
|
|
@ -1689,7 +1692,7 @@ class A:
|
||||||
class B(Any): ...
|
class B(Any): ...
|
||||||
class C(B, A): ...
|
class C(B, A): ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, Any, <class 'A'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'B'>, Any, <class 'A'>, <class 'object'>)
|
||||||
reveal_type(C.x) # revealed: Literal[1] & Any
|
reveal_type(C.x) # revealed: Literal[1] & Any
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ python-version = "3.12"
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A:
|
class A:
|
||||||
def a(self): ...
|
def a(self): ...
|
||||||
|
|
@ -39,7 +40,7 @@ class C(B):
|
||||||
def c(self): ...
|
def c(self): ...
|
||||||
cc: int = 3
|
cc: int = 3
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
|
|
||||||
super(C, C()).a
|
super(C, C()).a
|
||||||
super(C, C()).b
|
super(C, C()).b
|
||||||
|
|
@ -420,6 +421,8 @@ When the owner is a union type, `super()` is built separately for each branch, a
|
||||||
super objects are combined into a union.
|
super objects are combined into a union.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
|
|
||||||
class B:
|
class B:
|
||||||
|
|
@ -429,8 +432,8 @@ class C(A, B): ...
|
||||||
class D(B, A): ...
|
class D(B, A): ...
|
||||||
|
|
||||||
def f(x: C | D):
|
def f(x: C | D):
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)
|
||||||
reveal_type(D.__mro__) # revealed: tuple[<class 'D'>, <class 'B'>, <class 'A'>, <class 'object'>]
|
reveal_mro(D) # revealed: (<class 'D'>, <class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
|
|
||||||
s = super(A, x)
|
s = super(A, x)
|
||||||
reveal_type(s) # revealed: <super: <class 'A'>, C> | <super: <class 'A'>, D>
|
reveal_type(s) # revealed: <super: <class 'A'>, C> | <super: <class 'A'>, D>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ python-version = "3.12"
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
A = int
|
A = int
|
||||||
|
|
||||||
class G[T]: ...
|
class G[T]: ...
|
||||||
|
|
@ -21,5 +23,5 @@ class C(A, G["B"]): ...
|
||||||
A = str
|
A = str
|
||||||
B = bytes
|
B = bytes
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'int'>, <class 'G[bytes]'>, typing.Generic, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'int'>, <class 'G[bytes]'>, typing.Generic, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1173,6 +1173,7 @@ and attributes like the MRO are unchanged:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Person:
|
class Person:
|
||||||
|
|
@ -1180,7 +1181,8 @@ class Person:
|
||||||
age: int | None = None
|
age: int | None = None
|
||||||
|
|
||||||
reveal_type(type(Person)) # revealed: <class 'type'>
|
reveal_type(type(Person)) # revealed: <class 'type'>
|
||||||
reveal_type(Person.__mro__) # revealed: tuple[<class 'Person'>, <class 'object'>]
|
reveal_type(Person.__mro__) # revealed: tuple[type, ...]
|
||||||
|
reveal_mro(Person) # revealed: (<class 'Person'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
The generated methods have the following signatures:
|
The generated methods have the following signatures:
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ At its simplest, to define a generic class using PEP 695 syntax, you add a list
|
||||||
`ParamSpec`s or `TypeVarTuple`s after the class name.
|
`ParamSpec`s or `TypeVarTuple`s after the class name.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import generic_context
|
from ty_extensions import generic_context, reveal_mro
|
||||||
|
|
||||||
class SingleTypevar[T]: ...
|
class SingleTypevar[T]: ...
|
||||||
class MultipleTypevars[T, S]: ...
|
class MultipleTypevars[T, S]: ...
|
||||||
|
|
@ -77,13 +77,13 @@ T = TypeVar("T")
|
||||||
# error: [invalid-generic-class] "Cannot both inherit from `typing.Generic` and use PEP 695 type variables"
|
# error: [invalid-generic-class] "Cannot both inherit from `typing.Generic` and use PEP 695 type variables"
|
||||||
class BothGenericSyntaxes[U](Generic[T]): ...
|
class BothGenericSyntaxes[U](Generic[T]): ...
|
||||||
|
|
||||||
reveal_type(BothGenericSyntaxes.__mro__) # revealed: tuple[<class 'BothGenericSyntaxes[Unknown]'>, Unknown, <class 'object'>]
|
reveal_mro(BothGenericSyntaxes) # revealed: (<class 'BothGenericSyntaxes[Unknown]'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
# error: [invalid-generic-class] "Cannot both inherit from `typing.Generic` and use PEP 695 type variables"
|
# error: [invalid-generic-class] "Cannot both inherit from `typing.Generic` and use PEP 695 type variables"
|
||||||
# error: [invalid-base] "Cannot inherit from plain `Generic`"
|
# error: [invalid-base] "Cannot inherit from plain `Generic`"
|
||||||
class DoublyInvalid[T](Generic): ...
|
class DoublyInvalid[T](Generic): ...
|
||||||
|
|
||||||
reveal_type(DoublyInvalid.__mro__) # revealed: tuple[<class 'DoublyInvalid[Unknown]'>, Unknown, <class 'object'>]
|
reveal_mro(DoublyInvalid) # revealed: (<class 'DoublyInvalid[Unknown]'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
Generic classes implicitly inherit from `Generic`:
|
Generic classes implicitly inherit from `Generic`:
|
||||||
|
|
@ -91,26 +91,26 @@ Generic classes implicitly inherit from `Generic`:
|
||||||
```py
|
```py
|
||||||
class Foo[T]: ...
|
class Foo[T]: ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'Foo[Unknown]'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Foo[Unknown]'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Foo.__mro__)
|
reveal_mro(Foo)
|
||||||
# revealed: tuple[<class 'Foo[int]'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Foo[int]'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Foo[int].__mro__)
|
reveal_mro(Foo[int])
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
class Bar[T](A): ...
|
class Bar[T](A): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'Bar[Unknown]'>, <class 'A'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Bar[Unknown]'>, <class 'A'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Bar.__mro__)
|
reveal_mro(Bar)
|
||||||
# revealed: tuple[<class 'Bar[int]'>, <class 'A'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Bar[int]'>, <class 'A'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Bar[int].__mro__)
|
reveal_mro(Bar[int])
|
||||||
|
|
||||||
class B: ...
|
class B: ...
|
||||||
class Baz[T](A, B): ...
|
class Baz[T](A, B): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'Baz[Unknown]'>, <class 'A'>, <class 'B'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Baz[Unknown]'>, <class 'A'>, <class 'B'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Baz.__mro__)
|
reveal_mro(Baz)
|
||||||
# revealed: tuple[<class 'Baz[int]'>, <class 'A'>, <class 'B'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Baz[int]'>, <class 'A'>, <class 'B'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Baz[int].__mro__)
|
reveal_mro(Baz[int])
|
||||||
```
|
```
|
||||||
|
|
||||||
## Specializing generic classes explicitly
|
## Specializing generic classes explicitly
|
||||||
|
|
|
||||||
|
|
@ -67,22 +67,25 @@ x = "foo" # error: [invalid-assignment] "Object of type `Literal["foo"]"
|
||||||
`a.py`:
|
`a.py`:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
|
|
||||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, <class 'object'>]
|
reveal_mro(A) # revealed: (<class 'A'>, <class 'object'>)
|
||||||
import b
|
import b
|
||||||
|
|
||||||
class C(b.B): ...
|
class C(b.B): ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
`b.py`:
|
`b.py`:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
from a import A
|
from a import A
|
||||||
|
|
||||||
class B(A): ...
|
class B(A): ...
|
||||||
|
|
||||||
reveal_type(B.__mro__) # revealed: tuple[<class 'B'>, <class 'A'>, <class 'object'>]
|
reveal_mro(B) # revealed: (<class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -798,11 +798,11 @@ A class literal can be iterated over if it has `Any` or `Unknown` in its MRO, si
|
||||||
```py
|
```py
|
||||||
from unresolved_module import SomethingUnknown # error: [unresolved-import]
|
from unresolved_module import SomethingUnknown # error: [unresolved-import]
|
||||||
from typing import Any, Iterable
|
from typing import Any, Iterable
|
||||||
from ty_extensions import static_assert, is_assignable_to, TypeOf, Unknown
|
from ty_extensions import static_assert, is_assignable_to, TypeOf, Unknown, reveal_mro
|
||||||
|
|
||||||
class Foo(SomethingUnknown): ...
|
class Foo(SomethingUnknown): ...
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
# TODO: these should pass
|
# TODO: these should pass
|
||||||
static_assert(is_assignable_to(TypeOf[Foo], Iterable[Unknown])) # error: [static-assert-error]
|
static_assert(is_assignable_to(TypeOf[Foo], Iterable[Unknown])) # error: [static-assert-error]
|
||||||
|
|
@ -815,7 +815,7 @@ for x in Foo:
|
||||||
|
|
||||||
class Bar(Any): ...
|
class Bar(Any): ...
|
||||||
|
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Any, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, Any, <class 'object'>)
|
||||||
|
|
||||||
# TODO: these should pass
|
# TODO: these should pass
|
||||||
static_assert(is_assignable_to(TypeOf[Bar], Iterable[Any])) # error: [static-assert-error]
|
static_assert(is_assignable_to(TypeOf[Bar], Iterable[Any])) # error: [static-assert-error]
|
||||||
|
|
|
||||||
|
|
@ -1,55 +1,74 @@
|
||||||
# Method Resolution Order tests
|
# Method Resolution Order tests
|
||||||
|
|
||||||
Tests that assert that we can infer the correct type for a class's `__mro__` attribute.
|
Tests that assert that we can infer the correct MRO for a class.
|
||||||
|
|
||||||
This attribute is rarely accessed directly at runtime. However, it's extremely important for *us* to
|
It's extremely important for us to know the precise possible values of a class's Method Resolution
|
||||||
know the precise possible values of a class's Method Resolution Order, or we won't be able to infer
|
Order, or we won't be able to infer the correct type of attributes accessed from instances.
|
||||||
the correct type of attributes accessed from instances.
|
|
||||||
|
|
||||||
For documentation on method resolution orders, see:
|
For documentation on method resolution orders, see:
|
||||||
|
|
||||||
- <https://docs.python.org/3/glossary.html#term-method-resolution-order>
|
- <https://docs.python.org/3/glossary.html#term-method-resolution-order>
|
||||||
- <https://docs.python.org/3/howto/mro.html#python-2-3-mro>
|
- <https://docs.python.org/3/howto/mro.html#python-2-3-mro>
|
||||||
|
|
||||||
|
At runtime, the MRO for a class can be inspected using the `__mro__` attribute. However, rather than
|
||||||
|
special-casing inference of that attribute, we allow our inferred MRO of a class to be introspected
|
||||||
|
using the `ty_extensions.reveal_mro` function. This is because the MRO ty infers for a class will
|
||||||
|
often be different than a class's "real MRO" at runtime. This is often deliberate and desirable, but
|
||||||
|
would be confusing to users. For example, typeshed pretends that builtin sequences such as `tuple`
|
||||||
|
and `list` inherit from `collections.abc.Sequence`, resulting in a much longer inferred MRO for
|
||||||
|
these classes than what they actually have at runtime. Other differences to "real MROs" at runtime
|
||||||
|
include the facts that ty's inferred MRO will often include non-class elements, such as generic
|
||||||
|
aliases, `Any` and `Unknown`.
|
||||||
|
|
||||||
## No bases
|
## No bases
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class C: ...
|
class C: ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## The special case: `object` itself
|
## The special case: `object` itself
|
||||||
|
|
||||||
```py
|
```py
|
||||||
reveal_type(object.__mro__) # revealed: tuple[<class 'object'>]
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
|
reveal_mro(object) # revealed: (<class 'object'>,)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Explicit inheritance from `object`
|
## Explicit inheritance from `object`
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class C(object): ...
|
class C(object): ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Explicit inheritance from non-`object` single base
|
## Explicit inheritance from non-`object` single base
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
class B(A): ...
|
class B(A): ...
|
||||||
|
|
||||||
reveal_type(B.__mro__) # revealed: tuple[<class 'B'>, <class 'A'>, <class 'object'>]
|
reveal_mro(B) # revealed: (<class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Linearization of multiple bases
|
## Linearization of multiple bases
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A: ...
|
class A: ...
|
||||||
class B: ...
|
class B: ...
|
||||||
class C(A, B): ...
|
class C(A, B): ...
|
||||||
|
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'A'>, <class 'B'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complex diamond inheritance (1)
|
## Complex diamond inheritance (1)
|
||||||
|
|
@ -57,14 +76,16 @@ reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'A'>, <class 'B'>,
|
||||||
This is "ex_2" from <https://docs.python.org/3/howto/mro.html#the-end>
|
This is "ex_2" from <https://docs.python.org/3/howto/mro.html#the-end>
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class O: ...
|
class O: ...
|
||||||
class X(O): ...
|
class X(O): ...
|
||||||
class Y(O): ...
|
class Y(O): ...
|
||||||
class A(X, Y): ...
|
class A(X, Y): ...
|
||||||
class B(Y, X): ...
|
class B(Y, X): ...
|
||||||
|
|
||||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>]
|
reveal_mro(A) # revealed: (<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(B.__mro__) # revealed: tuple[<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>]
|
reveal_mro(B) # revealed: (<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complex diamond inheritance (2)
|
## Complex diamond inheritance (2)
|
||||||
|
|
@ -72,6 +93,8 @@ reveal_type(B.__mro__) # revealed: tuple[<class 'B'>, <class 'Y'>, <class 'X'>,
|
||||||
This is "ex_5" from <https://docs.python.org/3/howto/mro.html#the-end>
|
This is "ex_5" from <https://docs.python.org/3/howto/mro.html#the-end>
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class O: ...
|
class O: ...
|
||||||
class F(O): ...
|
class F(O): ...
|
||||||
class E(O): ...
|
class E(O): ...
|
||||||
|
|
@ -80,12 +103,12 @@ class C(D, F): ...
|
||||||
class B(D, E): ...
|
class B(D, E): ...
|
||||||
class A(B, C): ...
|
class A(B, C): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(C.__mro__)
|
reveal_mro(C)
|
||||||
# revealed: tuple[<class 'B'>, <class 'D'>, <class 'E'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'B'>, <class 'D'>, <class 'E'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(B.__mro__)
|
reveal_mro(B)
|
||||||
# revealed: tuple[<class 'A'>, <class 'B'>, <class 'C'>, <class 'D'>, <class 'E'>, <class 'F'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'A'>, <class 'B'>, <class 'C'>, <class 'D'>, <class 'E'>, <class 'F'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(A.__mro__)
|
reveal_mro(A)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complex diamond inheritance (3)
|
## Complex diamond inheritance (3)
|
||||||
|
|
@ -93,6 +116,8 @@ reveal_type(A.__mro__)
|
||||||
This is "ex_6" from <https://docs.python.org/3/howto/mro.html#the-end>
|
This is "ex_6" from <https://docs.python.org/3/howto/mro.html#the-end>
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class O: ...
|
class O: ...
|
||||||
class F(O): ...
|
class F(O): ...
|
||||||
class E(O): ...
|
class E(O): ...
|
||||||
|
|
@ -101,12 +126,12 @@ class C(D, F): ...
|
||||||
class B(E, D): ...
|
class B(E, D): ...
|
||||||
class A(B, C): ...
|
class A(B, C): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(C.__mro__)
|
reveal_mro(C)
|
||||||
# revealed: tuple[<class 'B'>, <class 'E'>, <class 'D'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'B'>, <class 'E'>, <class 'D'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(B.__mro__)
|
reveal_mro(B)
|
||||||
# revealed: tuple[<class 'A'>, <class 'B'>, <class 'E'>, <class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'A'>, <class 'B'>, <class 'E'>, <class 'C'>, <class 'D'>, <class 'F'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(A.__mro__)
|
reveal_mro(A)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Complex diamond inheritance (4)
|
## Complex diamond inheritance (4)
|
||||||
|
|
@ -114,6 +139,8 @@ reveal_type(A.__mro__)
|
||||||
This is "ex_9" from <https://docs.python.org/3/howto/mro.html#the-end>
|
This is "ex_9" from <https://docs.python.org/3/howto/mro.html#the-end>
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class O: ...
|
class O: ...
|
||||||
class A(O): ...
|
class A(O): ...
|
||||||
class B(O): ...
|
class B(O): ...
|
||||||
|
|
@ -125,19 +152,20 @@ class K2(D, B, E): ...
|
||||||
class K3(D, A): ...
|
class K3(D, A): ...
|
||||||
class Z(K1, K2, K3): ...
|
class Z(K1, K2, K3): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'K1'>, <class 'A'>, <class 'B'>, <class 'C'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'K1'>, <class 'A'>, <class 'B'>, <class 'C'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(K1.__mro__)
|
reveal_mro(K1)
|
||||||
# revealed: tuple[<class 'K2'>, <class 'D'>, <class 'B'>, <class 'E'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'K2'>, <class 'D'>, <class 'B'>, <class 'E'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(K2.__mro__)
|
reveal_mro(K2)
|
||||||
# revealed: tuple[<class 'K3'>, <class 'D'>, <class 'A'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'K3'>, <class 'D'>, <class 'A'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(K3.__mro__)
|
reveal_mro(K3)
|
||||||
# revealed: tuple[<class 'Z'>, <class 'K1'>, <class 'K2'>, <class 'K3'>, <class 'D'>, <class 'A'>, <class 'B'>, <class 'C'>, <class 'E'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'Z'>, <class 'K1'>, <class 'K2'>, <class 'K3'>, <class 'D'>, <class 'A'>, <class 'B'>, <class 'C'>, <class 'E'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(Z.__mro__)
|
reveal_mro(Z)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Inheritance from `Unknown`
|
## Inheritance from `Unknown`
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
from does_not_exist import DoesNotExist # error: [unresolved-import]
|
from does_not_exist import DoesNotExist # error: [unresolved-import]
|
||||||
|
|
||||||
class A(DoesNotExist): ...
|
class A(DoesNotExist): ...
|
||||||
|
|
@ -147,11 +175,11 @@ class D(A, B, C): ...
|
||||||
class E(B, C): ...
|
class E(B, C): ...
|
||||||
class F(E, A): ...
|
class F(E, A): ...
|
||||||
|
|
||||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, Unknown, <class 'object'>]
|
reveal_mro(A) # revealed: (<class 'A'>, Unknown, <class 'object'>)
|
||||||
reveal_type(D.__mro__) # revealed: tuple[<class 'D'>, <class 'A'>, Unknown, <class 'B'>, <class 'C'>, <class 'object'>]
|
reveal_mro(D) # revealed: (<class 'D'>, <class 'A'>, Unknown, <class 'B'>, <class 'C'>, <class 'object'>)
|
||||||
reveal_type(E.__mro__) # revealed: tuple[<class 'E'>, <class 'B'>, <class 'C'>, <class 'object'>]
|
reveal_mro(E) # revealed: (<class 'E'>, <class 'B'>, <class 'C'>, <class 'object'>)
|
||||||
# revealed: tuple[<class 'F'>, <class 'E'>, <class 'B'>, <class 'C'>, <class 'A'>, Unknown, <class 'object'>]
|
# revealed: (<class 'F'>, <class 'E'>, <class 'B'>, <class 'C'>, <class 'A'>, Unknown, <class 'object'>)
|
||||||
reveal_type(F.__mro__)
|
reveal_mro(F)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Inheritance with intersections that include `Unknown`
|
## Inheritance with intersections that include `Unknown`
|
||||||
|
|
@ -160,23 +188,22 @@ An intersection that includes `Unknown` or `Any` is permitted as long as the int
|
||||||
disjoint from `type`.
|
disjoint from `type`.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
from does_not_exist import DoesNotExist # error: [unresolved-import]
|
from does_not_exist import DoesNotExist # error: [unresolved-import]
|
||||||
|
|
||||||
reveal_type(DoesNotExist) # revealed: Unknown
|
reveal_type(DoesNotExist) # revealed: Unknown
|
||||||
|
|
||||||
if hasattr(DoesNotExist, "__mro__"):
|
if hasattr(DoesNotExist, "__mro__"):
|
||||||
# TODO: this should be `Unknown & <Protocol with members '__mro__'>` or similar
|
reveal_type(DoesNotExist) # revealed: Unknown & <Protocol with members '__mro__'>
|
||||||
# (The second part of the intersection is incorrectly simplified to `object` due to https://github.com/astral-sh/ty/issues/986)
|
|
||||||
reveal_type(DoesNotExist) # revealed: Unknown
|
|
||||||
|
|
||||||
class Foo(DoesNotExist): ... # no error!
|
class Foo(DoesNotExist): ... # no error!
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
if not isinstance(DoesNotExist, type):
|
if not isinstance(DoesNotExist, type):
|
||||||
reveal_type(DoesNotExist) # revealed: Unknown & ~type
|
reveal_type(DoesNotExist) # revealed: Unknown & ~type
|
||||||
|
|
||||||
class Foo(DoesNotExist): ... # error: [unsupported-base]
|
class Foo(DoesNotExist): ... # error: [unsupported-base]
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Inheritance from `type[Any]` and `type[Unknown]`
|
## Inheritance from `type[Any]` and `type[Unknown]`
|
||||||
|
|
@ -186,14 +213,14 @@ guarantee:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from ty_extensions import Unknown, Intersection
|
from ty_extensions import Unknown, Intersection, reveal_mro
|
||||||
|
|
||||||
def f(x: type[Any], y: Intersection[Unknown, type[Any]]):
|
def f(x: type[Any], y: Intersection[Unknown, type[Any]]):
|
||||||
class Foo(x): ...
|
class Foo(x): ...
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Any, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Any, <class 'object'>)
|
||||||
|
|
||||||
class Bar(y): ...
|
class Bar(y): ...
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Unknown, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` lists that cause errors at runtime
|
## `__bases__` lists that cause errors at runtime
|
||||||
|
|
@ -202,14 +229,16 @@ If the class's `__bases__` cause an exception to be raised at runtime and theref
|
||||||
creation to fail, we infer the class's `__mro__` as being `[<class>, Unknown, object]`:
|
creation to fail, we infer the class's `__mro__` as being `[<class>, Unknown, object]`:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
# error: [inconsistent-mro] "Cannot create a consistent method resolution order (MRO) for class `Foo` with bases list `[<class 'object'>, <class 'int'>]`"
|
# error: [inconsistent-mro] "Cannot create a consistent method resolution order (MRO) for class `Foo` with bases list `[<class 'object'>, <class 'int'>]`"
|
||||||
class Foo(object, int): ...
|
class Foo(object, int): ...
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class Bar(Foo): ...
|
class Bar(Foo): ...
|
||||||
|
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, <class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, <class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
# This is the `TypeError` at the bottom of "ex_2"
|
# This is the `TypeError` at the bottom of "ex_2"
|
||||||
# in the examples at <https://docs.python.org/3/howto/mro.html#the-end>
|
# in the examples at <https://docs.python.org/3/howto/mro.html#the-end>
|
||||||
|
|
@ -219,17 +248,17 @@ class Y(O): ...
|
||||||
class A(X, Y): ...
|
class A(X, Y): ...
|
||||||
class B(Y, X): ...
|
class B(Y, X): ...
|
||||||
|
|
||||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>]
|
reveal_mro(A) # revealed: (<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(B.__mro__) # revealed: tuple[<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>]
|
reveal_mro(B) # revealed: (<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>)
|
||||||
|
|
||||||
# error: [inconsistent-mro] "Cannot create a consistent method resolution order (MRO) for class `Z` with bases list `[<class 'A'>, <class 'B'>]`"
|
# error: [inconsistent-mro] "Cannot create a consistent method resolution order (MRO) for class `Z` with bases list `[<class 'A'>, <class 'B'>]`"
|
||||||
class Z(A, B): ...
|
class Z(A, B): ...
|
||||||
|
|
||||||
reveal_type(Z.__mro__) # revealed: tuple[<class 'Z'>, Unknown, <class 'object'>]
|
reveal_mro(Z) # revealed: (<class 'Z'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class AA(Z): ...
|
class AA(Z): ...
|
||||||
|
|
||||||
reveal_type(AA.__mro__) # revealed: tuple[<class 'AA'>, <class 'Z'>, Unknown, <class 'object'>]
|
reveal_mro(AA) # revealed: (<class 'AA'>, <class 'Z'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` includes a `Union`
|
## `__bases__` includes a `Union`
|
||||||
|
|
@ -241,6 +270,8 @@ find a union type in a class's bases, we infer the class's `__mro__` as being
|
||||||
`[<class>, Unknown, object]`, the same as for MROs that cause errors at runtime.
|
`[<class>, Unknown, object]`, the same as for MROs that cause errors at runtime.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
def returns_bool() -> bool:
|
def returns_bool() -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -257,7 +288,7 @@ reveal_type(x) # revealed: <class 'A'> | <class 'B'>
|
||||||
# error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
# error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
||||||
class Foo(x): ...
|
class Foo(x): ...
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` is a union of a dynamic type and valid bases
|
## `__bases__` is a union of a dynamic type and valid bases
|
||||||
|
|
@ -268,6 +299,7 @@ diagnostic, and we use the dynamic type as a base to prevent further downstream
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
def _(flag: bool, any: Any):
|
def _(flag: bool, any: Any):
|
||||||
if flag:
|
if flag:
|
||||||
|
|
@ -276,12 +308,14 @@ def _(flag: bool, any: Any):
|
||||||
class Base: ...
|
class Base: ...
|
||||||
|
|
||||||
class Foo(Base): ...
|
class Foo(Base): ...
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Any, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Any, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` includes multiple `Union`s
|
## `__bases__` includes multiple `Union`s
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
def returns_bool() -> bool:
|
def returns_bool() -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -307,12 +341,14 @@ reveal_type(y) # revealed: <class 'C'> | <class 'D'>
|
||||||
# error: 14 [unsupported-base] "Unsupported class base with type `<class 'C'> | <class 'D'>`"
|
# error: 14 [unsupported-base] "Unsupported class base with type `<class 'C'> | <class 'D'>`"
|
||||||
class Foo(x, y): ...
|
class Foo(x, y): ...
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` lists that cause errors... now with `Union`s
|
## `__bases__` lists that cause errors... now with `Union`s
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
def returns_bool() -> bool:
|
def returns_bool() -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
@ -328,11 +364,11 @@ else:
|
||||||
# error: 21 [unsupported-base] "Unsupported class base with type `<class 'Y'> | <class 'object'>`"
|
# error: 21 [unsupported-base] "Unsupported class base with type `<class 'Y'> | <class 'object'>`"
|
||||||
class PossibleError(foo, X): ...
|
class PossibleError(foo, X): ...
|
||||||
|
|
||||||
reveal_type(PossibleError.__mro__) # revealed: tuple[<class 'PossibleError'>, Unknown, <class 'object'>]
|
reveal_mro(PossibleError) # revealed: (<class 'PossibleError'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class A(X, Y): ...
|
class A(X, Y): ...
|
||||||
|
|
||||||
reveal_type(A.__mro__) # revealed: tuple[<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>]
|
reveal_mro(A) # revealed: (<class 'A'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>)
|
||||||
|
|
||||||
if returns_bool():
|
if returns_bool():
|
||||||
class B(X, Y): ...
|
class B(X, Y): ...
|
||||||
|
|
@ -340,13 +376,13 @@ if returns_bool():
|
||||||
else:
|
else:
|
||||||
class B(Y, X): ...
|
class B(Y, X): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'B'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>] | tuple[<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>]
|
# revealed: (<class 'B'>, <class 'X'>, <class 'Y'>, <class 'O'>, <class 'object'>) | (<class 'B'>, <class 'Y'>, <class 'X'>, <class 'O'>, <class 'object'>)
|
||||||
reveal_type(B.__mro__)
|
reveal_mro(B)
|
||||||
|
|
||||||
# error: 12 [unsupported-base] "Unsupported class base with type `<class 'B'> | <class 'B'>`"
|
# error: 12 [unsupported-base] "Unsupported class base with type `<class 'B'> | <class 'B'>`"
|
||||||
class Z(A, B): ...
|
class Z(A, B): ...
|
||||||
|
|
||||||
reveal_type(Z.__mro__) # revealed: tuple[<class 'Z'>, Unknown, <class 'object'>]
|
reveal_mro(Z) # revealed: (<class 'Z'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `__bases__` lists that include objects that are not instances of `type`
|
## `__bases__` lists that include objects that are not instances of `type`
|
||||||
|
|
@ -389,9 +425,11 @@ class BadSub2(Bad2()): ... # error: [invalid-base]
|
||||||
<!-- snapshot-diagnostics -->
|
<!-- snapshot-diagnostics -->
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class Spam: ...
|
class Spam: ...
|
||||||
class Eggs: ...
|
class Eggs: ...
|
||||||
|
|
@ -413,12 +451,12 @@ class Ham(
|
||||||
|
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
reveal_type(Ham.__mro__) # revealed: tuple[<class 'Ham'>, Unknown, <class 'object'>]
|
reveal_mro(Ham) # revealed: (<class 'Ham'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class Mushrooms: ...
|
class Mushrooms: ...
|
||||||
class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
||||||
|
|
||||||
reveal_type(Omelette.__mro__) # revealed: tuple[<class 'Omelette'>, Unknown, <class 'object'>]
|
reveal_mro(Omelette) # revealed: (<class 'Omelette'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
|
||||||
|
|
@ -494,6 +532,7 @@ however, for gradual types this would break the
|
||||||
the dynamic base can usually be materialised to a type that would lead to a resolvable MRO.
|
the dynamic base can usually be materialised to a type that would lead to a resolvable MRO.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
from unresolvable_module import UnknownBase1, UnknownBase2 # error: [unresolved-import]
|
from unresolvable_module import UnknownBase1, UnknownBase2 # error: [unresolved-import]
|
||||||
|
|
||||||
reveal_type(UnknownBase1) # revealed: Unknown
|
reveal_type(UnknownBase1) # revealed: Unknown
|
||||||
|
|
@ -502,7 +541,7 @@ reveal_type(UnknownBase2) # revealed: Unknown
|
||||||
# no error here -- we respect the gradual guarantee:
|
# no error here -- we respect the gradual guarantee:
|
||||||
class Foo(UnknownBase1, UnknownBase2): ...
|
class Foo(UnknownBase1, UnknownBase2): ...
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
However, if there are duplicate class elements, we do emit an error, even if there are also multiple
|
However, if there are duplicate class elements, we do emit an error, even if there are also multiple
|
||||||
|
|
@ -513,7 +552,7 @@ bases materialize to:
|
||||||
# error: [duplicate-base] "Duplicate base class `Foo`"
|
# error: [duplicate-base] "Duplicate base class `Foo`"
|
||||||
class Bar(UnknownBase1, Foo, UnknownBase2, Foo): ...
|
class Bar(UnknownBase1, Foo, UnknownBase2, Foo): ...
|
||||||
|
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Unknown, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Unrelated objects inferred as `Any`/`Unknown` do not have special `__mro__` attributes
|
## Unrelated objects inferred as `Any`/`Unknown` do not have special `__mro__` attributes
|
||||||
|
|
@ -529,25 +568,26 @@ reveal_type(unknown_object.__mro__) # revealed: Unknown
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Generic, TypeVar, Iterator
|
from typing import Generic, TypeVar, Iterator
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
|
|
||||||
class peekable(Generic[T], Iterator[T]): ...
|
class peekable(Generic[T], Iterator[T]): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'peekable[Unknown]'>, <class 'Iterator[T@peekable]'>, <class 'Iterable[T@peekable]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'peekable[Unknown]'>, <class 'Iterator[T@peekable]'>, <class 'Iterable[T@peekable]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(peekable.__mro__)
|
reveal_mro(peekable)
|
||||||
|
|
||||||
class peekable2(Iterator[T], Generic[T]): ...
|
class peekable2(Iterator[T], Generic[T]): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'peekable2[Unknown]'>, <class 'Iterator[T@peekable2]'>, <class 'Iterable[T@peekable2]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'peekable2[Unknown]'>, <class 'Iterator[T@peekable2]'>, <class 'Iterable[T@peekable2]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(peekable2.__mro__)
|
reveal_mro(peekable2)
|
||||||
|
|
||||||
class Base: ...
|
class Base: ...
|
||||||
class Intermediate(Base, Generic[T]): ...
|
class Intermediate(Base, Generic[T]): ...
|
||||||
class Sub(Intermediate[T], Base): ...
|
class Sub(Intermediate[T], Base): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'Sub[Unknown]'>, <class 'Intermediate[T@Sub]'>, <class 'Base'>, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Sub[Unknown]'>, <class 'Intermediate[T@Sub]'>, <class 'Base'>, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Sub.__mro__)
|
reveal_mro(Sub)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Unresolvable MROs involving generics have the original bases reported in the error message, not the resolved bases
|
## Unresolvable MROs involving generics have the original bases reported in the error message, not the resolved bases
|
||||||
|
|
@ -569,17 +609,19 @@ class Baz(Protocol[T], Foo, Bar[T]): ... # error: [inconsistent-mro]
|
||||||
These are invalid, but we need to be able to handle them gracefully without panicking.
|
These are invalid, but we need to be able to handle them gracefully without panicking.
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo(Foo): ... # error: [cyclic-class-definition]
|
class Foo(Foo): ... # error: [cyclic-class-definition]
|
||||||
|
|
||||||
reveal_type(Foo) # revealed: <class 'Foo'>
|
reveal_type(Foo) # revealed: <class 'Foo'>
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class Bar: ...
|
class Bar: ...
|
||||||
class Baz: ...
|
class Baz: ...
|
||||||
class Boz(Bar, Baz, Boz): ... # error: [cyclic-class-definition]
|
class Boz(Bar, Baz, Boz): ... # error: [cyclic-class-definition]
|
||||||
|
|
||||||
reveal_type(Boz) # revealed: <class 'Boz'>
|
reveal_type(Boz) # revealed: <class 'Boz'>
|
||||||
reveal_type(Boz.__mro__) # revealed: tuple[<class 'Boz'>, Unknown, <class 'object'>]
|
reveal_mro(Boz) # revealed: (<class 'Boz'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Classes with indirect cycles in their MROs
|
## Classes with indirect cycles in their MROs
|
||||||
|
|
@ -587,31 +629,37 @@ reveal_type(Boz.__mro__) # revealed: tuple[<class 'Boz'>, Unknown, <class 'obje
|
||||||
These are similarly unlikely, but we still shouldn't crash:
|
These are similarly unlikely, but we still shouldn't crash:
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo(Bar): ... # error: [cyclic-class-definition]
|
class Foo(Bar): ... # error: [cyclic-class-definition]
|
||||||
class Bar(Baz): ... # error: [cyclic-class-definition]
|
class Bar(Baz): ... # error: [cyclic-class-definition]
|
||||||
class Baz(Foo): ... # error: [cyclic-class-definition]
|
class Baz(Foo): ... # error: [cyclic-class-definition]
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Unknown, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Baz.__mro__) # revealed: tuple[<class 'Baz'>, Unknown, <class 'object'>]
|
reveal_mro(Baz) # revealed: (<class 'Baz'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Classes with cycles in their MROs, and multiple inheritance
|
## Classes with cycles in their MROs, and multiple inheritance
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Spam: ...
|
class Spam: ...
|
||||||
class Foo(Bar): ... # error: [cyclic-class-definition]
|
class Foo(Bar): ... # error: [cyclic-class-definition]
|
||||||
class Bar(Baz): ... # error: [cyclic-class-definition]
|
class Bar(Baz): ... # error: [cyclic-class-definition]
|
||||||
class Baz(Foo, Spam): ... # error: [cyclic-class-definition]
|
class Baz(Foo, Spam): ... # error: [cyclic-class-definition]
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, Unknown, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Baz.__mro__) # revealed: tuple[<class 'Baz'>, Unknown, <class 'object'>]
|
reveal_mro(Baz) # revealed: (<class 'Baz'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Classes with cycles in their MRO, and a sub-graph
|
## Classes with cycles in their MRO, and a sub-graph
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class FooCycle(BarCycle): ... # error: [cyclic-class-definition]
|
class FooCycle(BarCycle): ... # error: [cyclic-class-definition]
|
||||||
class Foo: ...
|
class Foo: ...
|
||||||
class BarCycle(FooCycle): ... # error: [cyclic-class-definition]
|
class BarCycle(FooCycle): ... # error: [cyclic-class-definition]
|
||||||
|
|
@ -622,10 +670,10 @@ class Bar(Foo): ...
|
||||||
class Baz(Bar, BarCycle): ...
|
class Baz(Bar, BarCycle): ...
|
||||||
class Spam(Baz): ...
|
class Spam(Baz): ...
|
||||||
|
|
||||||
reveal_type(FooCycle.__mro__) # revealed: tuple[<class 'FooCycle'>, Unknown, <class 'object'>]
|
reveal_mro(FooCycle) # revealed: (<class 'FooCycle'>, Unknown, <class 'object'>)
|
||||||
reveal_type(BarCycle.__mro__) # revealed: tuple[<class 'BarCycle'>, Unknown, <class 'object'>]
|
reveal_mro(BarCycle) # revealed: (<class 'BarCycle'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Baz.__mro__) # revealed: tuple[<class 'Baz'>, Unknown, <class 'object'>]
|
reveal_mro(Baz) # revealed: (<class 'Baz'>, Unknown, <class 'object'>)
|
||||||
reveal_type(Spam.__mro__) # revealed: tuple[<class 'Spam'>, Unknown, <class 'object'>]
|
reveal_mro(Spam) # revealed: (<class 'Spam'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Other classes with possible cycles
|
## Other classes with possible cycles
|
||||||
|
|
@ -636,20 +684,22 @@ python-version = "3.13"
|
||||||
```
|
```
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class C(C.a): ...
|
class C(C.a): ...
|
||||||
reveal_type(C.__class__) # revealed: <class 'type'>
|
reveal_type(C.__class__) # revealed: <class 'type'>
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, Unknown, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class D(D.a):
|
class D(D.a):
|
||||||
a: D
|
a: D
|
||||||
reveal_type(D.__class__) # revealed: <class 'type'>
|
reveal_type(D.__class__) # revealed: <class 'type'>
|
||||||
reveal_type(D.__mro__) # revealed: tuple[<class 'D'>, Unknown, <class 'object'>]
|
reveal_mro(D) # revealed: (<class 'D'>, Unknown, <class 'object'>)
|
||||||
|
|
||||||
class E[T](E.a): ...
|
class E[T](E.a): ...
|
||||||
reveal_type(E.__class__) # revealed: <class 'type'>
|
reveal_type(E.__class__) # revealed: <class 'type'>
|
||||||
reveal_type(E.__mro__) # revealed: tuple[<class 'E[Unknown]'>, Unknown, typing.Generic, <class 'object'>]
|
reveal_mro(E) # revealed: (<class 'E[Unknown]'>, Unknown, typing.Generic, <class 'object'>)
|
||||||
|
|
||||||
class F[T](F(), F): ... # error: [cyclic-class-definition]
|
class F[T](F(), F): ... # error: [cyclic-class-definition]
|
||||||
reveal_type(F.__class__) # revealed: type[Unknown]
|
reveal_type(F.__class__) # revealed: type[Unknown]
|
||||||
reveal_type(F.__mro__) # revealed: tuple[<class 'F[Unknown]'>, Unknown, <class 'object'>]
|
reveal_mro(F) # revealed: (<class 'F[Unknown]'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ name, and not just by its numeric position within the tuple:
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from ty_extensions import static_assert, is_subtype_of, is_assignable_to
|
from ty_extensions import static_assert, is_subtype_of, is_assignable_to, reveal_mro
|
||||||
|
|
||||||
class Person(NamedTuple):
|
class Person(NamedTuple):
|
||||||
id: int
|
id: int
|
||||||
|
|
@ -25,8 +25,8 @@ reveal_type(alice.id) # revealed: int
|
||||||
reveal_type(alice.name) # revealed: str
|
reveal_type(alice.name) # revealed: str
|
||||||
reveal_type(alice.age) # revealed: int | None
|
reveal_type(alice.age) # revealed: int | None
|
||||||
|
|
||||||
# revealed: tuple[<class 'Person'>, <class 'tuple[int, str, int | None]'>, <class 'Sequence[int | str | None]'>, <class 'Reversible[int | str | None]'>, <class 'Collection[int | str | None]'>, <class 'Iterable[int | str | None]'>, <class 'Container[int | str | None]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Person'>, <class 'tuple[int, str, int | None]'>, <class 'Sequence[int | str | None]'>, <class 'Reversible[int | str | None]'>, <class 'Collection[int | str | None]'>, <class 'Iterable[int | str | None]'>, <class 'Container[int | str | None]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Person.__mro__)
|
reveal_mro(Person)
|
||||||
|
|
||||||
static_assert(is_subtype_of(Person, tuple[int, str, int | None]))
|
static_assert(is_subtype_of(Person, tuple[int, str, int | None]))
|
||||||
static_assert(is_subtype_of(Person, tuple[object, ...]))
|
static_assert(is_subtype_of(Person, tuple[object, ...]))
|
||||||
|
|
@ -329,9 +329,8 @@ reveal_type(typing.NamedTuple.__name__) # revealed: str
|
||||||
reveal_type(typing.NamedTuple.__qualname__) # revealed: str
|
reveal_type(typing.NamedTuple.__qualname__) # revealed: str
|
||||||
reveal_type(typing.NamedTuple.__kwdefaults__) # revealed: dict[str, Any] | None
|
reveal_type(typing.NamedTuple.__kwdefaults__) # revealed: dict[str, Any] | None
|
||||||
|
|
||||||
# TODO: this should cause us to emit a diagnostic and reveal `Unknown` (function objects don't have an `__mro__` attribute),
|
# error: [unresolved-attribute]
|
||||||
# but the fact that we don't isn't actually a `NamedTuple` bug (https://github.com/astral-sh/ty/issues/986)
|
reveal_type(typing.NamedTuple.__mro__) # revealed: Unknown
|
||||||
reveal_type(typing.NamedTuple.__mro__) # revealed: tuple[<class 'FunctionType'>, <class 'object'>]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
By the normal rules, `NamedTuple` and `type[NamedTuple]` should not be valid in type expressions --
|
By the normal rules, `NamedTuple` and `type[NamedTuple]` should not be valid in type expressions --
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,11 @@ A protocol is defined by inheriting from the `Protocol` class, which is annotate
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class MyProtocol(Protocol): ...
|
class MyProtocol(Protocol): ...
|
||||||
|
|
||||||
reveal_type(MyProtocol.__mro__) # revealed: tuple[<class 'MyProtocol'>, typing.Protocol, typing.Generic, <class 'object'>]
|
reveal_mro(MyProtocol) # revealed: (<class 'MyProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
Just like for any other class base, it is an error for `Protocol` to appear multiple times in a
|
Just like for any other class base, it is an error for `Protocol` to appear multiple times in a
|
||||||
|
|
@ -37,7 +38,7 @@ class's bases:
|
||||||
```py
|
```py
|
||||||
class Foo(Protocol, Protocol): ... # error: [duplicate-base]
|
class Foo(Protocol, Protocol): ... # error: [duplicate-base]
|
||||||
|
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
Protocols can also be generic, either by including `Generic[]` in the bases list, subscripting
|
Protocols can also be generic, either by including `Generic[]` in the bases list, subscripting
|
||||||
|
|
@ -64,7 +65,7 @@ class Bar3[T](Protocol[T]):
|
||||||
|
|
||||||
# Note that this class definition *will* actually succeed at runtime,
|
# Note that this class definition *will* actually succeed at runtime,
|
||||||
# unlike classes that combine PEP-695 type parameters with inheritance from `Generic[]`
|
# unlike classes that combine PEP-695 type parameters with inheritance from `Generic[]`
|
||||||
reveal_type(Bar3.__mro__) # revealed: tuple[<class 'Bar3[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
reveal_mro(Bar3) # revealed: (<class 'Bar3[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
It's an error to include both bare `Protocol` and subscripted `Protocol[]` in the bases list
|
It's an error to include both bare `Protocol` and subscripted `Protocol[]` in the bases list
|
||||||
|
|
@ -74,8 +75,8 @@ simultaneously:
|
||||||
class DuplicateBases(Protocol, Protocol[T]): # error: [duplicate-base]
|
class DuplicateBases(Protocol, Protocol[T]): # error: [duplicate-base]
|
||||||
x: T
|
x: T
|
||||||
|
|
||||||
# revealed: tuple[<class 'DuplicateBases[Unknown]'>, Unknown, <class 'object'>]
|
# revealed: (<class 'DuplicateBases[Unknown]'>, Unknown, <class 'object'>)
|
||||||
reveal_type(DuplicateBases.__mro__)
|
reveal_mro(DuplicateBases)
|
||||||
```
|
```
|
||||||
|
|
||||||
The introspection helper `typing(_extensions).is_protocol` can be used to verify whether a class is
|
The introspection helper `typing(_extensions).is_protocol` can be used to verify whether a class is
|
||||||
|
|
@ -124,8 +125,8 @@ it is not sufficient for it to have `Protocol` in its MRO.
|
||||||
```py
|
```py
|
||||||
class SubclassOfMyProtocol(MyProtocol): ...
|
class SubclassOfMyProtocol(MyProtocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'SubclassOfMyProtocol'>, <class 'MyProtocol'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'SubclassOfMyProtocol'>, <class 'MyProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(SubclassOfMyProtocol.__mro__)
|
reveal_mro(SubclassOfMyProtocol)
|
||||||
|
|
||||||
reveal_type(is_protocol(SubclassOfMyProtocol)) # revealed: Literal[False]
|
reveal_type(is_protocol(SubclassOfMyProtocol)) # revealed: Literal[False]
|
||||||
```
|
```
|
||||||
|
|
@ -143,8 +144,8 @@ class OtherProtocol(Protocol):
|
||||||
|
|
||||||
class ComplexInheritance(SubProtocol, OtherProtocol, Protocol): ...
|
class ComplexInheritance(SubProtocol, OtherProtocol, Protocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'ComplexInheritance'>, <class 'SubProtocol'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'ComplexInheritance'>, <class 'SubProtocol'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(ComplexInheritance.__mro__)
|
reveal_mro(ComplexInheritance)
|
||||||
|
|
||||||
reveal_type(is_protocol(ComplexInheritance)) # revealed: Literal[True]
|
reveal_type(is_protocol(ComplexInheritance)) # revealed: Literal[True]
|
||||||
```
|
```
|
||||||
|
|
@ -156,22 +157,22 @@ or `TypeError` is raised at runtime when the class is created.
|
||||||
# error: [invalid-protocol] "Protocol class `Invalid` cannot inherit from non-protocol class `NotAProtocol`"
|
# error: [invalid-protocol] "Protocol class `Invalid` cannot inherit from non-protocol class `NotAProtocol`"
|
||||||
class Invalid(NotAProtocol, Protocol): ...
|
class Invalid(NotAProtocol, Protocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'Invalid'>, <class 'NotAProtocol'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'Invalid'>, <class 'NotAProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(Invalid.__mro__)
|
reveal_mro(Invalid)
|
||||||
|
|
||||||
# error: [invalid-protocol] "Protocol class `AlsoInvalid` cannot inherit from non-protocol class `NotAProtocol`"
|
# error: [invalid-protocol] "Protocol class `AlsoInvalid` cannot inherit from non-protocol class `NotAProtocol`"
|
||||||
class AlsoInvalid(MyProtocol, OtherProtocol, NotAProtocol, Protocol): ...
|
class AlsoInvalid(MyProtocol, OtherProtocol, NotAProtocol, Protocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'AlsoInvalid'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, <class 'NotAProtocol'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'AlsoInvalid'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, <class 'NotAProtocol'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(AlsoInvalid.__mro__)
|
reveal_mro(AlsoInvalid)
|
||||||
|
|
||||||
class NotAGenericProtocol[T]: ...
|
class NotAGenericProtocol[T]: ...
|
||||||
|
|
||||||
# error: [invalid-protocol] "Protocol class `StillInvalid` cannot inherit from non-protocol class `NotAGenericProtocol`"
|
# error: [invalid-protocol] "Protocol class `StillInvalid` cannot inherit from non-protocol class `NotAGenericProtocol`"
|
||||||
class StillInvalid(NotAGenericProtocol[int], Protocol): ...
|
class StillInvalid(NotAGenericProtocol[int], Protocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'StillInvalid'>, <class 'NotAGenericProtocol[int]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'StillInvalid'>, <class 'NotAGenericProtocol[int]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(StillInvalid.__mro__)
|
reveal_mro(StillInvalid)
|
||||||
```
|
```
|
||||||
|
|
||||||
But two exceptions to this rule are `object` and `Generic`:
|
But two exceptions to this rule are `object` and `Generic`:
|
||||||
|
|
@ -188,7 +189,7 @@ T = TypeVar("T")
|
||||||
# type checkers.
|
# type checkers.
|
||||||
class Fine(Protocol, object): ...
|
class Fine(Protocol, object): ...
|
||||||
|
|
||||||
reveal_type(Fine.__mro__) # revealed: tuple[<class 'Fine'>, typing.Protocol, typing.Generic, <class 'object'>]
|
reveal_mro(Fine) # revealed: (<class 'Fine'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
|
|
||||||
class StillFine(Protocol, Generic[T], object): ...
|
class StillFine(Protocol, Generic[T], object): ...
|
||||||
class EvenThis[T](Protocol, object): ...
|
class EvenThis[T](Protocol, object): ...
|
||||||
|
|
@ -202,8 +203,8 @@ And multiple inheritance from a mix of protocol and non-protocol classes is fine
|
||||||
```py
|
```py
|
||||||
class FineAndDandy(MyProtocol, OtherProtocol, NotAProtocol): ...
|
class FineAndDandy(MyProtocol, OtherProtocol, NotAProtocol): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'FineAndDandy'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, typing.Protocol, typing.Generic, <class 'NotAProtocol'>, <class 'object'>]
|
# revealed: (<class 'FineAndDandy'>, <class 'MyProtocol'>, <class 'OtherProtocol'>, typing.Protocol, typing.Generic, <class 'NotAProtocol'>, <class 'object'>)
|
||||||
reveal_type(FineAndDandy.__mro__)
|
reveal_mro(FineAndDandy)
|
||||||
```
|
```
|
||||||
|
|
||||||
But if `Protocol` is not present in the bases list, the resulting class doesn't count as a protocol
|
But if `Protocol` is not present in the bases list, the resulting class doesn't count as a protocol
|
||||||
|
|
@ -270,11 +271,11 @@ second argument to `issubclass()` at runtime:
|
||||||
```py
|
```py
|
||||||
import abc
|
import abc
|
||||||
import typing
|
import typing
|
||||||
from ty_extensions import TypeOf
|
from ty_extensions import TypeOf, reveal_mro
|
||||||
|
|
||||||
reveal_type(type(Protocol)) # revealed: <class '_ProtocolMeta'>
|
reveal_type(type(Protocol)) # revealed: <class '_ProtocolMeta'>
|
||||||
# revealed: tuple[<class '_ProtocolMeta'>, <class 'ABCMeta'>, <class 'type'>, <class 'object'>]
|
# revealed: (<class '_ProtocolMeta'>, <class 'ABCMeta'>, <class 'type'>, <class 'object'>)
|
||||||
reveal_type(type(Protocol).__mro__)
|
reveal_mro(type(Protocol))
|
||||||
static_assert(is_subtype_of(TypeOf[Protocol], type))
|
static_assert(is_subtype_of(TypeOf[Protocol], type))
|
||||||
static_assert(is_subtype_of(TypeOf[Protocol], abc.ABCMeta))
|
static_assert(is_subtype_of(TypeOf[Protocol], abc.ABCMeta))
|
||||||
static_assert(is_subtype_of(TypeOf[Protocol], typing._ProtocolMeta))
|
static_assert(is_subtype_of(TypeOf[Protocol], typing._ProtocolMeta))
|
||||||
|
|
|
||||||
|
|
@ -12,36 +12,38 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||||
## mdtest_snippet.py
|
## mdtest_snippet.py
|
||||||
|
|
||||||
```
|
```
|
||||||
1 | def returns_bool() -> bool:
|
1 | from ty_extensions import reveal_mro
|
||||||
2 | return True
|
2 |
|
||||||
3 |
|
3 | def returns_bool() -> bool:
|
||||||
4 | class A: ...
|
4 | return True
|
||||||
5 | class B: ...
|
5 |
|
||||||
6 |
|
6 | class A: ...
|
||||||
7 | if returns_bool():
|
7 | class B: ...
|
||||||
8 | x = A
|
8 |
|
||||||
9 | else:
|
9 | if returns_bool():
|
||||||
10 | x = B
|
10 | x = A
|
||||||
11 |
|
11 | else:
|
||||||
12 | reveal_type(x) # revealed: <class 'A'> | <class 'B'>
|
12 | x = B
|
||||||
13 |
|
13 |
|
||||||
14 | # error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
14 | reveal_type(x) # revealed: <class 'A'> | <class 'B'>
|
||||||
15 | class Foo(x): ...
|
15 |
|
||||||
16 |
|
16 | # error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
||||||
17 | reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
17 | class Foo(x): ...
|
||||||
|
18 |
|
||||||
|
19 | reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
warning[unsupported-base]: Unsupported class base with type `<class 'A'> | <class 'B'>`
|
warning[unsupported-base]: Unsupported class base with type `<class 'A'> | <class 'B'>`
|
||||||
--> src/mdtest_snippet.py:15:11
|
--> src/mdtest_snippet.py:17:11
|
||||||
|
|
|
|
||||||
14 | # error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
16 | # error: 11 [unsupported-base] "Unsupported class base with type `<class 'A'> | <class 'B'>`"
|
||||||
15 | class Foo(x): ...
|
17 | class Foo(x): ...
|
||||||
| ^
|
| ^
|
||||||
16 |
|
18 |
|
||||||
17 | reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
19 | reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
|
|
||||||
info: ty cannot resolve a consistent MRO for class `Foo` due to this base
|
info: ty cannot resolve a consistent MRO for class `Foo` due to this base
|
||||||
info: Only class objects or `Any` are supported as class bases
|
info: Only class objects or `Any` are supported as class bases
|
||||||
|
|
|
||||||
|
|
@ -12,109 +12,115 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/mro.md
|
||||||
## mdtest_snippet.py
|
## mdtest_snippet.py
|
||||||
|
|
||||||
```
|
```
|
||||||
1 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
1 | from ty_extensions import reveal_mro
|
||||||
2 |
|
2 |
|
||||||
3 | reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
||||||
4 |
|
4 |
|
||||||
5 | class Spam: ...
|
5 | reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
6 | class Eggs: ...
|
6 |
|
||||||
7 | class Bar: ...
|
7 | class Spam: ...
|
||||||
8 | class Baz: ...
|
8 | class Eggs: ...
|
||||||
9 |
|
9 | class Bar: ...
|
||||||
10 | # fmt: off
|
10 | class Baz: ...
|
||||||
11 |
|
11 |
|
||||||
12 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
12 | # fmt: off
|
||||||
13 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
13 |
|
||||||
14 | class Ham(
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
||||||
15 | Spam,
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
16 | Eggs,
|
16 | class Ham(
|
||||||
17 | Bar,
|
17 | Spam,
|
||||||
18 | Baz,
|
18 | Eggs,
|
||||||
19 | Spam,
|
19 | Bar,
|
||||||
20 | Eggs,
|
20 | Baz,
|
||||||
21 | ): ...
|
21 | Spam,
|
||||||
22 |
|
22 | Eggs,
|
||||||
23 | # fmt: on
|
23 | ): ...
|
||||||
24 |
|
24 |
|
||||||
25 | reveal_type(Ham.__mro__) # revealed: tuple[<class 'Ham'>, Unknown, <class 'object'>]
|
25 | # fmt: on
|
||||||
26 |
|
26 |
|
||||||
27 | class Mushrooms: ...
|
27 | reveal_mro(Ham) # revealed: (<class 'Ham'>, Unknown, <class 'object'>)
|
||||||
28 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
28 |
|
||||||
29 |
|
29 | class Mushrooms: ...
|
||||||
30 | reveal_type(Omelette.__mro__) # revealed: tuple[<class 'Omelette'>, Unknown, <class 'object'>]
|
30 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
||||||
31 |
|
31 |
|
||||||
32 | # fmt: off
|
32 | reveal_mro(Omelette) # revealed: (<class 'Omelette'>, Unknown, <class 'object'>)
|
||||||
33 |
|
33 |
|
||||||
34 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
34 | # fmt: off
|
||||||
35 | class VeryEggyOmelette(
|
35 |
|
||||||
36 | Eggs,
|
36 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
37 | Ham,
|
37 | class VeryEggyOmelette(
|
||||||
38 | Spam,
|
38 | Eggs,
|
||||||
39 | Eggs,
|
39 | Ham,
|
||||||
40 | Mushrooms,
|
40 | Spam,
|
||||||
41 | Bar,
|
41 | Eggs,
|
||||||
42 | Eggs,
|
42 | Mushrooms,
|
||||||
43 | Baz,
|
43 | Bar,
|
||||||
44 | Eggs,
|
44 | Eggs,
|
||||||
45 | ): ...
|
45 | Baz,
|
||||||
46 |
|
46 | Eggs,
|
||||||
47 | # fmt: off
|
47 | ): ...
|
||||||
48 | # fmt: off
|
48 |
|
||||||
49 |
|
49 | # fmt: off
|
||||||
50 | class A: ...
|
50 | # fmt: off
|
||||||
51 |
|
51 |
|
||||||
52 | class B( # type: ignore[duplicate-base]
|
52 | class A: ...
|
||||||
53 | A,
|
53 |
|
||||||
54 | A,
|
54 | class B( # type: ignore[duplicate-base]
|
||||||
55 | ): ...
|
55 | A,
|
||||||
56 |
|
56 | A,
|
||||||
57 | class C(
|
57 | ): ...
|
||||||
58 | A,
|
58 |
|
||||||
59 | A
|
59 | class C(
|
||||||
60 | ): # type: ignore[duplicate-base]
|
60 | A,
|
||||||
61 | x: int
|
61 | A
|
||||||
62 |
|
62 | ): # type: ignore[duplicate-base]
|
||||||
63 | # fmt: on
|
63 | x: int
|
||||||
64 | # fmt: off
|
64 |
|
||||||
65 |
|
65 | # fmt: on
|
||||||
66 | # error: [duplicate-base]
|
66 | # fmt: off
|
||||||
67 | class D(
|
67 |
|
||||||
68 | A,
|
68 | # error: [duplicate-base]
|
||||||
69 | # error: [unused-ignore-comment]
|
69 | class D(
|
||||||
70 | A, # type: ignore[duplicate-base]
|
70 | A,
|
||||||
71 | ): ...
|
71 | # error: [unused-ignore-comment]
|
||||||
72 |
|
72 | A, # type: ignore[duplicate-base]
|
||||||
73 | # error: [duplicate-base]
|
73 | ): ...
|
||||||
74 | class E(
|
74 |
|
||||||
75 | A,
|
75 | # error: [duplicate-base]
|
||||||
76 | A
|
76 | class E(
|
||||||
77 | ):
|
77 | A,
|
||||||
78 | # error: [unused-ignore-comment]
|
78 | A
|
||||||
79 | x: int # type: ignore[duplicate-base]
|
79 | ):
|
||||||
80 |
|
80 | # error: [unused-ignore-comment]
|
||||||
81 | # fmt: on
|
81 | x: int # type: ignore[duplicate-base]
|
||||||
|
82 |
|
||||||
|
83 | # fmt: on
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `str`
|
error[duplicate-base]: Duplicate base class `str`
|
||||||
--> src/mdtest_snippet.py:1:7
|
--> src/mdtest_snippet.py:3:7
|
||||||
|
|
|
|
||||||
1 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
1 | from ty_extensions import reveal_mro
|
||||||
| ^^^^^^^^^^^^^
|
|
||||||
2 |
|
2 |
|
||||||
3 | reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
4 |
|
||||||
|
5 | reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
|
|
||||||
info: The definition of class `Foo` will raise `TypeError` at runtime
|
info: The definition of class `Foo` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:1:11
|
--> src/mdtest_snippet.py:3:11
|
||||||
|
|
|
|
||||||
1 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
1 | from ty_extensions import reveal_mro
|
||||||
|
2 |
|
||||||
|
3 | class Foo(str, str): ... # error: [duplicate-base] "Duplicate base class `str`"
|
||||||
| --- ^^^ Class `str` later repeated here
|
| --- ^^^ Class `str` later repeated here
|
||||||
| |
|
| |
|
||||||
| Class `str` first included in bases list here
|
| Class `str` first included in bases list here
|
||||||
2 |
|
4 |
|
||||||
3 | reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, Unknown, <class 'object'>]
|
5 | reveal_mro(Foo) # revealed: (<class 'Foo'>, Unknown, <class 'object'>)
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -122,37 +128,37 @@ 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:14:7
|
--> src/mdtest_snippet.py:16:7
|
||||||
|
|
|
|
||||||
12 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
||||||
13 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
14 | class Ham(
|
16 | class Ham(
|
||||||
| _______^
|
| _______^
|
||||||
15 | | Spam,
|
17 | | Spam,
|
||||||
16 | | Eggs,
|
18 | | Eggs,
|
||||||
17 | | Bar,
|
19 | | Bar,
|
||||||
18 | | Baz,
|
20 | | Baz,
|
||||||
19 | | Spam,
|
21 | | Spam,
|
||||||
20 | | Eggs,
|
22 | | Eggs,
|
||||||
21 | | ): ...
|
23 | | ): ...
|
||||||
| |_^
|
| |_^
|
||||||
22 |
|
24 |
|
||||||
23 | # fmt: on
|
25 | # fmt: on
|
||||||
|
|
|
|
||||||
info: The definition of class `Ham` will raise `TypeError` at runtime
|
info: The definition of class `Ham` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:15:5
|
--> src/mdtest_snippet.py:17:5
|
||||||
|
|
|
|
||||||
13 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
14 | class Ham(
|
16 | class Ham(
|
||||||
15 | Spam,
|
17 | Spam,
|
||||||
| ---- Class `Spam` first included in bases list here
|
| ---- Class `Spam` first included in bases list here
|
||||||
16 | Eggs,
|
18 | Eggs,
|
||||||
17 | Bar,
|
19 | Bar,
|
||||||
18 | Baz,
|
20 | Baz,
|
||||||
19 | Spam,
|
21 | Spam,
|
||||||
| ^^^^ Class `Spam` later repeated here
|
| ^^^^ Class `Spam` later repeated here
|
||||||
20 | Eggs,
|
22 | Eggs,
|
||||||
21 | ): ...
|
23 | ): ...
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -160,36 +166,36 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `Eggs`
|
error[duplicate-base]: Duplicate base class `Eggs`
|
||||||
--> src/mdtest_snippet.py:14:7
|
--> src/mdtest_snippet.py:16:7
|
||||||
|
|
|
|
||||||
12 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
14 | # error: [duplicate-base] "Duplicate base class `Spam`"
|
||||||
13 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
15 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
14 | class Ham(
|
16 | class Ham(
|
||||||
| _______^
|
| _______^
|
||||||
15 | | Spam,
|
17 | | Spam,
|
||||||
16 | | Eggs,
|
18 | | Eggs,
|
||||||
17 | | Bar,
|
19 | | Bar,
|
||||||
18 | | Baz,
|
20 | | Baz,
|
||||||
19 | | Spam,
|
21 | | Spam,
|
||||||
20 | | Eggs,
|
22 | | Eggs,
|
||||||
21 | | ): ...
|
23 | | ): ...
|
||||||
| |_^
|
| |_^
|
||||||
22 |
|
24 |
|
||||||
23 | # fmt: on
|
25 | # fmt: on
|
||||||
|
|
|
|
||||||
info: The definition of class `Ham` will raise `TypeError` at runtime
|
info: The definition of class `Ham` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:16:5
|
--> src/mdtest_snippet.py:18:5
|
||||||
|
|
|
|
||||||
14 | class Ham(
|
16 | class Ham(
|
||||||
15 | Spam,
|
17 | Spam,
|
||||||
16 | Eggs,
|
18 | Eggs,
|
||||||
| ---- Class `Eggs` first included in bases list here
|
| ---- Class `Eggs` first included in bases list here
|
||||||
17 | Bar,
|
19 | Bar,
|
||||||
18 | Baz,
|
20 | Baz,
|
||||||
19 | Spam,
|
21 | Spam,
|
||||||
20 | Eggs,
|
22 | Eggs,
|
||||||
| ^^^^ Class `Eggs` later repeated here
|
| ^^^^ Class `Eggs` later repeated here
|
||||||
21 | ): ...
|
23 | ): ...
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -197,24 +203,24 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `Mushrooms`
|
error[duplicate-base]: Duplicate base class `Mushrooms`
|
||||||
--> src/mdtest_snippet.py:28:7
|
--> src/mdtest_snippet.py:30:7
|
||||||
|
|
|
|
||||||
27 | class Mushrooms: ...
|
29 | class Mushrooms: ...
|
||||||
28 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
30 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
29 |
|
31 |
|
||||||
30 | reveal_type(Omelette.__mro__) # revealed: tuple[<class 'Omelette'>, Unknown, <class 'object'>]
|
32 | reveal_mro(Omelette) # revealed: (<class 'Omelette'>, Unknown, <class 'object'>)
|
||||||
|
|
|
|
||||||
info: The definition of class `Omelette` will raise `TypeError` at runtime
|
info: The definition of class `Omelette` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:28:28
|
--> src/mdtest_snippet.py:30:28
|
||||||
|
|
|
|
||||||
27 | class Mushrooms: ...
|
29 | class Mushrooms: ...
|
||||||
28 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
30 | class Omelette(Spam, Eggs, Mushrooms, Mushrooms): ... # error: [duplicate-base]
|
||||||
| --------- ^^^^^^^^^ Class `Mushrooms` later repeated here
|
| --------- ^^^^^^^^^ Class `Mushrooms` later repeated here
|
||||||
| |
|
| |
|
||||||
| Class `Mushrooms` first included in bases list here
|
| Class `Mushrooms` first included in bases list here
|
||||||
29 |
|
31 |
|
||||||
30 | reveal_type(Omelette.__mro__) # revealed: tuple[<class 'Omelette'>, Unknown, <class 'object'>]
|
32 | reveal_mro(Omelette) # revealed: (<class 'Omelette'>, Unknown, <class 'object'>)
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -222,44 +228,44 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `Eggs`
|
error[duplicate-base]: Duplicate base class `Eggs`
|
||||||
--> src/mdtest_snippet.py:35:7
|
--> src/mdtest_snippet.py:37:7
|
||||||
|
|
|
|
||||||
34 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
36 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
35 | class VeryEggyOmelette(
|
37 | class VeryEggyOmelette(
|
||||||
| _______^
|
| _______^
|
||||||
36 | | Eggs,
|
38 | | Eggs,
|
||||||
37 | | Ham,
|
39 | | Ham,
|
||||||
38 | | Spam,
|
40 | | Spam,
|
||||||
39 | | Eggs,
|
41 | | Eggs,
|
||||||
40 | | Mushrooms,
|
42 | | Mushrooms,
|
||||||
41 | | Bar,
|
43 | | Bar,
|
||||||
42 | | Eggs,
|
|
||||||
43 | | Baz,
|
|
||||||
44 | | Eggs,
|
44 | | Eggs,
|
||||||
45 | | ): ...
|
45 | | Baz,
|
||||||
|
46 | | Eggs,
|
||||||
|
47 | | ): ...
|
||||||
| |_^
|
| |_^
|
||||||
46 |
|
48 |
|
||||||
47 | # fmt: off
|
49 | # fmt: off
|
||||||
|
|
|
|
||||||
info: The definition of class `VeryEggyOmelette` will raise `TypeError` at runtime
|
info: The definition of class `VeryEggyOmelette` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:36:5
|
--> src/mdtest_snippet.py:38:5
|
||||||
|
|
|
|
||||||
34 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
36 | # error: [duplicate-base] "Duplicate base class `Eggs`"
|
||||||
35 | class VeryEggyOmelette(
|
37 | class VeryEggyOmelette(
|
||||||
36 | Eggs,
|
38 | Eggs,
|
||||||
| ---- Class `Eggs` first included in bases list here
|
| ---- Class `Eggs` first included in bases list here
|
||||||
37 | Ham,
|
39 | Ham,
|
||||||
38 | Spam,
|
40 | Spam,
|
||||||
39 | Eggs,
|
41 | Eggs,
|
||||||
| ^^^^ Class `Eggs` later repeated here
|
| ^^^^ Class `Eggs` later repeated here
|
||||||
40 | Mushrooms,
|
42 | Mushrooms,
|
||||||
41 | Bar,
|
43 | Bar,
|
||||||
42 | Eggs,
|
|
||||||
| ^^^^ Class `Eggs` later repeated here
|
|
||||||
43 | Baz,
|
|
||||||
44 | Eggs,
|
44 | Eggs,
|
||||||
| ^^^^ Class `Eggs` later repeated here
|
| ^^^^ Class `Eggs` later repeated here
|
||||||
45 | ): ...
|
45 | Baz,
|
||||||
|
46 | Eggs,
|
||||||
|
| ^^^^ Class `Eggs` later repeated here
|
||||||
|
47 | ): ...
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -267,30 +273,30 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `A`
|
error[duplicate-base]: Duplicate base class `A`
|
||||||
--> src/mdtest_snippet.py:67:7
|
--> src/mdtest_snippet.py:69:7
|
||||||
|
|
|
|
||||||
66 | # error: [duplicate-base]
|
68 | # error: [duplicate-base]
|
||||||
67 | class D(
|
69 | class D(
|
||||||
| _______^
|
| _______^
|
||||||
68 | | A,
|
70 | | A,
|
||||||
69 | | # error: [unused-ignore-comment]
|
71 | | # error: [unused-ignore-comment]
|
||||||
70 | | A, # type: ignore[duplicate-base]
|
72 | | A, # type: ignore[duplicate-base]
|
||||||
71 | | ): ...
|
73 | | ): ...
|
||||||
| |_^
|
| |_^
|
||||||
72 |
|
74 |
|
||||||
73 | # error: [duplicate-base]
|
75 | # error: [duplicate-base]
|
||||||
|
|
|
|
||||||
info: The definition of class `D` will raise `TypeError` at runtime
|
info: The definition of class `D` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:68:5
|
--> src/mdtest_snippet.py:70:5
|
||||||
|
|
|
|
||||||
66 | # error: [duplicate-base]
|
68 | # error: [duplicate-base]
|
||||||
67 | class D(
|
69 | class D(
|
||||||
68 | A,
|
70 | A,
|
||||||
| - Class `A` first included in bases list here
|
| - Class `A` first included in bases list here
|
||||||
69 | # error: [unused-ignore-comment]
|
71 | # error: [unused-ignore-comment]
|
||||||
70 | A, # type: ignore[duplicate-base]
|
72 | A, # type: ignore[duplicate-base]
|
||||||
| ^ Class `A` later repeated here
|
| ^ Class `A` later repeated here
|
||||||
71 | ): ...
|
73 | ): ...
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -298,42 +304,42 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
info[unused-ignore-comment]
|
info[unused-ignore-comment]
|
||||||
--> src/mdtest_snippet.py:70:9
|
--> src/mdtest_snippet.py:72:9
|
||||||
|
|
|
|
||||||
68 | A,
|
70 | A,
|
||||||
69 | # error: [unused-ignore-comment]
|
71 | # error: [unused-ignore-comment]
|
||||||
70 | A, # type: ignore[duplicate-base]
|
72 | A, # type: ignore[duplicate-base]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused blanket `type: ignore` directive
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused blanket `type: ignore` directive
|
||||||
71 | ): ...
|
73 | ): ...
|
||||||
|
|
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
error[duplicate-base]: Duplicate base class `A`
|
error[duplicate-base]: Duplicate base class `A`
|
||||||
--> src/mdtest_snippet.py:74:7
|
--> src/mdtest_snippet.py:76:7
|
||||||
|
|
|
|
||||||
73 | # error: [duplicate-base]
|
75 | # error: [duplicate-base]
|
||||||
74 | class E(
|
76 | class E(
|
||||||
| _______^
|
| _______^
|
||||||
75 | | A,
|
77 | | A,
|
||||||
76 | | A
|
78 | | A
|
||||||
77 | | ):
|
79 | | ):
|
||||||
| |_^
|
| |_^
|
||||||
78 | # error: [unused-ignore-comment]
|
80 | # error: [unused-ignore-comment]
|
||||||
79 | x: int # type: ignore[duplicate-base]
|
81 | x: int # type: ignore[duplicate-base]
|
||||||
|
|
|
|
||||||
info: The definition of class `E` will raise `TypeError` at runtime
|
info: The definition of class `E` will raise `TypeError` at runtime
|
||||||
--> src/mdtest_snippet.py:75:5
|
--> src/mdtest_snippet.py:77:5
|
||||||
|
|
|
|
||||||
73 | # error: [duplicate-base]
|
75 | # error: [duplicate-base]
|
||||||
74 | class E(
|
76 | class E(
|
||||||
75 | A,
|
77 | A,
|
||||||
| - Class `A` first included in bases list here
|
| - Class `A` first included in bases list here
|
||||||
76 | A
|
78 | A
|
||||||
| ^ Class `A` later repeated here
|
| ^ Class `A` later repeated here
|
||||||
77 | ):
|
79 | ):
|
||||||
78 | # error: [unused-ignore-comment]
|
80 | # error: [unused-ignore-comment]
|
||||||
|
|
|
|
||||||
info: rule `duplicate-base` is enabled by default
|
info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
|
|
@ -341,14 +347,14 @@ info: rule `duplicate-base` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
info[unused-ignore-comment]
|
info[unused-ignore-comment]
|
||||||
--> src/mdtest_snippet.py:79:13
|
--> src/mdtest_snippet.py:81:13
|
||||||
|
|
|
|
||||||
77 | ):
|
79 | ):
|
||||||
78 | # error: [unused-ignore-comment]
|
80 | # error: [unused-ignore-comment]
|
||||||
79 | x: int # type: ignore[duplicate-base]
|
81 | x: int # type: ignore[duplicate-base]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused blanket `type: ignore` directive
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unused blanket `type: ignore` directive
|
||||||
80 |
|
82 |
|
||||||
81 | # fmt: on
|
83 | # fmt: on
|
||||||
|
|
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -13,120 +13,121 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md
|
||||||
|
|
||||||
```
|
```
|
||||||
1 | from __future__ import annotations
|
1 | from __future__ import annotations
|
||||||
2 |
|
2 | from ty_extensions import reveal_mro
|
||||||
3 | class A:
|
3 |
|
||||||
4 | def a(self): ...
|
4 | class A:
|
||||||
5 | aa: int = 1
|
5 | def a(self): ...
|
||||||
6 |
|
6 | aa: int = 1
|
||||||
7 | class B(A):
|
7 |
|
||||||
8 | def b(self): ...
|
8 | class B(A):
|
||||||
9 | bb: int = 2
|
9 | def b(self): ...
|
||||||
10 |
|
10 | bb: int = 2
|
||||||
11 | class C(B):
|
11 |
|
||||||
12 | def c(self): ...
|
12 | class C(B):
|
||||||
13 | cc: int = 3
|
13 | def c(self): ...
|
||||||
14 |
|
14 | cc: int = 3
|
||||||
15 | reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>]
|
15 |
|
||||||
16 |
|
16 | reveal_mro(C) # revealed: (<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>)
|
||||||
17 | super(C, C()).a
|
17 |
|
||||||
18 | super(C, C()).b
|
18 | super(C, C()).a
|
||||||
19 | super(C, C()).c # error: [unresolved-attribute]
|
19 | super(C, C()).b
|
||||||
20 |
|
20 | super(C, C()).c # error: [unresolved-attribute]
|
||||||
21 | super(B, C()).a
|
21 |
|
||||||
22 | super(B, C()).b # error: [unresolved-attribute]
|
22 | super(B, C()).a
|
||||||
23 | super(B, C()).c # error: [unresolved-attribute]
|
23 | super(B, C()).b # error: [unresolved-attribute]
|
||||||
24 |
|
24 | super(B, C()).c # error: [unresolved-attribute]
|
||||||
25 | super(A, C()).a # error: [unresolved-attribute]
|
25 |
|
||||||
26 | super(A, C()).b # error: [unresolved-attribute]
|
26 | super(A, C()).a # error: [unresolved-attribute]
|
||||||
27 | super(A, C()).c # error: [unresolved-attribute]
|
27 | super(A, C()).b # error: [unresolved-attribute]
|
||||||
28 |
|
28 | super(A, C()).c # error: [unresolved-attribute]
|
||||||
29 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
29 |
|
||||||
30 | reveal_type(super(C, C()).b) # revealed: bound method C.b() -> Unknown
|
30 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
||||||
31 | reveal_type(super(C, C()).aa) # revealed: int
|
31 | reveal_type(super(C, C()).b) # revealed: bound method C.b() -> Unknown
|
||||||
32 | reveal_type(super(C, C()).bb) # revealed: int
|
32 | reveal_type(super(C, C()).aa) # revealed: int
|
||||||
33 | import types
|
33 | reveal_type(super(C, C()).bb) # revealed: int
|
||||||
34 | from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
34 | import types
|
||||||
35 |
|
35 | from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
||||||
36 | def f(): ...
|
36 |
|
||||||
37 |
|
37 | def f(): ...
|
||||||
38 | class Foo[T]:
|
38 |
|
||||||
39 | def method(self): ...
|
39 | class Foo[T]:
|
||||||
40 | @property
|
40 | def method(self): ...
|
||||||
41 | def some_property(self): ...
|
41 | @property
|
||||||
42 |
|
42 | def some_property(self): ...
|
||||||
43 | type Alias = int
|
43 |
|
||||||
44 |
|
44 | type Alias = int
|
||||||
45 | class SomeTypedDict(TypedDict):
|
45 |
|
||||||
46 | x: int
|
46 | class SomeTypedDict(TypedDict):
|
||||||
47 | y: bytes
|
47 | x: int
|
||||||
48 |
|
48 | y: bytes
|
||||||
49 | # revealed: <super: <class 'object'>, FunctionType>
|
49 |
|
||||||
50 | reveal_type(super(object, f))
|
50 | # revealed: <super: <class 'object'>, FunctionType>
|
||||||
51 | # revealed: <super: <class 'object'>, WrapperDescriptorType>
|
51 | reveal_type(super(object, f))
|
||||||
52 | reveal_type(super(object, types.FunctionType.__get__))
|
52 | # revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||||
53 | # revealed: <super: <class 'object'>, GenericAlias>
|
53 | reveal_type(super(object, types.FunctionType.__get__))
|
||||||
54 | reveal_type(super(object, Foo[int]))
|
54 | # revealed: <super: <class 'object'>, GenericAlias>
|
||||||
55 | # revealed: <super: <class 'object'>, _SpecialForm>
|
55 | reveal_type(super(object, Foo[int]))
|
||||||
56 | reveal_type(super(object, Literal))
|
56 | # revealed: <super: <class 'object'>, _SpecialForm>
|
||||||
57 | # revealed: <super: <class 'object'>, TypeAliasType>
|
57 | reveal_type(super(object, Literal))
|
||||||
58 | reveal_type(super(object, Alias))
|
58 | # revealed: <super: <class 'object'>, TypeAliasType>
|
||||||
59 | # revealed: <super: <class 'object'>, MethodType>
|
59 | reveal_type(super(object, Alias))
|
||||||
60 | reveal_type(super(object, Foo().method))
|
60 | # revealed: <super: <class 'object'>, MethodType>
|
||||||
61 | # revealed: <super: <class 'object'>, property>
|
61 | reveal_type(super(object, Foo().method))
|
||||||
62 | reveal_type(super(object, Foo.some_property))
|
62 | # revealed: <super: <class 'object'>, property>
|
||||||
63 |
|
63 | reveal_type(super(object, Foo.some_property))
|
||||||
64 | def g(x: object) -> TypeIs[list[object]]:
|
64 |
|
||||||
65 | return isinstance(x, list)
|
65 | def g(x: object) -> TypeIs[list[object]]:
|
||||||
66 |
|
66 | return isinstance(x, list)
|
||||||
67 | def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
67 |
|
||||||
68 | if hasattr(x, "bar"):
|
68 | def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
||||||
69 | # revealed: <Protocol with members 'bar'>
|
69 | if hasattr(x, "bar"):
|
||||||
70 | reveal_type(x)
|
70 | # revealed: <Protocol with members 'bar'>
|
||||||
71 | # error: [invalid-super-argument]
|
71 | reveal_type(x)
|
||||||
72 | # revealed: Unknown
|
72 | # error: [invalid-super-argument]
|
||||||
73 | reveal_type(super(object, x))
|
73 | # revealed: Unknown
|
||||||
74 |
|
74 | reveal_type(super(object, x))
|
||||||
75 | # error: [invalid-super-argument]
|
75 |
|
||||||
76 | # revealed: Unknown
|
76 | # error: [invalid-super-argument]
|
||||||
77 | reveal_type(super(object, z))
|
77 | # revealed: Unknown
|
||||||
78 |
|
78 | reveal_type(super(object, z))
|
||||||
79 | is_list = g(x)
|
79 |
|
||||||
80 | # revealed: TypeIs[list[object] @ x]
|
80 | is_list = g(x)
|
||||||
81 | reveal_type(is_list)
|
81 | # revealed: TypeIs[list[object] @ x]
|
||||||
82 | # revealed: <super: <class 'object'>, bool>
|
82 | reveal_type(is_list)
|
||||||
83 | reveal_type(super(object, is_list))
|
83 | # revealed: <super: <class 'object'>, bool>
|
||||||
84 |
|
84 | reveal_type(super(object, is_list))
|
||||||
85 | # revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
85 |
|
||||||
86 | reveal_type(super(object, y))
|
86 | # revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
||||||
87 |
|
87 | reveal_type(super(object, y))
|
||||||
88 | # The first argument to `super()` must be an actual class object;
|
88 |
|
||||||
89 | # instances of `GenericAlias` are not accepted at runtime:
|
89 | # The first argument to `super()` must be an actual class object;
|
||||||
90 | #
|
90 | # instances of `GenericAlias` are not accepted at runtime:
|
||||||
91 | # error: [invalid-super-argument]
|
91 | #
|
||||||
92 | # revealed: Unknown
|
92 | # error: [invalid-super-argument]
|
||||||
93 | reveal_type(super(list[int], []))
|
93 | # revealed: Unknown
|
||||||
94 | class Super:
|
94 | reveal_type(super(list[int], []))
|
||||||
95 | def method(self) -> int:
|
95 | class Super:
|
||||||
96 | return 42
|
96 | def method(self) -> int:
|
||||||
97 |
|
97 | return 42
|
||||||
98 | class Sub(Super):
|
98 |
|
||||||
99 | def method(self: Sub) -> int:
|
99 | class Sub(Super):
|
||||||
100 | # revealed: <super: <class 'Sub'>, Sub>
|
100 | def method(self: Sub) -> int:
|
||||||
101 | return reveal_type(super(self.__class__, self)).method()
|
101 | # revealed: <super: <class 'Sub'>, Sub>
|
||||||
|
102 | return reveal_type(super(self.__class__, self)).method()
|
||||||
```
|
```
|
||||||
|
|
||||||
# Diagnostics
|
# Diagnostics
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'C'>, C>` has no attribute `c`
|
error[unresolved-attribute]: Object of type `<super: <class 'C'>, C>` has no attribute `c`
|
||||||
--> src/mdtest_snippet.py:19:1
|
--> src/mdtest_snippet.py:20:1
|
||||||
|
|
|
|
||||||
17 | super(C, C()).a
|
18 | super(C, C()).a
|
||||||
18 | super(C, C()).b
|
19 | super(C, C()).b
|
||||||
19 | super(C, C()).c # error: [unresolved-attribute]
|
20 | super(C, C()).c # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
20 |
|
21 |
|
||||||
21 | super(B, C()).a
|
22 | super(B, C()).a
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -134,12 +135,12 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'B'>, C>` has no attribute `b`
|
error[unresolved-attribute]: Object of type `<super: <class 'B'>, C>` has no attribute `b`
|
||||||
--> src/mdtest_snippet.py:22:1
|
--> src/mdtest_snippet.py:23:1
|
||||||
|
|
|
|
||||||
21 | super(B, C()).a
|
22 | super(B, C()).a
|
||||||
22 | super(B, C()).b # error: [unresolved-attribute]
|
23 | super(B, C()).b # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
23 | super(B, C()).c # error: [unresolved-attribute]
|
24 | super(B, C()).c # error: [unresolved-attribute]
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -147,14 +148,14 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'B'>, C>` has no attribute `c`
|
error[unresolved-attribute]: Object of type `<super: <class 'B'>, C>` has no attribute `c`
|
||||||
--> src/mdtest_snippet.py:23:1
|
--> src/mdtest_snippet.py:24:1
|
||||||
|
|
|
|
||||||
21 | super(B, C()).a
|
22 | super(B, C()).a
|
||||||
22 | super(B, C()).b # error: [unresolved-attribute]
|
23 | super(B, C()).b # error: [unresolved-attribute]
|
||||||
23 | super(B, C()).c # error: [unresolved-attribute]
|
24 | super(B, C()).c # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
24 |
|
25 |
|
||||||
25 | super(A, C()).a # error: [unresolved-attribute]
|
26 | super(A, C()).a # error: [unresolved-attribute]
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -162,14 +163,14 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `a`
|
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `a`
|
||||||
--> src/mdtest_snippet.py:25:1
|
--> src/mdtest_snippet.py:26:1
|
||||||
|
|
|
|
||||||
23 | super(B, C()).c # error: [unresolved-attribute]
|
24 | super(B, C()).c # error: [unresolved-attribute]
|
||||||
24 |
|
25 |
|
||||||
25 | super(A, C()).a # error: [unresolved-attribute]
|
26 | super(A, C()).a # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
26 | super(A, C()).b # error: [unresolved-attribute]
|
27 | super(A, C()).b # error: [unresolved-attribute]
|
||||||
27 | super(A, C()).c # error: [unresolved-attribute]
|
28 | super(A, C()).c # error: [unresolved-attribute]
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -177,12 +178,12 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `b`
|
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `b`
|
||||||
--> src/mdtest_snippet.py:26:1
|
--> src/mdtest_snippet.py:27:1
|
||||||
|
|
|
|
||||||
25 | super(A, C()).a # error: [unresolved-attribute]
|
26 | super(A, C()).a # error: [unresolved-attribute]
|
||||||
26 | super(A, C()).b # error: [unresolved-attribute]
|
27 | super(A, C()).b # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
27 | super(A, C()).c # error: [unresolved-attribute]
|
28 | super(A, C()).c # error: [unresolved-attribute]
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -190,14 +191,14 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `c`
|
error[unresolved-attribute]: Object of type `<super: <class 'A'>, C>` has no attribute `c`
|
||||||
--> src/mdtest_snippet.py:27:1
|
--> src/mdtest_snippet.py:28:1
|
||||||
|
|
|
|
||||||
25 | super(A, C()).a # error: [unresolved-attribute]
|
26 | super(A, C()).a # error: [unresolved-attribute]
|
||||||
26 | super(A, C()).b # error: [unresolved-attribute]
|
27 | super(A, C()).b # error: [unresolved-attribute]
|
||||||
27 | super(A, C()).c # error: [unresolved-attribute]
|
28 | super(A, C()).c # error: [unresolved-attribute]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
28 |
|
29 |
|
||||||
29 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
30 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
||||||
|
|
|
|
||||||
info: rule `unresolved-attribute` is enabled by default
|
info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
|
|
@ -205,14 +206,14 @@ info: rule `unresolved-attribute` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[invalid-super-argument]: `<Protocol with members 'bar'>` is an abstract/structural type in `super(<class 'object'>, <Protocol with members 'bar'>)` call
|
error[invalid-super-argument]: `<Protocol with members 'bar'>` is an abstract/structural type in `super(<class 'object'>, <Protocol with members 'bar'>)` call
|
||||||
--> src/mdtest_snippet.py:73:21
|
--> src/mdtest_snippet.py:74:21
|
||||||
|
|
|
|
||||||
71 | # error: [invalid-super-argument]
|
72 | # error: [invalid-super-argument]
|
||||||
72 | # revealed: Unknown
|
73 | # revealed: Unknown
|
||||||
73 | reveal_type(super(object, x))
|
74 | reveal_type(super(object, x))
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
74 |
|
75 |
|
||||||
75 | # error: [invalid-super-argument]
|
76 | # error: [invalid-super-argument]
|
||||||
|
|
|
|
||||||
info: rule `invalid-super-argument` is enabled by default
|
info: rule `invalid-super-argument` is enabled by default
|
||||||
|
|
||||||
|
|
@ -220,14 +221,14 @@ info: rule `invalid-super-argument` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[invalid-super-argument]: `(int, str, /) -> bool` is an abstract/structural type in `super(<class 'object'>, (int, str, /) -> bool)` call
|
error[invalid-super-argument]: `(int, str, /) -> bool` is an abstract/structural type in `super(<class 'object'>, (int, str, /) -> bool)` call
|
||||||
--> src/mdtest_snippet.py:77:17
|
--> src/mdtest_snippet.py:78:17
|
||||||
|
|
|
|
||||||
75 | # error: [invalid-super-argument]
|
76 | # error: [invalid-super-argument]
|
||||||
76 | # revealed: Unknown
|
77 | # revealed: Unknown
|
||||||
77 | reveal_type(super(object, z))
|
78 | reveal_type(super(object, z))
|
||||||
| ^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^
|
||||||
78 |
|
79 |
|
||||||
79 | is_list = g(x)
|
80 | is_list = g(x)
|
||||||
|
|
|
|
||||||
info: rule `invalid-super-argument` is enabled by default
|
info: rule `invalid-super-argument` is enabled by default
|
||||||
|
|
||||||
|
|
@ -235,14 +236,14 @@ info: rule `invalid-super-argument` is enabled by default
|
||||||
|
|
||||||
```
|
```
|
||||||
error[invalid-super-argument]: `types.GenericAlias` instance `list[int]` is not a valid class
|
error[invalid-super-argument]: `types.GenericAlias` instance `list[int]` is not a valid class
|
||||||
--> src/mdtest_snippet.py:93:13
|
--> src/mdtest_snippet.py:94:13
|
||||||
|
|
|
|
||||||
91 | # error: [invalid-super-argument]
|
92 | # error: [invalid-super-argument]
|
||||||
92 | # revealed: Unknown
|
93 | # revealed: Unknown
|
||||||
93 | reveal_type(super(list[int], []))
|
94 | reveal_type(super(list[int], []))
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
94 | class Super:
|
95 | class Super:
|
||||||
95 | def method(self) -> int:
|
96 | def method(self) -> int:
|
||||||
|
|
|
|
||||||
info: rule `invalid-super-argument` is enabled by default
|
info: rule `invalid-super-argument` is enabled by default
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,14 @@ In type stubs, classes can reference themselves in their base class definitions.
|
||||||
`typeshed`, we have `class str(Sequence[str]): ...`.
|
`typeshed`, we have `class str(Sequence[str]): ...`.
|
||||||
|
|
||||||
```pyi
|
```pyi
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo[T]: ...
|
class Foo[T]: ...
|
||||||
|
|
||||||
class Bar(Foo[Bar]): ...
|
class Bar(Foo[Bar]): ...
|
||||||
|
|
||||||
reveal_type(Bar) # revealed: <class 'Bar'>
|
reveal_type(Bar) # revealed: <class 'Bar'>
|
||||||
reveal_type(Bar.__mro__) # revealed: tuple[<class 'Bar'>, <class 'Foo[Bar]'>, typing.Generic, <class 'object'>]
|
reveal_mro(Bar) # revealed: (<class 'Bar'>, <class 'Foo[Bar]'>, typing.Generic, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Access to attributes declared in stubs
|
## Access to attributes declared in stubs
|
||||||
|
|
|
||||||
|
|
@ -125,13 +125,14 @@ The stdlib API `os.stat` is a commonly used API that returns an instance of a tu
|
||||||
```py
|
```py
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
reveal_type(os.stat("my_file.txt")) # revealed: stat_result
|
reveal_type(os.stat("my_file.txt")) # revealed: stat_result
|
||||||
reveal_type(os.stat("my_file.txt")[stat.ST_MODE]) # revealed: int
|
reveal_type(os.stat("my_file.txt")[stat.ST_MODE]) # revealed: int
|
||||||
reveal_type(os.stat("my_file.txt")[stat.ST_ATIME]) # revealed: int | float
|
reveal_type(os.stat("my_file.txt")[stat.ST_ATIME]) # revealed: int | float
|
||||||
|
|
||||||
# revealed: tuple[<class 'stat_result'>, <class 'structseq[int | float]'>, <class 'tuple[int, int, int, int, int, int, int, int | float, int | float, int | float]'>, <class 'Sequence[int | float]'>, <class 'Reversible[int | float]'>, <class 'Collection[int | float]'>, <class 'Iterable[int | float]'>, <class 'Container[int | float]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'stat_result'>, <class 'structseq[int | float]'>, <class 'tuple[int, int, int, int, int, int, int, int | float, int | float, int | float]'>, <class 'Sequence[int | float]'>, <class 'Reversible[int | float]'>, <class 'Collection[int | float]'>, <class 'Iterable[int | float]'>, <class 'Container[int | float]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(os.stat_result.__mro__)
|
reveal_mro(os.stat_result)
|
||||||
|
|
||||||
# There are no specific overloads for the `float` elements in `os.stat_result`,
|
# There are no specific overloads for the `float` elements in `os.stat_result`,
|
||||||
# because the fallback `(self, index: SupportsIndex, /) -> int | float` overload
|
# because the fallback `(self, index: SupportsIndex, /) -> int | float` overload
|
||||||
|
|
@ -336,15 +337,17 @@ python-version = "3.9"
|
||||||
```
|
```
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A(tuple[int, str]): ...
|
class A(tuple[int, str]): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(A.__mro__)
|
reveal_mro(A)
|
||||||
|
|
||||||
class C(tuple): ...
|
class C(tuple): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(C.__mro__)
|
reveal_mro(C)
|
||||||
```
|
```
|
||||||
|
|
||||||
## `typing.Tuple`
|
## `typing.Tuple`
|
||||||
|
|
@ -376,16 +379,17 @@ python-version = "3.9"
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class A(Tuple[int, str]): ...
|
class A(Tuple[int, str]): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'A'>, <class 'tuple[int, str]'>, <class 'Sequence[int | str]'>, <class 'Reversible[int | str]'>, <class 'Collection[int | str]'>, <class 'Iterable[int | str]'>, <class 'Container[int | str]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(A.__mro__)
|
reveal_mro(A)
|
||||||
|
|
||||||
class C(Tuple): ...
|
class C(Tuple): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>]
|
# revealed: (<class 'C'>, <class 'tuple[Unknown, ...]'>, <class 'Sequence[Unknown]'>, <class 'Reversible[Unknown]'>, <class 'Collection[Unknown]'>, <class 'Iterable[Unknown]'>, <class 'Container[Unknown]'>, typing.Protocol, typing.Generic, <class 'object'>)
|
||||||
reveal_type(C.__mro__)
|
reveal_mro(C)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Union subscript access
|
### Union subscript access
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ The `Unknown` type is a special type that we use to represent actually unknown t
|
||||||
annotation), as opposed to `Any` which represents an explicitly unknown type.
|
annotation), as opposed to `Any` which represents an explicitly unknown type.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import Unknown, static_assert, is_assignable_to
|
from ty_extensions import Unknown, static_assert, is_assignable_to, reveal_mro
|
||||||
|
|
||||||
static_assert(is_assignable_to(Unknown, int))
|
static_assert(is_assignable_to(Unknown, int))
|
||||||
static_assert(is_assignable_to(int, Unknown))
|
static_assert(is_assignable_to(int, Unknown))
|
||||||
|
|
@ -107,8 +107,8 @@ def explicit_unknown(x: Unknown, y: tuple[str, Unknown], z: Unknown = 1) -> None
|
||||||
```py
|
```py
|
||||||
class C(Unknown): ...
|
class C(Unknown): ...
|
||||||
|
|
||||||
# revealed: tuple[<class 'C'>, Unknown, <class 'object'>]
|
# revealed: (<class 'C'>, Unknown, <class 'object'>)
|
||||||
reveal_type(C.__mro__)
|
reveal_mro(C)
|
||||||
|
|
||||||
# error: "Special form `ty_extensions.Unknown` expected no type parameter"
|
# error: "Special form `ty_extensions.Unknown` expected no type parameter"
|
||||||
u: Unknown[str]
|
u: Unknown[str]
|
||||||
|
|
|
||||||
|
|
@ -145,10 +145,12 @@ _: type[A, B]
|
||||||
## As a base class
|
## As a base class
|
||||||
|
|
||||||
```py
|
```py
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class Foo(type[int]): ...
|
class Foo(type[int]): ...
|
||||||
|
|
||||||
# TODO: should be `tuple[<class 'Foo'>, <class 'type'>, <class 'object'>]
|
# TODO: should be `tuple[<class 'Foo'>, <class 'type'>, <class 'object'>]
|
||||||
reveal_type(Foo.__mro__) # revealed: tuple[<class 'Foo'>, @Todo(GenericAlias instance), <class 'object'>]
|
reveal_mro(Foo) # revealed: (<class 'Foo'>, @Todo(GenericAlias instance), <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Display of generic `type[]` types
|
## Display of generic `type[]` types
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,11 @@ not a class.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
class C(Type): ...
|
class C(Type): ...
|
||||||
|
|
||||||
# Runtime value: `(C, type, typing.Generic, object)`
|
# Runtime value: `(C, type, typing.Generic, object)`
|
||||||
# TODO: Add `Generic` to the MRO
|
# TODO: Add `Generic` to the MRO
|
||||||
reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'type'>, <class 'object'>]
|
reveal_mro(C) # revealed: (<class 'C'>, <class 'type'>, <class 'object'>)
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ python-version = "3.12"
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import ClassVar
|
from typing import ClassVar
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
# error: [invalid-type-form] "`ClassVar` annotations are only allowed in class-body scopes"
|
# error: [invalid-type-form] "`ClassVar` annotations are only allowed in class-body scopes"
|
||||||
x: ClassVar[int] = 1
|
x: ClassVar[int] = 1
|
||||||
|
|
@ -155,8 +156,8 @@ def f[T](x: T) -> ClassVar[T]:
|
||||||
class Foo(ClassVar[tuple[int]]): ...
|
class Foo(ClassVar[tuple[int]]): ...
|
||||||
|
|
||||||
# TODO: Show `Unknown` instead of `@Todo` type in the MRO; or ignore `ClassVar` and show the MRO as if `ClassVar` was not there
|
# TODO: Show `Unknown` instead of `@Todo` type in the MRO; or ignore `ClassVar` and show the MRO as if `ClassVar` was not there
|
||||||
# revealed: tuple[<class 'Foo'>, @Todo(Inference of subscript on special form), <class 'object'>]
|
# revealed: (<class 'Foo'>, @Todo(Inference of subscript on special form), <class 'object'>)
|
||||||
reveal_type(Foo.__mro__)
|
reveal_mro(Foo)
|
||||||
```
|
```
|
||||||
|
|
||||||
[`typing.classvar`]: https://docs.python.org/3/library/typing.html#typing.ClassVar
|
[`typing.classvar`]: https://docs.python.org/3/library/typing.html#typing.ClassVar
|
||||||
|
|
|
||||||
|
|
@ -265,6 +265,7 @@ python-version = "3.12"
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from typing import Final, ClassVar, Annotated
|
from typing import Final, ClassVar, Annotated
|
||||||
|
from ty_extensions import reveal_mro
|
||||||
|
|
||||||
LEGAL_A: Final[int] = 1
|
LEGAL_A: Final[int] = 1
|
||||||
LEGAL_B: Final = 1
|
LEGAL_B: Final = 1
|
||||||
|
|
@ -304,8 +305,8 @@ def f[T](x: T) -> Final[T]:
|
||||||
class Foo(Final[tuple[int]]): ...
|
class Foo(Final[tuple[int]]): ...
|
||||||
|
|
||||||
# TODO: Show `Unknown` instead of `@Todo` type in the MRO; or ignore `Final` and show the MRO as if `Final` was not there
|
# TODO: Show `Unknown` instead of `@Todo` type in the MRO; or ignore `Final` and show the MRO as if `Final` was not there
|
||||||
# revealed: tuple[<class 'Foo'>, @Todo(Inference of subscript on special form), <class 'object'>]
|
# revealed: (<class 'Foo'>, @Todo(Inference of subscript on special form), <class 'object'>)
|
||||||
reveal_type(Foo.__mro__)
|
reveal_mro(Foo)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Attribute assignment outside `__init__`
|
### Attribute assignment outside `__init__`
|
||||||
|
|
|
||||||
|
|
@ -4364,14 +4364,6 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Type::ClassLiteral(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => {
|
Type::ClassLiteral(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => {
|
||||||
let class_attr_plain = self.find_name_in_mro_with_policy(db, name_str,policy).expect(
|
|
||||||
"Calling `find_name_in_mro` on class literals and subclass-of types should always return `Some`",
|
|
||||||
);
|
|
||||||
|
|
||||||
if name == "__mro__" {
|
|
||||||
return class_attr_plain;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(enum_class) = match self {
|
if let Some(enum_class) = match self {
|
||||||
Type::ClassLiteral(literal) => Some(literal),
|
Type::ClassLiteral(literal) => Some(literal),
|
||||||
Type::SubclassOf(subclass_of) => subclass_of
|
Type::SubclassOf(subclass_of) => subclass_of
|
||||||
|
|
@ -4392,6 +4384,10 @@ impl<'db> Type<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let class_attr_plain = self.find_name_in_mro_with_policy(db, name_str,policy).expect(
|
||||||
|
"Calling `find_name_in_mro` on class literals and subclass-of types should always return `Some`",
|
||||||
|
);
|
||||||
|
|
||||||
let class_attr_fallback = Self::try_call_dunder_get_on_attribute(
|
let class_attr_fallback = Self::try_call_dunder_get_on_attribute(
|
||||||
db,
|
db,
|
||||||
class_attr_plain,
|
class_attr_plain,
|
||||||
|
|
|
||||||
|
|
@ -1981,11 +1981,6 @@ impl<'db> ClassLiteral<'db> {
|
||||||
name: &str,
|
name: &str,
|
||||||
policy: MemberLookupPolicy,
|
policy: MemberLookupPolicy,
|
||||||
) -> PlaceAndQualifiers<'db> {
|
) -> PlaceAndQualifiers<'db> {
|
||||||
if name == "__mro__" {
|
|
||||||
let tuple_elements = self.iter_mro(db, specialization);
|
|
||||||
return Place::bound(Type::heterogeneous_tuple(db, tuple_elements)).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.class_member_from_mro(db, name, policy, self.iter_mro(db, specialization))
|
self.class_member_from_mro(db, name, policy, self.iter_mro(db, specialization))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,27 @@ impl<'db> ClassBase<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn display(self, db: &'db dyn Db) -> impl std::fmt::Display {
|
||||||
|
struct ClassBaseDisplay<'db> {
|
||||||
|
db: &'db dyn Db,
|
||||||
|
base: ClassBase<'db>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ClassBaseDisplay<'_> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.base {
|
||||||
|
ClassBase::Dynamic(dynamic) => dynamic.fmt(f),
|
||||||
|
ClassBase::Class(class) => Type::from(class).display(self.db).fmt(f),
|
||||||
|
ClassBase::Protocol => f.write_str("typing.Protocol"),
|
||||||
|
ClassBase::Generic => f.write_str("typing.Generic"),
|
||||||
|
ClassBase::TypedDict => f.write_str("typing.TypedDict"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassBaseDisplay { db, base: self }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
|
||||||
|
|
|
||||||
|
|
@ -1319,6 +1319,8 @@ pub enum KnownFunction {
|
||||||
HasMember,
|
HasMember,
|
||||||
/// `ty_extensions.reveal_protocol_interface`
|
/// `ty_extensions.reveal_protocol_interface`
|
||||||
RevealProtocolInterface,
|
RevealProtocolInterface,
|
||||||
|
/// `ty_extensions.reveal_mro`
|
||||||
|
RevealMro,
|
||||||
/// `ty_extensions.range_constraint`
|
/// `ty_extensions.range_constraint`
|
||||||
RangeConstraint,
|
RangeConstraint,
|
||||||
/// `ty_extensions.negated_range_constraint`
|
/// `ty_extensions.negated_range_constraint`
|
||||||
|
|
@ -1397,6 +1399,7 @@ impl KnownFunction {
|
||||||
| Self::RevealProtocolInterface
|
| Self::RevealProtocolInterface
|
||||||
| Self::RangeConstraint
|
| Self::RangeConstraint
|
||||||
| Self::NegatedRangeConstraint
|
| Self::NegatedRangeConstraint
|
||||||
|
| Self::RevealMro
|
||||||
| Self::AllMembers => module.is_ty_extensions(),
|
| Self::AllMembers => module.is_ty_extensions(),
|
||||||
Self::ImportModule => module.is_importlib(),
|
Self::ImportModule => module.is_importlib(),
|
||||||
|
|
||||||
|
|
@ -1619,6 +1622,85 @@ impl KnownFunction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KnownFunction::RevealMro => {
|
||||||
|
let [Some(param_type)] = parameter_types else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut good_argument = true;
|
||||||
|
let classes = match param_type {
|
||||||
|
Type::ClassLiteral(class) => vec![ClassType::NonGeneric(*class)],
|
||||||
|
Type::GenericAlias(generic_alias) => vec![ClassType::Generic(*generic_alias)],
|
||||||
|
Type::Union(union) => {
|
||||||
|
let elements = union.elements(db);
|
||||||
|
let mut classes = Vec::with_capacity(elements.len());
|
||||||
|
for element in elements {
|
||||||
|
match element {
|
||||||
|
Type::ClassLiteral(class) => {
|
||||||
|
classes.push(ClassType::NonGeneric(*class));
|
||||||
|
}
|
||||||
|
Type::GenericAlias(generic_alias) => {
|
||||||
|
classes.push(ClassType::Generic(*generic_alias));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
good_argument = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
classes
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
good_argument = false;
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !good_argument {
|
||||||
|
let Some(builder) =
|
||||||
|
context.report_lint(&INVALID_ARGUMENT_TYPE, call_expression)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut diagnostic =
|
||||||
|
builder.into_diagnostic("Invalid argument to `reveal_mro`");
|
||||||
|
diagnostic.set_primary_message(format_args!(
|
||||||
|
"Can only pass a class object, generic alias or a union thereof"
|
||||||
|
));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(builder) =
|
||||||
|
context.report_diagnostic(DiagnosticId::RevealedType, Severity::Info)
|
||||||
|
{
|
||||||
|
let mut diag = builder.into_diagnostic("Revealed MRO");
|
||||||
|
let span = context.span(&call_expression.arguments.args[0]);
|
||||||
|
let mut message = String::new();
|
||||||
|
for (i, class) in classes.iter().enumerate() {
|
||||||
|
message.push('(');
|
||||||
|
for class in class.iter_mro(db) {
|
||||||
|
message.push_str(&class.display(db).to_string());
|
||||||
|
// Omit the comma for the last element (which is always `object`)
|
||||||
|
if class
|
||||||
|
.into_class()
|
||||||
|
.is_none_or(|base| !base.is_object(context.db()))
|
||||||
|
{
|
||||||
|
message.push_str(", ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the last element was also the first element
|
||||||
|
// (i.e., it's a length-1 tuple -- which can only happen if we're revealing
|
||||||
|
// the MRO for `object` itself), add a trailing comma so that it's still a
|
||||||
|
// valid tuple display.
|
||||||
|
if class.is_object(db) {
|
||||||
|
message.push(',');
|
||||||
|
}
|
||||||
|
message.push(')');
|
||||||
|
if i < classes.len() - 1 {
|
||||||
|
message.push_str(" | ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diag.annotate(Annotation::primary(span).message(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KnownFunction::IsInstance | KnownFunction::IsSubclass => {
|
KnownFunction::IsInstance | KnownFunction::IsSubclass => {
|
||||||
let [Some(first_arg), Some(second_argument)] = parameter_types else {
|
let [Some(first_arg), Some(second_argument)] = parameter_types else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -1822,6 +1904,7 @@ pub(crate) mod tests {
|
||||||
| KnownFunction::RevealProtocolInterface
|
| KnownFunction::RevealProtocolInterface
|
||||||
| KnownFunction::RangeConstraint
|
| KnownFunction::RangeConstraint
|
||||||
| KnownFunction::NegatedRangeConstraint
|
| KnownFunction::NegatedRangeConstraint
|
||||||
|
| KnownFunction::RevealMro
|
||||||
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
| KnownFunction::AllMembers => KnownModule::TyExtensions,
|
||||||
|
|
||||||
KnownFunction::ImportModule => KnownModule::ImportLib,
|
KnownFunction::ImportModule => KnownModule::ImportLib,
|
||||||
|
|
|
||||||
|
|
@ -345,32 +345,24 @@ impl Matcher {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// reveal_type
|
// reveal_type, reveal_protocol_interface
|
||||||
if primary_message == "Revealed type"
|
if matches!(
|
||||||
&& primary_annotation == expected_reveal_type_message
|
primary_message,
|
||||||
|
"Revealed type" | "Revealed protocol interface"
|
||||||
|
) && primary_annotation == expected_reveal_type_message
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reveal_protocol_interface
|
// reveal_when_assignable_to, reveal_when_subtype_of, reveal_mro
|
||||||
if primary_message == "Revealed protocol interface"
|
if matches!(
|
||||||
&& primary_annotation == expected_reveal_type_message
|
primary_message,
|
||||||
|
"Assignability holds" | "Subtyping holds" | "Revealed MRO"
|
||||||
|
) && primary_annotation == expected_type
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reveal_when_assignable_to
|
|
||||||
if primary_message == "Assignability holds"
|
|
||||||
&& primary_annotation == expected_type
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// reveal_when_subtype_of
|
|
||||||
if primary_message == "Subtyping holds" && primary_annotation == expected_type {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# ruff: noqa: PYI021
|
# ruff: noqa: PYI021
|
||||||
import sys
|
import sys
|
||||||
|
import types
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import (
|
from typing import (
|
||||||
|
|
@ -115,11 +116,16 @@ def all_members(obj: Any) -> tuple[str, ...]: ...
|
||||||
|
|
||||||
# Returns `True` if the given object has a member with the given name.
|
# Returns `True` if the given object has a member with the given name.
|
||||||
def has_member(obj: Any, name: str) -> bool: ...
|
def has_member(obj: Any, name: str) -> bool: ...
|
||||||
|
def reveal_protocol_interface(protocol: type) -> None:
|
||||||
|
"""
|
||||||
|
Passing a protocol type to this function will cause ty to emit an info-level
|
||||||
|
diagnostic describing the protocol's interface.
|
||||||
|
|
||||||
# Passing a protocol type to this function will cause ty to emit an info-level
|
Passing a non-protocol type will cause ty to emit an error diagnostic.
|
||||||
# diagnostic describing the protocol's interface. Passing a non-protocol type
|
"""
|
||||||
# will cause ty to emit an error diagnostic.
|
|
||||||
def reveal_protocol_interface(protocol: type) -> None: ...
|
def reveal_mro(cls: type | types.GenericAlias) -> None:
|
||||||
|
"""Reveal the MRO that ty infers for the given class or generic alias."""
|
||||||
|
|
||||||
# A protocol describing an interface that should be satisfied by all named tuples
|
# A protocol describing an interface that should be satisfied by all named tuples
|
||||||
# created using `typing.NamedTuple` or `collections.namedtuple`.
|
# created using `typing.NamedTuple` or `collections.namedtuple`.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue