Skip to content

Commit

Permalink
Vendor typing._SpecialForm to fool typing._type_check
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)
  • Loading branch information
Chris Wesseling authored and Chris Wesseling committed Nov 26, 2021
1 parent 28b64f4 commit e07c767
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 21 deletions.
6 changes: 6 additions & 0 deletions typing_extensions/src_py3/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
64 changes: 43 additions & 21 deletions typing_extensions/src_py3/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -2048,40 +2048,61 @@ def __eq__(self, other):

TypeGuard = _TypeGuard(_root=True)

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

if hasattr(typing, "Self"):
Self = typing.Self
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 'typing.' + 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 Union[self, other]

class ReturnsSelf:
def parse(self, data: bytes) -> Self:
...
return self
def __ror__(self, other):
return 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()")

@typing._tp_cache
def __getitem__(self, parameters):
return self._getitem(self, parameters)

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

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

Self = _SelfForm(
"Self",
doc="""Used to spell the type of "self" in classes.
@_SelfForm
def Self(self, params):
"""Used to spell the type of "self" in classes.
Example::
Expand All @@ -2093,7 +2114,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 e07c767

Please sign in to comment.