mirror of https://github.com/python/cpython
bpo-46534: Implement PEP 673 Self in typing.py (GH-30924)
Co-authored-by: Pradeep Kumar Srinivasan <gohanpra@gmail.com> Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
parent
39dec1c09c
commit
7ba1cc8049
|
@ -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>`.
|
||||
|
|
|
@ -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
|
||||
============
|
||||
|
|
|
@ -27,6 +27,7 @@ from typing import NamedTuple, TypedDict
|
|||
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 @@ class NoReturnTests(BaseTestCase):
|
|||
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.
|
||||
|
|
|
@ -132,6 +132,7 @@ __all__ = [
|
|||
'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 NoReturn(self, parameters):
|
|||
"""
|
||||
raise TypeError(f"{self} is not subscriptable")
|
||||
|
||||
|
||||
@_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.
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Implement :pep:`673` :class:`typing.Self`.
|
||||
Patch by James Hilton-Balfe.
|
Loading…
Reference in New Issue