← Back to index

generics_self_usage.py

True Positive
False Positive
False Negative
Optional (detected)
Warning or Info
TP: 6
FP: 0
FN: 5
Optional: 0 / 0
1"""
2Tests for valid and invalid usage of the typing.Self type.
3"""
4
5# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#valid-locations-for-self
6
7from typing import Any, Callable, Generic, Self, TypeAlias, TypeVar
8
9class ReturnsSelf:
10 def foo(self) -> Self: # Accepted
11 return self
13 @classmethod
14 def bar(cls) -> Self: # Accepted
15 return cls(1)
17 def __new__(cls, value: int) -> Self: # Accepted
18 return cls(1)
20 def explicitly_use_self(self: Self) -> Self: # Accepted
21 return self
23 # Accepted (Self can be nested within other types)
24 def returns_list(self) -> list[Self]:
25 return []
27 # Accepted (Self can be nested within other types)
28 @classmethod
29 def return_cls(cls) -> type[Self]:
30 return cls
32class Child(ReturnsSelf):
33 # Accepted (we can override a method that uses Self annotations)
34 def foo(self) -> Self:
35 return self
37class TakesSelf:
38 def foo(self, other: Self) -> bool: # Accepted
39 return True
41class Recursive:
42 # Accepted (treated as an @property returning ``Self | None``)
43 next: Self | None
45class CallableAttribute:
46 def foo(self) -> int:
47 return 0
49 # Accepted (treated as an @property returning the Callable type)
50 bar: Callable[[Self], int] = foo
52class HasNestedFunction:
53 x: int = 42
55 def foo(self) -> None:
57 # Accepted (Self is bound to HasNestedFunction).
58 def nested(z: int, inner_self: Self) -> Self:
59 print(z)
60 print(inner_self.x)
61 return inner_self
63 nested(42, self) # OK
66class Outer:
67 class Inner:
68 def foo(self) -> Self: # Accepted (Self is bound to Inner)
69 return self
72# This should generate an error.
73def foo(bar: Self) -> Self: ... # E: not within a class
[invalid-type-form] Variable of type `<special-form 'typing.Self'>` is not allowed in a type expression [invalid-type-form] Variable of type `<special-form 'typing.Self'>` is not allowed in a type expression
75# This should generate an error.
76bar: Self # E: not within a class
[invalid-type-form] Variable of type `<special-form 'typing.Self'>` is not allowed in a type expression
78TFoo2 = TypeVar("TFoo2", bound="Foo2")
80class Foo2:
81 # Rejected (Self is treated as unknown).
82 def has_existing_self_annotation(self: TFoo2) -> Self: # E
Expected a ty diagnostic for this line
83 raise NotImplementedError
85class Foo3:
86 def return_concrete_type(self) -> Self:
87 return Foo3() # E (see FooChild below for rationale)
[invalid-return-type] Return type does not match returned value: expected `Self@return_concrete_type`, found `Foo3`
89class Foo3Child(Foo3):
90 child_value: int = 42
92 def child_method(self) -> None:
93 y = self.return_concrete_type()
94 y.child_value
96T = TypeVar("T")
98class Bar(Generic[T]):
99 def bar(self) -> T:
100 raise NotImplementedError
102# This should generate an error.
103class Baz(Bar[Self]): ... # E
[invalid-type-form] Variable of type `<special-form 'typing.Self'>` is not allowed in a type expression
105class Baz2(Self): ... # E
[invalid-base] Invalid class base with type `<special-form 'typing.Self'>`
107# This should generate an error.
108TupleSelf: TypeAlias = tuple[Self] # E
[invalid-type-form] Variable of type `<special-form 'typing.Self'>` is not allowed in a type expression
110class Base:
111 @staticmethod
112 # This should generate an error.
113 def make() -> Self: # E
Expected a ty diagnostic for this line
114 raise NotImplementedError
116 @staticmethod
117 # This should generate an error.
118 def return_parameter(foo: Self) -> Self: # E
Expected a ty diagnostic for this line
119 raise NotImplementedError
121class MyMetaclass(type):
122 # This should generate an error.
123 def __new__(cls, *args: Any) -> Self: # E
Expected a ty diagnostic for this line
124 raise NotImplementedError
126 # This should generate an error.
127 def __mul__(cls, count: int) -> list[Self]: # E
Expected a ty diagnostic for this line
128 raise NotImplementedError