Skip to content

Commit 7ba1cc8

Browse files
Gobot1234pradeep90JelleZijlstra
authored
bpo-46534: Implement PEP 673 Self in typing.py (GH-30924)
Co-authored-by: Pradeep Kumar Srinivasan <[email protected]> Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent 39dec1c commit 7ba1cc8

File tree

5 files changed

+116
-2
lines changed

5 files changed

+116
-2
lines changed

Doc/library/typing.rst

+47
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ annotations. These include:
6868
*Introducing* :data:`TypeAlias`
6969
* :pep:`647`: User-Defined Type Guards
7070
*Introducing* :data:`TypeGuard`
71+
* :pep:`673`: Self type
72+
*Introducing* :data:`Self`
7173

7274
.. _type-aliases:
7375

@@ -585,6 +587,51 @@ These can be used as types in annotations and do not support ``[]``.
585587
.. versionadded:: 3.5.4
586588
.. versionadded:: 3.6.2
587589

590+
.. data:: Self
591+
592+
Special type to represent the current enclosed class.
593+
For example::
594+
595+
from typing import Self
596+
597+
class Foo:
598+
def returns_self(self) -> Self:
599+
...
600+
return self
601+
602+
603+
This annotation is semantically equivalent to the following,
604+
albeit in a more succinct fashion::
605+
606+
from typing import TypeVar
607+
608+
Self = TypeVar("Self", bound="Foo")
609+
610+
class Foo:
611+
def returns_self(self: Self) -> Self:
612+
...
613+
return self
614+
615+
In general if something currently follows the pattern of::
616+
617+
class Foo:
618+
def return_self(self) -> "Foo":
619+
...
620+
return self
621+
622+
You should use use :data:`Self` as calls to ``SubclassOfFoo.returns_self`` would have
623+
``Foo`` as the return type and not ``SubclassOfFoo``.
624+
625+
Other common use cases include:
626+
627+
- :class:`classmethod`\s that are used as alternative constructors and return instances
628+
of the ``cls`` parameter.
629+
- Annotating an :meth:`object.__enter__` method which returns self.
630+
631+
For more information, see :pep:`673`.
632+
633+
.. versionadded:: 3.11
634+
588635
.. data:: TypeAlias
589636

590637
Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.

Doc/whatsnew/3.11.rst

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ Summary -- Release highlights
6767
6868
PEP-654: Exception Groups and ``except*``.
6969
(Contributed by Irit Katriel in :issue:`45292`.)
70+
PEP-673: ``Self`` Type.
71+
(Contributed by James Hilton-Balfe and Pradeep Kumar in :issue:`30924`.)
7072

7173
New Features
7274
============

Lib/test/test_typing.py

+42-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from typing import IO, TextIO, BinaryIO
2828
from typing import Pattern, Match
2929
from typing import Annotated, ForwardRef
30+
from typing import Self
3031
from typing import TypeAlias
3132
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
3233
from typing import TypeGuard
@@ -157,8 +158,48 @@ def test_cannot_instantiate(self):
157158
type(NoReturn)()
158159

159160

160-
class TypeVarTests(BaseTestCase):
161+
class SelfTests(BaseTestCase):
162+
def test_basics(self):
163+
class Foo:
164+
def bar(self) -> Self: ...
165+
166+
self.assertEqual(gth(Foo.bar), {'return': Self})
167+
168+
def test_repr(self):
169+
self.assertEqual(repr(Self), 'typing.Self')
170+
171+
def test_cannot_subscript(self):
172+
with self.assertRaises(TypeError):
173+
Self[int]
174+
175+
def test_cannot_subclass(self):
176+
with self.assertRaises(TypeError):
177+
class C(type(Self)):
178+
pass
179+
180+
def test_cannot_init(self):
181+
with self.assertRaises(TypeError):
182+
Self()
183+
with self.assertRaises(TypeError):
184+
type(Self)()
185+
186+
def test_no_isinstance(self):
187+
with self.assertRaises(TypeError):
188+
isinstance(1, Self)
189+
with self.assertRaises(TypeError):
190+
issubclass(int, Self)
161191

192+
def test_alias(self):
193+
# TypeAliases are not actually part of the spec
194+
alias_1 = Tuple[Self, Self]
195+
alias_2 = List[Self]
196+
alias_3 = ClassVar[Self]
197+
self.assertEqual(get_args(alias_1), (Self, Self))
198+
self.assertEqual(get_args(alias_2), (Self,))
199+
self.assertEqual(get_args(alias_3), (Self,))
200+
201+
202+
class TypeVarTests(BaseTestCase):
162203
def test_basic_plain(self):
163204
T = TypeVar('T')
164205
# T equals itself.

Lib/typing.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ def _idfunc(_, x):
132132
'ParamSpecKwargs',
133133
'reveal_type',
134134
'runtime_checkable',
135+
'Self',
135136
'Text',
136137
'TYPE_CHECKING',
137138
'TypeAlias',
@@ -174,7 +175,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
174175
if (isinstance(arg, _GenericAlias) and
175176
arg.__origin__ in invalid_generic_forms):
176177
raise TypeError(f"{arg} is not valid as type argument")
177-
if arg in (Any, NoReturn, ClassVar, Final, TypeAlias):
178+
if arg in (Any, NoReturn, Self, ClassVar, Final, TypeAlias):
178179
return arg
179180
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
180181
raise TypeError(f"Plain {arg} is not valid as type argument")
@@ -445,6 +446,27 @@ def stop() -> NoReturn:
445446
"""
446447
raise TypeError(f"{self} is not subscriptable")
447448

449+
450+
@_SpecialForm
451+
def Self(self, parameters):
452+
"""Used to spell the type of "self" in classes.
453+
454+
Example::
455+
456+
from typing import Self
457+
458+
class Foo:
459+
def returns_self(self) -> Self:
460+
...
461+
return self
462+
463+
This is especially useful for:
464+
- classmethods that are used as alternative constructors
465+
- annotating an `__enter__` method which returns self
466+
"""
467+
raise TypeError(f"{self} is not subscriptable")
468+
469+
448470
@_SpecialForm
449471
def ClassVar(self, parameters):
450472
"""Special type construct to mark class variables.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Implement :pep:`673` :class:`typing.Self`.
2+
Patch by James Hilton-Balfe.

0 commit comments

Comments
 (0)