From 936ebbea9deac86b5aa6ddc681eb60987578f624 Mon Sep 17 00:00:00 2001 From: Chris Wesseling Date: Fri, 26 Nov 2021 16:44:16 +0100 Subject: [PATCH 1/4] Vendor typing._SpecialForm to fool typing._type_check Adds a local copy of _SpecialForm in our namespace, so typing._type_check won't raise TypeError. (#964) --- .../src/test_typing_extensions.py | 6 ++ typing_extensions/src/typing_extensions.py | 64 +++++++++++++------ 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index 2fc5b3f2..731f9731 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -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): diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 15fec259..13bf7df7 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -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:: @@ -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. From 8f8c7930d33e7c966beea308b97669376c893348 Mon Sep 17 00:00:00 2001 From: Chris Wesseling Date: Fri, 26 Nov 2021 21:04:41 +0100 Subject: [PATCH 2/4] Fix module name in __repr__ Co-authored-by: James Hilton-Balfe <50501825+Gobot1234@users.noreply.github.com> --- typing_extensions/src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 13bf7df7..8a909960 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2068,7 +2068,7 @@ def __mro_entries__(self, bases): raise TypeError(f"Cannot subclass {self!r}") def __repr__(self): - return 'typing.' + self._name + return f'typing_extensions.{self._name}' def __reduce__(self): return self._name From 4c221928281d78050dbdbbfba9daa4f3f07ee44b Mon Sep 17 00:00:00 2001 From: Chris Wesseling Date: Fri, 26 Nov 2021 21:33:48 +0100 Subject: [PATCH 3/4] Fix NameError --- typing_extensions/src/typing_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 8a909960..c7b59f67 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2077,10 +2077,10 @@ def __call__(self, *args, **kwds): raise TypeError(f"Cannot instantiate {self!r}") def __or__(self, other): - return Union[self, other] + return typing.Union[self, other] def __ror__(self, other): - return Union[other, self] + return typing.Union[other, self] def __instancecheck__(self, obj): raise TypeError(f"{self} cannot be used with isinstance()") From e8b1c13dea7383f60034cac645d9bdb852b171a4 Mon Sep 17 00:00:00 2001 From: Chris Wesseling Date: Sun, 28 Nov 2021 17:56:04 +0100 Subject: [PATCH 4/4] Simplify flow --- typing_extensions/src/typing_extensions.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index c7b59f67..9f1c7aa3 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2048,7 +2048,9 @@ def __eq__(self, other): TypeGuard = _TypeGuard(_root=True) -if not hasattr(typing, "Self") and sys.version_info[:2] >= (3, 7): +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') @@ -2092,15 +2094,7 @@ def __subclasscheck__(self, cls): 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(_SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - @_SelfForm + @_SpecialForm def Self(self, params): """Used to spell the type of "self" in classes.