Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PEP 673 Self type #933

Merged
merged 14 commits into from Nov 12, 2021
4 changes: 4 additions & 0 deletions typing_extensions/CHANGELOG
Expand Up @@ -8,6 +8,10 @@ Dropped support for Python versions 3.5 and older.
Simplified backports for Python 3.6.0 and newer.
Patch by Adam Turner (@AA-Turner).

## Added in version 4.0.0

- Runtime support for PEP 673 and `typing_extensions.Self`.

## Removed in version 4.0.0

The following non-exported but non-private names have been removed as they are
Expand Down
38 changes: 37 additions & 1 deletion typing_extensions/src_py3/test_typing_extensions.py
Expand Up @@ -16,7 +16,7 @@
from typing import Generic, NamedTuple
from typing import no_type_check
import typing_extensions
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict
from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict, Self
from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard
from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager
from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload
Expand Down Expand Up @@ -2075,6 +2075,42 @@ def test_no_isinstance(self):
issubclass(int, TypeGuard)



class SelfTests(BaseTestCase):
def test_basics(self):
class Foo:
def bar(self) -> Self: ...

self.assertEqual(gth(Foo.bar), {'return': Self})

def test_repr(self):
if hasattr(typing, 'Self'):
mod_name = 'typing'
else:
mod_name = 'typing_extensions'
self.assertEqual(repr(Self), '{}.Self'.format(mod_name))

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)

class AllTests(BaseTestCase):

def test_typing_extensions_includes_standard(self):
Expand Down
72 changes: 72 additions & 0 deletions typing_extensions/src_py3/typing_extensions.py
Expand Up @@ -45,6 +45,7 @@ def _check_generic(cls, parameters):
'Concatenate',
'Final',
'ParamSpec',
'Self',
'Type',

# ABCs (from collections.abc).
Expand Down Expand Up @@ -2046,3 +2047,74 @@ def __eq__(self, other):
return self is other

TypeGuard = _TypeGuard(_root=True)


if hasattr(typing, "Self"):
Self = typing.Self

elif sys.version_info[:2] >= (3, 9):
class _SelfForm(typing._SpecialForm, _root=True):
def __repr__(self):
return 'typing_extensions.' + self._name

@_SelfForm
def Self(self, params):
"""Used to spell the type of "self" in classes.

Example::

from typing import Self

class ReturnsSelf:
def parse(self, data: bytes) -> Self:
...
return self

"""

raise TypeError(f"{self} is not subscriptable")

elif sys.version_info[:2] >= (3, 7):
class _SelfForm(typing._SpecialForm, _root=True):
def __repr__(self):
return 'typing_extensions.' + self._name

Self = _SelfForm(
"Self",
doc="""Used to spell the type of "self" in classes.

Example::

from typing import Self

class ReturnsSelf:
def parse(self, data: bytes) -> Self:
...
return self

"""
)
else:
class _Self(typing._FinalTypingBase, _root=True):
"""Used to spell the type of "self" in classes.

Example::

from typing import Self

class ReturnsSelf:
def parse(self, data: bytes) -> Self:
...
return self

"""

__slots__ = ()

def __instancecheck__(self, obj):
raise TypeError(f"{self} cannot be used with isinstance().")

def __subclasscheck__(self, cls):
raise TypeError(f"{self} cannot be used with issubclass().")

Self = _Self(_root=True)