Skip to content

Commit

Permalink
Vendor typing._SpecialForm to fool typing._type_check (python#966)
Browse files Browse the repository at this point in the history
Adds a local copy of _SpecialForm in our namespace, so
typing._type_check won't raise TypeError. (#964)

Co-authored-by: James Hilton-Balfe <[email protected]>
  • Loading branch information
2 people authored and srittau committed Dec 2, 2021
1 parent 0d9f35e commit b5fc284
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 23 deletions.
6 changes: 6 additions & 0 deletions typing_extensions/src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2199,6 +2199,12 @@ def test_no_isinstance(self):
with self.assertRaises(TypeError):
issubclass(int, Self)

def test_alias(self):
TupleSelf = Tuple[Self, Self]
class Alias:
def return_tuple(self) -> TupleSelf:
return (self, self)

class AllTests(BaseTestCase):

def test_typing_extensions_includes_standard(self):
Expand Down
62 changes: 39 additions & 23 deletions typing_extensions/src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2048,40 +2048,55 @@ def __eq__(self, other):

TypeGuard = _TypeGuard(_root=True)


if hasattr(typing, "Self"):
Self = typing.Self
elif sys.version_info[:2] >= (3, 7):
# Vendored from cpython typing._SpecialFrom
class _SpecialForm(typing._Final, _root=True):
__slots__ = ('_name', '__doc__', '_getitem')

def __init__(self, getitem):
self._getitem = getitem
self._name = getitem.__name__
self.__doc__ = getitem.__doc__

def __getattr__(self, item):
if item in {'__name__', '__qualname__'}:
return self._name

raise AttributeError(item)

def __mro_entries__(self, bases):
raise TypeError(f"Cannot subclass {self!r}")

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

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

Example::
def __call__(self, *args, **kwds):
raise TypeError(f"Cannot instantiate {self!r}")

from typing import Self
def __or__(self, other):
return typing.Union[self, other]

class ReturnsSelf:
def parse(self, data: bytes) -> Self:
...
return self
def __ror__(self, other):
return typing.Union[other, self]

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

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

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

Self = _SelfForm(
"Self",
doc="""Used to spell the type of "self" in classes.
@_SpecialForm
def Self(self, params):
"""Used to spell the type of "self" in classes.
Example::
Expand All @@ -2093,7 +2108,8 @@ def parse(self, data: bytes) -> Self:
return self
"""
)

raise TypeError(f"{self} is not subscriptable")
else:
class _Self(typing._FinalTypingBase, _root=True):
"""Used to spell the type of "self" in classes.
Expand Down

0 comments on commit b5fc284

Please sign in to comment.