Skip to content

Commit 0c8e55f

Browse files
Vendor typing._SpecialForm to fool typing._type_check (#966)
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]>
1 parent 273fe26 commit 0c8e55f

File tree

2 files changed

+45
-23
lines changed

2 files changed

+45
-23
lines changed

typing_extensions/src/test_typing_extensions.py

+6
Original file line numberDiff line numberDiff line change
@@ -2199,6 +2199,12 @@ def test_no_isinstance(self):
21992199
with self.assertRaises(TypeError):
22002200
issubclass(int, Self)
22012201

2202+
def test_alias(self):
2203+
TupleSelf = Tuple[Self, Self]
2204+
class Alias:
2205+
def return_tuple(self) -> TupleSelf:
2206+
return (self, self)
2207+
22022208
class AllTests(BaseTestCase):
22032209

22042210
def test_typing_extensions_includes_standard(self):

typing_extensions/src/typing_extensions.py

+39-23
Original file line numberDiff line numberDiff line change
@@ -2048,40 +2048,55 @@ def __eq__(self, other):
20482048

20492049
TypeGuard = _TypeGuard(_root=True)
20502050

2051-
20522051
if hasattr(typing, "Self"):
20532052
Self = typing.Self
2053+
elif sys.version_info[:2] >= (3, 7):
2054+
# Vendored from cpython typing._SpecialFrom
2055+
class _SpecialForm(typing._Final, _root=True):
2056+
__slots__ = ('_name', '__doc__', '_getitem')
2057+
2058+
def __init__(self, getitem):
2059+
self._getitem = getitem
2060+
self._name = getitem.__name__
2061+
self.__doc__ = getitem.__doc__
2062+
2063+
def __getattr__(self, item):
2064+
if item in {'__name__', '__qualname__'}:
2065+
return self._name
2066+
2067+
raise AttributeError(item)
2068+
2069+
def __mro_entries__(self, bases):
2070+
raise TypeError(f"Cannot subclass {self!r}")
20542071

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

2060-
@_SelfForm
2061-
def Self(self, params):
2062-
"""Used to spell the type of "self" in classes.
2075+
def __reduce__(self):
2076+
return self._name
20632077

2064-
Example::
2078+
def __call__(self, *args, **kwds):
2079+
raise TypeError(f"Cannot instantiate {self!r}")
20652080

2066-
from typing import Self
2081+
def __or__(self, other):
2082+
return typing.Union[self, other]
20672083

2068-
class ReturnsSelf:
2069-
def parse(self, data: bytes) -> Self:
2070-
...
2071-
return self
2084+
def __ror__(self, other):
2085+
return typing.Union[other, self]
20722086

2073-
"""
2087+
def __instancecheck__(self, obj):
2088+
raise TypeError(f"{self} cannot be used with isinstance()")
20742089

2075-
raise TypeError(f"{self} is not subscriptable")
2090+
def __subclasscheck__(self, cls):
2091+
raise TypeError(f"{self} cannot be used with issubclass()")
20762092

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
2093+
@typing._tp_cache
2094+
def __getitem__(self, parameters):
2095+
return self._getitem(self, parameters)
20812096

2082-
Self = _SelfForm(
2083-
"Self",
2084-
doc="""Used to spell the type of "self" in classes.
2097+
@_SpecialForm
2098+
def Self(self, params):
2099+
"""Used to spell the type of "self" in classes.
20852100
20862101
Example::
20872102
@@ -2093,7 +2108,8 @@ def parse(self, data: bytes) -> Self:
20932108
return self
20942109
20952110
"""
2096-
)
2111+
2112+
raise TypeError(f"{self} is not subscriptable")
20972113
else:
20982114
class _Self(typing._FinalTypingBase, _root=True):
20992115
"""Used to spell the type of "self" in classes.

0 commit comments

Comments
 (0)