mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
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
5 changed files with 116 additions and 2 deletions
|
@ -68,6 +68,8 @@ annotations. These include:
|
||||||
*Introducing* :data:`TypeAlias`
|
*Introducing* :data:`TypeAlias`
|
||||||
* :pep:`647`: User-Defined Type Guards
|
* :pep:`647`: User-Defined Type Guards
|
||||||
*Introducing* :data:`TypeGuard`
|
*Introducing* :data:`TypeGuard`
|
||||||
|
* :pep:`673`: Self type
|
||||||
|
*Introducing* :data:`Self`
|
||||||
|
|
||||||
.. _type-aliases:
|
.. _type-aliases:
|
||||||
|
|
||||||
|
@ -585,6 +587,51 @@ These can be used as types in annotations and do not support ``[]``.
|
||||||
.. versionadded:: 3.5.4
|
.. versionadded:: 3.5.4
|
||||||
.. versionadded:: 3.6.2
|
.. 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
|
.. data:: TypeAlias
|
||||||
|
|
||||||
Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
|
Special annotation for explicitly declaring a :ref:`type alias <type-aliases>`.
|
||||||
|
|
|
@ -67,6 +67,8 @@ Summary -- Release highlights
|
||||||
|
|
||||||
PEP-654: Exception Groups and ``except*``.
|
PEP-654: Exception Groups and ``except*``.
|
||||||
(Contributed by Irit Katriel in :issue:`45292`.)
|
(Contributed by Irit Katriel in :issue:`45292`.)
|
||||||
|
PEP-673: ``Self`` Type.
|
||||||
|
(Contributed by James Hilton-Balfe and Pradeep Kumar in :issue:`30924`.)
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
============
|
============
|
||||||
|
|
|
@ -27,6 +27,7 @@ from typing import NamedTuple, TypedDict
|
||||||
from typing import IO, TextIO, BinaryIO
|
from typing import IO, TextIO, BinaryIO
|
||||||
from typing import Pattern, Match
|
from typing import Pattern, Match
|
||||||
from typing import Annotated, ForwardRef
|
from typing import Annotated, ForwardRef
|
||||||
|
from typing import Self
|
||||||
from typing import TypeAlias
|
from typing import TypeAlias
|
||||||
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
|
from typing import ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs
|
||||||
from typing import TypeGuard
|
from typing import TypeGuard
|
||||||
|
@ -157,8 +158,48 @@ class NoReturnTests(BaseTestCase):
|
||||||
type(NoReturn)()
|
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):
|
def test_basic_plain(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
# T equals itself.
|
# T equals itself.
|
||||||
|
|
|
@ -132,6 +132,7 @@ __all__ = [
|
||||||
'ParamSpecKwargs',
|
'ParamSpecKwargs',
|
||||||
'reveal_type',
|
'reveal_type',
|
||||||
'runtime_checkable',
|
'runtime_checkable',
|
||||||
|
'Self',
|
||||||
'Text',
|
'Text',
|
||||||
'TYPE_CHECKING',
|
'TYPE_CHECKING',
|
||||||
'TypeAlias',
|
'TypeAlias',
|
||||||
|
@ -174,7 +175,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
|
||||||
if (isinstance(arg, _GenericAlias) and
|
if (isinstance(arg, _GenericAlias) and
|
||||||
arg.__origin__ in invalid_generic_forms):
|
arg.__origin__ in invalid_generic_forms):
|
||||||
raise TypeError(f"{arg} is not valid as type argument")
|
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
|
return arg
|
||||||
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
|
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
|
||||||
raise TypeError(f"Plain {arg} is not valid as type argument")
|
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")
|
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
|
@_SpecialForm
|
||||||
def ClassVar(self, parameters):
|
def ClassVar(self, parameters):
|
||||||
"""Special type construct to mark class variables.
|
"""Special type construct to mark class variables.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Implement :pep:`673` :class:`typing.Self`.
|
||||||
|
Patch by James Hilton-Balfe.
|
Loading…
Add table
Add a link
Reference in a new issue