mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-26 18:06:43 +00:00
[ty] Fix false-positive diagnostics on super() calls (#20814)
This commit is contained in:
parent
565dbf3c9d
commit
d83d7a0dcd
6 changed files with 975 additions and 123 deletions
|
|
@ -14,9 +14,16 @@ common usage.
|
|||
|
||||
### Explicit Super Object
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
`super(pivot_class, owner)` performs attribute lookup along the MRO, starting immediately after the
|
||||
specified pivot class.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
class A:
|
||||
def a(self): ...
|
||||
|
|
@ -34,21 +41,15 @@ reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, <class 'A'>,
|
|||
|
||||
super(C, C()).a
|
||||
super(C, C()).b
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'C'>, C>` has no attribute `c`"
|
||||
super(C, C()).c
|
||||
super(C, C()).c # error: [unresolved-attribute]
|
||||
|
||||
super(B, C()).a
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'B'>, C>` has no attribute `b`"
|
||||
super(B, C()).b
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'B'>, C>` has no attribute `c`"
|
||||
super(B, C()).c
|
||||
super(B, C()).b # error: [unresolved-attribute]
|
||||
super(B, C()).c # error: [unresolved-attribute]
|
||||
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'A'>, C>` has no attribute `a`"
|
||||
super(A, C()).a
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'A'>, C>` has no attribute `b`"
|
||||
super(A, C()).b
|
||||
# error: [unresolved-attribute] "Type `<super: <class 'A'>, C>` has no attribute `c`"
|
||||
super(A, C()).c
|
||||
super(A, C()).a # error: [unresolved-attribute]
|
||||
super(A, C()).b # error: [unresolved-attribute]
|
||||
super(A, C()).c # error: [unresolved-attribute]
|
||||
|
||||
reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
||||
reveal_type(super(C, C()).b) # revealed: bound method C.b() -> Unknown
|
||||
|
|
@ -56,12 +57,80 @@ reveal_type(super(C, C()).aa) # revealed: int
|
|||
reveal_type(super(C, C()).bb) # revealed: int
|
||||
```
|
||||
|
||||
Examples of explicit `super()` with unusual types. We allow almost any type to be passed as the
|
||||
second argument to `super()` -- the only exceptions are "pure abstract" types such as `Callable` and
|
||||
synthesized `Protocol`s that cannot be upcast to, or interpreted as, a non-`object` nominal type.
|
||||
|
||||
```py
|
||||
import types
|
||||
from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
||||
|
||||
def f(): ...
|
||||
|
||||
class Foo[T]:
|
||||
def method(self): ...
|
||||
@property
|
||||
def some_property(self): ...
|
||||
|
||||
type Alias = int
|
||||
|
||||
class SomeTypedDict(TypedDict):
|
||||
x: int
|
||||
y: bytes
|
||||
|
||||
# revealed: <super: <class 'object'>, FunctionType>
|
||||
reveal_type(super(object, f))
|
||||
# revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||
reveal_type(super(object, types.FunctionType.__get__))
|
||||
# revealed: <super: <class 'object'>, GenericAlias>
|
||||
reveal_type(super(object, Foo[int]))
|
||||
# revealed: <super: <class 'object'>, _SpecialForm>
|
||||
reveal_type(super(object, Literal))
|
||||
# revealed: <super: <class 'object'>, TypeAliasType>
|
||||
reveal_type(super(object, Alias))
|
||||
# revealed: <super: <class 'object'>, MethodType>
|
||||
reveal_type(super(object, Foo().method))
|
||||
# revealed: <super: <class 'object'>, property>
|
||||
reveal_type(super(object, Foo.some_property))
|
||||
|
||||
def g(x: object) -> TypeIs[list[object]]:
|
||||
return isinstance(x, list)
|
||||
|
||||
def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
||||
if hasattr(x, "bar"):
|
||||
# revealed: <Protocol with members 'bar'>
|
||||
reveal_type(x)
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super(object, x))
|
||||
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super(object, z))
|
||||
|
||||
is_list = g(x)
|
||||
# revealed: TypeIs[list[object] @ x]
|
||||
reveal_type(is_list)
|
||||
# revealed: <super: <class 'object'>, bool>
|
||||
reveal_type(super(object, is_list))
|
||||
|
||||
# revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
||||
reveal_type(super(object, y))
|
||||
```
|
||||
|
||||
### Implicit Super Object
|
||||
|
||||
<!-- snapshot-diagnostics -->
|
||||
|
||||
The implicit form `super()` is same as `super(__class__, <first argument>)`. The `__class__` refers
|
||||
to the class that contains the function where `super()` is used. The first argument refers to the
|
||||
current method’s first parameter (typically `self` or `cls`).
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.12"
|
||||
```
|
||||
|
||||
```py
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
@ -74,6 +143,7 @@ class B(A):
|
|||
def __init__(self, a: int):
|
||||
# TODO: Once `Self` is supported, this should be `<super: <class 'B'>, B>`
|
||||
reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
|
||||
reveal_type(super(object, super())) # revealed: <super: <class 'object'>, super>
|
||||
super().__init__(a)
|
||||
|
||||
@classmethod
|
||||
|
|
@ -86,6 +156,123 @@ super(B, B(42)).__init__(42)
|
|||
super(B, B).f()
|
||||
```
|
||||
|
||||
Some examples with unusual annotations for `self` or `cls`:
|
||||
|
||||
```py
|
||||
import enum
|
||||
from typing import Any, Self, Never, Protocol, Callable
|
||||
from ty_extensions import Intersection
|
||||
|
||||
class BuilderMeta(type):
|
||||
def __new__(
|
||||
cls: type[Any],
|
||||
name: str,
|
||||
bases: tuple[type, ...],
|
||||
dct: dict[str, Any],
|
||||
) -> BuilderMeta:
|
||||
# revealed: <super: <class 'BuilderMeta'>, Any>
|
||||
s = reveal_type(super())
|
||||
# revealed: Any
|
||||
return reveal_type(s.__new__(cls, name, bases, dct))
|
||||
|
||||
class BuilderMeta2(type):
|
||||
def __new__(
|
||||
cls: type[BuilderMeta2],
|
||||
name: str,
|
||||
bases: tuple[type, ...],
|
||||
dct: dict[str, Any],
|
||||
) -> BuilderMeta2:
|
||||
# revealed: <super: <class 'BuilderMeta2'>, <class 'BuilderMeta2'>>
|
||||
s = reveal_type(super())
|
||||
# TODO: should be `BuilderMeta2` (needs https://github.com/astral-sh/ty/issues/501)
|
||||
# revealed: Unknown
|
||||
return reveal_type(s.__new__(cls, name, bases, dct))
|
||||
|
||||
class Foo[T]:
|
||||
x: T
|
||||
|
||||
def method(self: Any):
|
||||
reveal_type(super()) # revealed: <super: <class 'Foo'>, Any>
|
||||
|
||||
if isinstance(self, Foo):
|
||||
reveal_type(super()) # revealed: <super: <class 'Foo'>, Any>
|
||||
|
||||
def method2(self: Foo[T]):
|
||||
# revealed: <super: <class 'Foo'>, Foo[T@Foo]>
|
||||
reveal_type(super())
|
||||
|
||||
def method3(self: Foo):
|
||||
# revealed: <super: <class 'Foo'>, Foo[Unknown]>
|
||||
reveal_type(super())
|
||||
|
||||
def method4(self: Self):
|
||||
# revealed: <super: <class 'Foo'>, Foo[T@Foo]>
|
||||
reveal_type(super())
|
||||
|
||||
def method5[S: Foo[int]](self: S, other: S) -> S:
|
||||
# revealed: <super: <class 'Foo'>, Foo[int]>
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
def method6[S: (Foo[int], Foo[str])](self: S, other: S) -> S:
|
||||
# revealed: <super: <class 'Foo'>, Foo[int]> | <super: <class 'Foo'>, Foo[str]>
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
def method7[S](self: S, other: S) -> S:
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
def method8[S: int](self: S, other: S) -> S:
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
def method9[S: (int, str)](self: S, other: S) -> S:
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
def method10[S: Callable[..., str]](self: S, other: S) -> S:
|
||||
# error: [invalid-super-argument]
|
||||
# revealed: Unknown
|
||||
reveal_type(super())
|
||||
return self
|
||||
|
||||
type Alias = Bar
|
||||
|
||||
class Bar:
|
||||
def method(self: Alias):
|
||||
# revealed: <super: <class 'Bar'>, Bar>
|
||||
reveal_type(super())
|
||||
|
||||
def pls_dont_call_me(self: Never):
|
||||
# revealed: <super: <class 'Bar'>, Unknown>
|
||||
reveal_type(super())
|
||||
|
||||
def only_call_me_on_callable_subclasses(self: Intersection[Bar, Callable[..., object]]):
|
||||
# revealed: <super: <class 'Bar'>, Bar>
|
||||
reveal_type(super())
|
||||
|
||||
class P(Protocol):
|
||||
def method(self: P):
|
||||
# revealed: <super: <class 'P'>, P>
|
||||
reveal_type(super())
|
||||
|
||||
class E(enum.Enum):
|
||||
X = 1
|
||||
|
||||
def method(self: E):
|
||||
match self:
|
||||
case E.X:
|
||||
# revealed: <super: <class 'E'>, E>
|
||||
reveal_type(super())
|
||||
```
|
||||
|
||||
### Unbound Super Object
|
||||
|
||||
Calling `super(cls)` without a second argument returns an _unbound super object_. This is treated as
|
||||
|
|
@ -167,11 +354,19 @@ class A:
|
|||
## Built-ins and Literals
|
||||
|
||||
```py
|
||||
from enum import Enum
|
||||
|
||||
reveal_type(super(bool, True)) # revealed: <super: <class 'bool'>, bool>
|
||||
reveal_type(super(bool, bool())) # revealed: <super: <class 'bool'>, bool>
|
||||
reveal_type(super(int, bool())) # revealed: <super: <class 'int'>, bool>
|
||||
reveal_type(super(int, 3)) # revealed: <super: <class 'int'>, int>
|
||||
reveal_type(super(str, "")) # revealed: <super: <class 'str'>, str>
|
||||
reveal_type(super(bytes, b"")) # revealed: <super: <class 'bytes'>, bytes>
|
||||
|
||||
class E(Enum):
|
||||
X = 42
|
||||
|
||||
reveal_type(super(E, E.X)) # revealed: <super: <class 'E'>, E>
|
||||
```
|
||||
|
||||
## Descriptor Behavior with Super
|
||||
|
|
@ -342,7 +537,7 @@ def f(x: int):
|
|||
# error: [invalid-super-argument] "`typing.TypeAliasType` is not a valid class"
|
||||
super(IntAlias, 0)
|
||||
|
||||
# error: [invalid-super-argument] "`Literal[""]` is not an instance or subclass of `<class 'int'>` in `super(<class 'int'>, Literal[""])` call"
|
||||
# error: [invalid-super-argument] "`str` is not an instance or subclass of `<class 'int'>` in `super(<class 'int'>, str)` call"
|
||||
# revealed: Unknown
|
||||
reveal_type(super(int, str()))
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,217 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: super.md - Super - Basic Usage - Explicit Super Object
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | class A:
|
||||
2 | def a(self): ...
|
||||
3 | aa: int = 1
|
||||
4 |
|
||||
5 | class B(A):
|
||||
6 | def b(self): ...
|
||||
7 | bb: int = 2
|
||||
8 |
|
||||
9 | class C(B):
|
||||
10 | def c(self): ...
|
||||
11 | cc: int = 3
|
||||
12 |
|
||||
13 | reveal_type(C.__mro__) # revealed: tuple[<class 'C'>, <class 'B'>, <class 'A'>, <class 'object'>]
|
||||
14 |
|
||||
15 | super(C, C()).a
|
||||
16 | super(C, C()).b
|
||||
17 | super(C, C()).c # error: [unresolved-attribute]
|
||||
18 |
|
||||
19 | super(B, C()).a
|
||||
20 | super(B, C()).b # error: [unresolved-attribute]
|
||||
21 | super(B, C()).c # error: [unresolved-attribute]
|
||||
22 |
|
||||
23 | super(A, C()).a # error: [unresolved-attribute]
|
||||
24 | super(A, C()).b # error: [unresolved-attribute]
|
||||
25 | super(A, C()).c # error: [unresolved-attribute]
|
||||
26 |
|
||||
27 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
||||
28 | reveal_type(super(C, C()).b) # revealed: bound method C.b() -> Unknown
|
||||
29 | reveal_type(super(C, C()).aa) # revealed: int
|
||||
30 | reveal_type(super(C, C()).bb) # revealed: int
|
||||
31 | import types
|
||||
32 | from typing_extensions import Callable, TypeIs, Literal, TypedDict
|
||||
33 |
|
||||
34 | def f(): ...
|
||||
35 |
|
||||
36 | class Foo[T]:
|
||||
37 | def method(self): ...
|
||||
38 | @property
|
||||
39 | def some_property(self): ...
|
||||
40 |
|
||||
41 | type Alias = int
|
||||
42 |
|
||||
43 | class SomeTypedDict(TypedDict):
|
||||
44 | x: int
|
||||
45 | y: bytes
|
||||
46 |
|
||||
47 | # revealed: <super: <class 'object'>, FunctionType>
|
||||
48 | reveal_type(super(object, f))
|
||||
49 | # revealed: <super: <class 'object'>, WrapperDescriptorType>
|
||||
50 | reveal_type(super(object, types.FunctionType.__get__))
|
||||
51 | # revealed: <super: <class 'object'>, GenericAlias>
|
||||
52 | reveal_type(super(object, Foo[int]))
|
||||
53 | # revealed: <super: <class 'object'>, _SpecialForm>
|
||||
54 | reveal_type(super(object, Literal))
|
||||
55 | # revealed: <super: <class 'object'>, TypeAliasType>
|
||||
56 | reveal_type(super(object, Alias))
|
||||
57 | # revealed: <super: <class 'object'>, MethodType>
|
||||
58 | reveal_type(super(object, Foo().method))
|
||||
59 | # revealed: <super: <class 'object'>, property>
|
||||
60 | reveal_type(super(object, Foo.some_property))
|
||||
61 |
|
||||
62 | def g(x: object) -> TypeIs[list[object]]:
|
||||
63 | return isinstance(x, list)
|
||||
64 |
|
||||
65 | def _(x: object, y: SomeTypedDict, z: Callable[[int, str], bool]):
|
||||
66 | if hasattr(x, "bar"):
|
||||
67 | # revealed: <Protocol with members 'bar'>
|
||||
68 | reveal_type(x)
|
||||
69 | # error: [invalid-super-argument]
|
||||
70 | # revealed: Unknown
|
||||
71 | reveal_type(super(object, x))
|
||||
72 |
|
||||
73 | # error: [invalid-super-argument]
|
||||
74 | # revealed: Unknown
|
||||
75 | reveal_type(super(object, z))
|
||||
76 |
|
||||
77 | is_list = g(x)
|
||||
78 | # revealed: TypeIs[list[object] @ x]
|
||||
79 | reveal_type(is_list)
|
||||
80 | # revealed: <super: <class 'object'>, bool>
|
||||
81 | reveal_type(super(object, is_list))
|
||||
82 |
|
||||
83 | # revealed: <super: <class 'object'>, dict[Literal["x", "y"], int | bytes]>
|
||||
84 | reveal_type(super(object, y))
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'C'>, C>` has no attribute `c`
|
||||
--> src/mdtest_snippet.py:17:1
|
||||
|
|
||||
15 | super(C, C()).a
|
||||
16 | super(C, C()).b
|
||||
17 | super(C, C()).c # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
18 |
|
||||
19 | super(B, C()).a
|
||||
|
|
||||
info: rule `unresolved-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'B'>, C>` has no attribute `b`
|
||||
--> src/mdtest_snippet.py:20:1
|
||||
|
|
||||
19 | super(B, C()).a
|
||||
20 | super(B, C()).b # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
21 | super(B, C()).c # error: [unresolved-attribute]
|
||||
|
|
||||
info: rule `unresolved-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'B'>, C>` has no attribute `c`
|
||||
--> src/mdtest_snippet.py:21:1
|
||||
|
|
||||
19 | super(B, C()).a
|
||||
20 | super(B, C()).b # error: [unresolved-attribute]
|
||||
21 | super(B, C()).c # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
22 |
|
||||
23 | super(A, C()).a # error: [unresolved-attribute]
|
||||
|
|
||||
info: rule `unresolved-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'A'>, C>` has no attribute `a`
|
||||
--> src/mdtest_snippet.py:23:1
|
||||
|
|
||||
21 | super(B, C()).c # error: [unresolved-attribute]
|
||||
22 |
|
||||
23 | super(A, C()).a # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
24 | super(A, C()).b # error: [unresolved-attribute]
|
||||
25 | super(A, C()).c # error: [unresolved-attribute]
|
||||
|
|
||||
info: rule `unresolved-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'A'>, C>` has no attribute `b`
|
||||
--> src/mdtest_snippet.py:24:1
|
||||
|
|
||||
23 | super(A, C()).a # error: [unresolved-attribute]
|
||||
24 | super(A, C()).b # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
25 | super(A, C()).c # error: [unresolved-attribute]
|
||||
|
|
||||
info: rule `unresolved-attribute` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[unresolved-attribute]: Type `<super: <class 'A'>, C>` has no attribute `c`
|
||||
--> src/mdtest_snippet.py:25:1
|
||||
|
|
||||
23 | super(A, C()).a # error: [unresolved-attribute]
|
||||
24 | super(A, C()).b # error: [unresolved-attribute]
|
||||
25 | super(A, C()).c # error: [unresolved-attribute]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
26 |
|
||||
27 | reveal_type(super(C, C()).a) # revealed: bound method C.a() -> Unknown
|
||||
|
|
||||
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
|
||||
--> src/mdtest_snippet.py:71:21
|
||||
|
|
||||
69 | # error: [invalid-super-argument]
|
||||
70 | # revealed: Unknown
|
||||
71 | reveal_type(super(object, x))
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
72 |
|
||||
73 | # error: [invalid-super-argument]
|
||||
|
|
||||
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
|
||||
--> src/mdtest_snippet.py:75:17
|
||||
|
|
||||
73 | # error: [invalid-super-argument]
|
||||
74 | # revealed: Unknown
|
||||
75 | reveal_type(super(object, z))
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
76 |
|
||||
77 | is_list = g(x)
|
||||
|
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
---
|
||||
source: crates/ty_test/src/lib.rs
|
||||
expression: snapshot
|
||||
---
|
||||
---
|
||||
mdtest name: super.md - Super - Basic Usage - Implicit Super Object
|
||||
mdtest path: crates/ty_python_semantic/resources/mdtest/class/super.md
|
||||
---
|
||||
|
||||
# Python source files
|
||||
|
||||
## mdtest_snippet.py
|
||||
|
||||
```
|
||||
1 | from __future__ import annotations
|
||||
2 |
|
||||
3 | class A:
|
||||
4 | def __init__(self, a: int): ...
|
||||
5 | @classmethod
|
||||
6 | def f(cls): ...
|
||||
7 |
|
||||
8 | class B(A):
|
||||
9 | def __init__(self, a: int):
|
||||
10 | # TODO: Once `Self` is supported, this should be `<super: <class 'B'>, B>`
|
||||
11 | reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
|
||||
12 | reveal_type(super(object, super())) # revealed: <super: <class 'object'>, super>
|
||||
13 | super().__init__(a)
|
||||
14 |
|
||||
15 | @classmethod
|
||||
16 | def f(cls):
|
||||
17 | # TODO: Once `Self` is supported, this should be `<super: <class 'B'>, <class 'B'>>`
|
||||
18 | reveal_type(super()) # revealed: <super: <class 'B'>, Unknown>
|
||||
19 | super().f()
|
||||
20 |
|
||||
21 | super(B, B(42)).__init__(42)
|
||||
22 | super(B, B).f()
|
||||
23 | import enum
|
||||
24 | from typing import Any, Self, Never, Protocol, Callable
|
||||
25 | from ty_extensions import Intersection
|
||||
26 |
|
||||
27 | class BuilderMeta(type):
|
||||
28 | def __new__(
|
||||
29 | cls: type[Any],
|
||||
30 | name: str,
|
||||
31 | bases: tuple[type, ...],
|
||||
32 | dct: dict[str, Any],
|
||||
33 | ) -> BuilderMeta:
|
||||
34 | # revealed: <super: <class 'BuilderMeta'>, Any>
|
||||
35 | s = reveal_type(super())
|
||||
36 | # revealed: Any
|
||||
37 | return reveal_type(s.__new__(cls, name, bases, dct))
|
||||
38 |
|
||||
39 | class BuilderMeta2(type):
|
||||
40 | def __new__(
|
||||
41 | cls: type[BuilderMeta2],
|
||||
42 | name: str,
|
||||
43 | bases: tuple[type, ...],
|
||||
44 | dct: dict[str, Any],
|
||||
45 | ) -> BuilderMeta2:
|
||||
46 | # revealed: <super: <class 'BuilderMeta2'>, <class 'BuilderMeta2'>>
|
||||
47 | s = reveal_type(super())
|
||||
48 | # TODO: should be `BuilderMeta2` (needs https://github.com/astral-sh/ty/issues/501)
|
||||
49 | # revealed: Unknown
|
||||
50 | return reveal_type(s.__new__(cls, name, bases, dct))
|
||||
51 |
|
||||
52 | class Foo[T]:
|
||||
53 | x: T
|
||||
54 |
|
||||
55 | def method(self: Any):
|
||||
56 | reveal_type(super()) # revealed: <super: <class 'Foo'>, Any>
|
||||
57 |
|
||||
58 | if isinstance(self, Foo):
|
||||
59 | reveal_type(super()) # revealed: <super: <class 'Foo'>, Any>
|
||||
60 |
|
||||
61 | def method2(self: Foo[T]):
|
||||
62 | # revealed: <super: <class 'Foo'>, Foo[T@Foo]>
|
||||
63 | reveal_type(super())
|
||||
64 |
|
||||
65 | def method3(self: Foo):
|
||||
66 | # revealed: <super: <class 'Foo'>, Foo[Unknown]>
|
||||
67 | reveal_type(super())
|
||||
68 |
|
||||
69 | def method4(self: Self):
|
||||
70 | # revealed: <super: <class 'Foo'>, Foo[T@Foo]>
|
||||
71 | reveal_type(super())
|
||||
72 |
|
||||
73 | def method5[S: Foo[int]](self: S, other: S) -> S:
|
||||
74 | # revealed: <super: <class 'Foo'>, Foo[int]>
|
||||
75 | reveal_type(super())
|
||||
76 | return self
|
||||
77 |
|
||||
78 | def method6[S: (Foo[int], Foo[str])](self: S, other: S) -> S:
|
||||
79 | # revealed: <super: <class 'Foo'>, Foo[int]> | <super: <class 'Foo'>, Foo[str]>
|
||||
80 | reveal_type(super())
|
||||
81 | return self
|
||||
82 |
|
||||
83 | def method7[S](self: S, other: S) -> S:
|
||||
84 | # error: [invalid-super-argument]
|
||||
85 | # revealed: Unknown
|
||||
86 | reveal_type(super())
|
||||
87 | return self
|
||||
88 |
|
||||
89 | def method8[S: int](self: S, other: S) -> S:
|
||||
90 | # error: [invalid-super-argument]
|
||||
91 | # revealed: Unknown
|
||||
92 | reveal_type(super())
|
||||
93 | return self
|
||||
94 |
|
||||
95 | def method9[S: (int, str)](self: S, other: S) -> S:
|
||||
96 | # error: [invalid-super-argument]
|
||||
97 | # revealed: Unknown
|
||||
98 | reveal_type(super())
|
||||
99 | return self
|
||||
100 |
|
||||
101 | def method10[S: Callable[..., str]](self: S, other: S) -> S:
|
||||
102 | # error: [invalid-super-argument]
|
||||
103 | # revealed: Unknown
|
||||
104 | reveal_type(super())
|
||||
105 | return self
|
||||
106 |
|
||||
107 | type Alias = Bar
|
||||
108 |
|
||||
109 | class Bar:
|
||||
110 | def method(self: Alias):
|
||||
111 | # revealed: <super: <class 'Bar'>, Bar>
|
||||
112 | reveal_type(super())
|
||||
113 |
|
||||
114 | def pls_dont_call_me(self: Never):
|
||||
115 | # revealed: <super: <class 'Bar'>, Unknown>
|
||||
116 | reveal_type(super())
|
||||
117 |
|
||||
118 | def only_call_me_on_callable_subclasses(self: Intersection[Bar, Callable[..., object]]):
|
||||
119 | # revealed: <super: <class 'Bar'>, Bar>
|
||||
120 | reveal_type(super())
|
||||
121 |
|
||||
122 | class P(Protocol):
|
||||
123 | def method(self: P):
|
||||
124 | # revealed: <super: <class 'P'>, P>
|
||||
125 | reveal_type(super())
|
||||
126 |
|
||||
127 | class E(enum.Enum):
|
||||
128 | X = 1
|
||||
129 |
|
||||
130 | def method(self: E):
|
||||
131 | match self:
|
||||
132 | case E.X:
|
||||
133 | # revealed: <super: <class 'E'>, E>
|
||||
134 | reveal_type(super())
|
||||
```
|
||||
|
||||
# Diagnostics
|
||||
|
||||
```
|
||||
error[invalid-super-argument]: `S@method7` is not an instance or subclass of `<class 'Foo'>` in `super(<class 'Foo'>, S@method7)` call
|
||||
--> src/mdtest_snippet.py:86:21
|
||||
|
|
||||
84 | # error: [invalid-super-argument]
|
||||
85 | # revealed: Unknown
|
||||
86 | reveal_type(super())
|
||||
| ^^^^^^^
|
||||
87 | return self
|
||||
|
|
||||
info: Type variable `S` has `object` as its implicit upper bound
|
||||
info: `object` is not an instance or subclass of `<class 'Foo'>`
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-super-argument]: `S@method8` is not an instance or subclass of `<class 'Foo'>` in `super(<class 'Foo'>, S@method8)` call
|
||||
--> src/mdtest_snippet.py:92:21
|
||||
|
|
||||
90 | # error: [invalid-super-argument]
|
||||
91 | # revealed: Unknown
|
||||
92 | reveal_type(super())
|
||||
| ^^^^^^^
|
||||
93 | return self
|
||||
|
|
||||
info: Type variable `S` has upper bound `int`
|
||||
info: `int` is not an instance or subclass of `<class 'Foo'>`
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-super-argument]: `S@method9` is not an instance or subclass of `<class 'Foo'>` in `super(<class 'Foo'>, S@method9)` call
|
||||
--> src/mdtest_snippet.py:98:21
|
||||
|
|
||||
96 | # error: [invalid-super-argument]
|
||||
97 | # revealed: Unknown
|
||||
98 | reveal_type(super())
|
||||
| ^^^^^^^
|
||||
99 | return self
|
||||
|
|
||||
info: Type variable `S` has constraints `int, str`
|
||||
info: `int | str` is not an instance or subclass of `<class 'Foo'>`
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-super-argument]: `S@method10` is a type variable with an abstract/structural type as its bounds or constraints, in `super(<class 'Foo'>, S@method10)` call
|
||||
--> src/mdtest_snippet.py:104:21
|
||||
|
|
||||
102 | # error: [invalid-super-argument]
|
||||
103 | # revealed: Unknown
|
||||
104 | reveal_type(super())
|
||||
| ^^^^^^^
|
||||
105 | return self
|
||||
|
|
||||
info: Type variable `S` has upper bound `(...) -> str`
|
||||
info: rule `invalid-super-argument` is enabled by default
|
||||
|
||||
```
|
||||
Loading…
Add table
Add a link
Reference in a new issue