Skip to content

Commit 60aa1e2

Browse files
authored
Add PEP 673 Self type (#933)
1 parent 8fd49b5 commit 60aa1e2

File tree

3 files changed

+113
-1
lines changed

3 files changed

+113
-1
lines changed

typing_extensions/CHANGELOG

+4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ Dropped support for Python versions 3.5 and older.
88
Simplified backports for Python 3.6.0 and newer.
99
Patch by Adam Turner (@AA-Turner).
1010

11+
## Added in version 4.0.0
12+
13+
- Runtime support for PEP 673 and `typing_extensions.Self`.
14+
1115
## Removed in version 4.0.0
1216

1317
The following non-exported but non-private names have been removed as they are

typing_extensions/src_py3/test_typing_extensions.py

+37-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from typing import Generic, NamedTuple
1717
from typing import no_type_check
1818
import typing_extensions
19-
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict
19+
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self
2020
from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard
2121
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager
2222
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload
@@ -2075,6 +2075,42 @@ def test_no_isinstance(self):
20752075
issubclass(int, TypeGuard)
20762076

20772077

2078+
2079+
class SelfTests(BaseTestCase):
2080+
def test_basics(self):
2081+
class Foo:
2082+
def bar(self) -> Self: ...
2083+
2084+
self.assertEqual(gth(Foo.bar), {'return': Self})
2085+
2086+
def test_repr(self):
2087+
if hasattr(typing, 'Self'):
2088+
mod_name = 'typing'
2089+
else:
2090+
mod_name = 'typing_extensions'
2091+
self.assertEqual(repr(Self), '{}.Self'.format(mod_name))
2092+
2093+
def test_cannot_subscript(self):
2094+
with self.assertRaises(TypeError):
2095+
Self[int]
2096+
2097+
def test_cannot_subclass(self):
2098+
with self.assertRaises(TypeError):
2099+
class C(type(Self)):
2100+
pass
2101+
2102+
def test_cannot_init(self):
2103+
with self.assertRaises(TypeError):
2104+
Self()
2105+
with self.assertRaises(TypeError):
2106+
type(Self)()
2107+
2108+
def test_no_isinstance(self):
2109+
with self.assertRaises(TypeError):
2110+
isinstance(1, Self)
2111+
with self.assertRaises(TypeError):
2112+
issubclass(int, Self)
2113+
20782114
class AllTests(BaseTestCase):
20792115

20802116
def test_typing_extensions_includes_standard(self):

typing_extensions/src_py3/typing_extensions.py

+72
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def _check_generic(cls, parameters):
4545
'Concatenate',
4646
'Final',
4747
'ParamSpec',
48+
'Self',
4849
'Type',
4950

5051
# ABCs (from collections.abc).
@@ -2046,3 +2047,74 @@ def __eq__(self, other):
20462047
return self is other
20472048

20482049
TypeGuard = _TypeGuard(_root=True)
2050+
2051+
2052+
if hasattr(typing, "Self"):
2053+
Self = typing.Self
2054+
2055+
elif sys.version_info[:2] >= (3, 9):
2056+
class _SelfForm(typing._SpecialForm, _root=True):
2057+
def __repr__(self):
2058+
return 'typing_extensions.' + self._name
2059+
2060+
@_SelfForm
2061+
def Self(self, params):
2062+
"""Used to spell the type of "self" in classes.
2063+
2064+
Example::
2065+
2066+
from typing import Self
2067+
2068+
class ReturnsSelf:
2069+
def parse(self, data: bytes) -> Self:
2070+
...
2071+
return self
2072+
2073+
"""
2074+
2075+
raise TypeError(f"{self} is not subscriptable")
2076+
2077+
elif sys.version_info[:2] >= (3, 7):
2078+
class _SelfForm(typing._SpecialForm, _root=True):
2079+
def __repr__(self):
2080+
return 'typing_extensions.' + self._name
2081+
2082+
Self = _SelfForm(
2083+
"Self",
2084+
doc="""Used to spell the type of "self" in classes.
2085+
2086+
Example::
2087+
2088+
from typing import Self
2089+
2090+
class ReturnsSelf:
2091+
def parse(self, data: bytes) -> Self:
2092+
...
2093+
return self
2094+
2095+
"""
2096+
)
2097+
else:
2098+
class _Self(typing._FinalTypingBase, _root=True):
2099+
"""Used to spell the type of "self" in classes.
2100+
2101+
Example::
2102+
2103+
from typing import Self
2104+
2105+
class ReturnsSelf:
2106+
def parse(self, data: bytes) -> Self:
2107+
...
2108+
return self
2109+
2110+
"""
2111+
2112+
__slots__ = ()
2113+
2114+
def __instancecheck__(self, obj):
2115+
raise TypeError(f"{self} cannot be used with isinstance().")
2116+
2117+
def __subclasscheck__(self, cls):
2118+
raise TypeError(f"{self} cannot be used with issubclass().")
2119+
2120+
Self = _Self(_root=True)

0 commit comments

Comments
 (0)