From 297017ddeb7651f7c98ec205804ae558d5b6ff8e Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:04:43 +0100 Subject: [PATCH 01/32] Unconditional _collections_abc._check_methods import --- typing_extensions/src_py3/typing_extensions.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 184da0ad1..cfa5755f0 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1,3 +1,4 @@ +from _collections_abc import _check_methods as _check_methods_in_mro import abc import collections import contextlib @@ -95,23 +96,6 @@ def _generic_new(base_cls, cls, *args, **kwargs): _geqv = None _geqv_defined = False -if sys.version_info[:2] >= (3, 6): - import _collections_abc - _check_methods_in_mro = _collections_abc._check_methods -else: - def _check_methods_in_mro(C, *methods): - mro = C.__mro__ - for method in methods: - for B in mro: - if method in B.__dict__: - if B.__dict__[method] is None: - return NotImplemented - break - else: - return NotImplemented - return True - - # Please keep __all__ alphabetized within each category. __all__ = [ # Super-special typing primitives. From aef48a20f5b7bac38fee6d5550f81b01c9bf5d11 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:05:00 +0100 Subject: [PATCH 02/32] HAVE_PROTOCOLS is True on all supported versions --- typing_extensions/src_py3/typing_extensions.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index cfa5755f0..5f0f06c5d 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -149,11 +149,7 @@ def _generic_new(base_cls, cls, *args, **kwargs): if HAVE_ANNOTATED: __all__.append("Annotated") -# Protocols are hard to backport to the original version of typing 3.5.0 -HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0) - -if HAVE_PROTOCOLS: - __all__.extend(['Protocol', 'runtime', 'runtime_checkable']) +__all__.extend(['Protocol', 'runtime', 'runtime_checkable']) # TODO @@ -1136,7 +1132,7 @@ def _is_callable_members_only(cls): if hasattr(typing, 'Protocol'): Protocol = typing.Protocol -elif HAVE_PROTOCOLS and not PEP_560: +elif not PEP_560: def _no_init(self, *args, **kwargs): if type(self)._is_protocol: @@ -1571,7 +1567,7 @@ def _proto_hook(other): if hasattr(typing, 'runtime_checkable'): runtime_checkable = typing.runtime_checkable -elif HAVE_PROTOCOLS: +else: def runtime_checkable(cls): """Mark a protocol class as a runtime protocol, so that it can be used with isinstance() and issubclass(). Raise TypeError @@ -1587,14 +1583,13 @@ def runtime_checkable(cls): return cls -if HAVE_PROTOCOLS: - # Exists for backwards compatibility. - runtime = runtime_checkable +# Exists for backwards compatibility. +runtime = runtime_checkable if hasattr(typing, 'SupportsIndex'): SupportsIndex = typing.SupportsIndex -elif HAVE_PROTOCOLS: +else: @runtime_checkable class SupportsIndex(Protocol): __slots__ = () From 6c60077ebd93ccd380bc12fd12a6ae921d5be224 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:10:28 +0100 Subject: [PATCH 03/32] Remove sys.version_info gates on unsupported versions --- .../src_py3/typing_extensions.py | 68 ++++++------------- 1 file changed, 20 insertions(+), 48 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 5f0f06c5d..bd7791a16 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -762,12 +762,11 @@ def __subclasscheck__(self, subclass): between collections, typing, and typing_extensions on older versions of Python, see https://github.com/python/typing/issues/501. """ - if sys.version_info[:3] >= (3, 5, 3) or sys.version_info[:3] < (3, 5, 0): - if self.__origin__ is not None: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: - raise TypeError("Parameterized generics cannot be used with class " - "or instance checks") - return False + if self.__origin__ is not None: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: + raise TypeError("Parameterized generics cannot be used with class " + "or instance checks") + return False if not self.__extra__: return super().__subclasscheck__(subclass) res = self.__extra__.__subclasshook__(subclass) @@ -876,26 +875,24 @@ class AsyncContextManager(typing.Generic[T_co], __slots__ = () __all__.append('AsyncContextManager') -elif sys.version_info[:2] >= (3, 5): - exec(""" -class AsyncContextManager(typing.Generic[T_co]): - __slots__ = () +else: + class AsyncContextManager(typing.Generic[T_co]): + __slots__ = () - async def __aenter__(self): - return self + async def __aenter__(self): + return self - @abc.abstractmethod - async def __aexit__(self, exc_type, exc_value, traceback): - return None + @abc.abstractmethod + async def __aexit__(self, exc_type, exc_value, traceback): + return None - @classmethod - def __subclasshook__(cls, C): - if cls is AsyncContextManager: - return _check_methods_in_mro(C, "__aenter__", "__aexit__") - return NotImplemented + @classmethod + def __subclasshook__(cls, C): + if cls is AsyncContextManager: + return _check_methods_in_mro(C, "__aenter__", "__aexit__") + return NotImplemented -__all__.append('AsyncContextManager') -""") + __all__.append('AsyncContextManager') if hasattr(typing, 'DefaultDict'): @@ -954,26 +951,6 @@ def __new__(cls, *args, **kwds): if hasattr(typing, 'Counter'): Counter = typing.Counter -elif (3, 5, 0) <= sys.version_info[:3] <= (3, 5, 1): - assert _geqv_defined - _TInt = typing.TypeVar('_TInt') - - class _CounterMeta(typing.GenericMeta): - """Metaclass for Counter""" - def __getitem__(self, item): - return super().__getitem__((item, int)) - - class Counter(collections.Counter, - typing.Dict[T, int], - metaclass=_CounterMeta, - extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Counter): - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) elif _geqv_defined: class Counter(collections.Counter, @@ -2374,18 +2351,13 @@ class _ConcatenateGenericAlias(list): # Trick Generic into looking into this for __parameters__. if PEP_560: __class__ = typing._GenericAlias - elif sys.version_info[:3] == (3, 5, 2): - __class__ = typing.TypingMeta else: __class__ = typing._TypingBase # Flag in 3.8. _special = False # Attribute in 3.6 and earlier. - if sys.version_info[:3] == (3, 5, 2): - _gorg = typing.GenericMeta - else: - _gorg = typing.Generic + _gorg = typing.Generic def __init__(self, origin, args): super().__init__(args) From f32c280e6f9b3988002727179cca25e75ae18306 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:16:17 +0100 Subject: [PATCH 04/32] Remove TYPING_3_5_* gates --- .../src_py3/test_typing_extensions.py | 70 ++++++------------- 1 file changed, 23 insertions(+), 47 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 75bdb8ebb..6ea16114b 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -40,27 +40,18 @@ except ImportError: OLD_GENERICS = True -# We assume Python versions *below* 3.5.0 will have the most -# up-to-date version of the typing module installed. Since -# the typing module isn't a part of the standard library in older -# versions of Python, those users are likely to have a reasonably -# modern version of `typing` installed from PyPi. -TYPING_LATEST = sys.version_info[:3] < (3, 5, 0) - # Flags used to mark tests that only apply after a specific # version of the typing module. -TYPING_3_5_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 1) -TYPING_3_5_3 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 3) -TYPING_3_6_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 6, 1) -TYPING_3_10_0 = TYPING_LATEST or sys.version_info[:3] >= (3, 10, 0) -TYPING_3_11_0 = TYPING_LATEST or sys.version_info[:3] >= (3, 11, 0) +TYPING_3_6_1 = sys.version_info[:3] >= (3, 6, 1) +TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0) +TYPING_3_11_0 = sys.version_info[:3] >= (3, 11, 0) # For typing versions where issubclass(...) and # isinstance(...) checks are forbidden. # # See https://github.com/python/typing/issues/136 # and https://github.com/python/typing/pull/283 -SUBCLASS_CHECK_FORBIDDEN = TYPING_3_5_3 +SUBCLASS_CHECK_FORBIDDEN = True # For typing versions where instantiating collection # types are allowed. @@ -654,8 +645,7 @@ def test_async_iterable(self): def test_async_iterator(self): base_it = range(10) # type: Iterator[int] it = AsyncIteratorWrapper(base_it) - if TYPING_3_5_1: - self.assertIsInstance(it, typing_extensions.AsyncIterator) + self.assertIsInstance(it, typing_extensions.AsyncIterator) self.assertNotIsInstance(42, typing_extensions.AsyncIterator) def test_deque(self): @@ -687,8 +677,7 @@ class MyDefDict(typing_extensions.DefaultDict[str, int]): self.assertIsInstance(dd, MyDefDict) self.assertIsSubclass(MyDefDict, collections.defaultdict) - if TYPING_3_5_3: - self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + self.assertNotIsSubclass(collections.defaultdict, MyDefDict) @skipUnless(CAN_INSTANTIATE_COLLECTIONS, "Behavior added in typing 3.6.1") def test_ordereddict_instantiation(self): @@ -711,16 +700,14 @@ class MyOrdDict(typing_extensions.OrderedDict[str, int]): self.assertIsInstance(od, MyOrdDict) self.assertIsSubclass(MyOrdDict, collections.OrderedDict) - if TYPING_3_5_3: - self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) + self.assertNotIsSubclass(collections.OrderedDict, MyOrdDict) def test_chainmap_instantiation(self): self.assertIs(type(typing_extensions.ChainMap()), collections.ChainMap) self.assertIs(type(typing_extensions.ChainMap[KT, VT]()), collections.ChainMap) self.assertIs(type(typing_extensions.ChainMap[str, int]()), collections.ChainMap) class CM(typing_extensions.ChainMap[KT, VT]): ... - if TYPING_3_5_3: - self.assertIs(type(CM[int, str]()), CM) + self.assertIs(type(CM[int, str]()), CM) def test_chainmap_subclass(self): @@ -731,28 +718,25 @@ class MyChainMap(typing_extensions.ChainMap[str, int]): self.assertIsInstance(cm, MyChainMap) self.assertIsSubclass(MyChainMap, collections.ChainMap) - if TYPING_3_5_3: - self.assertNotIsSubclass(collections.ChainMap, MyChainMap) + self.assertNotIsSubclass(collections.ChainMap, MyChainMap) def test_deque_instantiation(self): self.assertIs(type(typing_extensions.Deque()), collections.deque) self.assertIs(type(typing_extensions.Deque[T]()), collections.deque) self.assertIs(type(typing_extensions.Deque[int]()), collections.deque) class D(typing_extensions.Deque[T]): ... - if TYPING_3_5_3: - self.assertIs(type(D[int]()), D) + self.assertIs(type(D[int]()), D) def test_counter_instantiation(self): self.assertIs(type(typing_extensions.Counter()), collections.Counter) self.assertIs(type(typing_extensions.Counter[T]()), collections.Counter) self.assertIs(type(typing_extensions.Counter[int]()), collections.Counter) class C(typing_extensions.Counter[T]): ... - if TYPING_3_5_3: - self.assertIs(type(C[int]()), C) - if not PEP_560: - self.assertEqual(C.__bases__, (typing_extensions.Counter,)) - else: - self.assertEqual(C.__bases__, (collections.Counter, typing.Generic)) + self.assertIs(type(C[int]()), C) + if not PEP_560: + self.assertEqual(C.__bases__, (typing_extensions.Counter,)) + else: + self.assertEqual(C.__bases__, (collections.Counter, typing.Generic)) def test_counter_subclass_instantiation(self): @@ -762,8 +746,7 @@ class MyCounter(typing_extensions.Counter[int]): d = MyCounter() self.assertIsInstance(d, MyCounter) self.assertIsInstance(d, collections.Counter) - if TYPING_3_5_1: - self.assertIsInstance(d, typing_extensions.Counter) + self.assertIsInstance(d, typing_extensions.Counter) @skipUnless(PY36, 'Python 3.6 required') def test_async_generator(self): @@ -831,8 +814,7 @@ def manager(): cm = manager() self.assertNotIsInstance(cm, typing_extensions.AsyncContextManager) - if TYPING_3_5_3: - self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,)) + self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,)) if TYPING_3_6_1: with self.assertRaises(TypeError): isinstance(42, typing_extensions.AsyncContextManager[int]) @@ -1323,9 +1305,8 @@ class P(PR[int, T], Protocol[T]): P[int, str] with self.assertRaises(TypeError): PR[int, 1] - if TYPING_3_5_3: - with self.assertRaises(TypeError): - PR[int, ClassVar] + with self.assertRaises(TypeError): + PR[int, ClassVar] class C(PR[int, T]): pass self.assertIsInstance(C[str](), C) @@ -1360,7 +1341,7 @@ class Test: def bar(self, x: str) -> str: return x self.assertIsInstance(Test(), PSub) - if TYPING_3_5_3 and not TYPING_3_10_0: + if not TYPING_3_10_0: with self.assertRaises(TypeError): PR[int, ClassVar] @@ -1385,7 +1366,6 @@ class P(Protocol[T], Protocol[S]): pass with self.assertRaises(TypeError): class P(typing.Mapping[T, S], Protocol[T]): pass - @skipUnless(TYPING_3_5_3, 'New style __repr__ and __eq__ only') def test_generic_protocols_repr(self): T = TypeVar('T') S = TypeVar('S') @@ -1396,7 +1376,6 @@ class P(Protocol[T, S]): pass self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) - @skipUnless(TYPING_3_5_3, 'New style __repr__ and __eq__ only') def test_generic_protocols_eq(self): T = TypeVar('T') S = TypeVar('S') @@ -1704,7 +1683,6 @@ def test_keys_inheritance(self): } -@skipUnless(TYPING_3_5_3, "Python >= 3.5.3 required") class AnnotatedTests(BaseTestCase): def test_repr(self): @@ -2214,8 +2192,7 @@ def test_typing_extensions_includes_standard(self): self.assertIn('ParamSpec', a) self.assertIn("Concatenate", a) - if TYPING_3_5_3: - self.assertIn('Annotated', a) + self.assertIn('Annotated', a) if PEP_560: self.assertIn('get_type_hints', a) @@ -2229,9 +2206,8 @@ def test_typing_extensions_includes_standard(self): if PY36: self.assertIn('AsyncGenerator', a) - if TYPING_3_5_3: - self.assertIn('Protocol', a) - self.assertIn('runtime', a) + self.assertIn('Protocol', a) + self.assertIn('runtime', a) # Check that all objects in `__all__` are present in the module for name in a: From 2c4e79fbba9fb0e7164cebc961ec437ae72e0f90 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:17:25 +0100 Subject: [PATCH 05/32] Remove SUBCLASS_CHECK_FORBIDDEN gates --- .../src_py3/test_typing_extensions.py | 39 ++++++------------- 1 file changed, 12 insertions(+), 27 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 6ea16114b..1d998106a 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -46,13 +46,6 @@ TYPING_3_10_0 = sys.version_info[:3] >= (3, 10, 0) TYPING_3_11_0 = sys.version_info[:3] >= (3, 11, 0) -# For typing versions where issubclass(...) and -# isinstance(...) checks are forbidden. -# -# See https://github.com/python/typing/issues/136 -# and https://github.com/python/typing/pull/283 -SUBCLASS_CHECK_FORBIDDEN = True - # For typing versions where instantiating collection # types are allowed. # @@ -99,7 +92,6 @@ def test_noreturn_subclass_type_error_1(self): with self.assertRaises(TypeError): issubclass(Employee, NoReturn) - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") def test_noreturn_subclass_type_error_2(self): with self.assertRaises(TypeError): issubclass(NoReturn, Employee) @@ -118,10 +110,9 @@ def test_cannot_subclass(self): with self.assertRaises(TypeError): class A(NoReturn): pass - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - class A(type(NoReturn)): - pass + with self.assertRaises(TypeError): + class A(type(NoReturn)): + pass def test_cannot_instantiate(self): with self.assertRaises(TypeError): @@ -151,7 +142,6 @@ def test_repr(self): cv = ClassVar[Employee] self.assertEqual(repr(cv), mod_name + '.ClassVar[%s.Employee]' % __name__) - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") def test_cannot_subclass(self): with self.assertRaises(TypeError): class C(type(ClassVar)): @@ -196,7 +186,6 @@ def test_repr(self): cv = Final[Employee] self.assertEqual(repr(cv), mod_name + '.Final[%s.Employee]' % __name__) - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") def test_cannot_subclass(self): with self.assertRaises(TypeError): class C(type(Final)): @@ -596,11 +585,10 @@ def test_isinstance_collections(self): self.assertNotIsInstance(1, collections_abc.Iterable) self.assertNotIsInstance(1, collections_abc.Container) self.assertNotIsInstance(1, collections_abc.Sized) - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - isinstance(collections.deque(), typing_extensions.Deque[int]) - with self.assertRaises(TypeError): - issubclass(collections.Counter, typing_extensions.Counter[str]) + with self.assertRaises(TypeError): + isinstance(collections.deque(), typing_extensions.Deque[int]) + with self.assertRaises(TypeError): + issubclass(collections.Counter, typing_extensions.Counter[str]) @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') def test_awaitable(self): @@ -1945,19 +1933,17 @@ def test_no_issubclass(self): with self.assertRaises(TypeError): issubclass(Employee, TypeAlias) - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - issubclass(TypeAlias, Employee) + with self.assertRaises(TypeError): + issubclass(TypeAlias, Employee) def test_cannot_subclass(self): with self.assertRaises(TypeError): class C(TypeAlias): pass - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - class C(type(TypeAlias)): - pass + with self.assertRaises(TypeError): + class C(type(TypeAlias)): + pass def test_repr(self): if hasattr(typing, 'TypeAlias'): @@ -2149,7 +2135,6 @@ def test_repr(self): cv = TypeGuard[Tuple[int]] self.assertEqual(repr(cv), '{}.TypeGuard[typing.Tuple[int]]'.format(mod_name)) - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") def test_cannot_subclass(self): with self.assertRaises(TypeError): class C(type(TypeGuard)): From a7fbda317b5425a0c8d68126f93e54987d3a0d25 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:19:26 +0100 Subject: [PATCH 06/32] Remove ASYNCIO gates --- .../src_py3/test_typing_extensions.py | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 1d998106a..cca4a32ce 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -52,9 +52,6 @@ # See https://github.com/python/typing/issues/367 CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1 -# For Python versions supporting async/await and friends. -ASYNCIO = sys.version_info[:2] >= (3, 5) - # For checks reliant on Python 3.6 syntax changes (e.g. classvar) PY36 = sys.version_info[:2] >= (3, 6) @@ -311,7 +308,6 @@ def blah(): blah() -ASYNCIO_TESTS = """ from typing import Iterable from typing_extensions import Awaitable, AsyncIterator @@ -346,17 +342,7 @@ async def __aenter__(self) -> int: return 42 async def __aexit__(self, etype, eval, tb): return None -""" -if ASYNCIO: - try: - exec(ASYNCIO_TESTS) - except ImportError: - ASYNCIO = False -else: - # fake names for the sake of static analysis - asyncio = None - AwaitableWrapper = AsyncIteratorWrapper = ACM = object PY36_TESTS = """ from test import ann_module, ann_module2, ann_module3 @@ -590,7 +576,6 @@ def test_isinstance_collections(self): with self.assertRaises(TypeError): issubclass(collections.Counter, typing_extensions.Counter[str]) - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') def test_awaitable(self): ns = {} exec( @@ -603,7 +588,6 @@ def test_awaitable(self): self.assertNotIsInstance(foo, typing_extensions.Awaitable) g.send(None) # Run foo() till completion, to avoid warning. - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') def test_coroutine(self): ns = {} exec( @@ -621,7 +605,6 @@ def test_coroutine(self): except StopIteration: pass - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') def test_async_iterable(self): base_it = range(10) # type: Iterator[int] it = AsyncIteratorWrapper(base_it) @@ -629,7 +612,6 @@ def test_async_iterable(self): self.assertIsInstance(it, typing_extensions.AsyncIterable) self.assertNotIsInstance(42, typing_extensions.AsyncIterable) - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') def test_async_iterator(self): base_it = range(10) # type: Iterator[int] it = AsyncIteratorWrapper(base_it) @@ -790,7 +772,6 @@ def manager(): self.assertIsInstance(cm, typing_extensions.ContextManager) self.assertNotIsInstance(42, typing_extensions.ContextManager) - @skipUnless(ASYNCIO, 'Python 3.5 required') def test_async_contextmanager(self): class NotACM: pass @@ -2181,12 +2162,11 @@ def test_typing_extensions_includes_standard(self): if PEP_560: self.assertIn('get_type_hints', a) - if ASYNCIO: - self.assertIn('Awaitable', a) - self.assertIn('AsyncIterator', a) - self.assertIn('AsyncIterable', a) - self.assertIn('Coroutine', a) - self.assertIn('AsyncContextManager', a) + self.assertIn('Awaitable', a) + self.assertIn('AsyncIterator', a) + self.assertIn('AsyncIterable', a) + self.assertIn('Coroutine', a) + self.assertIn('AsyncContextManager', a) if PY36: self.assertIn('AsyncGenerator', a) From 011cfb65c683bfe78fccd5ac88bb0978458f1ec6 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:28:50 +0100 Subject: [PATCH 07/32] Remove PY36 gates --- .../src_py3/test_typing_extensions.py | 52 +++---------------- 1 file changed, 6 insertions(+), 46 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index cca4a32ce..d2b6ab0d2 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -52,9 +52,6 @@ # See https://github.com/python/typing/issues/367 CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1 -# For checks reliant on Python 3.6 syntax changes (e.g. classvar) -PY36 = sys.version_info[:2] >= (3, 6) - # Protocols are hard to backport to the original version of typing 3.5.0 HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0) @@ -344,7 +341,6 @@ async def __aexit__(self, etype, eval, tb): return None -PY36_TESTS = """ from test import ann_module, ann_module2, ann_module3 from typing_extensions import AsyncContextManager from typing import NamedTuple @@ -414,23 +410,11 @@ class Animal(BaseAnimal, total=False): class Cat(Animal): fur_color: str -""" - -if PY36: - exec(PY36_TESTS) -else: - # fake names for the sake of static analysis - ann_module = ann_module2 = ann_module3 = None - A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object - XMeth = XRepr = HasCallProtocol = NoneAndForward = Loop = object - Point2D = Point2Dor3D = LabelPoint2D = Options = object - BaseAnimal = Animal = Cat = object gth = get_type_hints class GetTypeHintTests(BaseTestCase): - @skipUnless(PY36, 'Python 3.6 required') def test_get_type_hints_modules(self): ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} if (TYPING_3_11_0 @@ -441,7 +425,6 @@ def test_get_type_hints_modules(self): self.assertEqual(gth(ann_module2), {}) self.assertEqual(gth(ann_module3), {}) - @skipUnless(PY36, 'Python 3.6 required') def test_get_type_hints_classes(self): self.assertEqual(gth(ann_module.C, ann_module.__dict__), {'y': Optional[ann_module.C]}) @@ -457,7 +440,6 @@ def test_get_type_hints_classes(self): self.assertEqual(gth(NoneAndForward, globals()), {'parent': NoneAndForward, 'meaning': type(None)}) - @skipUnless(PY36, 'Python 3.6 required') def test_respect_no_type_check(self): @no_type_check class NoTpCheck: @@ -472,7 +454,6 @@ def meth(x: int): ... class Der(ABase): ... self.assertEqual(gth(ABase.meth), {'x': int}) - @skipUnless(PY36, 'Python 3.6 required') def test_get_type_hints_ClassVar(self): self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), {'var': ClassVar[ann_module2.CV]}) @@ -483,7 +464,6 @@ def test_get_type_hints_ClassVar(self): 'x': ClassVar[Optional[B]]}) self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) - @skipUnless(PY36, 'Python 3.6 required') def test_final_forward_ref(self): self.assertEqual(gth(Loop, globals())['attr'], Final[Loop]) self.assertNotEqual(gth(Loop, globals())['attr'], Final[int]) @@ -718,7 +698,6 @@ class MyCounter(typing_extensions.Counter[int]): self.assertIsInstance(d, collections.Counter) self.assertIsInstance(d, typing_extensions.Counter) - @skipUnless(PY36, 'Python 3.6 required') def test_async_generator(self): ns = {} exec("async def f():\n" @@ -726,7 +705,6 @@ def test_async_generator(self): g = ns['f']() self.assertIsSubclass(type(g), typing_extensions.AsyncGenerator) - @skipUnless(PY36, 'Python 3.6 required') def test_no_async_generator_instantiation(self): with self.assertRaises(TypeError): typing_extensions.AsyncGenerator() @@ -735,7 +713,6 @@ def test_no_async_generator_instantiation(self): with self.assertRaises(TypeError): typing_extensions.AsyncGenerator[int, int]() - @skipUnless(PY36, 'Python 3.6 required') def test_subclassing_async_generator(self): class G(typing_extensions.AsyncGenerator[int, int]): def asend(self, value): @@ -851,7 +828,6 @@ class D(UserName): pass -PY36_PROTOCOL_TESTS = """ class Coordinate(Protocol): x: int y: int @@ -894,14 +870,6 @@ def meth(self, arg: str) -> int: class NT(NamedTuple): x: int y: int -""" - -if PY36: - exec(PY36_PROTOCOL_TESTS) -else: - # fake names for the sake of static analysis - Coordinate = Point = MyPoint = BadPoint = NT = object - XAxis = YAxis = Position = Proto = Concrete = Other = object if HAVE_PROTOCOLS: @@ -936,7 +904,6 @@ def f(): for thing in (object(), 1, (), typing, f): self.assertIsInstance(thing, Empty) - @skipUnless(PY36, 'Python 3.6 required') def test_function_implements_protocol(self): def f(): pass @@ -1127,7 +1094,6 @@ def meth(x): ... with self.assertRaises(TypeError): isinstance(C(), BadPG) - @skipUnless(PY36, 'Python 3.6 required') def test_protocols_isinstance_py36(self): class APoint: def __init__(self, x, y, label): @@ -1575,7 +1541,6 @@ def test_typeddict_errors(self): with self.assertRaises(TypeError): TypedDict('Hi', [('x', int)], y=int) - @skipUnless(PY36, 'Python 3.6 required') def test_py36_class_syntax_usage(self): self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') self.assertEqual(LabelPoint2D.__module__, __name__) @@ -1616,19 +1581,16 @@ def test_total(self): self.assertEqual(D.__required_keys__, frozenset()) self.assertEqual(D.__optional_keys__, {'x'}) - if PY36: - self.assertEqual(Options(), {}) - self.assertEqual(Options(log_level=2), {'log_level': 2}) - self.assertEqual(Options.__total__, False) - self.assertEqual(Options.__required_keys__, frozenset()) - self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'}) + self.assertEqual(Options(), {}) + self.assertEqual(Options(log_level=2), {'log_level': 2}) + self.assertEqual(Options.__total__, False) + self.assertEqual(Options.__required_keys__, frozenset()) + self.assertEqual(Options.__optional_keys__, {'log_level', 'log_path'}) - @skipUnless(PY36, 'Python 3.6 required') def test_optional_keys(self): assert Point2Dor3D.__required_keys__ == frozenset(['x', 'y']) assert Point2Dor3D.__optional_keys__ == frozenset(['z']) - @skipUnless(PY36, 'Python 3.6 required') def test_keys_inheritance(self): assert BaseAnimal.__required_keys__ == frozenset(['name']) assert BaseAnimal.__optional_keys__ == frozenset([]) @@ -1894,7 +1856,6 @@ def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]": class TypeAliasTests(BaseTestCase): - @skipUnless(PY36, 'Python 3.6 required') def test_canonical_usage_with_variable_annotation(self): ns = {} exec('Alias: TypeAlias = Employee', globals(), ns) @@ -2168,8 +2129,7 @@ def test_typing_extensions_includes_standard(self): self.assertIn('Coroutine', a) self.assertIn('AsyncContextManager', a) - if PY36: - self.assertIn('AsyncGenerator', a) + self.assertIn('AsyncGenerator', a) self.assertIn('Protocol', a) self.assertIn('runtime', a) From 87a9e10f46561994413585c9f2434beefb0da873 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:30:19 +0100 Subject: [PATCH 08/32] Remove HAVE_PROTOCOLS gate --- .../src_py3/test_typing_extensions.py | 1098 ++++++++--------- 1 file changed, 547 insertions(+), 551 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index d2b6ab0d2..ea88f214c 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -52,9 +52,6 @@ # See https://github.com/python/typing/issues/367 CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1 -# Protocols are hard to backport to the original version of typing 3.5.0 -HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0) - class BaseTestCase(TestCase): def assertIsSubclass(self, cls, class_or_tuple, msg=None): @@ -872,583 +869,582 @@ class NT(NamedTuple): y: int -if HAVE_PROTOCOLS: - class ProtocolTests(BaseTestCase): +class ProtocolTests(BaseTestCase): - def test_basic_protocol(self): - @runtime - class P(Protocol): - def meth(self): - pass - class C: pass - class D: - def meth(self): - pass - def f(): + def test_basic_protocol(self): + @runtime + class P(Protocol): + def meth(self): pass - self.assertIsSubclass(D, P) - self.assertIsInstance(D(), P) - self.assertNotIsSubclass(C, P) - self.assertNotIsInstance(C(), P) - self.assertNotIsSubclass(types.FunctionType, P) - self.assertNotIsInstance(f, P) - - def test_everything_implements_empty_protocol(self): - @runtime - class Empty(Protocol): pass - class C: pass - def f(): + class C: pass + class D: + def meth(self): pass - for thing in (object, type, tuple, C, types.FunctionType): - self.assertIsSubclass(thing, Empty) - for thing in (object(), 1, (), typing, f): - self.assertIsInstance(thing, Empty) + def f(): + pass + self.assertIsSubclass(D, P) + self.assertIsInstance(D(), P) + self.assertNotIsSubclass(C, P) + self.assertNotIsInstance(C(), P) + self.assertNotIsSubclass(types.FunctionType, P) + self.assertNotIsInstance(f, P) + + def test_everything_implements_empty_protocol(self): + @runtime + class Empty(Protocol): pass + class C: pass + def f(): + pass + for thing in (object, type, tuple, C, types.FunctionType): + self.assertIsSubclass(thing, Empty) + for thing in (object(), 1, (), typing, f): + self.assertIsInstance(thing, Empty) - def test_function_implements_protocol(self): - def f(): + def test_function_implements_protocol(self): + def f(): + pass + self.assertIsInstance(f, HasCallProtocol) + + def test_no_inheritance_from_nominal(self): + class C: pass + class BP(Protocol): pass + with self.assertRaises(TypeError): + class P(C, Protocol): + pass + with self.assertRaises(TypeError): + class P(Protocol, C): + pass + with self.assertRaises(TypeError): + class P(BP, C, Protocol): pass - self.assertIsInstance(f, HasCallProtocol) + class D(BP, C): pass + class E(C, BP): pass + self.assertNotIsInstance(D(), E) + self.assertNotIsInstance(E(), D) - def test_no_inheritance_from_nominal(self): - class C: pass - class BP(Protocol): pass - with self.assertRaises(TypeError): - class P(C, Protocol): - pass - with self.assertRaises(TypeError): - class P(Protocol, C): - pass - with self.assertRaises(TypeError): - class P(BP, C, Protocol): - pass - class D(BP, C): pass - class E(C, BP): pass - self.assertNotIsInstance(D(), E) - self.assertNotIsInstance(E(), D) - - def test_no_instantiation(self): - class P(Protocol): pass - with self.assertRaises(TypeError): - P() - class C(P): pass - self.assertIsInstance(C(), C) - T = TypeVar('T') - class PG(Protocol[T]): pass - with self.assertRaises(TypeError): - PG() - with self.assertRaises(TypeError): - PG[int]() - with self.assertRaises(TypeError): - PG[T]() - class CG(PG[T]): pass - self.assertIsInstance(CG[int](), CG) + def test_no_instantiation(self): + class P(Protocol): pass + with self.assertRaises(TypeError): + P() + class C(P): pass + self.assertIsInstance(C(), C) + T = TypeVar('T') + class PG(Protocol[T]): pass + with self.assertRaises(TypeError): + PG() + with self.assertRaises(TypeError): + PG[int]() + with self.assertRaises(TypeError): + PG[T]() + class CG(PG[T]): pass + self.assertIsInstance(CG[int](), CG) - def test_cannot_instantiate_abstract(self): - @runtime - class P(Protocol): - @abc.abstractmethod - def ameth(self) -> int: - raise NotImplementedError - class B(P): - pass - class C(B): - def ameth(self) -> int: - return 26 - with self.assertRaises(TypeError): - B() - self.assertIsInstance(C(), P) + def test_cannot_instantiate_abstract(self): + @runtime + class P(Protocol): + @abc.abstractmethod + def ameth(self) -> int: + raise NotImplementedError + class B(P): + pass + class C(B): + def ameth(self) -> int: + return 26 + with self.assertRaises(TypeError): + B() + self.assertIsInstance(C(), P) - def test_subprotocols_extending(self): - class P1(Protocol): - def meth1(self): - pass - @runtime - class P2(P1, Protocol): - def meth2(self): - pass - class C: - def meth1(self): - pass - def meth2(self): - pass - class C1: - def meth1(self): - pass - class C2: - def meth2(self): - pass - self.assertNotIsInstance(C1(), P2) - self.assertNotIsInstance(C2(), P2) - self.assertNotIsSubclass(C1, P2) - self.assertNotIsSubclass(C2, P2) - self.assertIsInstance(C(), P2) - self.assertIsSubclass(C, P2) - - def test_subprotocols_merging(self): - class P1(Protocol): - def meth1(self): - pass - class P2(Protocol): - def meth2(self): - pass - @runtime - class P(P1, P2, Protocol): + def test_subprotocols_extending(self): + class P1(Protocol): + def meth1(self): pass - class C: - def meth1(self): - pass - def meth2(self): - pass - class C1: - def meth1(self): - pass - class C2: - def meth2(self): - pass - self.assertNotIsInstance(C1(), P) - self.assertNotIsInstance(C2(), P) - self.assertNotIsSubclass(C1, P) - self.assertNotIsSubclass(C2, P) - self.assertIsInstance(C(), P) - self.assertIsSubclass(C, P) - - def test_protocols_issubclass(self): - T = TypeVar('T') - @runtime - class P(Protocol): - def x(self): ... - @runtime - class PG(Protocol[T]): - def x(self): ... - class BadP(Protocol): - def x(self): ... - class BadPG(Protocol[T]): - def x(self): ... - class C: - def x(self): ... - self.assertIsSubclass(C, P) - self.assertIsSubclass(C, PG) - self.assertIsSubclass(BadP, PG) - if not PEP_560: - self.assertIsSubclass(PG[int], PG) - self.assertIsSubclass(BadPG[int], P) - self.assertIsSubclass(BadPG[T], PG) - with self.assertRaises(TypeError): - issubclass(C, PG[T]) - with self.assertRaises(TypeError): - issubclass(C, PG[C]) - with self.assertRaises(TypeError): - issubclass(C, BadP) - with self.assertRaises(TypeError): - issubclass(C, BadPG) - with self.assertRaises(TypeError): - issubclass(P, PG[T]) - with self.assertRaises(TypeError): - issubclass(PG, PG[int]) + @runtime + class P2(P1, Protocol): + def meth2(self): + pass + class C: + def meth1(self): + pass + def meth2(self): + pass + class C1: + def meth1(self): + pass + class C2: + def meth2(self): + pass + self.assertNotIsInstance(C1(), P2) + self.assertNotIsInstance(C2(), P2) + self.assertNotIsSubclass(C1, P2) + self.assertNotIsSubclass(C2, P2) + self.assertIsInstance(C(), P2) + self.assertIsSubclass(C, P2) + + def test_subprotocols_merging(self): + class P1(Protocol): + def meth1(self): + pass + class P2(Protocol): + def meth2(self): + pass + @runtime + class P(P1, P2, Protocol): + pass + class C: + def meth1(self): + pass + def meth2(self): + pass + class C1: + def meth1(self): + pass + class C2: + def meth2(self): + pass + self.assertNotIsInstance(C1(), P) + self.assertNotIsInstance(C2(), P) + self.assertNotIsSubclass(C1, P) + self.assertNotIsSubclass(C2, P) + self.assertIsInstance(C(), P) + self.assertIsSubclass(C, P) + + def test_protocols_issubclass(self): + T = TypeVar('T') + @runtime + class P(Protocol): + def x(self): ... + @runtime + class PG(Protocol[T]): + def x(self): ... + class BadP(Protocol): + def x(self): ... + class BadPG(Protocol[T]): + def x(self): ... + class C: + def x(self): ... + self.assertIsSubclass(C, P) + self.assertIsSubclass(C, PG) + self.assertIsSubclass(BadP, PG) + if not PEP_560: + self.assertIsSubclass(PG[int], PG) + self.assertIsSubclass(BadPG[int], P) + self.assertIsSubclass(BadPG[T], PG) + with self.assertRaises(TypeError): + issubclass(C, PG[T]) + with self.assertRaises(TypeError): + issubclass(C, PG[C]) + with self.assertRaises(TypeError): + issubclass(C, BadP) + with self.assertRaises(TypeError): + issubclass(C, BadPG) + with self.assertRaises(TypeError): + issubclass(P, PG[T]) + with self.assertRaises(TypeError): + issubclass(PG, PG[int]) - def test_protocols_issubclass_non_callable(self): - class C: - x = 1 - @runtime - class PNonCall(Protocol): - x = 1 - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - PNonCall.register(C) - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - # check that non-protocol subclasses are not affected - class D(PNonCall): ... - self.assertNotIsSubclass(C, D) - self.assertNotIsInstance(C(), D) - D.register(C) - self.assertIsSubclass(C, D) - self.assertIsInstance(C(), D) - with self.assertRaises(TypeError): - issubclass(D, PNonCall) + def test_protocols_issubclass_non_callable(self): + class C: + x = 1 + @runtime + class PNonCall(Protocol): + x = 1 + with self.assertRaises(TypeError): + issubclass(C, PNonCall) + self.assertIsInstance(C(), PNonCall) + PNonCall.register(C) + with self.assertRaises(TypeError): + issubclass(C, PNonCall) + self.assertIsInstance(C(), PNonCall) + # check that non-protocol subclasses are not affected + class D(PNonCall): ... + self.assertNotIsSubclass(C, D) + self.assertNotIsInstance(C(), D) + D.register(C) + self.assertIsSubclass(C, D) + self.assertIsInstance(C(), D) + with self.assertRaises(TypeError): + issubclass(D, PNonCall) - def test_protocols_isinstance(self): - T = TypeVar('T') - @runtime - class P(Protocol): - def meth(x): ... - @runtime - class PG(Protocol[T]): - def meth(x): ... - class BadP(Protocol): - def meth(x): ... - class BadPG(Protocol[T]): - def meth(x): ... - class C: - def meth(x): ... - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), PG) - with self.assertRaises(TypeError): - isinstance(C(), PG[T]) - with self.assertRaises(TypeError): - isinstance(C(), PG[C]) - with self.assertRaises(TypeError): - isinstance(C(), BadP) - with self.assertRaises(TypeError): - isinstance(C(), BadPG) - - def test_protocols_isinstance_py36(self): - class APoint: - def __init__(self, x, y, label): - self.x = x - self.y = y - self.label = label - class BPoint: - label = 'B' - def __init__(self, x, y): - self.x = x - self.y = y - class C: - def __init__(self, attr): - self.attr = attr - def meth(self, arg): - return 0 - class Bad: pass - self.assertIsInstance(APoint(1, 2, 'A'), Point) - self.assertIsInstance(BPoint(1, 2), Point) - self.assertNotIsInstance(MyPoint(), Point) - self.assertIsInstance(BPoint(1, 2), Position) - self.assertIsInstance(Other(), Proto) - self.assertIsInstance(Concrete(), Proto) - self.assertIsInstance(C(42), Proto) - self.assertNotIsInstance(Bad(), Proto) - self.assertNotIsInstance(Bad(), Point) - self.assertNotIsInstance(Bad(), Position) - self.assertNotIsInstance(Bad(), Concrete) - self.assertNotIsInstance(Other(), Concrete) - self.assertIsInstance(NT(1, 2), Position) - - def test_protocols_isinstance_init(self): - T = TypeVar('T') - @runtime - class P(Protocol): - x = 1 - @runtime - class PG(Protocol[T]): - x = 1 - class C: - def __init__(self, x): - self.x = x - self.assertIsInstance(C(1), P) - self.assertIsInstance(C(1), PG) + def test_protocols_isinstance(self): + T = TypeVar('T') + @runtime + class P(Protocol): + def meth(x): ... + @runtime + class PG(Protocol[T]): + def meth(x): ... + class BadP(Protocol): + def meth(x): ... + class BadPG(Protocol[T]): + def meth(x): ... + class C: + def meth(x): ... + self.assertIsInstance(C(), P) + self.assertIsInstance(C(), PG) + with self.assertRaises(TypeError): + isinstance(C(), PG[T]) + with self.assertRaises(TypeError): + isinstance(C(), PG[C]) + with self.assertRaises(TypeError): + isinstance(C(), BadP) + with self.assertRaises(TypeError): + isinstance(C(), BadPG) - def test_protocols_support_register(self): - @runtime - class P(Protocol): - x = 1 - class PM(Protocol): - def meth(self): pass - class D(PM): pass - class C: pass - D.register(C) - P.register(C) - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), D) + def test_protocols_isinstance_py36(self): + class APoint: + def __init__(self, x, y, label): + self.x = x + self.y = y + self.label = label + class BPoint: + label = 'B' + def __init__(self, x, y): + self.x = x + self.y = y + class C: + def __init__(self, attr): + self.attr = attr + def meth(self, arg): + return 0 + class Bad: pass + self.assertIsInstance(APoint(1, 2, 'A'), Point) + self.assertIsInstance(BPoint(1, 2), Point) + self.assertNotIsInstance(MyPoint(), Point) + self.assertIsInstance(BPoint(1, 2), Position) + self.assertIsInstance(Other(), Proto) + self.assertIsInstance(Concrete(), Proto) + self.assertIsInstance(C(42), Proto) + self.assertNotIsInstance(Bad(), Proto) + self.assertNotIsInstance(Bad(), Point) + self.assertNotIsInstance(Bad(), Position) + self.assertNotIsInstance(Bad(), Concrete) + self.assertNotIsInstance(Other(), Concrete) + self.assertIsInstance(NT(1, 2), Position) + + def test_protocols_isinstance_init(self): + T = TypeVar('T') + @runtime + class P(Protocol): + x = 1 + @runtime + class PG(Protocol[T]): + x = 1 + class C: + def __init__(self, x): + self.x = x + self.assertIsInstance(C(1), P) + self.assertIsInstance(C(1), PG) - def test_none_on_non_callable_doesnt_block_implementation(self): - @runtime - class P(Protocol): - x = 1 - class A: - x = 1 - class B(A): - x = None - class C: - def __init__(self): - self.x = None - self.assertIsInstance(B(), P) - self.assertIsInstance(C(), P) - - def test_none_on_callable_blocks_implementation(self): - @runtime - class P(Protocol): - def x(self): ... - class A: - def x(self): ... - class B(A): - x = None - class C: - def __init__(self): - self.x = None - self.assertNotIsInstance(B(), P) - self.assertNotIsInstance(C(), P) - - def test_non_protocol_subclasses(self): - class P(Protocol): - x = 1 - @runtime - class PR(Protocol): - def meth(self): pass - class NonP(P): - x = 1 - class NonPR(PR): pass - class C: - x = 1 - class D: - def meth(self): pass - self.assertNotIsInstance(C(), NonP) - self.assertNotIsInstance(D(), NonPR) - self.assertNotIsSubclass(C, NonP) - self.assertNotIsSubclass(D, NonPR) - self.assertIsInstance(NonPR(), PR) - self.assertIsSubclass(NonPR, PR) - - def test_custom_subclasshook(self): - class P(Protocol): - x = 1 - class OKClass: pass - class BadClass: - x = 1 - class C(P): - @classmethod - def __subclasshook__(cls, other): - return other.__name__.startswith("OK") - self.assertIsInstance(OKClass(), C) - self.assertNotIsInstance(BadClass(), C) - self.assertIsSubclass(OKClass, C) - self.assertNotIsSubclass(BadClass, C) - - def test_issubclass_fails_correctly(self): - @runtime - class P(Protocol): - x = 1 - class C: pass - with self.assertRaises(TypeError): - issubclass(C(), P) + def test_protocols_support_register(self): + @runtime + class P(Protocol): + x = 1 + class PM(Protocol): + def meth(self): pass + class D(PM): pass + class C: pass + D.register(C) + P.register(C) + self.assertIsInstance(C(), P) + self.assertIsInstance(C(), D) + + def test_none_on_non_callable_doesnt_block_implementation(self): + @runtime + class P(Protocol): + x = 1 + class A: + x = 1 + class B(A): + x = None + class C: + def __init__(self): + self.x = None + self.assertIsInstance(B(), P) + self.assertIsInstance(C(), P) + + def test_none_on_callable_blocks_implementation(self): + @runtime + class P(Protocol): + def x(self): ... + class A: + def x(self): ... + class B(A): + x = None + class C: + def __init__(self): + self.x = None + self.assertNotIsInstance(B(), P) + self.assertNotIsInstance(C(), P) - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_defining_generic_protocols(self): - T = TypeVar('T') - S = TypeVar('S') - @runtime - class PR(Protocol[T, S]): - def meth(self): pass - class P(PR[int, T], Protocol[T]): - y = 1 - self.assertIsSubclass(PR[int, T], PR) - self.assertIsSubclass(P[str], PR) - with self.assertRaises(TypeError): - PR[int] + def test_non_protocol_subclasses(self): + class P(Protocol): + x = 1 + @runtime + class PR(Protocol): + def meth(self): pass + class NonP(P): + x = 1 + class NonPR(PR): pass + class C: + x = 1 + class D: + def meth(self): pass + self.assertNotIsInstance(C(), NonP) + self.assertNotIsInstance(D(), NonPR) + self.assertNotIsSubclass(C, NonP) + self.assertNotIsSubclass(D, NonPR) + self.assertIsInstance(NonPR(), PR) + self.assertIsSubclass(NonPR, PR) + + def test_custom_subclasshook(self): + class P(Protocol): + x = 1 + class OKClass: pass + class BadClass: + x = 1 + class C(P): + @classmethod + def __subclasshook__(cls, other): + return other.__name__.startswith("OK") + self.assertIsInstance(OKClass(), C) + self.assertNotIsInstance(BadClass(), C) + self.assertIsSubclass(OKClass, C) + self.assertNotIsSubclass(BadClass, C) + + def test_issubclass_fails_correctly(self): + @runtime + class P(Protocol): + x = 1 + class C: pass + with self.assertRaises(TypeError): + issubclass(C(), P) + + @skipUnless(not OLD_GENERICS, "New style generics required") + def test_defining_generic_protocols(self): + T = TypeVar('T') + S = TypeVar('S') + @runtime + class PR(Protocol[T, S]): + def meth(self): pass + class P(PR[int, T], Protocol[T]): + y = 1 + self.assertIsSubclass(PR[int, T], PR) + self.assertIsSubclass(P[str], PR) + with self.assertRaises(TypeError): + PR[int] + with self.assertRaises(TypeError): + P[int, str] + with self.assertRaises(TypeError): + PR[int, 1] + with self.assertRaises(TypeError): + PR[int, ClassVar] + class C(PR[int, T]): pass + self.assertIsInstance(C[str](), C) + + def test_defining_generic_protocols_old_style(self): + T = TypeVar('T') + S = TypeVar('S') + @runtime + class PR(Protocol, Generic[T, S]): + def meth(self): pass + class P(PR[int, str], Protocol): + y = 1 + if not PEP_560: + self.assertIsSubclass(PR[int, str], PR) + else: with self.assertRaises(TypeError): - P[int, str] + self.assertIsSubclass(PR[int, str], PR) + self.assertIsSubclass(P, PR) + with self.assertRaises(TypeError): + PR[int] + if not TYPING_3_10_0: with self.assertRaises(TypeError): PR[int, 1] + class P1(Protocol, Generic[T]): + def bar(self, x: T) -> str: ... + class P2(Generic[T], Protocol): + def bar(self, x: T) -> str: ... + @runtime + class PSub(P1[str], Protocol): + x = 1 + class Test: + x = 1 + def bar(self, x: str) -> str: + return x + self.assertIsInstance(Test(), PSub) + if not TYPING_3_10_0: with self.assertRaises(TypeError): PR[int, ClassVar] - class C(PR[int, T]): pass - self.assertIsInstance(C[str](), C) - def test_defining_generic_protocols_old_style(self): - T = TypeVar('T') - S = TypeVar('S') - @runtime - class PR(Protocol, Generic[T, S]): - def meth(self): pass - class P(PR[int, str], Protocol): - y = 1 - if not PEP_560: - self.assertIsSubclass(PR[int, str], PR) - else: - with self.assertRaises(TypeError): - self.assertIsSubclass(PR[int, str], PR) - self.assertIsSubclass(P, PR) - with self.assertRaises(TypeError): - PR[int] - if not TYPING_3_10_0: - with self.assertRaises(TypeError): - PR[int, 1] - class P1(Protocol, Generic[T]): - def bar(self, x: T) -> str: ... - class P2(Generic[T], Protocol): - def bar(self, x: T) -> str: ... - @runtime - class PSub(P1[str], Protocol): - x = 1 - class Test: - x = 1 - def bar(self, x: str) -> str: - return x - self.assertIsInstance(Test(), PSub) - if not TYPING_3_10_0: - with self.assertRaises(TypeError): - PR[int, ClassVar] - - def test_init_called(self): - T = TypeVar('T') - class P(Protocol[T]): pass - class C(P[T]): - def __init__(self): - self.test = 'OK' - self.assertEqual(C[int]().test, 'OK') - - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_protocols_bad_subscripts(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - class P(Protocol[T, T]): pass - with self.assertRaises(TypeError): - class P(Protocol[int]): pass - with self.assertRaises(TypeError): - class P(Protocol[T], Protocol[S]): pass - with self.assertRaises(TypeError): - class P(typing.Mapping[T, S], Protocol[T]): pass - - def test_generic_protocols_repr(self): - T = TypeVar('T') - S = TypeVar('S') - class P(Protocol[T, S]): pass - # After PEP 560 unsubscripted generics have a standard repr. - if not PEP_560: - self.assertTrue(repr(P).endswith('P')) - self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) - self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) - - def test_generic_protocols_eq(self): - T = TypeVar('T') - S = TypeVar('S') - class P(Protocol[T, S]): pass - self.assertEqual(P, P) - self.assertEqual(P[int, T], P[int, T]) - self.assertEqual(P[T, T][Tuple[T, S]][int, str], - P[Tuple[int, str], Tuple[int, str]]) - - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_generic_protocols_special_from_generic(self): - T = TypeVar('T') - class P(Protocol[T]): pass - self.assertEqual(P.__parameters__, (T,)) - self.assertIs(P.__args__, None) - self.assertIs(P.__origin__, None) - self.assertEqual(P[int].__parameters__, ()) - self.assertEqual(P[int].__args__, (int,)) - self.assertIs(P[int].__origin__, P) - - def test_generic_protocols_special_from_protocol(self): - @runtime - class PR(Protocol): - x = 1 - class P(Protocol): - def meth(self): - pass - T = TypeVar('T') - class PG(Protocol[T]): - x = 1 - def meth(self): - pass - self.assertTrue(P._is_protocol) - self.assertTrue(PR._is_protocol) - self.assertTrue(PG._is_protocol) - if hasattr(typing, 'Protocol'): + def test_init_called(self): + T = TypeVar('T') + class P(Protocol[T]): pass + class C(P[T]): + def __init__(self): + self.test = 'OK' + self.assertEqual(C[int]().test, 'OK') + + @skipUnless(not OLD_GENERICS, "New style generics required") + def test_protocols_bad_subscripts(self): + T = TypeVar('T') + S = TypeVar('S') + with self.assertRaises(TypeError): + class P(Protocol[T, T]): pass + with self.assertRaises(TypeError): + class P(Protocol[int]): pass + with self.assertRaises(TypeError): + class P(Protocol[T], Protocol[S]): pass + with self.assertRaises(TypeError): + class P(typing.Mapping[T, S], Protocol[T]): pass + + def test_generic_protocols_repr(self): + T = TypeVar('T') + S = TypeVar('S') + class P(Protocol[T, S]): pass + # After PEP 560 unsubscripted generics have a standard repr. + if not PEP_560: + self.assertTrue(repr(P).endswith('P')) + self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) + self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) + + def test_generic_protocols_eq(self): + T = TypeVar('T') + S = TypeVar('S') + class P(Protocol[T, S]): pass + self.assertEqual(P, P) + self.assertEqual(P[int, T], P[int, T]) + self.assertEqual(P[T, T][Tuple[T, S]][int, str], + P[Tuple[int, str], Tuple[int, str]]) + + @skipUnless(not OLD_GENERICS, "New style generics required") + def test_generic_protocols_special_from_generic(self): + T = TypeVar('T') + class P(Protocol[T]): pass + self.assertEqual(P.__parameters__, (T,)) + self.assertIs(P.__args__, None) + self.assertIs(P.__origin__, None) + self.assertEqual(P[int].__parameters__, ()) + self.assertEqual(P[int].__args__, (int,)) + self.assertIs(P[int].__origin__, P) + + def test_generic_protocols_special_from_protocol(self): + @runtime + class PR(Protocol): + x = 1 + class P(Protocol): + def meth(self): + pass + T = TypeVar('T') + class PG(Protocol[T]): + x = 1 + def meth(self): + pass + self.assertTrue(P._is_protocol) + self.assertTrue(PR._is_protocol) + self.assertTrue(PG._is_protocol) + if hasattr(typing, 'Protocol'): + self.assertFalse(P._is_runtime_protocol) + else: + with self.assertRaises(AttributeError): self.assertFalse(P._is_runtime_protocol) - else: - with self.assertRaises(AttributeError): - self.assertFalse(P._is_runtime_protocol) - self.assertTrue(PR._is_runtime_protocol) - self.assertTrue(PG[int]._is_protocol) - self.assertEqual(typing_extensions._get_protocol_attrs(P), {'meth'}) - self.assertEqual(typing_extensions._get_protocol_attrs(PR), {'x'}) - self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG)), + self.assertTrue(PR._is_runtime_protocol) + self.assertTrue(PG[int]._is_protocol) + self.assertEqual(typing_extensions._get_protocol_attrs(P), {'meth'}) + self.assertEqual(typing_extensions._get_protocol_attrs(PR), {'x'}) + self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG)), + frozenset({'x', 'meth'})) + if not PEP_560: + self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG[int])), frozenset({'x', 'meth'})) - if not PEP_560: - self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG[int])), - frozenset({'x', 'meth'})) - - def test_no_runtime_deco_on_nominal(self): - with self.assertRaises(TypeError): - @runtime - class C: pass - class Proto(Protocol): - x = 1 - with self.assertRaises(TypeError): - @runtime - class Concrete(Proto): - pass - def test_none_treated_correctly(self): + def test_no_runtime_deco_on_nominal(self): + with self.assertRaises(TypeError): @runtime - class P(Protocol): - x = None # type: int - class B(object): pass - self.assertNotIsInstance(B(), P) - class C: - x = 1 - class D: - x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - class CI: - def __init__(self): - self.x = 1 - class DI: - def __init__(self): - self.x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - - def test_protocols_in_unions(self): - class P(Protocol): - x = None # type: int - Alias = typing.Union[typing.Iterable, P] - Alias2 = typing.Union[P, typing.Iterable] - self.assertEqual(Alias, Alias2) - - def test_protocols_pickleable(self): - global P, CP # pickle wants to reference the class by name - T = TypeVar('T') - + class C: pass + class Proto(Protocol): + x = 1 + with self.assertRaises(TypeError): @runtime - class P(Protocol[T]): - x = 1 - class CP(P[int]): + class Concrete(Proto): pass - c = CP() - c.foo = 42 - c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - s = pickle.dumps(P) - D = pickle.loads(s) - class E: - x = 1 - self.assertIsInstance(E(), D) - - def test_collections_protocols_allowed(self): - @runtime_checkable - class Custom(collections.abc.Iterable, Protocol): - def close(self): pass - - class A: ... - class B: - def __iter__(self): - return [] - def close(self): - return 0 - - self.assertIsSubclass(B, Custom) - self.assertNotIsSubclass(A, Custom) - - def test_no_init_same_for_different_protocol_implementations(self): - class CustomProtocolWithoutInitA(Protocol): - pass + def test_none_treated_correctly(self): + @runtime + class P(Protocol): + x = None # type: int + class B(object): pass + self.assertNotIsInstance(B(), P) + class C: + x = 1 + class D: + x = None + self.assertIsInstance(C(), P) + self.assertIsInstance(D(), P) + class CI: + def __init__(self): + self.x = 1 + class DI: + def __init__(self): + self.x = None + self.assertIsInstance(C(), P) + self.assertIsInstance(D(), P) + + def test_protocols_in_unions(self): + class P(Protocol): + x = None # type: int + Alias = typing.Union[typing.Iterable, P] + Alias2 = typing.Union[P, typing.Iterable] + self.assertEqual(Alias, Alias2) + + def test_protocols_pickleable(self): + global P, CP # pickle wants to reference the class by name + T = TypeVar('T') - class CustomProtocolWithoutInitB(Protocol): - pass + @runtime + class P(Protocol[T]): + x = 1 + class CP(P[int]): + pass + + c = CP() + c.foo = 42 + c.bar = 'abc' + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + z = pickle.dumps(c, proto) + x = pickle.loads(z) + self.assertEqual(x.foo, 42) + self.assertEqual(x.bar, 'abc') + self.assertEqual(x.x, 1) + self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) + s = pickle.dumps(P) + D = pickle.loads(s) + class E: + x = 1 + self.assertIsInstance(E(), D) + + def test_collections_protocols_allowed(self): + @runtime_checkable + class Custom(collections.abc.Iterable, Protocol): + def close(self): pass + + class A: ... + class B: + def __iter__(self): + return [] + def close(self): + return 0 + + self.assertIsSubclass(B, Custom) + self.assertNotIsSubclass(A, Custom) + + def test_no_init_same_for_different_protocol_implementations(self): + class CustomProtocolWithoutInitA(Protocol): + pass + + class CustomProtocolWithoutInitB(Protocol): + pass - self.assertEqual(CustomProtocolWithoutInitA.__init__, CustomProtocolWithoutInitB.__init__) + self.assertEqual(CustomProtocolWithoutInitA.__init__, CustomProtocolWithoutInitB.__init__) class TypedDictTests(BaseTestCase): From ab64fc9607e941fa1951ae3f64f3a1fc2b917d65 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 20:35:28 +0100 Subject: [PATCH 09/32] Remove remaining sys.version_info gates --- .../src_py3/test_typing_extensions.py | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index ea88f214c..136220db0 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -791,8 +791,6 @@ def new_user(user_class: Type[U]) -> U: new_user(BasicUser) - @skipUnless(sys.version_info[:3] != (3, 5, 2), - 'Python 3.5.2 has a somewhat buggy Type impl') def test_type_optional(self): A = Optional[Type[BaseException]] @@ -1453,9 +1451,7 @@ def test_basics_iterable_syntax(self): Emp = TypedDict('Emp', {'name': str, 'id': int}) self.assertIsSubclass(Emp, dict) self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) + self.assertNotIsSubclass(Emp, collections.abc.Sequence) jim = Emp(name='Jim', id=1) self.assertIs(type(jim), dict) self.assertEqual(jim['name'], 'Jim') @@ -1470,9 +1466,7 @@ def test_basics_keywords_syntax(self): Emp = TypedDict('Emp', name=str, id=int) self.assertIsSubclass(Emp, dict) self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) + self.assertNotIsSubclass(Emp, collections.abc.Sequence) jim = Emp(name='Jim', id=1) self.assertIs(type(jim), dict) self.assertEqual(jim['name'], 'Jim') @@ -1919,16 +1913,11 @@ def test_valid_uses(self): P = ParamSpec('P') T = TypeVar('T') C1 = typing.Callable[P, int] - # Callable in Python 3.5.2 might be bugged when collecting __args__. - # https://github.com/python/cpython/blob/91185fe0284a04162e0b3425b53be49bdbfad67d/Lib/typing.py#L1026 - PY_3_5_2 = sys.version_info[:3] == (3, 5, 2) - if not PY_3_5_2: - self.assertEqual(C1.__args__, (P, int)) - self.assertEqual(C1.__parameters__, (P,)) + self.assertEqual(C1.__args__, (P, int)) + self.assertEqual(C1.__parameters__, (P,)) C2 = typing.Callable[P, T] - if not PY_3_5_2: - self.assertEqual(C2.__args__, (P, T)) - self.assertEqual(C2.__parameters__, (P, T)) + self.assertEqual(C2.__args__, (P, T)) + self.assertEqual(C2.__parameters__, (P, T)) # Test collections.abc.Callable too. From 14236e844b0d0971183532d62f475d861c084cf1 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:30:09 +0100 Subject: [PATCH 10/32] Address flake8 warnings --- .../src_py3/test_typing_extensions.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 136220db0..1eb8f4c34 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -7,13 +7,15 @@ import subprocess import types from unittest import TestCase, main, skipUnless, skipIf +from test import ann_module, ann_module2, ann_module3 from typing import TypeVar, Optional, Union from typing import T, KT, VT # Not in __all__. -from typing import Tuple, List, Dict, Iterator, Callable -from typing import Generic +from typing import Tuple, List, Dict, Iterable, Iterator, Callable +from typing import Generic, NamedTuple from typing import no_type_check from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard +from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager try: from typing_extensions import Protocol, runtime, runtime_checkable @@ -302,8 +304,6 @@ def blah(): blah() -from typing import Iterable -from typing_extensions import Awaitable, AsyncIterator T_a = TypeVar('T_a') @@ -334,13 +334,11 @@ async def __anext__(self) -> T_a: class ACM: async def __aenter__(self) -> int: return 42 + async def __aexit__(self, etype, eval, tb): return None -from test import ann_module, ann_module2, ann_module3 -from typing_extensions import AsyncContextManager -from typing import NamedTuple class A: y: float @@ -363,8 +361,10 @@ class NoneAndForward: class XRepr(NamedTuple): x: int y: int = 1 + def __str__(self): return f'{self.x} -> {self.y}' + def __add__(self, other): return 0 @@ -408,6 +408,7 @@ class Animal(BaseAnimal, total=False): class Cat(Animal): fur_color: str + gth = get_type_hints @@ -849,6 +850,7 @@ class Position(XAxis, YAxis, Protocol): @runtime class Proto(Protocol): attr: int + def meth(self, arg: str) -> int: ... @@ -857,6 +859,7 @@ class Concrete(Proto): class Other: attr: int = 1 + def meth(self, arg: str) -> int: if arg == 'this': return 1 From 406c807609ba96fded6b8a58c58406ae33076f7e Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:46:38 +0100 Subject: [PATCH 11/32] Unconditionally import where possible --- .../src_py3/typing_extensions.py | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index bd7791a16..47567237b 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1,9 +1,10 @@ -from _collections_abc import _check_methods as _check_methods_in_mro +from _collections_abc import _check_methods as _check_methods_in_mro # noqa import abc import collections import contextlib import sys import typing +from typing import _tp_cache, _TypingEllipsis, _TypingEmpty # noqa import collections.abc as collections_abc import operator @@ -27,25 +28,8 @@ from typing import _type_vars, _next_in_mro, _type_check except ImportError: OLD_GENERICS = True -try: +if sys.version_info[:2] == (3, 6): from typing import _subs_tree # noqa - SUBS_TREE = True -except ImportError: - SUBS_TREE = False -try: - from typing import _tp_cache -except ImportError: - def _tp_cache(x): - return x -try: - from typing import _TypingEllipsis, _TypingEmpty -except ImportError: - class _TypingEllipsis: - pass - - class _TypingEmpty: - pass - # The two functions below are copies of typing internal helpers. # They are needed by _ProtocolMeta @@ -139,17 +123,10 @@ def _generic_new(base_cls, cls, *args, **kwargs): 'TYPE_CHECKING', ] -# Annotated relies on substitution trees of pep 560. It will not work for -# versions of typing older than 3.5.3 -HAVE_ANNOTATED = PEP_560 or SUBS_TREE - if PEP_560: __all__.extend(["get_args", "get_origin", "get_type_hints"]) -if HAVE_ANNOTATED: - __all__.append("Annotated") - -__all__.extend(['Protocol', 'runtime', 'runtime_checkable']) +__all__.extend(['Annotated', 'Protocol', 'runtime', 'runtime_checkable']) # TODO @@ -1882,7 +1859,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): return hint return {k: _strip_annotations(t) for k, t in hint.items()} -elif HAVE_ANNOTATED: +else: def _is_dunder(name): """Returns True if name is a __dunder_variable_name__.""" From 5579ed893a6818283a13ed5f6a6101d9541ac856 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 21:48:45 +0100 Subject: [PATCH 12/32] _geqv was removed in Python 3.6 --- .../src_py3/typing_extensions.py | 81 ++----------------- 1 file changed, 8 insertions(+), 73 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 47567237b..a1b650ddc 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -72,14 +72,6 @@ def _check_generic(cls, parameters): def _generic_new(base_cls, cls, *args, **kwargs): return base_cls.__new__(cls, *args, **kwargs) -# See https://github.com/python/typing/pull/439 -if hasattr(typing, '_geqv'): - from typing import _geqv - _geqv_defined = True -else: - _geqv = None - _geqv_defined = False - # Please keep __all__ alphabetized within each category. __all__ = [ # Super-special typing primitives. @@ -788,16 +780,6 @@ class AsyncIterator(AsyncIterable[T_co], if hasattr(typing, 'Deque'): Deque = typing.Deque -elif _geqv_defined: - class Deque(collections.deque, typing.MutableSequence[T], - metaclass=_ExtensionsGenericMeta, - extra=collections.deque): - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Deque): - return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) else: class Deque(collections.deque, typing.MutableSequence[T], metaclass=_ExtensionsGenericMeta, @@ -874,17 +856,6 @@ def __subclasshook__(cls, C): if hasattr(typing, 'DefaultDict'): DefaultDict = typing.DefaultDict -elif _geqv_defined: - class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.defaultdict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, DefaultDict): - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) else: class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], metaclass=_ExtensionsGenericMeta, @@ -902,17 +873,6 @@ def __new__(cls, *args, **kwds): OrderedDict = typing.OrderedDict elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2): OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) -elif _geqv_defined: - class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.OrderedDict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, OrderedDict): - return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) else: class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], metaclass=_ExtensionsGenericMeta, @@ -928,19 +888,6 @@ def __new__(cls, *args, **kwds): if hasattr(typing, 'Counter'): Counter = typing.Counter - -elif _geqv_defined: - class Counter(collections.Counter, - typing.Dict[T, int], - metaclass=_ExtensionsGenericMeta, extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Counter): - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - else: class Counter(collections.Counter, typing.Dict[T, int], @@ -959,28 +906,16 @@ def __new__(cls, *args, **kwds): __all__.append('ChainMap') elif hasattr(collections, 'ChainMap'): # ChainMap only exists in 3.3+ - if _geqv_defined: - class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.ChainMap): + class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], + metaclass=_ExtensionsGenericMeta, + extra=collections.ChainMap): - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, ChainMap): - return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) - else: - class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.ChainMap): - - __slots__ = () + __slots__ = () - def __new__(cls, *args, **kwds): - if cls._gorg is ChainMap: - return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) + def __new__(cls, *args, **kwds): + if cls._gorg is ChainMap: + return collections.ChainMap(*args, **kwds) + return _generic_new(collections.ChainMap, cls, *args, **kwds) __all__.append('ChainMap') From 93b0a7d1c5943ccef89be5262d5a07e8e65d5dd0 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 22:31:01 +0100 Subject: [PATCH 13/32] Unconditionally define values where possible --- .../src_py3/typing_extensions.py | 750 ++---------------- 1 file changed, 83 insertions(+), 667 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index a1b650ddc..b2633ee4f 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1,11 +1,10 @@ from _collections_abc import _check_methods as _check_methods_in_mro # noqa import abc import collections -import contextlib +import collections.abc import sys import typing from typing import _tp_cache, _TypingEllipsis, _TypingEmpty # noqa -import collections.abc as collections_abc import operator # These are used by Protocol implementation @@ -82,15 +81,13 @@ def _generic_new(base_cls, cls, *args, **kwargs): 'Type', # ABCs (from collections.abc). - # The following are added depending on presence - # of their non-generic counterparts in stdlib: - # 'Awaitable', - # 'AsyncIterator', - # 'AsyncIterable', - # 'Coroutine', - # 'AsyncGenerator', - # 'AsyncContextManager', - # 'ChainMap', + 'Awaitable', + 'AsyncIterator', + 'AsyncIterable', + 'Coroutine', + 'AsyncGenerator', + 'AsyncContextManager', + 'ChainMap', # Concrete collection types. 'ContextManager', @@ -120,11 +117,11 @@ def _generic_new(base_cls, cls, *args, **kwargs): __all__.extend(['Annotated', 'Protocol', 'runtime', 'runtime_checkable']) - -# TODO +# 3.6.2+ if hasattr(typing, 'NoReturn'): NoReturn = typing.NoReturn -elif hasattr(typing, '_FinalTypingBase'): +# 3.6.0-3.6.1 +else: class _NoReturn(typing._FinalTypingBase, _root=True): """Special type indicating functions that never return. Example:: @@ -146,32 +143,6 @@ def __subclasscheck__(self, cls): raise TypeError("NoReturn cannot be used with issubclass().") NoReturn = _NoReturn(_root=True) -else: - class _NoReturnMeta(typing.TypingMeta): - """Metaclass for NoReturn""" - def __new__(cls, name, bases, namespace, _root=False): - return super().__new__(cls, name, bases, namespace, _root=_root) - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - class NoReturn(typing.Final, metaclass=_NoReturnMeta, _root=True): - """Special type indicating functions that never return. - Example:: - - from typing import NoReturn - - def stop() -> NoReturn: - raise Exception('no way') - - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - __slots__ = () - # Some unconstrained type variables. These are used by the container types. # (These are not for export.) @@ -183,138 +154,13 @@ def stop() -> NoReturn: VT_co = typing.TypeVar('VT_co', covariant=True) # Value type covariant containers. T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. - -if hasattr(typing, 'ClassVar'): - ClassVar = typing.ClassVar -elif hasattr(typing, '_FinalTypingBase'): - class _ClassVar(typing._FinalTypingBase, _root=True): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable - damage: int = 10 # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(typing._type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - ClassVar = _ClassVar(_root=True) -else: - class _ClassVarMeta(typing.TypingMeta): - """Metaclass for ClassVar""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("ClassVar cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("ClassVar cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class ClassVar(typing.Final, metaclass=_ClassVarMeta, _root=True): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable - damage: int = 10 # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __type__ = None +ClassVar = typing.ClassVar # On older versions of typing there is an internal class named "Final". +# 3.8+ if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): Final = typing.Final +# 3.7 elif sys.version_info[:2] >= (3, 7): class _FinalForm(typing._SpecialForm, _root=True): @@ -340,7 +186,8 @@ class FastConnector(Connection): TIMEOUT = 1 # Error reported by type checker There is no runtime checking of these properties.""") -elif hasattr(typing, '_FinalTypingBase'): +# 3.6 +else: class _Final(typing._FinalTypingBase, _root=True): """A special typing construct to indicate that a name cannot be re-assigned or overridden in a subclass. @@ -394,79 +241,12 @@ def __eq__(self, other): return self is other Final = _Final(_root=True) -else: - class _FinalMeta(typing.TypingMeta): - """Metaclass for Final""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("Final cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Final cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, Final): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class Final(typing.Final, metaclass=_FinalMeta, _root=True): - """A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties. - """ - - __type__ = None +# 3.8+ if hasattr(typing, 'final'): final = typing.final +# 3.6-3.7 else: def final(f): """This decorator can be used to indicate to type checkers that @@ -495,8 +275,10 @@ def IntVar(name): return TypeVar(name) +# 3.8+: if hasattr(typing, 'Literal'): Literal = typing.Literal +# 3.7: elif sys.version_info[:2] >= (3, 7): class _LiteralForm(typing._SpecialForm, _root=True): @@ -519,7 +301,8 @@ def __getitem__(self, parameters): Literal[...] cannot be subclassed. There is no runtime checking verifying that the parameter is actually a value instead of a type.""") -elif hasattr(typing, '_FinalTypingBase'): +# 3.6: +else: class _Literal(typing._FinalTypingBase, _root=True): """A type that can be used to indicate to type checkers that the corresponding value has a value literally equivalent to the @@ -568,139 +351,14 @@ def __eq__(self, other): return self is other Literal = _Literal(_root=True) -else: - class _LiteralMeta(typing.TypingMeta): - """Metaclass for Literal""" - def __new__(cls, name, bases, namespace, values=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if values is not None: - self.__values__ = values - return self - def __instancecheck__(self, obj): - raise TypeError("Literal cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Literal cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__values__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - if not isinstance(item, tuple): - item = (item,) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), values=item, _root=True) - - def _eval_type(self, globalns, localns): - return self - - def __repr__(self): - r = super().__repr__() - if self.__values__ is not None: - r += '[{}]'.format(', '.join(map(typing._type_repr, self.__values__))) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__values__)) - - def __eq__(self, other): - if not isinstance(other, Literal): - return NotImplemented - if self.__values__ is not None: - return self.__values__ == other.__values__ - return self is other - - class Literal(typing.Final, metaclass=_LiteralMeta, _root=True): - """A type that can be used to indicate to type checkers that the - corresponding value has a value literally equivalent to the - provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to the - value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime checking - verifying that the parameter is actually a value instead of a type. - """ - - __values__ = None - - -def _overload_dummy(*args, **kwds): - """Helper for @overload to raise when called.""" - raise NotImplementedError( - "You should not call an overloaded function. " - "A series of @overload-decorated functions " - "outside a stub module should always be followed " - "by an implementation that is not @overload-ed.") - - -def overload(func): - """Decorator for overloaded functions/methods. - - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - def utf8(value): - # implementation goes here - """ - return _overload_dummy +_overload_dummy = typing._overload_dummy # noqa +overload = typing.overload # This is not a real generic class. Don't use outside annotations. -if hasattr(typing, 'Type'): - Type = typing.Type -else: - # Internal type variable used for Type[]. - CT_co = typing.TypeVar('CT_co', covariant=True, bound=type) - - class Type(typing.Generic[CT_co], extra=type): - """A special construct usable to annotate class objects. - - For example, suppose we have the following classes:: - - class User: ... # Abstract base for User classes - class BasicUser(User): ... - class ProUser(User): ... - class TeamUser(User): ... - - And a function that takes a class argument that's a subclass of - User and returns an instance of the corresponding class:: - - U = TypeVar('U', bound=User) - def new_user(user_class: Type[U]) -> U: - user = user_class() - # (Here we could write the user object to a database) - return user - joe = new_user(BasicUser) - - At this point the type checker knows that joe has type BasicUser. - """ - - __slots__ = () - +Type = typing.Type # Various ABCs mimicking those in collections.abc. # A few are simply re-exported for completeness. @@ -751,35 +409,15 @@ def __subclasscheck__(self, subclass): return False -if _define_guard('Awaitable'): - class Awaitable(typing.Generic[T_co], metaclass=_ExtensionsGenericMeta, - extra=collections_abc.Awaitable): - __slots__ = () - - -if _define_guard('Coroutine'): - class Coroutine(Awaitable[V_co], typing.Generic[T_co, T_contra, V_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.Coroutine): - __slots__ = () - - -if _define_guard('AsyncIterable'): - class AsyncIterable(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncIterable): - __slots__ = () - - -if _define_guard('AsyncIterator'): - class AsyncIterator(AsyncIterable[T_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncIterator): - __slots__ = () - +Awaitable = typing.Awaitable +Coroutine = typing.Coroutine +AsyncIterable = typing.AsyncIterable +AsyncIterator = typing.AsyncIterator +# 3.6.1+ if hasattr(typing, 'Deque'): Deque = typing.Deque +# 3.6.0 else: class Deque(collections.deque, typing.MutableSequence[T], metaclass=_ExtensionsGenericMeta, @@ -791,49 +429,11 @@ def __new__(cls, *args, **kwds): return collections.deque(*args, **kwds) return _generic_new(collections.deque, cls, *args, **kwds) - -if hasattr(typing, 'ContextManager'): - ContextManager = typing.ContextManager -elif hasattr(contextlib, 'AbstractContextManager'): - class ContextManager(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=contextlib.AbstractContextManager): - __slots__ = () -else: - class ContextManager(typing.Generic[T_co]): - __slots__ = () - - def __enter__(self): - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - - +ContextManager = typing.ContextManager +# 3.6.2+ if hasattr(typing, 'AsyncContextManager'): AsyncContextManager = typing.AsyncContextManager - __all__.append('AsyncContextManager') -elif hasattr(contextlib, 'AbstractAsyncContextManager'): - class AsyncContextManager(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=contextlib.AbstractAsyncContextManager): - __slots__ = () - - __all__.append('AsyncContextManager') +# 3.6.0-3.6.1 else: class AsyncContextManager(typing.Generic[T_co]): __slots__ = () @@ -851,28 +451,15 @@ def __subclasshook__(cls, C): return _check_methods_in_mro(C, "__aenter__", "__aexit__") return NotImplemented - __all__.append('AsyncContextManager') - - -if hasattr(typing, 'DefaultDict'): - DefaultDict = typing.DefaultDict -else: - class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.defaultdict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is DefaultDict: - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) - +DefaultDict = typing.DefaultDict +# 3.7.2+ if hasattr(typing, 'OrderedDict'): OrderedDict = typing.OrderedDict +# 3.7.0-3.7.2 elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2): OrderedDict = typing._alias(collections.OrderedDict, (KT, VT)) +# 3.6 else: class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], metaclass=_ExtensionsGenericMeta, @@ -885,9 +472,10 @@ def __new__(cls, *args, **kwds): return collections.OrderedDict(*args, **kwds) return _generic_new(collections.OrderedDict, cls, *args, **kwds) - +# 3.6.2+ if hasattr(typing, 'Counter'): Counter = typing.Counter +# 3.6.0-3.6.1 else: class Counter(collections.Counter, typing.Dict[T, int], @@ -900,12 +488,10 @@ def __new__(cls, *args, **kwds): return collections.Counter(*args, **kwds) return _generic_new(collections.Counter, cls, *args, **kwds) - +# 3.6.1+ if hasattr(typing, 'ChainMap'): ChainMap = typing.ChainMap - __all__.append('ChainMap') elif hasattr(collections, 'ChainMap'): - # ChainMap only exists in 3.3+ class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], metaclass=_ExtensionsGenericMeta, extra=collections.ChainMap): @@ -917,57 +503,19 @@ def __new__(cls, *args, **kwds): return collections.ChainMap(*args, **kwds) return _generic_new(collections.ChainMap, cls, *args, **kwds) - __all__.append('ChainMap') - - -if _define_guard('AsyncGenerator'): +# 3.6.1+ +if hasattr(typing, 'AsyncGenerator'): + AsyncGenerator = typing.AsyncGenerator +# 3.6.0 +else: class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra], metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncGenerator): + extra=collections.abc.AsyncGenerator): __slots__ = () - -if hasattr(typing, 'NewType'): - NewType = typing.NewType -else: - def NewType(name, tp): - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy function that simply returns its argument. Usage:: - - UserId = NewType('UserId', int) - - def name_by_id(user_id: UserId) -> str: - ... - - UserId('user') # Fails type check - - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - - num = UserId(5) + 1 # type: int - """ - - def new_type(x): - return x - - new_type.__name__ = name - new_type.__supertype__ = tp - return new_type - - -if hasattr(typing, 'Text'): - Text = typing.Text -else: - Text = str - - -if hasattr(typing, 'TYPE_CHECKING'): - TYPE_CHECKING = typing.TYPE_CHECKING -else: - # Constant that's True when type checking, but False here. - TYPE_CHECKING = False +NewType = typing.NewType +Text = typing.Text +TYPE_CHECKING = typing.TYPE_CHECKING def _gorg(cls): @@ -1019,8 +567,10 @@ def _is_callable_members_only(cls): return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) +# 3.8+ if hasattr(typing, 'Protocol'): Protocol = typing.Protocol +# 3.6 elif not PEP_560: def _no_init(self, *args, **kwargs): @@ -1266,9 +816,8 @@ def __new__(cls, *args, **kwds): if Protocol.__doc__ is not None: Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol, Generic[T]" if OLD_GENERICS else "Protocol[T]") - - -elif PEP_560: +# 3.7 +else: from typing import _type_check, _collect_type_vars # noqa def _no_init(self, *args, **kwargs): @@ -1454,8 +1003,10 @@ def _proto_hook(other): cls.__init__ = _no_init +# 3.8+ if hasattr(typing, 'runtime_checkable'): runtime_checkable = typing.runtime_checkable +# 3.6-3.7 else: def runtime_checkable(cls): """Mark a protocol class as a runtime protocol, so that it @@ -1476,8 +1027,10 @@ def runtime_checkable(cls): runtime = runtime_checkable +# 3.8+ if hasattr(typing, 'SupportsIndex'): SupportsIndex = typing.SupportsIndex +# 3.6-3.7 else: @runtime_checkable class SupportsIndex(Protocol): @@ -1648,6 +1201,7 @@ class Point2D(TypedDict): # Not exported and not a public API, but needed for get_origin() and get_args() # to work. _AnnotatedAlias = typing._AnnotatedAlias +# 3.7-3.8 elif PEP_560: class _AnnotatedAlias(typing._GenericAlias, _root=True): """Runtime representation of an annotated type. @@ -1793,7 +1347,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False): if include_extras: return hint return {k: _strip_annotations(t) for k, t in hint.items()} - +# 3.6 else: def _is_dunder(name): @@ -1935,12 +1489,12 @@ class Annotated(metaclass=AnnotatedMeta): """ # Python 3.8 has get_origin() and get_args() but those implementations aren't -# Annotated-aware, so we can't use those, only Python 3.9 versions will do. -# Similarly, Python 3.9's implementation doesn't support ParamSpecArgs and -# ParamSpecKwargs. +# Annotated-aware, so we can't use those. Python 3.9's versions don't support +# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do. if sys.version_info[:2] >= (3, 10): get_origin = typing.get_origin get_args = typing.get_args +# 3.7-3.9 elif PEP_560: try: # 3.9+ @@ -2000,8 +1554,10 @@ def get_args(tp): return () +# 3.10+ if hasattr(typing, 'TypeAlias'): TypeAlias = typing.TypeAlias +# 3.9 elif sys.version_info[:2] >= (3, 9): class _TypeAliasForm(typing._SpecialForm, _root=True): def __repr__(self): @@ -2020,7 +1576,7 @@ def TypeAlias(self, parameters): It's invalid when used anywhere except as in the example above. """ raise TypeError("{} is not subscriptable".format(self)) - +# 3.7-3.8 elif sys.version_info[:2] >= (3, 7): class _TypeAliasForm(typing._SpecialForm, _root=True): def __repr__(self): @@ -2037,8 +1593,8 @@ def __repr__(self): It's invalid when used anywhere except as in the example above.""") - -elif hasattr(typing, '_FinalTypingBase'): +# 3.6 +else: class _TypeAliasMeta(typing.TypingMeta): """Metaclass for TypeAlias""" @@ -2068,37 +1624,13 @@ def __repr__(self): return 'typing_extensions.TypeAlias' TypeAlias = _TypeAliasBase(_root=True) -else: - class _TypeAliasMeta(typing.TypingMeta): - """Metaclass for TypeAlias""" - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __call__(self, *args, **kwargs): - raise TypeError("Cannot instantiate TypeAlias") - - class TypeAlias(metaclass=_TypeAliasMeta, _root=True): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example above. - """ - __slots__ = () # Python 3.10+ has PEP 612 if hasattr(typing, 'ParamSpecArgs'): ParamSpecArgs = typing.ParamSpecArgs ParamSpecKwargs = typing.ParamSpecKwargs +# 3.6-3.9 else: class _Immutable: """Mixin to indicate that object should not be copied.""" @@ -2146,8 +1678,10 @@ def __init__(self, origin): def __repr__(self): return "{}.kwargs".format(self.__origin__.__name__) +# 3.10+ if hasattr(typing, 'ParamSpec'): ParamSpec = typing.ParamSpec +# 3.6-3.9 else: # Inherits from list as a workaround for Callable checks in Python < 3.9.2. @@ -2256,6 +1790,7 @@ def _get_type_vars(self, tvars): tvars.append(self) +# 3.6-3.9 if not hasattr(typing, 'Concatenate'): # Inherits from list as a workaround for Callable checks in Python < 3.9.2. class _ConcatenateGenericAlias(list): @@ -2302,6 +1837,7 @@ def _get_type_vars(self, tvars): typing._get_type_vars(self.__parameters__, tvars) +# 3.6-3.9 @_tp_cache def _concatenate_getitem(self, parameters): if parameters == (): @@ -2316,9 +1852,11 @@ def _concatenate_getitem(self, parameters): return _ConcatenateGenericAlias(self, parameters) +# 3.10+ if hasattr(typing, 'Concatenate'): Concatenate = typing.Concatenate _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa +# 3.9 elif sys.version_info[:2] >= (3, 9): @_TypeAliasForm def Concatenate(self, parameters): @@ -2333,7 +1871,7 @@ def Concatenate(self, parameters): See PEP 612 for detailed information. """ return _concatenate_getitem(self, parameters) - +# 3.7-8 elif sys.version_info[:2] >= (3, 7): class _ConcatenateForm(typing._SpecialForm, _root=True): def __repr__(self): @@ -2354,8 +1892,8 @@ def __getitem__(self, parameters): See PEP 612 for detailed information. """) - -elif hasattr(typing, '_FinalTypingBase'): +# 3.6 +else: class _ConcatenateAliasMeta(typing.TypingMeta): """Metaclass for Concatenate.""" @@ -2390,38 +1928,11 @@ def __getitem__(self, parameters): return _concatenate_getitem(self, parameters) Concatenate = _ConcatenateAliasBase(_root=True) -# For 3.5.0 - 3.5.2 -else: - class _ConcatenateAliasMeta(typing.TypingMeta): - """Metaclass for Concatenate.""" - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __call__(self, *args, **kwargs): - raise TypeError("Cannot instantiate TypeAlias") - - def __getitem__(self, parameters): - return _concatenate_getitem(self, parameters) - - class Concatenate(metaclass=_ConcatenateAliasMeta, _root=True): - """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a - higher order function which adds, removes or transforms parameters of a - callable. - - For example:: - - Callable[Concatenate[int, P], int] - - See PEP 612 for detailed information. - """ - __slots__ = () +# 3.10+ if hasattr(typing, 'TypeGuard'): TypeGuard = typing.TypeGuard +# 3.9 elif sys.version_info[:2] >= (3, 9): class _TypeGuardForm(typing._SpecialForm, _root=True): def __repr__(self): @@ -2473,7 +1984,7 @@ def is_str(val: Union[str, float]): """ item = typing._type_check(parameters, '{} accepts only single type.'.format(self)) return _GenericAlias(self, (item,)) - +# 3.7-3.8 elif sys.version_info[:2] >= (3, 7): class _TypeGuardForm(typing._SpecialForm, _root=True): @@ -2529,7 +2040,8 @@ def is_str(val: Union[str, float]): ``TypeGuard`` also works with type variables. For more information, see PEP 647 (User-Defined Type Guards). """) -elif hasattr(typing, '_FinalTypingBase'): +# 3.6 +else: class _TypeGuard(typing._FinalTypingBase, _root=True): """Special typing form used to annotate the return type of a user-defined type guard function. ``TypeGuard`` only accepts a single type argument. @@ -2611,99 +2123,3 @@ def __eq__(self, other): return self is other TypeGuard = _TypeGuard(_root=True) -else: - class _TypeGuardMeta(typing.TypingMeta): - """Metaclass for TypeGuard""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("TypeGuard cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeGuard cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not hasattr(other, "__type__"): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class TypeGuard(typing.Final, metaclass=_TypeGuardMeta, _root=True): - """Special typing form used to annotate the return type of a user-defined - type guard function. ``TypeGuard`` only accepts a single type argument. - At runtime, functions marked this way should return a boolean. - - ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static - type checkers to determine a more precise type of an expression within a - program's code flow. Usually type narrowing is done by analyzing - conditional code flow and applying the narrowing to a block of code. The - conditional expression here is sometimes referred to as a "type guard". - - Sometimes it would be convenient to use a user-defined boolean function - as a type guard. Such a function should use ``TypeGuard[...]`` as its - return type to alert static type checkers to this intention. - - Using ``-> TypeGuard`` tells the static type checker that for a given - function: - - 1. The return value is a boolean. - 2. If the return value is ``True``, the type of its argument - is the type inside ``TypeGuard``. - - For example:: - - def is_str(val: Union[str, float]): - # "isinstance" type guard - if isinstance(val, str): - # Type of ``val`` is narrowed to ``str`` - ... - else: - # Else, type of ``val`` is narrowed to ``float``. - ... - - Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower - form of ``TypeA`` (it can even be a wider form) and this may lead to - type-unsafe results. The main reason is to allow for things like - narrowing ``List[object]`` to ``List[str]`` even though the latter is not - a subtype of the former, since ``List`` is invariant. The responsibility of - writing type-safe type guards is left to the user. - - ``TypeGuard`` also works with type variables. For more information, see - PEP 647 (User-Defined Type Guards). - """ - __type__ = None From d3dcbccbfc36c0801cd5324034c3557142e9b259 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 22:31:38 +0100 Subject: [PATCH 14/32] Remove now unused _define_guard --- typing_extensions/src_py3/typing_extensions.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index b2633ee4f..78ca49682 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -363,24 +363,6 @@ def __eq__(self, other): # Various ABCs mimicking those in collections.abc. # A few are simply re-exported for completeness. -def _define_guard(type_name): - """ - Returns True if the given type isn't defined in typing but - is defined in collections_abc. - - Adds the type to __all__ if the collection is found in either - typing or collection_abc. - """ - if hasattr(typing, type_name): - __all__.append(type_name) - globals()[type_name] = getattr(typing, type_name) - return False - elif hasattr(collections_abc, type_name): - __all__.append(type_name) - return True - else: - return False - class _ExtensionsGenericMeta(GenericMeta): def __subclasscheck__(self, subclass): From ea6657a7d5d2ea1b4f56a852881b8104908fd320 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:17:24 +0100 Subject: [PATCH 15/32] Remove OLD_GENERICS --- .../src_py3/typing_extensions.py | 236 ++++++++---------- 1 file changed, 108 insertions(+), 128 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 78ca49682..aef764700 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -22,13 +22,8 @@ from typing import _GenericAlias else: from typing import GenericMeta, TypingMeta -OLD_GENERICS = False -try: - from typing import _type_vars, _next_in_mro, _type_check -except ImportError: - OLD_GENERICS = True if sys.version_info[:2] == (3, 6): - from typing import _subs_tree # noqa + from typing import _type_vars, _next_in_mro, _type_check, _subs_tree # noqa # The two functions below are copies of typing internal helpers. # They are needed by _ProtocolMeta @@ -510,16 +505,6 @@ def _gorg(cls): return cls -if OLD_GENERICS: - def _next_in_mro(cls): # noqa - """This function exists for compatibility with old typing versions.""" - next_in_mro = object - for i, c in enumerate(cls.__mro__[:-1]): - if isinstance(c, GenericMeta) and _gorg(c) is Generic: - next_in_mro = cls.__mro__[i + 1] - return next_in_mro - - _PROTO_WHITELIST = ['Callable', 'Awaitable', 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', @@ -565,71 +550,70 @@ class _ProtocolMeta(GenericMeta): This exists so Protocol classes can be generic without deriving from Generic. """ - if not OLD_GENERICS: - def __new__(cls, name, bases, namespace, - tvars=None, args=None, origin=None, extra=None, orig_bases=None): - # This is just a version copied from GenericMeta.__new__ that - # includes "Protocol" special treatment. (Comments removed for brevity.) - assert extra is None # Protocols should not have extra - if tvars is not None: - assert origin is not None - assert all(isinstance(t, TypeVar) for t in tvars), tvars - else: - tvars = _type_vars(bases) - gvars = None - for base in bases: - if base is Generic: - raise TypeError("Cannot inherit from plain Generic") - if (isinstance(base, GenericMeta) and - base.__origin__ in (Generic, Protocol)): - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...] or" - " Protocol[...] multiple times.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: + def __new__(cls, name, bases, namespace, + tvars=None, args=None, origin=None, extra=None, orig_bases=None): + # This is just a version copied from GenericMeta.__new__ that + # includes "Protocol" special treatment. (Comments removed for brevity.) + assert extra is None # Protocols should not have extra + if tvars is not None: + assert origin is not None + assert all(isinstance(t, TypeVar) for t in tvars), tvars + else: + tvars = _type_vars(bases) + gvars = None + for base in bases: + if base is Generic: + raise TypeError("Cannot inherit from plain Generic") + if (isinstance(base, GenericMeta) and + base.__origin__ in (Generic, Protocol)): + if gvars is not None: raise TypeError( - "Some type variables (%s) " - "are not listed in %s[%s]" % - (", ".join(str(t) for t in tvars if t not in gvarset), - "Generic" if any(b.__origin__ is Generic - for b in bases) else "Protocol", - ", ".join(str(g) for g in gvars))) - tvars = gvars - - initial_bases = bases - if (extra is not None and type(extra) is abc.ABCMeta and - extra not in bases): - bases = (extra,) + bases - bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b - for b in bases) - if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): - bases = tuple(b for b in bases if b is not Generic) - namespace.update({'__origin__': origin, '__extra__': extra}) - self = super(GenericMeta, cls).__new__(cls, name, bases, namespace, - _root=True) - super(GenericMeta, self).__setattr__('_gorg', - self if not origin else - _gorg(origin)) - self.__parameters__ = tvars - self.__args__ = tuple(... if a is _TypingEllipsis else - () if a is _TypingEmpty else - a for a in args) if args else None - self.__next_in_mro__ = _next_in_mro(self) - if orig_bases is None: - self.__orig_bases__ = initial_bases - elif origin is not None: - self._abc_registry = origin._abc_registry - self._abc_cache = origin._abc_cache - if hasattr(self, '_subs_tree'): - self.__tree_hash__ = (hash(self._subs_tree()) if origin else - super(GenericMeta, self).__hash__()) - return self + "Cannot inherit from Generic[...] or" + " Protocol[...] multiple times.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + raise TypeError( + "Some type variables (%s) " + "are not listed in %s[%s]" % + (", ".join(str(t) for t in tvars if t not in gvarset), + "Generic" if any(b.__origin__ is Generic + for b in bases) else "Protocol", + ", ".join(str(g) for g in gvars))) + tvars = gvars + + initial_bases = bases + if (extra is not None and type(extra) is abc.ABCMeta and + extra not in bases): + bases = (extra,) + bases + bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b + for b in bases) + if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): + bases = tuple(b for b in bases if b is not Generic) + namespace.update({'__origin__': origin, '__extra__': extra}) + self = super(GenericMeta, cls).__new__(cls, name, bases, namespace, + _root=True) + super(GenericMeta, self).__setattr__('_gorg', + self if not origin else + _gorg(origin)) + self.__parameters__ = tvars + self.__args__ = tuple(... if a is _TypingEllipsis else + () if a is _TypingEmpty else + a for a in args) if args else None + self.__next_in_mro__ = _next_in_mro(self) + if orig_bases is None: + self.__orig_bases__ = initial_bases + elif origin is not None: + self._abc_registry = origin._abc_registry + self._abc_cache = origin._abc_cache + if hasattr(self, '_subs_tree'): + self.__tree_hash__ = (hash(self._subs_tree()) if origin else + super(GenericMeta, self).__hash__()) + return self def __init__(cls, *args, **kwargs): super().__init__(*args, **kwargs) @@ -714,47 +698,46 @@ def __subclasscheck__(self, cls): " don't support issubclass()") return super(GenericMeta, self).__subclasscheck__(cls) - if not OLD_GENERICS: - @_tp_cache - def __getitem__(self, params): - # We also need to copy this from GenericMeta.__getitem__ to get - # special treatment of "Protocol". (Comments removed for brevity.) - if not isinstance(params, tuple): - params = (params,) - if not params and _gorg(self) is not Tuple: + @_tp_cache + def __getitem__(self, params): + # We also need to copy this from GenericMeta.__getitem__ to get + # special treatment of "Protocol". (Comments removed for brevity.) + if not isinstance(params, tuple): + params = (params,) + if not params and _gorg(self) is not Tuple: + raise TypeError( + "Parameter list to %s[...] cannot be empty" % self.__qualname__) + msg = "Parameters to generic types must be types." + params = tuple(_type_check(p, msg) for p in params) + if self in (Generic, Protocol): + if not all(isinstance(p, TypeVar) for p in params): raise TypeError( - "Parameter list to %s[...] cannot be empty" % self.__qualname__) - msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) - if self in (Generic, Protocol): - if not all(isinstance(p, TypeVar) for p in params): - raise TypeError( - "Parameters to %r[...] must all be type variables" % self) - if len(set(params)) != len(params): - raise TypeError( - "Parameters to %r[...] must all be unique" % self) - tvars = params - args = params - elif self in (Tuple, Callable): - tvars = _type_vars(params) - args = params - elif self.__origin__ in (Generic, Protocol): - raise TypeError("Cannot subscript already-subscripted %s" % - repr(self)) - else: - _check_generic(self, params) - tvars = _type_vars(params) - args = params - - prepend = (self,) if self.__origin__ is None else () - return self.__class__(self.__name__, - prepend + self.__bases__, - _no_slots_copy(self.__dict__), - tvars=tvars, - args=args, - origin=self, - extra=self.__extra__, - orig_bases=self.__orig_bases__) + "Parameters to %r[...] must all be type variables" % self) + if len(set(params)) != len(params): + raise TypeError( + "Parameters to %r[...] must all be unique" % self) + tvars = params + args = params + elif self in (Tuple, Callable): + tvars = _type_vars(params) + args = params + elif self.__origin__ in (Generic, Protocol): + raise TypeError("Cannot subscript already-subscripted %s" % + repr(self)) + else: + _check_generic(self, params) + tvars = _type_vars(params) + args = params + + prepend = (self,) if self.__origin__ is None else () + return self.__class__(self.__name__, + prepend + self.__bases__, + _no_slots_copy(self.__dict__), + tvars=tvars, + args=args, + origin=self, + extra=self.__extra__, + orig_bases=self.__orig_bases__) class Protocol(metaclass=_ProtocolMeta): """Base class for protocol classes. Protocol classes are defined as:: @@ -792,15 +775,12 @@ def __new__(cls, *args, **kwds): if _gorg(cls) is Protocol: raise TypeError("Type Protocol cannot be instantiated; " "it can be used only as a base class") - if OLD_GENERICS: - return _generic_new(_next_in_mro(cls), cls, *args, **kwds) return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) if Protocol.__doc__ is not None: - Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol, Generic[T]" if - OLD_GENERICS else "Protocol[T]") + Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol[T]") # 3.7 else: - from typing import _type_check, _collect_type_vars # noqa + from typing import _collect_type_vars # noqa def _no_init(self, *args, **kwargs): if type(self)._is_protocol: @@ -873,7 +853,7 @@ def __class_getitem__(cls, params): raise TypeError( "Parameter list to {}[...] cannot be empty".format(cls.__qualname__)) msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) + params = tuple(typing._type_check(p, msg) for p in params) # noqa if cls is Protocol: # Generic can only be subscripted with unique type variables. if not all(isinstance(p, TypeVar) for p in params): @@ -1766,7 +1746,7 @@ def __call__(self, *args, **kwargs): pass if not PEP_560: - # Only needed in 3.6 and lower. + # Only needed in 3.6. def _get_type_vars(self, tvars): if self not in tvars: tvars.append(self) @@ -1813,7 +1793,7 @@ def __parameters__(self): ) if not PEP_560: - # Only required in 3.6 and lower. + # Only required in 3.6. def _get_type_vars(self, tvars): if self.__origin__ and self.__parameters__: typing._get_type_vars(self.__parameters__, tvars) From ccdcb441418c806485cc81c8e66d15b4fb71b9be Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:42:07 +0100 Subject: [PATCH 16/32] Use f-strings --- .../src_py3/typing_extensions.py | 62 +++++++++---------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index aef764700..235e146af 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -39,12 +39,12 @@ def _no_slots_copy(dct): def _check_generic(cls, parameters): if not cls.__parameters__: - raise TypeError("%s is not a generic class" % repr(cls)) + raise TypeError(f"{cls} is not a generic class") alen = len(parameters) elen = len(cls.__parameters__) if alen != elen: - raise TypeError("Too %s parameters for %s; actual %s, expected %s" % - ("many" if alen > elen else "few", repr(cls), alen, elen)) + raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};" + f" actual {alen}, expected {elen}") if hasattr(typing, '_generic_new'): @@ -164,7 +164,7 @@ def __repr__(self): def __getitem__(self, parameters): item = typing._type_check(parameters, - '{} accepts only single type'.format(self._name)) + f'{self._name} accepts only single type') return _GenericAlias(self, (item,)) Final = _FinalForm('Final', @@ -208,7 +208,7 @@ def __getitem__(self, item): cls = type(self) if self.__type__ is None: return cls(typing._type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), + f'{cls.__name__[1:]} accepts only single type.'), _root=True) raise TypeError('{} cannot be further subscripted' .format(cls.__name__[1:])) @@ -222,7 +222,7 @@ def _eval_type(self, globalns, localns): def __repr__(self): r = super().__repr__() if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) + r += f'[{typing._type_repr(self.__type__)}]' return r def __hash__(self): @@ -577,13 +577,12 @@ def __new__(cls, name, bases, namespace, tvarset = set(tvars) gvarset = set(gvars) if not tvarset <= gvarset: - raise TypeError( - "Some type variables (%s) " - "are not listed in %s[%s]" % - (", ".join(str(t) for t in tvars if t not in gvarset), - "Generic" if any(b.__origin__ is Generic - for b in bases) else "Protocol", - ", ".join(str(g) for g in gvars))) + s_vars = ", ".join(str(t) for t in tvars if t not in gvarset) + s_args = ", ".join(str(g) for g in gvars) + cls_name = "Generic" if any(b.__origin__ is Generic + for b in bases) else "Protocol" + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {cls_name}[{s_args}]") tvars = gvars initial_bases = bases @@ -630,8 +629,8 @@ def __init__(cls, *args, **kwargs): isinstance(base, TypingMeta) and base._is_protocol or isinstance(base, GenericMeta) and base.__origin__ is Generic): - raise TypeError('Protocols can only inherit from other' - ' protocols, got %r' % base) + raise TypeError(f'Protocols can only inherit from other' + f' protocols, got {repr(base)}') cls.__init__ = _no_init @@ -706,24 +705,23 @@ def __getitem__(self, params): params = (params,) if not params and _gorg(self) is not Tuple: raise TypeError( - "Parameter list to %s[...] cannot be empty" % self.__qualname__) + f"Parameter list to {self.__qualname__}[...] cannot be empty") msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) if self in (Generic, Protocol): if not all(isinstance(p, TypeVar) for p in params): raise TypeError( - "Parameters to %r[...] must all be type variables" % self) + f"Parameters to {repr(self)}[...] must all be type variables") if len(set(params)) != len(params): raise TypeError( - "Parameters to %r[...] must all be unique" % self) + f"Parameters to {repr(self)}[...] must all be unique") tvars = params args = params elif self in (Tuple, Callable): tvars = _type_vars(params) args = params elif self.__origin__ in (Generic, Protocol): - raise TypeError("Cannot subscript already-subscripted %s" % - repr(self)) + raise TypeError(f"Cannot subscript already-subscripted {repr(self)}") else: _check_generic(self, params) tvars = _type_vars(params) @@ -851,7 +849,7 @@ def __class_getitem__(cls, params): params = (params,) if not params and cls is not Tuple: raise TypeError( - "Parameter list to {}[...] cannot be empty".format(cls.__qualname__)) + f"Parameter list to {cls.__qualname__}[...] cannot be empty") msg = "Parameters to generic types must be types." params = tuple(typing._type_check(p, msg) for p in params) # noqa if cls is Protocol: @@ -961,7 +959,7 @@ def _proto_hook(other): base.__name__ in _PROTO_WHITELIST or isinstance(base, _ProtocolMeta) and base._is_protocol): raise TypeError('Protocols can only inherit from other' - ' protocols, got %r' % base) + f' protocols, got {repr(base)}') cls.__init__ = _no_init @@ -980,7 +978,7 @@ def runtime_checkable(cls): """ if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: raise TypeError('@runtime_checkable can be only applied to protocol classes,' - ' got %r' % cls) + f' got {repr(cls)}') cls._is_runtime_protocol = True return cls @@ -1257,7 +1255,7 @@ def __class_getitem__(cls, params): def __init_subclass__(cls, *args, **kwargs): raise TypeError( - "Cannot subclass {}.Annotated".format(cls.__module__) + f"Cannot subclass {cls.__module__}.Annotated" ) def _strip_annotations(t): @@ -1339,7 +1337,7 @@ def _tree_repr(self, tree): else: tp_repr = origin[0]._tree_repr(origin) metadata_reprs = ", ".join(repr(arg) for arg in metadata) - return '%s[%s, %s]' % (cls, tp_repr, metadata_reprs) + return f'{cls}[{tp_repr}, {metadata_reprs}]' def _subs_tree(self, tvars=None, args=None): # noqa if self is Annotated: @@ -1537,7 +1535,7 @@ def TypeAlias(self, parameters): It's invalid when used anywhere except as in the example above. """ - raise TypeError("{} is not subscriptable".format(self)) + raise TypeError(f"{self} is not subscriptable") # 3.7-3.8 elif sys.version_info[:2] >= (3, 7): class _TypeAliasForm(typing._SpecialForm, _root=True): @@ -1620,7 +1618,7 @@ def __init__(self, origin): self.__origin__ = origin def __repr__(self): - return "{}.args".format(self.__origin__.__name__) + return f"{self.__origin__.__name__}.args" class ParamSpecKwargs(_Immutable): """The kwargs for a ParamSpec object. @@ -1638,7 +1636,7 @@ def __init__(self, origin): self.__origin__ = origin def __repr__(self): - return "{}.kwargs".format(self.__origin__.__name__) + return f"{self.__origin__.__name__}.kwargs" # 3.10+ if hasattr(typing, 'ParamSpec'): @@ -1944,7 +1942,7 @@ def is_str(val: Union[str, float]): ``TypeGuard`` also works with type variables. For more information, see PEP 647 (User-Defined Type Guards). """ - item = typing._type_check(parameters, '{} accepts only single type.'.format(self)) + item = typing._type_check(parameters, f'{self} accepts only single type.') return _GenericAlias(self, (item,)) # 3.7-3.8 elif sys.version_info[:2] >= (3, 7): @@ -1955,7 +1953,7 @@ def __repr__(self): def __getitem__(self, parameters): item = typing._type_check(parameters, - '{} accepts only a single type'.format(self._name)) + f'{self._name} accepts only a single type') return _GenericAlias(self, (item,)) TypeGuard = _TypeGuardForm( @@ -2057,7 +2055,7 @@ def __getitem__(self, item): cls = type(self) if self.__type__ is None: return cls(typing._type_check(item, - '{} accepts only a single type.'.format(cls.__name__[1:])), + f'{cls.__name__[1:]} accepts only a single type.'), _root=True) raise TypeError('{} cannot be further subscripted' .format(cls.__name__[1:])) @@ -2071,7 +2069,7 @@ def _eval_type(self, globalns, localns): def __repr__(self): r = super().__repr__() if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) + r += f'[{typing._type_repr(self.__type__)}]' return r def __hash__(self): From f0bad775108d094557e1da5c986a6fcfb627b680 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Wed, 15 Sep 2021 23:42:20 +0100 Subject: [PATCH 17/32] Use new style super() --- typing_extensions/src_py3/typing_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 235e146af..7e43dc0ed 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1079,7 +1079,7 @@ def __init__(cls, name, bases, ns, total=True): # In Python 3.4 and 3.5 the __init__ method also needs to support the # keyword arguments. # See https://www.python.org/dev/peps/pep-0487/#implementation-details - super(_TypedDictMeta, cls).__init__(name, bases, ns) + super().__init__(name, bases, ns) def __new__(cls, name, bases, ns, total=True): # Create new typed dict class object. @@ -1089,7 +1089,7 @@ def __new__(cls, name, bases, ns, total=True): # Subclasses and instances of TypedDict return actual dictionaries # via _dict_new. ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new - tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) + tp_dict = super().__new__(cls, name, (dict,), ns) annotations = {} own_annotations = ns.get('__annotations__', {}) From d9f613fb722e4c92b5838c4c6a2ec15cbc2dbab2 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 00:14:29 +0100 Subject: [PATCH 18/32] Names will always exist --- .../src_py3/test_typing_extensions.py | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 1eb8f4c34..237e6d895 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -3,37 +3,28 @@ import abc import contextlib import collections +import collections.abc import pickle import subprocess import types from unittest import TestCase, main, skipUnless, skipIf from test import ann_module, ann_module2, ann_module3 +import typing from typing import TypeVar, Optional, Union from typing import T, KT, VT # Not in __all__. from typing import Tuple, List, Dict, Iterable, Iterator, Callable 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 TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager - -try: - from typing_extensions import Protocol, runtime, runtime_checkable -except ImportError: - pass -try: - from typing_extensions import Annotated -except ImportError: - pass +from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload try: from typing_extensions import get_type_hints except ImportError: from typing import get_type_hints -import typing -import typing_extensions -import collections.abc as collections_abc - PEP_560 = sys.version_info[:3] >= (3, 7, 0) OLD_GENERICS = False @@ -281,8 +272,6 @@ def test_no_multiple_subscripts(self): class OverloadTests(BaseTestCase): def test_overload_fails(self): - from typing_extensions import overload - with self.assertRaises(RuntimeError): @overload @@ -292,8 +281,6 @@ def blah(): blah() def test_overload_succeeds(self): - from typing_extensions import overload - @overload def blah(): pass @@ -545,10 +532,10 @@ class C(Generic[T]): pass class CollectionsAbcTests(BaseTestCase): def test_isinstance_collections(self): - self.assertNotIsInstance(1, collections_abc.Mapping) - self.assertNotIsInstance(1, collections_abc.Iterable) - self.assertNotIsInstance(1, collections_abc.Container) - self.assertNotIsInstance(1, collections_abc.Sized) + self.assertNotIsInstance(1, collections.abc.Mapping) + self.assertNotIsInstance(1, collections.abc.Iterable) + self.assertNotIsInstance(1, collections.abc.Container) + self.assertNotIsInstance(1, collections.abc.Sized) with self.assertRaises(TypeError): isinstance(collections.deque(), typing_extensions.Deque[int]) with self.assertRaises(TypeError): @@ -723,15 +710,15 @@ def athrow(self, typ, val=None, tb=None): g = ns['g'] self.assertIsSubclass(G, typing_extensions.AsyncGenerator) self.assertIsSubclass(G, typing_extensions.AsyncIterable) - self.assertIsSubclass(G, collections_abc.AsyncGenerator) - self.assertIsSubclass(G, collections_abc.AsyncIterable) + self.assertIsSubclass(G, collections.abc.AsyncGenerator) + self.assertIsSubclass(G, collections.abc.AsyncIterable) self.assertNotIsSubclass(type(g), G) instance = G() self.assertIsInstance(instance, typing_extensions.AsyncGenerator) self.assertIsInstance(instance, typing_extensions.AsyncIterable) - self.assertIsInstance(instance, collections_abc.AsyncGenerator) - self.assertIsInstance(instance, collections_abc.AsyncIterable) + self.assertIsInstance(instance, collections.abc.AsyncGenerator) + self.assertIsInstance(instance, collections.abc.AsyncIterable) self.assertNotIsInstance(type(g), G) self.assertNotIsInstance(g, G) From dafcd01daae469bc245f83eb73defc880dbf6c32 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 00:20:10 +0100 Subject: [PATCH 19/32] Use f-strings in tests --- typing_extensions/src_py3/test_typing_extensions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 237e6d895..0848bc0b3 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -49,16 +49,16 @@ class BaseTestCase(TestCase): def assertIsSubclass(self, cls, class_or_tuple, msg=None): if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) + message = f'{repr(cls)} is not a subclass of {repr(class_or_tuple)}' if msg is not None: - message += ' : %s' % msg + message += f' : {msg}' raise self.failureException(message) def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) + message = f'{repr(cls)} is a subclass of {repr(class_or_tuple)}' if msg is not None: - message += ' : %s' % msg + message += f' : {msg}' raise self.failureException(message) @@ -124,7 +124,7 @@ def test_repr(self): cv = ClassVar[int] self.assertEqual(repr(cv), mod_name + '.ClassVar[int]') cv = ClassVar[Employee] - self.assertEqual(repr(cv), mod_name + '.ClassVar[%s.Employee]' % __name__) + self.assertEqual(repr(cv), mod_name + f'.ClassVar[{__name__}.Employee]') def test_cannot_subclass(self): with self.assertRaises(TypeError): @@ -168,7 +168,7 @@ def test_repr(self): cv = Final[int] self.assertEqual(repr(cv), mod_name + '.Final[int]') cv = Final[Employee] - self.assertEqual(repr(cv), mod_name + '.Final[%s.Employee]' % __name__) + self.assertEqual(repr(cv), mod_name + f'.Final[{__name__}.Employee]') def test_cannot_subclass(self): with self.assertRaises(TypeError): From b18d1e752b551873147a53be6c39228dc28cfced Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 15:11:49 +0100 Subject: [PATCH 20/32] Use !r in f-strings --- typing_extensions/src_py3/test_typing_extensions.py | 4 ++-- typing_extensions/src_py3/typing_extensions.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index 0848bc0b3..e401002dd 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -49,14 +49,14 @@ class BaseTestCase(TestCase): def assertIsSubclass(self, cls, class_or_tuple, msg=None): if not issubclass(cls, class_or_tuple): - message = f'{repr(cls)} is not a subclass of {repr(class_or_tuple)}' + message = f'{cls!r} is not a subclass of {repr(class_or_tuple)}' if msg is not None: message += f' : {msg}' raise self.failureException(message) def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): if issubclass(cls, class_or_tuple): - message = f'{repr(cls)} is a subclass of {repr(class_or_tuple)}' + message = f'{cls!r} is a subclass of {repr(class_or_tuple)}' if msg is not None: message += f' : {msg}' raise self.failureException(message) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 7e43dc0ed..2e8c65494 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -978,7 +978,7 @@ def runtime_checkable(cls): """ if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: raise TypeError('@runtime_checkable can be only applied to protocol classes,' - f' got {repr(cls)}') + f' got {cls!r}') cls._is_runtime_protocol = True return cls From e0af14f17583ab6b9a0fb19889c3e56f92f3ec19 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:11:29 +0100 Subject: [PATCH 21/32] Inline _generic_new --- .../src_py3/typing_extensions.py | 29 ++++--------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 2e8c65494..bc69b040c 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -47,25 +47,6 @@ def _check_generic(cls, parameters): f" actual {alen}, expected {elen}") -if hasattr(typing, '_generic_new'): - _generic_new = typing._generic_new -else: - # Note: The '_generic_new(...)' function is used as a part of the - # process of creating a generic type and was added to the typing module - # as of Python 3.5.3. - # - # We've defined '_generic_new(...)' below to exactly match the behavior - # implemented in older versions of 'typing' bundled with Python 3.5.0 to - # 3.5.2. This helps eliminate redundancy when defining collection types - # like 'Deque' later. - # - # See https://github.com/python/typing/pull/308 for more details -- in - # particular, compare and contrast the definition of types like - # 'typing.List' before and after the merge. - - def _generic_new(base_cls, cls, *args, **kwargs): - return base_cls.__new__(cls, *args, **kwargs) - # Please keep __all__ alphabetized within each category. __all__ = [ # Super-special typing primitives. @@ -404,7 +385,7 @@ class Deque(collections.deque, typing.MutableSequence[T], def __new__(cls, *args, **kwds): if cls._gorg is Deque: return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) + return typing._generic_new(collections.deque, cls, *args, **kwds) ContextManager = typing.ContextManager # 3.6.2+ @@ -447,7 +428,7 @@ class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT], def __new__(cls, *args, **kwds): if cls._gorg is OrderedDict: return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) + return typing._generic_new(collections.OrderedDict, cls, *args, **kwds) # 3.6.2+ if hasattr(typing, 'Counter'): @@ -463,7 +444,7 @@ class Counter(collections.Counter, def __new__(cls, *args, **kwds): if cls._gorg is Counter: return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) + return typing._generic_new(collections.Counter, cls, *args, **kwds) # 3.6.1+ if hasattr(typing, 'ChainMap'): @@ -478,7 +459,7 @@ class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], def __new__(cls, *args, **kwds): if cls._gorg is ChainMap: return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) + return typing._generic_new(collections.ChainMap, cls, *args, **kwds) # 3.6.1+ if hasattr(typing, 'AsyncGenerator'): @@ -773,7 +754,7 @@ def __new__(cls, *args, **kwds): if _gorg(cls) is Protocol: raise TypeError("Type Protocol cannot be instantiated; " "it can be used only as a base class") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds) if Protocol.__doc__ is not None: Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol[T]") # 3.7 From 8df7fdfa15ab28868f221dd21c12eeded6e64f11 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:19:58 +0100 Subject: [PATCH 22/32] Inline typing imports --- .../src_py3/typing_extensions.py | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index bc69b040c..429fba4b0 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -7,11 +7,6 @@ from typing import _tp_cache, _TypingEllipsis, _TypingEmpty # noqa import operator -# These are used by Protocol implementation -# We use internal typing helpers here, but this significantly reduces -# code duplication. (Also this is only until Protocol is in typing.) -from typing import Generic, Callable, TypeVar, Tuple - # After PEP 560, internal typing API was substantially reworked. # This is especially important for Protocol class which uses internal APIs # quite extensivelly. @@ -248,7 +243,7 @@ class Other(Leaf): # Error reported by type checker def IntVar(name): - return TypeVar(name) + return typing.TypeVar(name) # 3.8+: @@ -538,15 +533,15 @@ def __new__(cls, name, bases, namespace, assert extra is None # Protocols should not have extra if tvars is not None: assert origin is not None - assert all(isinstance(t, TypeVar) for t in tvars), tvars + assert all(isinstance(t, typing.TypeVar) for t in tvars), tvars else: tvars = _type_vars(bases) gvars = None for base in bases: - if base is Generic: + if base is typing.Generic: raise TypeError("Cannot inherit from plain Generic") if (isinstance(base, GenericMeta) and - base.__origin__ in (Generic, Protocol)): + base.__origin__ in (typing.Generic, Protocol)): if gvars is not None: raise TypeError( "Cannot inherit from Generic[...] or" @@ -560,7 +555,7 @@ def __new__(cls, name, bases, namespace, if not tvarset <= gvarset: s_vars = ", ".join(str(t) for t in tvars if t not in gvarset) s_args = ", ".join(str(g) for g in gvars) - cls_name = "Generic" if any(b.__origin__ is Generic + cls_name = "Generic" if any(b.__origin__ is typing.Generic for b in bases) else "Protocol" raise TypeError(f"Some type variables ({s_vars}) are" f" not listed in {cls_name}[{s_args}]") @@ -572,8 +567,8 @@ def __new__(cls, name, bases, namespace, bases = (extra,) + bases bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases) - if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): - bases = tuple(b for b in bases if b is not Generic) + if any(isinstance(b, GenericMeta) and b is not typing.Generic for b in bases): + bases = tuple(b for b in bases if b is not typing.Generic) namespace.update({'__origin__': origin, '__extra__': extra}) self = super(GenericMeta, cls).__new__(cls, name, bases, namespace, _root=True) @@ -604,12 +599,12 @@ def __init__(cls, *args, **kwargs): for b in cls.__bases__) if cls._is_protocol: for base in cls.__mro__[1:]: - if not (base in (object, Generic) or + if not (base in (object, typing.Generic) or base.__module__ == 'collections.abc' and base.__name__ in _PROTO_WHITELIST or isinstance(base, TypingMeta) and base._is_protocol or isinstance(base, GenericMeta) and - base.__origin__ is Generic): + base.__origin__ is typing.Generic): raise TypeError(f'Protocols can only inherit from other' f' protocols, got {repr(base)}') @@ -684,13 +679,13 @@ def __getitem__(self, params): # special treatment of "Protocol". (Comments removed for brevity.) if not isinstance(params, tuple): params = (params,) - if not params and _gorg(self) is not Tuple: + if not params and _gorg(self) is not typing.Tuple: raise TypeError( f"Parameter list to {self.__qualname__}[...] cannot be empty") msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) - if self in (Generic, Protocol): - if not all(isinstance(p, TypeVar) for p in params): + if self in (typing.Generic, Protocol): + if not all(isinstance(p, typing.TypeVar) for p in params): raise TypeError( f"Parameters to {repr(self)}[...] must all be type variables") if len(set(params)) != len(params): @@ -698,10 +693,10 @@ def __getitem__(self, params): f"Parameters to {repr(self)}[...] must all be unique") tvars = params args = params - elif self in (Tuple, Callable): + elif self in (typing.Tuple, typing.Callable): tvars = _type_vars(params) args = params - elif self.__origin__ in (Generic, Protocol): + elif self.__origin__ in (typing.Generic, Protocol): raise TypeError(f"Cannot subscript already-subscripted {repr(self)}") else: _check_generic(self, params) @@ -828,16 +823,16 @@ def __new__(cls, *args, **kwds): def __class_getitem__(cls, params): if not isinstance(params, tuple): params = (params,) - if not params and cls is not Tuple: + if not params and cls is not typing.Tuple: raise TypeError( f"Parameter list to {cls.__qualname__}[...] cannot be empty") msg = "Parameters to generic types must be types." params = tuple(typing._type_check(p, msg) for p in params) # noqa if cls is Protocol: # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, TypeVar) for p in params): + if not all(isinstance(p, typing.TypeVar) for p in params): i = 0 - while isinstance(params[i], TypeVar): + while isinstance(params[i], typing.TypeVar): i += 1 raise TypeError( "Parameters to Protocol[...] must all be type variables." @@ -853,9 +848,9 @@ def __class_getitem__(cls, params): def __init_subclass__(cls, *args, **kwargs): tvars = [] if '__orig_bases__' in cls.__dict__: - error = Generic in cls.__orig_bases__ + error = typing.Generic in cls.__orig_bases__ else: - error = Generic in cls.__bases__ + error = typing.Generic in cls.__bases__ if error: raise TypeError("Cannot inherit from plain Generic") if '__orig_bases__' in cls.__dict__: @@ -868,9 +863,9 @@ def __init_subclass__(cls, *args, **kwargs): gvars = None for base in cls.__orig_bases__: if (isinstance(base, _GenericAlias) and - base.__origin__ in (Generic, Protocol)): + base.__origin__ in (typing.Generic, Protocol)): # for error messages - the_base = 'Generic' if base.__origin__ is Generic else 'Protocol' + the_base = 'Generic' if base.__origin__ is typing.Generic else 'Protocol' if gvars is not None: raise TypeError( "Cannot inherit from Generic[...]" @@ -935,7 +930,7 @@ def _proto_hook(other): # Check consistency of bases. for base in cls.__bases__: - if not (base in (object, Generic) or + if not (base in (object, typing.Generic) or base.__module__ == 'collections.abc' and base.__name__ in _PROTO_WHITELIST or isinstance(base, _ProtocolMeta) and base._is_protocol): @@ -1468,8 +1463,8 @@ def get_origin(tp): if isinstance(tp, (_GenericAlias, GenericAlias, _BaseGenericAlias, ParamSpecArgs, ParamSpecKwargs)): return tp.__origin__ - if tp is Generic: - return Generic + if tp is typing.Generic: + return typing.Generic return None def get_args(tp): @@ -1674,7 +1669,7 @@ def add_two(x: float, y: float) -> float: """ # Trick Generic __parameters__. - __class__ = TypeVar + __class__ = typing.TypeVar @property def args(self): @@ -1768,7 +1763,7 @@ def __call__(self, *args, **kwargs): @property def __parameters__(self): return tuple( - tp for tp in self.__args__ if isinstance(tp, (TypeVar, ParamSpec)) + tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec)) ) if not PEP_560: From eda545a38f3c3c2d7c7245f972c9dfcb40e3cbb0 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:22:06 +0100 Subject: [PATCH 23/32] Remove unused type variables --- typing_extensions/src_py3/typing_extensions.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 429fba4b0..3d29a9bb5 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -121,8 +121,6 @@ def __subclasscheck__(self, cls): KT = typing.TypeVar('KT') # Key type. VT = typing.TypeVar('VT') # Value type. T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. -V_co = typing.TypeVar('V_co', covariant=True) # Any type covariant containers. -VT_co = typing.TypeVar('VT_co', covariant=True) # Value type covariant containers. T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. ClassVar = typing.ClassVar From 38cbc891ddd22244b9c6476300f4640643537a88 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:22:21 +0100 Subject: [PATCH 24/32] Localise helper imports --- typing_extensions/src_py3/typing_extensions.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 3d29a9bb5..77c9cc342 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -18,7 +18,7 @@ else: from typing import GenericMeta, TypingMeta if sys.version_info[:2] == (3, 6): - from typing import _type_vars, _next_in_mro, _type_check, _subs_tree # noqa + from typing import _type_vars # noqa # The two functions below are copies of typing internal helpers. # They are needed by _ProtocolMeta @@ -513,6 +513,7 @@ def _is_callable_members_only(cls): Protocol = typing.Protocol # 3.6 elif not PEP_560: + from typing import _next_in_mro, _type_check # noqa def _no_init(self, *args, **kwargs): if type(self)._is_protocol: From 14c816564f6014c40c382ddc9046929d4ab56d0e Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:37:38 +0100 Subject: [PATCH 25/32] Pick up unchanged .formats -> f-strings --- .../src_py3/test_typing_extensions.py | 13 +++---- .../src_py3/typing_extensions.py | 39 +++++++------------ 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py index e401002dd..ff786022f 100644 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ b/typing_extensions/src_py3/test_typing_extensions.py @@ -1970,7 +1970,7 @@ def test_pickle(self): P_co = ParamSpec('P_co', covariant=True) P_contra = ParamSpec('P_contra', contravariant=True) for proto in range(pickle.HIGHEST_PROTOCOL): - with self.subTest('Pickle protocol {proto}'.format(proto=proto)): + with self.subTest(f'Pickle protocol {proto}'): for paramspec in (P, P_co, P_contra): z = pickle.loads(pickle.dumps(paramspec, proto)) self.assertEqual(z.__name__, paramspec.__name__) @@ -2044,13 +2044,13 @@ def test_repr(self): mod_name = 'typing' else: mod_name = 'typing_extensions' - self.assertEqual(repr(TypeGuard), '{}.TypeGuard'.format(mod_name)) + self.assertEqual(repr(TypeGuard), f'{mod_name}.TypeGuard') cv = TypeGuard[int] - self.assertEqual(repr(cv), '{}.TypeGuard[int]'.format(mod_name)) + self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[int]') cv = TypeGuard[Employee] - self.assertEqual(repr(cv), '{}.TypeGuard[{}.Employee]'.format(mod_name, __name__)) + self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[{__name__}.Employee]') cv = TypeGuard[Tuple[int]] - self.assertEqual(repr(cv), '{}.TypeGuard[typing.Tuple[int]]'.format(mod_name)) + self.assertEqual(repr(cv), f'{mod_name}.TypeGuard[typing.Tuple[int]]') def test_cannot_subclass(self): with self.assertRaises(TypeError): @@ -2134,8 +2134,7 @@ def test_typing_extensions_compiles_with_opt(self): file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'typing_extensions.py') try: - subprocess.check_output('{} -OO {}'.format(sys.executable, - file_path), + subprocess.check_output(f'{sys.executable} -OO {file_path}', stderr=subprocess.STDOUT, shell=True) except subprocess.CalledProcessError: diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 77c9cc342..a6b210bc3 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -184,8 +184,7 @@ def __getitem__(self, item): return cls(typing._type_check(item, f'{cls.__name__[1:]} accepts only single type.'), _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') def _eval_type(self, globalns, localns): new_tp = typing._eval_type(self.__type__, globalns, localns) @@ -297,8 +296,7 @@ def __getitem__(self, values): if not isinstance(values, tuple): values = (values,) return cls(values, _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') def _eval_type(self, globalns, localns): return self @@ -306,7 +304,7 @@ def _eval_type(self, globalns, localns): def __repr__(self): r = super().__repr__() if self.__values__ is not None: - r += '[{}]'.format(', '.join(map(typing._type_repr, self.__values__))) + r += f'[{", ".join(map(typing._type_repr, self.__values__))}]' return r def __hash__(self): @@ -737,7 +735,7 @@ def func(x: Proto) -> int: Protocol classes can be generic, they are defined as:: - class GenProto({bases}): + class GenProto(Protocol[T]): def meth(self) -> T: ... """ @@ -749,8 +747,6 @@ def __new__(cls, *args, **kwds): raise TypeError("Type Protocol cannot be instantiated; " "it can be used only as a base class") return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds) - if Protocol.__doc__ is not None: - Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol[T]") # 3.7 else: from typing import _collect_type_vars # noqa @@ -835,7 +831,7 @@ def __class_getitem__(cls, params): i += 1 raise TypeError( "Parameters to Protocol[...] must all be type variables." - " Parameter {} is {}".format(i + 1, params[i])) + f" Parameter {i + 1} is {params[i]}") if len(set(params)) != len(params): raise TypeError( "Parameters to Protocol[...] must all be unique") @@ -864,7 +860,7 @@ def __init_subclass__(cls, *args, **kwargs): if (isinstance(base, _GenericAlias) and base.__origin__ in (typing.Generic, Protocol)): # for error messages - the_base = 'Generic' if base.__origin__ is typing.Generic else 'Protocol' + the_base = base.__origin__.__name__ if gvars is not None: raise TypeError( "Cannot inherit from Generic[...]" @@ -878,9 +874,8 @@ def __init_subclass__(cls, *args, **kwargs): if not tvarset <= gvarset: s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) s_args = ', '.join(str(g) for g in gvars) - raise TypeError("Some type variables ({}) are" - " not listed in {}[{}]".format(s_vars, - the_base, s_args)) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") tvars = gvars cls.__parameters__ = tuple(tvars) @@ -1021,8 +1016,8 @@ def _typeddict_new(*args, total=True, **kwargs): fields, = args # allow the "_fields" keyword be passed except ValueError: raise TypeError('TypedDict.__new__() takes from 2 to 3 ' - 'positional arguments but {} ' - 'were given'.format(len(args) + 2)) + f'positional arguments but {len(args) + 2} ' + 'were given') elif '_fields' in kwargs and len(kwargs) == 1: fields = kwargs.pop('_fields') import warnings @@ -1159,10 +1154,8 @@ def copy_with(self, params): return _AnnotatedAlias(new_type, self.__metadata__) def __repr__(self): - return "typing_extensions.Annotated[{}, {}]".format( - typing._type_repr(self.__origin__), - ", ".join(repr(a) for a in self.__metadata__) - ) + return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, " + f"{', '.join(repr(a) for a in self.__metadata__)}]") def __reduce__(self): return operator.getitem, ( @@ -1748,9 +1741,8 @@ def __init__(self, origin, args): def __repr__(self): _type_repr = typing._type_repr - return '{origin}[{args}]' \ - .format(origin=_type_repr(self.__origin__), - args=', '.join(_type_repr(arg) for arg in self.__args__)) + return (f'{_type_repr(self.__origin__)}' + f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]') def __hash__(self): return hash((self.__origin__, self.__args__)) @@ -2032,8 +2024,7 @@ def __getitem__(self, item): return cls(typing._type_check(item, f'{cls.__name__[1:]} accepts only a single type.'), _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) + raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted') def _eval_type(self, globalns, localns): new_tp = typing._eval_type(self.__type__, globalns, localns) From f03471f04013e4ec6bbfe06de12d705b21834237 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 16 Sep 2021 23:59:07 +0100 Subject: [PATCH 26/32] Inline TypingMeta --- typing_extensions/src_py3/typing_extensions.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index a6b210bc3..69d4624c4 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -13,12 +13,11 @@ PEP_560 = sys.version_info[:3] >= (3, 7, 0) if PEP_560: - GenericMeta = TypingMeta = type + GenericMeta = type from typing import _GenericAlias else: - from typing import GenericMeta, TypingMeta -if sys.version_info[:2] == (3, 6): - from typing import _type_vars # noqa + # 3.6 + from typing import GenericMeta, _type_vars # noqa # The two functions below are copies of typing internal helpers. # They are needed by _ProtocolMeta @@ -599,7 +598,7 @@ def __init__(cls, *args, **kwargs): if not (base in (object, typing.Generic) or base.__module__ == 'collections.abc' and base.__name__ in _PROTO_WHITELIST or - isinstance(base, TypingMeta) and base._is_protocol or + isinstance(base, typing.TypingMeta) and base._is_protocol or isinstance(base, GenericMeta) and base.__origin__ is typing.Generic): raise TypeError(f'Protocols can only inherit from other' From 307544be206b009e05ea4c3e86f32b6393685494 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 17 Sep 2021 00:00:45 +0100 Subject: [PATCH 27/32] Swap Protocol definitions for 3.6 and 3.7 for consistency with all others --- .../src_py3/typing_extensions.py | 370 +++++++++--------- 1 file changed, 185 insertions(+), 185 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 69d4624c4..584096576 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -508,8 +508,192 @@ def _is_callable_members_only(cls): # 3.8+ if hasattr(typing, 'Protocol'): Protocol = typing.Protocol +# 3.7 +elif PEP_560: + from typing import _collect_type_vars # noqa + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + + class _ProtocolMeta(abc.ABCMeta): + # This metaclass is a bit unfortunate and exists only because of the lack + # of __instancehook__. + def __instancecheck__(cls, instance): + # We need this method for situations where attributes are + # assigned in __init__. + if ((not getattr(cls, '_is_protocol', False) or + _is_callable_members_only(cls)) and + issubclass(instance.__class__, cls)): + return True + if cls._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(cls, attr, None)) or + getattr(instance, attr) is not None) + for attr in _get_protocol_attrs(cls)): + return True + return super().__instancecheck__(instance) + + class Protocol(metaclass=_ProtocolMeta): + # There is quite a lot of overlapping code with typing.Generic. + # Unfortunately it is hard to avoid this while these live in two different + # modules. The duplicated code will be removed when Protocol is moved to typing. + """Base class for protocol classes. Protocol classes are defined as:: + + class Proto(Protocol): + def meth(self) -> int: + ... + + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: + + class C: + def meth(self) -> int: + return 0 + + def func(x: Proto) -> int: + return x.meth() + + func(C()) # Passes static type check + + See PEP 544 for details. Protocol classes decorated with + @typing_extensions.runtime act as simple-minded runtime protocol that checks + only the presence of given attributes, ignoring their type signatures. + + Protocol classes can be generic, they are defined as:: + + class GenProto(Protocol[T]): + def meth(self) -> T: + ... + """ + __slots__ = () + _is_protocol = True + + def __new__(cls, *args, **kwds): + if cls is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can only be used as a base class") + return super().__new__(cls) + + @_tp_cache + def __class_getitem__(cls, params): + if not isinstance(params, tuple): + params = (params,) + if not params and cls is not typing.Tuple: + raise TypeError( + f"Parameter list to {cls.__qualname__}[...] cannot be empty") + msg = "Parameters to generic types must be types." + params = tuple(typing._type_check(p, msg) for p in params) # noqa + if cls is Protocol: + # Generic can only be subscripted with unique type variables. + if not all(isinstance(p, typing.TypeVar) for p in params): + i = 0 + while isinstance(params[i], typing.TypeVar): + i += 1 + raise TypeError( + "Parameters to Protocol[...] must all be type variables." + f" Parameter {i + 1} is {params[i]}") + if len(set(params)) != len(params): + raise TypeError( + "Parameters to Protocol[...] must all be unique") + else: + # Subscripting a regular Generic subclass. + _check_generic(cls, params) + return _GenericAlias(cls, params) + + def __init_subclass__(cls, *args, **kwargs): + tvars = [] + if '__orig_bases__' in cls.__dict__: + error = typing.Generic in cls.__orig_bases__ + else: + error = typing.Generic in cls.__bases__ + if error: + raise TypeError("Cannot inherit from plain Generic") + if '__orig_bases__' in cls.__dict__: + tvars = _collect_type_vars(cls.__orig_bases__) + # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. + # If found, tvars must be a subset of it. + # If not found, tvars is it. + # Also check for and reject plain Generic, + # and reject multiple Generic[...] and/or Protocol[...]. + gvars = None + for base in cls.__orig_bases__: + if (isinstance(base, _GenericAlias) and + base.__origin__ in (typing.Generic, Protocol)): + # for error messages + the_base = base.__origin__.__name__ + if gvars is not None: + raise TypeError( + "Cannot inherit from Generic[...]" + " and/or Protocol[...] multiple types.") + gvars = base.__parameters__ + if gvars is None: + gvars = tvars + else: + tvarset = set(tvars) + gvarset = set(gvars) + if not tvarset <= gvarset: + s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) + s_args = ', '.join(str(g) for g in gvars) + raise TypeError(f"Some type variables ({s_vars}) are" + f" not listed in {the_base}[{s_args}]") + tvars = gvars + cls.__parameters__ = tuple(tvars) + + # Determine if this is a protocol or a concrete subclass. + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol for b in cls.__bases__) + + # Set (or override) the protocol subclass hook. + def _proto_hook(other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not getattr(cls, '_is_runtime_protocol', False): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Instance and class checks can only be used with" + " @runtime protocols") + if not _is_callable_members_only(cls): + if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: + return NotImplemented + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + if not isinstance(other, type): + # Same error as for issubclass(1, int) + raise TypeError('issubclass() arg 1 must be a class') + for attr in _get_protocol_attrs(cls): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + annotations = getattr(base, '__annotations__', {}) + if (isinstance(annotations, typing.Mapping) and + attr in annotations and + isinstance(other, _ProtocolMeta) and + other._is_protocol): + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = _proto_hook + + # We have nothing more to do for non-protocols. + if not cls._is_protocol: + return + + # Check consistency of bases. + for base in cls.__bases__: + if not (base in (object, typing.Generic) or + base.__module__ == 'collections.abc' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, _ProtocolMeta) and base._is_protocol): + raise TypeError('Protocols can only inherit from other' + f' protocols, got {repr(base)}') + cls.__init__ = _no_init # 3.6 -elif not PEP_560: +else: from typing import _next_in_mro, _type_check # noqa def _no_init(self, *args, **kwargs): @@ -746,190 +930,6 @@ def __new__(cls, *args, **kwds): raise TypeError("Type Protocol cannot be instantiated; " "it can be used only as a base class") return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds) -# 3.7 -else: - from typing import _collect_type_vars # noqa - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - - class _ProtocolMeta(abc.ABCMeta): - # This metaclass is a bit unfortunate and exists only because of the lack - # of __instancehook__. - def __instancecheck__(cls, instance): - # We need this method for situations where attributes are - # assigned in __init__. - if ((not getattr(cls, '_is_protocol', False) or - _is_callable_members_only(cls)) and - issubclass(instance.__class__, cls)): - return True - if cls._is_protocol: - if all(hasattr(instance, attr) and - (not callable(getattr(cls, attr, None)) or - getattr(instance, attr) is not None) - for attr in _get_protocol_attrs(cls)): - return True - return super().__instancecheck__(instance) - - class Protocol(metaclass=_ProtocolMeta): - # There is quite a lot of overlapping code with typing.Generic. - # Unfortunately it is hard to avoid this while these live in two different - # modules. The duplicated code will be removed when Protocol is moved to typing. - """Base class for protocol classes. Protocol classes are defined as:: - - class Proto(Protocol): - def meth(self) -> int: - ... - - Such classes are primarily used with static type checkers that recognize - structural subtyping (static duck-typing), for example:: - - class C: - def meth(self) -> int: - return 0 - - def func(x: Proto) -> int: - return x.meth() - - func(C()) # Passes static type check - - See PEP 544 for details. Protocol classes decorated with - @typing_extensions.runtime act as simple-minded runtime protocol that checks - only the presence of given attributes, ignoring their type signatures. - - Protocol classes can be generic, they are defined as:: - - class GenProto(Protocol[T]): - def meth(self) -> T: - ... - """ - __slots__ = () - _is_protocol = True - - def __new__(cls, *args, **kwds): - if cls is Protocol: - raise TypeError("Type Protocol cannot be instantiated; " - "it can only be used as a base class") - return super().__new__(cls) - - @_tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple): - params = (params,) - if not params and cls is not typing.Tuple: - raise TypeError( - f"Parameter list to {cls.__qualname__}[...] cannot be empty") - msg = "Parameters to generic types must be types." - params = tuple(typing._type_check(p, msg) for p in params) # noqa - if cls is Protocol: - # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, typing.TypeVar) for p in params): - i = 0 - while isinstance(params[i], typing.TypeVar): - i += 1 - raise TypeError( - "Parameters to Protocol[...] must all be type variables." - f" Parameter {i + 1} is {params[i]}") - if len(set(params)) != len(params): - raise TypeError( - "Parameters to Protocol[...] must all be unique") - else: - # Subscripting a regular Generic subclass. - _check_generic(cls, params) - return _GenericAlias(cls, params) - - def __init_subclass__(cls, *args, **kwargs): - tvars = [] - if '__orig_bases__' in cls.__dict__: - error = typing.Generic in cls.__orig_bases__ - else: - error = typing.Generic in cls.__bases__ - if error: - raise TypeError("Cannot inherit from plain Generic") - if '__orig_bases__' in cls.__dict__: - tvars = _collect_type_vars(cls.__orig_bases__) - # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. - # If found, tvars must be a subset of it. - # If not found, tvars is it. - # Also check for and reject plain Generic, - # and reject multiple Generic[...] and/or Protocol[...]. - gvars = None - for base in cls.__orig_bases__: - if (isinstance(base, _GenericAlias) and - base.__origin__ in (typing.Generic, Protocol)): - # for error messages - the_base = base.__origin__.__name__ - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...]" - " and/or Protocol[...] multiple types.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) - s_args = ', '.join(str(g) for g in gvars) - raise TypeError(f"Some type variables ({s_vars}) are" - f" not listed in {the_base}[{s_args}]") - tvars = gvars - cls.__parameters__ = tuple(tvars) - - # Determine if this is a protocol or a concrete subclass. - if not cls.__dict__.get('_is_protocol', None): - cls._is_protocol = any(b is Protocol for b in cls.__bases__) - - # Set (or override) the protocol subclass hook. - def _proto_hook(other): - if not cls.__dict__.get('_is_protocol', None): - return NotImplemented - if not getattr(cls, '_is_runtime_protocol', False): - if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: - return NotImplemented - raise TypeError("Instance and class checks can only be used with" - " @runtime protocols") - if not _is_callable_members_only(cls): - if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: - return NotImplemented - raise TypeError("Protocols with non-method members" - " don't support issubclass()") - if not isinstance(other, type): - # Same error as for issubclass(1, int) - raise TypeError('issubclass() arg 1 must be a class') - for attr in _get_protocol_attrs(cls): - for base in other.__mro__: - if attr in base.__dict__: - if base.__dict__[attr] is None: - return NotImplemented - break - annotations = getattr(base, '__annotations__', {}) - if (isinstance(annotations, typing.Mapping) and - attr in annotations and - isinstance(other, _ProtocolMeta) and - other._is_protocol): - break - else: - return NotImplemented - return True - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = _proto_hook - - # We have nothing more to do for non-protocols. - if not cls._is_protocol: - return - - # Check consistency of bases. - for base in cls.__bases__: - if not (base in (object, typing.Generic) or - base.__module__ == 'collections.abc' and - base.__name__ in _PROTO_WHITELIST or - isinstance(base, _ProtocolMeta) and base._is_protocol): - raise TypeError('Protocols can only inherit from other' - f' protocols, got {repr(base)}') - cls.__init__ = _no_init # 3.8+ From ef763f1d2b637ecf1913b425da75cbb3f03394a0 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 17 Sep 2021 00:03:38 +0100 Subject: [PATCH 28/32] Inline _tp_cache, _TypingEllipsis, _TypingEmpty --- typing_extensions/src_py3/typing_extensions.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 584096576..1f9a870fb 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -4,7 +4,6 @@ import collections.abc import sys import typing -from typing import _tp_cache, _TypingEllipsis, _TypingEmpty # noqa import operator # After PEP 560, internal typing API was substantially reworked. @@ -575,7 +574,7 @@ def __new__(cls, *args, **kwds): "it can only be used as a base class") return super().__new__(cls) - @_tp_cache + @typing._tp_cache def __class_getitem__(cls, params): if not isinstance(params, tuple): params = (params,) @@ -756,8 +755,8 @@ def __new__(cls, name, bases, namespace, self if not origin else _gorg(origin)) self.__parameters__ = tvars - self.__args__ = tuple(... if a is _TypingEllipsis else - () if a is _TypingEmpty else + self.__args__ = tuple(... if a is typing._TypingEllipsis else + () if a is typing._TypingEmpty else a for a in args) if args else None self.__next_in_mro__ = _next_in_mro(self) if orig_bases is None: @@ -853,7 +852,7 @@ def __subclasscheck__(self, cls): " don't support issubclass()") return super(GenericMeta, self).__subclasscheck__(cls) - @_tp_cache + @typing._tp_cache def __getitem__(self, params): # We also need to copy this from GenericMeta.__getitem__ to get # special treatment of "Protocol". (Comments removed for brevity.) @@ -1209,7 +1208,7 @@ class Annotated: def __new__(cls, *args, **kwargs): raise TypeError("Type Annotated cannot be instantiated.") - @_tp_cache + @typing._tp_cache def __class_getitem__(cls, params): if not isinstance(params, tuple) or len(params) < 2: raise TypeError("Annotated[...] should be used " @@ -1330,7 +1329,7 @@ def _get_cons(self): else: return tree - @_tp_cache + @typing._tp_cache def __getitem__(self, params): if not isinstance(params, tuple): params = (params,) @@ -1764,7 +1763,7 @@ def _get_type_vars(self, tvars): # 3.6-3.9 -@_tp_cache +@typing._tp_cache def _concatenate_getitem(self, parameters): if parameters == (): raise TypeError("Cannot take a Concatenate of no types.") From 51b21eee432b076baa37e7f7a94a2b17934c200f Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 17 Sep 2021 00:10:38 +0100 Subject: [PATCH 29/32] Localise imports for modules only used once --- typing_extensions/src_py3/typing_extensions.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 1f9a870fb..ca14555ef 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1,10 +1,8 @@ -from _collections_abc import _check_methods as _check_methods_in_mro # noqa import abc import collections import collections.abc import sys import typing -import operator # After PEP 560, internal typing API was substantially reworked. # This is especially important for Protocol class which uses internal APIs @@ -382,6 +380,8 @@ def __new__(cls, *args, **kwds): AsyncContextManager = typing.AsyncContextManager # 3.6.0-3.6.1 else: + from _collections_abc import _check_methods as _check_methods_in_mro # noqa + class AsyncContextManager(typing.Generic[T_co]): __slots__ = () @@ -1131,6 +1131,8 @@ class Point2D(TypedDict): _AnnotatedAlias = typing._AnnotatedAlias # 3.7-3.8 elif PEP_560: + import operator + class _AnnotatedAlias(typing._GenericAlias, _root=True): """Runtime representation of an annotated type. From 0c96a6f6461a7b28e64f465d4fed20a636757bd2 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 17 Sep 2021 00:12:55 +0100 Subject: [PATCH 30/32] Inline _GenericAlias --- .../src_py3/typing_extensions.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index ca14555ef..2d3f94369 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -11,7 +11,6 @@ if PEP_560: GenericMeta = type - from typing import _GenericAlias else: # 3.6 from typing import GenericMeta, _type_vars # noqa @@ -135,7 +134,7 @@ def __repr__(self): def __getitem__(self, parameters): item = typing._type_check(parameters, f'{self._name} accepts only single type') - return _GenericAlias(self, (item,)) + return typing._GenericAlias(self, (item,)) Final = _FinalForm('Final', doc="""A special typing construct to indicate that a name @@ -250,7 +249,7 @@ def __repr__(self): return 'typing_extensions.' + self._name def __getitem__(self, parameters): - return _GenericAlias(self, parameters) + return typing._GenericAlias(self, parameters) Literal = _LiteralForm('Literal', doc="""A type that can be used to indicate to type checkers @@ -598,7 +597,7 @@ def __class_getitem__(cls, params): else: # Subscripting a regular Generic subclass. _check_generic(cls, params) - return _GenericAlias(cls, params) + return typing._GenericAlias(cls, params) def __init_subclass__(cls, *args, **kwargs): tvars = [] @@ -617,7 +616,7 @@ def __init_subclass__(cls, *args, **kwargs): # and reject multiple Generic[...] and/or Protocol[...]. gvars = None for base in cls.__orig_bases__: - if (isinstance(base, _GenericAlias) and + if (isinstance(base, typing._GenericAlias) and base.__origin__ in (typing.Generic, Protocol)): # for error messages the_base = base.__origin__.__name__ @@ -1428,12 +1427,12 @@ class Annotated(metaclass=AnnotatedMeta): # 3.9+ from typing import _BaseGenericAlias except ImportError: - _BaseGenericAlias = _GenericAlias + _BaseGenericAlias = typing._GenericAlias try: # 3.9+ from typing import GenericAlias except ImportError: - GenericAlias = _GenericAlias + GenericAlias = typing._GenericAlias def get_origin(tp): """Get the unsubscripted version of a type. @@ -1452,7 +1451,7 @@ def get_origin(tp): """ if isinstance(tp, _AnnotatedAlias): return Annotated - if isinstance(tp, (_GenericAlias, GenericAlias, _BaseGenericAlias, + if isinstance(tp, (typing._GenericAlias, GenericAlias, _BaseGenericAlias, ParamSpecArgs, ParamSpecKwargs)): return tp.__origin__ if tp is typing.Generic: @@ -1472,7 +1471,7 @@ def get_args(tp): """ if isinstance(tp, _AnnotatedAlias): return (tp.__origin__,) + tp.__metadata__ - if isinstance(tp, (_GenericAlias, GenericAlias)): + if isinstance(tp, (typing._GenericAlias, GenericAlias)): if getattr(tp, "_special", False): return () res = tp.__args__ @@ -1910,7 +1909,7 @@ def is_str(val: Union[str, float]): PEP 647 (User-Defined Type Guards). """ item = typing._type_check(parameters, f'{self} accepts only single type.') - return _GenericAlias(self, (item,)) + return typing._GenericAlias(self, (item,)) # 3.7-3.8 elif sys.version_info[:2] >= (3, 7): class _TypeGuardForm(typing._SpecialForm, _root=True): @@ -1921,7 +1920,7 @@ def __repr__(self): def __getitem__(self, parameters): item = typing._type_check(parameters, f'{self._name} accepts only a single type') - return _GenericAlias(self, (item,)) + return typing._GenericAlias(self, (item,)) TypeGuard = _TypeGuardForm( 'TypeGuard', From bdc560fae1560548d212236089efd93b5ab5f756 Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Fri, 17 Sep 2021 00:18:07 +0100 Subject: [PATCH 31/32] Fix a typo --- typing_extensions/src_py3/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index 2d3f94369..cf429a892 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -6,7 +6,7 @@ # After PEP 560, internal typing API was substantially reworked. # This is especially important for Protocol class which uses internal APIs -# quite extensivelly. +# quite extensively. PEP_560 = sys.version_info[:3] >= (3, 7, 0) if PEP_560: From 12ec8deeea990d05b158eedc56f01cb05ab4ec0e Mon Sep 17 00:00:00 2001 From: AA Turner <9087854+AA-Turner@users.noreply.github.com> Date: Thu, 11 Nov 2021 15:12:37 +0000 Subject: [PATCH 32/32] Changes from review --- typing_extensions/CHANGELOG | 18 ++++++++++++++++++ typing_extensions/src_py3/typing_extensions.py | 12 +++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/typing_extensions/CHANGELOG b/typing_extensions/CHANGELOG index d8820afa0..ac8f868a8 100644 --- a/typing_extensions/CHANGELOG +++ b/typing_extensions/CHANGELOG @@ -2,3 +2,21 @@ Starting with version 4.0.0, typing_extensions uses Semantic Versioning. See the README for more information. + +Dropped support for Python versions 3.5 and older. + +Simplified backports for Python 3.6.0 and newer. +Patch by Adam Turner (@AA-Turner). + +## Removed in version 4.0.0 + +The following non-exported but non-private names have been removed as they are +unneeded for supporting Python 3.6 and newer. + +- TypingMeta +- OLD_GENERICS +- SUBS_TREE +- HAVE_ANNOTATED +- HAVE_PROTOCOLS +- V_co +- VT_co diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py index cf429a892..1e96d1460 100644 --- a/typing_extensions/src_py3/typing_extensions.py +++ b/typing_extensions/src_py3/typing_extensions.py @@ -1,6 +1,7 @@ import abc import collections import collections.abc +import operator import sys import typing @@ -67,11 +68,15 @@ def _check_generic(cls, parameters): 'SupportsIndex', # One-off things. + 'Annotated', 'final', 'IntVar', 'Literal', 'NewType', 'overload', + 'Protocol', + 'runtime', + 'runtime_checkable', 'Text', 'TypeAlias', 'TypeGuard', @@ -81,8 +86,6 @@ def _check_generic(cls, parameters): if PEP_560: __all__.extend(["get_args", "get_origin", "get_type_hints"]) -__all__.extend(['Annotated', 'Protocol', 'runtime', 'runtime_checkable']) - # 3.6.2+ if hasattr(typing, 'NoReturn'): NoReturn = typing.NoReturn @@ -1043,9 +1046,6 @@ def _typeddict_new(*args, total=True, **kwargs): class _TypedDictMeta(type): def __init__(cls, name, bases, ns, total=True): - # In Python 3.4 and 3.5 the __init__ method also needs to support the - # keyword arguments. - # See https://www.python.org/dev/peps/pep-0487/#implementation-details super().__init__(name, bases, ns) def __new__(cls, name, bases, ns, total=True): @@ -1130,8 +1130,6 @@ class Point2D(TypedDict): _AnnotatedAlias = typing._AnnotatedAlias # 3.7-3.8 elif PEP_560: - import operator - class _AnnotatedAlias(typing._GenericAlias, _root=True): """Runtime representation of an annotated type.