[Python-checkins] bpo-46534: Implement PEP 673 Self in typing.py (GH-30924)

gvanrossum webhook-mailer at python.org
Mon Feb 7 15:48:00 EST 2022


https://github.com/python/cpython/commit/7ba1cc8049fbcb94ac039ab02522f78177130588
commit: 7ba1cc8049fbcb94ac039ab02522f78177130588
branch: main
author: James Hilton-Balfe <gobot1234yt at gmail.com>
committer: gvanrossum <gvanrossum at gmail.com>
date: 2022-02-07T12:47:48-08:00
summary:

bpo-46534: Implement PEP 673 Self in typing.py (GH-30924)

Co-authored-by: Pradeep Kumar Srinivasan <gohanpra at gmail.com>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra at gmail.com>

files:
A Misc/NEWS.d/next/Library/2022-01-26-18-06-08.bpo-46534.vhzUM4.rst
M Doc/library/typing.rst
M Doc/whatsnew/3.11.rst
M Lib/test/test_typing.py
M Lib/typing.py

diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index 9007c0daf59a4..8c1c34e90507f 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -68,6 +68,8 @@ annotations. These include:
      *Introducing* :data:`TypeAlias`
 * :pep:`647`: User-Defined Type Guards
      *Introducing* :data:`TypeGuard`
+* :pep:`673`: Self type
+    *Introducing* :data:`Self`
 
 .. _type-aliases:
 
@@ -585,6 +587,51 @@ These can be used as types in annotations and do not support ``[]``.
    .. versionadded:: 3.5.4
    .. versionadded:: 3.6.2
 
+.. data:: Self
+
+   Special type to represent the current enclosed class.
+   For example::
+
+      from typing import Self
+
+      class Foo:
+         def returns_self(self) -> Self:
+            ...
+            return self
+
+
+   This annotation is semantically equivalent to the following,
+   albeit in a more succinct fashion::
+
+      from typing import TypeVar
+
+      Self = TypeVar("Self", bound="Foo")
+
+      class Foo:
+         def returns_self(self: Self) -> Self:
+            ...
+            return self
+
+   In general if something currently follows the pattern of::
+
+      class Foo:
+         def return_self(self) -> "Foo":
+            ...
+            return self
+
+   You should use use :data:`Self` as calls to ``SubclassOfFoo.returns_self`` would have
+   ``Foo`` as the return type and not ``SubclassOfFoo``.
+
+   Other common use cases include:
+
+      - :class:`classmethod`\s that are used as alternative constructors and return instances
+        of the ``cls`` parameter.
+      - Annotating an :meth:`object.__enter__` method which returns self.
+
+   For more information, see :pep:`673`.
+
+   .. versionadded:: 3.11
+
 .. data:: TypeAlias
 
    Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst
index c3c2c67deaf33..c1f267a4126a6 100644
--- a/Doc/whatsnew/3.11.rst
+++ b/Doc/whatsnew/3.11.rst
@@ -67,6 +67,8 @@ Summary -- Release highlights
 
 PEP-654: Exception Groups and ``except*``.
 (Contributed by Irit Katriel in :issue:`45292`.)
+PEP-673: ``Self`` Type.
+(Contributed by James Hilton-Balfe and Pradeep Kumar in :issue:`30924`.)
 
 New Features
 ============
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 9eab4618e2b01..a37bb43296929 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -27,6 +27,7 @@
 from typing import IO, TextIO, BinaryIO
 from typing import Pattern, Match
 from typing import Annotated, ForwardRef
+from typing import Self
 from typing import TypeAlias
 from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
 from typing import TypeGuard
@@ -157,8 +158,48 @@ def test_cannot_instantiate(self):
             type(NoReturn)()
 
 
-class TypeVarTests(BaseTestCase):
+class SelfTests(BaseTestCase):
+    def test_basics(self):
+        class Foo:
+            def bar(self) -> Self: ...
+
+        self.assertEqual(gth(Foo.bar), {'return': Self})
+
+    def test_repr(self):
+        self.assertEqual(repr(Self), 'typing.Self')
+
+    def test_cannot_subscript(self):
+        with self.assertRaises(TypeError):
+            Self[int]
+
+    def test_cannot_subclass(self):
+        with self.assertRaises(TypeError):
+            class C(type(Self)):
+                pass
+
+    def test_cannot_init(self):
+        with self.assertRaises(TypeError):
+            Self()
+        with self.assertRaises(TypeError):
+            type(Self)()
+
+    def test_no_isinstance(self):
+        with self.assertRaises(TypeError):
+            isinstance(1, Self)
+        with self.assertRaises(TypeError):
+            issubclass(int, Self)
 
+    def test_alias(self):
+        # TypeAliases are not actually part of the spec
+        alias_1 = Tuple[Self, Self]
+        alias_2 = List[Self]
+        alias_3 = ClassVar[Self]
+        self.assertEqual(get_args(alias_1), (Self, Self))
+        self.assertEqual(get_args(alias_2), (Self,))
+        self.assertEqual(get_args(alias_3), (Self,))
+
+
+class TypeVarTests(BaseTestCase):
     def test_basic_plain(self):
         T = TypeVar('T')
         # T equals itself.
diff --git a/Lib/typing.py b/Lib/typing.py
index f733efea09cb3..d1d513062394c 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -132,6 +132,7 @@ def _idfunc(_, x):
     'ParamSpecKwargs',
     'reveal_type',
     'runtime_checkable',
+    'Self',
     'Text',
     'TYPE_CHECKING',
     'TypeAlias',
@@ -174,7 +175,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
     if (isinstance(arg, _GenericAlias) and
             arg.__origin__ in invalid_generic_forms):
         raise TypeError(f"{arg} is not valid as type argument")
-    if arg in (Any, NoReturn, ClassVar, Final, TypeAlias):
+    if arg in (Any, NoReturn, Self, ClassVar, Final, TypeAlias):
         return arg
     if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
         raise TypeError(f"Plain {arg} is not valid as type argument")
@@ -445,6 +446,27 @@ def stop() -> NoReturn:
     """
     raise TypeError(f"{self} is not subscriptable")
 
+
+ at _SpecialForm
+def Self(self, parameters):
+    """Used to spell the type of "self" in classes.
+
+    Example::
+
+      from typing import Self
+
+      class Foo:
+          def returns_self(self) -> Self:
+              ...
+              return self
+
+    This is especially useful for:
+        - classmethods that are used as alternative constructors
+        - annotating an `__enter__` method which returns self
+    """
+    raise TypeError(f"{self} is not subscriptable")
+
+
 @_SpecialForm
 def ClassVar(self, parameters):
     """Special type construct to mark class variables.
diff --git a/Misc/NEWS.d/next/Library/2022-01-26-18-06-08.bpo-46534.vhzUM4.rst b/Misc/NEWS.d/next/Library/2022-01-26-18-06-08.bpo-46534.vhzUM4.rst
new file mode 100644
index 0000000000000..35a70aae17004
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-01-26-18-06-08.bpo-46534.vhzUM4.rst
@@ -0,0 +1,2 @@
+Implement :pep:`673` :class:`typing.Self`.
+Patch by James Hilton-Balfe.



More information about the Python-checkins mailing list