From c8463e83848f91369f38f32292eb48c5e104cffa Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 24 Nov 2021 09:32:38 -0800 Subject: [PATCH 01/19] all the tests pass --- .../src/test_typing_extensions.py | 120 +++++++++++ typing_extensions/src/typing_extensions.py | 197 +++++++++++++++++- 2 files changed, 315 insertions(+), 2 deletions(-) diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index 731f9731..042289ec 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -20,6 +20,7 @@ from typing_extensions import TypeAlias, ParamSpec, Concatenate, ParamSpecArgs, ParamSpecKwargs, TypeGuard from typing_extensions import Awaitable, AsyncIterator, AsyncContextManager, Required, NotRequired from typing_extensions import Protocol, runtime, runtime_checkable, Annotated, overload +from typing_extensions import TypeVarTuple, Unpack try: from typing_extensions import get_type_hints except ImportError: @@ -2205,6 +2206,125 @@ class Alias: def return_tuple(self) -> TupleSelf: return (self, self) +class TypeVarTupleTests(BaseTestCase): + + def test_basic_plain(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(Ts, Ts) + self.assertEqual(Unpack[Ts], Unpack[Ts]) + self.assertIsInstance(Ts, TypeVarTuple) + + def test_repr(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(repr(Ts), 'Ts') + self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]') + + def test_no_redefinition(self): + self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts')) + + def test_cannot_subclass_vars(self): + with self.assertRaises(TypeError): + class V(TypeVarTuple('Ts')): + pass + with self.assertRaises(TypeError): + class V(Unpack[TypeVarTuple('Ts')]): + pass + + def test_cannot_subclass_var_itself(self): + with self.assertRaises(TypeError): + class V(TypeVarTuple): + pass + + def test_cannot_instantiate_vars(self): + Ts = TypeVarTuple('Ts') + with self.assertRaises(TypeError): + Ts() + + def test_unpack(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(Unpack[Ts], Unpack[Ts]) + with self.assertRaises(TypeError): + Unpack() + + def test_tuple(self): + Ts = TypeVarTuple('Ts') + Tuple[Unpack[Ts]] + # Not legal at type checking time but we can't really check against it. + Tuple[Ts] + + def test_union(self): + Xs = TypeVarTuple('Xs') + Ys = TypeVarTuple('Ys') + self.assertNotEqual(Xs, Ys) + self.assertEqual( + Union[Unpack[Xs]], + Unpack[Xs] + ) + self.assertNotEqual( + Union[Unpack[Xs]], + Union[Unpack[Xs], Unpack[Ys]] + ) + self.assertEqual( + Union[Unpack[Xs], Unpack[Xs]], + Unpack[Xs] + ) + self.assertNotEqual( + Union[Unpack[Xs], int], + Union[Unpack[Xs]] + ) + self.assertNotEqual( + Union[Unpack[Xs], int], + Union[int] + ) + self.assertEqual( + Union[Unpack[Xs], int].__args__, + (Unpack[Xs], int) + ) + self.assertEqual( + Union[Unpack[Xs], int].__parameters__, + (Xs,) + ) + self.assertIs( + Union[Unpack[Xs], int].__origin__, + Union + ) + + def test_concatenation(self): + Xs = TypeVarTuple('Xs') + Tuple[int, Unpack[Xs]] + Tuple[Unpack[Xs], int] + Tuple[int, Unpack[Xs], str] + class C(Generic[Unpack[Xs]]): pass + C[int, Unpack[Xs]] + C[Unpack[Xs], int] + C[int, Unpack[Xs], str] + + def test_class(self): + Ts = TypeVarTuple('Ts') + + class C(Generic[Unpack[Ts]]): pass + C[int] + C[int, str] + + with self.assertRaises(TypeError): + class C(Generic[Unpack[Ts], int]): pass + + T1 = TypeVar('T') + T2 = TypeVar('T') + class C(Generic[T1, T2, Unpack[Ts]]): pass + C[int, str] + C[int, str, float] + with self.assertRaises(TypeError): + C[int] + + def test_args_and_parameters(self): + Ts = TypeVarTuple('Ts') + + t = Tuple[tuple(Ts)] + self.assertEqual(t.__args__, (Ts.__unpacked__,)) + self.assertEqual(t.__parameters__, (Ts,)) + + class AllTests(BaseTestCase): def test_typing_extensions_includes_standard(self): diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 9f1c7aa3..69aa5a1d 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -3,6 +3,7 @@ import collections.abc import operator import sys +import types as _types import typing # After PEP 560, internal typing API was substantially reworked. @@ -47,6 +48,8 @@ def _check_generic(cls, parameters): 'ParamSpec', 'Self', 'Type', + 'TypeVarTuple', + 'Unpack', # ABCs (from collections.abc). 'Awaitable', @@ -512,7 +515,6 @@ def _is_callable_members_only(cls): 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: @@ -612,7 +614,7 @@ def __init_subclass__(cls, *args, **kwargs): if error: raise TypeError("Cannot inherit from plain Generic") if '__orig_bases__' in cls.__dict__: - tvars = _collect_type_vars(cls.__orig_bases__) + tvars = typing._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. @@ -2294,3 +2296,194 @@ class Movie(TypedDict): Required = _Required(_root=True) NotRequired = _NotRequired(_root=True) + +if sys.version_info[:2] >= (3, 9): + class _UnpackSpecialForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + @_UnpackSpecialForm + def Unpack(self, parameters): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + + """ + item = typing._type_check(parameters, f'{self._name} accepts only single type') + return _UnpackAlias(self, (item,)) + + def _collect_type_vars(types): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + tvars = [] + for t in types: + if isinstance(t, typing.TypeVar) and t not in tvars and not isinstance(t, _UnpackAlias): + tvars.append(t) + if isinstance(t, (typing._GenericAlias, _types.GenericAlias)): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + typing._collect_type_vars = _collect_type_vars + + def _check_generic(cls, parameters, elen): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in cls.__parameters__) + if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + typing._check_generic = _check_generic + +elif sys.version_info[:2] >= (3, 7): + class _UnpackAlias(typing._GenericAlias, _root=True): + __class__ = typing.TypeVar + + class _UnpackForm(typing._SpecialForm, _root=True): + def __repr__(self): + return 'typing_extensions.' + self._name + + def __getitem__(self, parameters): + item = typing._type_check(parameters, + f'{self._name} accepts only single type') + return _UnpackAlias(self, (item,)) + + Unpack = _UnpackForm( + 'Required', + doc="""A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + + """) +else: + # NOTE: Modeled after _Final's implementation when _FinalTypingBase available + class _Unpack(typing._FinalTypingBase, _root=True): + """A special typing construct to unpack a variadic type. For example: + + Shape = TypeVarTuple('Shape') + Batch = NewType('Batch', int) + + def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + + """ + __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, + 'Unpack accepts only single type.'), + _root=True) + raise TypeError('Unpack cannot be further subscripted') + + 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, _Unpack): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + Unpack = _Unpack(_root=True) + + + # Inherits from list as a workaround for Callable checks in Python < 3.9.2. +class TypeVarTuple(typing._Final, _root=True): + """Type variable tuple. + + Usage:: + + Ts = TypeVarTuple('Ts') + + TODO docs + + """ + + # Trick Generic __parameters__. + __class__ = typing.TypeVar + + def __iter__(self): + yield self.__unpacked__ + + def __init__(self, name, *, bound=None, covariant=False, contravariant=False): + self.__name__ = name + self.__covariant__ = bool(covariant) + self.__contravariant__ = bool(contravariant) + if bound: + self.__bound__ = typing._type_check(bound, 'Bound must be a type.') + else: + self.__bound__ = None + + # for pickling: + try: + def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + def_mod = None + if def_mod != 'typing_extensions': + self.__module__ = def_mod + + self.__unpacked__ = Unpack[self] + + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '' + return prefix + self.__name__ + + def __hash__(self): + return object.__hash__(self) + + def __eq__(self, other): + return self is other + + def __reduce__(self): + return self.__name__ + + # # Hack to get typing._type_check to pass. + # def __call__(self, *args, **kwargs): + # pass + + if not PEP_560: + # Only needed in 3.6. + def _get_type_vars(self, tvars): + if self not in tvars: + tvars.append(self) From 7fcea86b73757fda1110a9a72f2e706595ec2682 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 24 Nov 2021 09:42:21 -0800 Subject: [PATCH 02/19] lint fixes --- typing_extensions/src/typing_extensions.py | 50 ++++++++++------------ 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 69aa5a1d..b54aeeb7 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -28,14 +28,21 @@ def _no_slots_copy(dct): dict_copy.pop(slot, None) return dict_copy - -def _check_generic(cls, parameters): - if not cls.__parameters__: +def _check_generic(cls, parameters, elen): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: raise TypeError(f"{cls} is not a generic class") alen = len(parameters) - elen = len(cls.__parameters__) if alen != elen: - raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};" + if hasattr(cls, "__parameters__"): + num_tv_tuples = sum( + isinstance(p, TypeVarTuple) for p in cls.__parameters__ + ) + if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" f" actual {alen}, expected {elen}") @@ -602,7 +609,7 @@ def __class_getitem__(cls, params): "Parameters to Protocol[...] must all be unique") else: # Subscripting a regular Generic subclass. - _check_generic(cls, params) + _check_generic(cls, params, len(cls.__parameters__)) return typing._GenericAlias(cls, params) def __init_subclass__(cls, *args, **kwargs): @@ -883,7 +890,7 @@ def __getitem__(self, params): elif self.__origin__ in (typing.Generic, Protocol): raise TypeError(f"Cannot subscript already-subscripted {repr(self)}") else: - _check_generic(self, params) + _check_generic(self, params, len(self.__parameters__)) tvars = _type_vars(params) args = params @@ -2312,7 +2319,9 @@ def Unpack(self, parameters): Shape = TypeVarTuple('Shape') Batch = NewType('Batch', int) - def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... """ item = typing._type_check(parameters, f'{self._name} accepts only single type') @@ -2333,22 +2342,6 @@ def _collect_type_vars(types): return tuple(tvars) typing._collect_type_vars = _collect_type_vars - - def _check_generic(cls, parameters, elen): - """Check correct count for parameters of a generic cls (internal helper). - This gives a nice error message in case of count mismatch. - """ - if not elen: - raise TypeError(f"{cls} is not a generic class") - alen = len(parameters) - if alen != elen: - if hasattr(cls, "__parameters__"): - num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in cls.__parameters__) - if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: - return - raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" - f" actual {alen}, expected {elen}") - typing._check_generic = _check_generic elif sys.version_info[:2] >= (3, 7): @@ -2371,7 +2364,9 @@ def __getitem__(self, parameters): Shape = TypeVarTuple('Shape') Batch = NewType('Batch', int) - def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... """) else: @@ -2382,7 +2377,9 @@ class _Unpack(typing._FinalTypingBase, _root=True): Shape = TypeVarTuple('Shape') Batch = NewType('Batch', int) - def add_batch_axis(x: Array[Unpack[Shape]]) -> Array[Batch, Unpack[Shape]]: ... + def add_batch_axis( + x: Array[Unpack[Shape]] + ) -> Array[Batch, Unpack[Shape]]: ... """ __slots__ = ('__type__',) @@ -2423,7 +2420,6 @@ def __eq__(self, other): Unpack = _Unpack(_root=True) - # Inherits from list as a workaround for Callable checks in Python < 3.9.2. class TypeVarTuple(typing._Final, _root=True): """Type variable tuple. From be56a0a09f475db3770431b22c3d24928f77c751 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 24 Nov 2021 09:43:50 -0800 Subject: [PATCH 03/19] more lint --- typing_extensions/src/typing_extensions.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index b54aeeb7..097f8842 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -28,6 +28,7 @@ def _no_slots_copy(dct): dict_copy.pop(slot, None) return dict_copy + def _check_generic(cls, parameters, elen): """Check correct count for parameters of a generic cls (internal helper). This gives a nice error message in case of count mismatch. @@ -2335,7 +2336,11 @@ def _collect_type_vars(types): """ tvars = [] for t in types: - if isinstance(t, typing.TypeVar) and t not in tvars and not isinstance(t, _UnpackAlias): + if ( + isinstance(t, typing.TypeVar) + and t not in tvars + and not isinstance(t, _UnpackAlias) + ): tvars.append(t) if isinstance(t, (typing._GenericAlias, _types.GenericAlias)): tvars.extend([t for t in t.__parameters__ if t not in tvars]) From c6d87474ae51f00ba9af4cc6a43c42a0559543a5 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Wed, 24 Nov 2021 09:54:40 -0800 Subject: [PATCH 04/19] fix 3.10 --- typing_extensions/src/typing_extensions.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 097f8842..4566b9e4 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2328,16 +2328,25 @@ def add_batch_axis( item = typing._type_check(parameters, f'{self._name} accepts only single type') return _UnpackAlias(self, (item,)) - def _collect_type_vars(types): + # We have to do some monkey patching to deal with the dual nature of + # Unpack/TypeVarTuple: + # - We want Unpack to be a kind of TypeVar so it gets accepted in + # Generic[Unpack[Ts]] + # - We want it to *not* be treated as a TypeVar for the purposes of + # counting generic parameters, so that when we subscript a generic, + # the runtime doesn't try to substitute the Unpack with the subscripted type. + def _collect_type_vars(types, typevar_types=None): """Collect all type variable contained in types in order of first appearance (lexicographic order). For example:: _collect_type_vars((T, List[S, T])) == (T, S) """ + if typevar_types is None: + typevar_types = typing.TypeVar tvars = [] for t in types: if ( - isinstance(t, typing.TypeVar) + isinstance(t, typevar_types) and t not in tvars and not isinstance(t, _UnpackAlias) ): From 6deaf4cc5ae43851e6cf7a71df840ef8eed8ce11 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:11:38 -0800 Subject: [PATCH 05/19] separate Unpack tests --- .../src/test_typing_extensions.py | 71 +++++++++++-------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index 042289ec..b098c542 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -2206,56 +2206,30 @@ class Alias: def return_tuple(self) -> TupleSelf: return (self, self) -class TypeVarTupleTests(BaseTestCase): +class UnpackTests(BaseTestCase): def test_basic_plain(self): Ts = TypeVarTuple('Ts') - self.assertEqual(Ts, Ts) self.assertEqual(Unpack[Ts], Unpack[Ts]) - self.assertIsInstance(Ts, TypeVarTuple) + with self.assertRaises(TypeError): + Unpack() def test_repr(self): Ts = TypeVarTuple('Ts') - self.assertEqual(repr(Ts), 'Ts') self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]') - def test_no_redefinition(self): - self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts')) - def test_cannot_subclass_vars(self): - with self.assertRaises(TypeError): - class V(TypeVarTuple('Ts')): - pass with self.assertRaises(TypeError): class V(Unpack[TypeVarTuple('Ts')]): pass - def test_cannot_subclass_var_itself(self): - with self.assertRaises(TypeError): - class V(TypeVarTuple): - pass - - def test_cannot_instantiate_vars(self): - Ts = TypeVarTuple('Ts') - with self.assertRaises(TypeError): - Ts() - - def test_unpack(self): - Ts = TypeVarTuple('Ts') - self.assertEqual(Unpack[Ts], Unpack[Ts]) - with self.assertRaises(TypeError): - Unpack() - def test_tuple(self): Ts = TypeVarTuple('Ts') Tuple[Unpack[Ts]] - # Not legal at type checking time but we can't really check against it. - Tuple[Ts] def test_union(self): Xs = TypeVarTuple('Xs') Ys = TypeVarTuple('Ys') - self.assertNotEqual(Xs, Ys) self.assertEqual( Union[Unpack[Xs]], Unpack[Xs] @@ -2317,6 +2291,45 @@ class C(Generic[T1, T2, Unpack[Ts]]): pass with self.assertRaises(TypeError): C[int] + +class TypeVarTupleTests(BaseTestCase): + + def test_basic_plain(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(Ts, Ts) + self.assertIsInstance(Ts, TypeVarTuple) + Xs = TypeVarTuple('Xs') + Ys = TypeVarTuple('Ys') + self.assertNotEqual(Xs, Ys) + + def test_repr(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(repr(Ts), 'Ts') + self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]') + + def test_no_redefinition(self): + self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts')) + + def test_cannot_subclass_vars(self): + with self.assertRaises(TypeError): + class V(TypeVarTuple('Ts')): + pass + + def test_cannot_subclass_var_itself(self): + with self.assertRaises(TypeError): + class V(TypeVarTuple): + pass + + def test_cannot_instantiate_vars(self): + Ts = TypeVarTuple('Ts') + with self.assertRaises(TypeError): + Ts() + + def test_tuple(self): + Ts = TypeVarTuple('Ts') + # Not legal at type checking time but we can't really check against it. + Tuple[Ts] + def test_args_and_parameters(self): Ts = TypeVarTuple('Ts') From 19233f76f24f15b823b646ffe9debde2ed09fe98 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:11:54 -0800 Subject: [PATCH 06/19] update README --- typing_extensions/README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst index 3730457f..9ce1e7ee 100644 --- a/typing_extensions/README.rst +++ b/typing_extensions/README.rst @@ -40,6 +40,8 @@ This module currently contains the following: - ``NotRequired`` (see PEP 655) - ``Required`` (see PEP 655) - ``Self`` (see PEP 673) + - ``TypeVarTuple`` (see PEP 646) + - ``Unpack`` (see PEP 646) - In ``typing`` since Python 3.10 From c241435f38212ad16d7a9266208c4947b6fb8d49 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:16:53 -0800 Subject: [PATCH 07/19] add get_origin/get_args tests --- typing_extensions/src/test_typing_extensions.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index b098c542..dd9b1884 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -551,6 +551,7 @@ def test_get_origin(self): T = TypeVar('T') P = ParamSpec('P') + Ts = TypeVarTuple('Ts') class C(Generic[T]): pass self.assertIs(get_origin(C[int]), C) self.assertIs(get_origin(C[T]), C) @@ -571,11 +572,16 @@ class C(Generic[T]): pass self.assertIs(get_origin(list), None) self.assertIs(get_origin(P.args), P) self.assertIs(get_origin(P.kwargs), P) + self.assertIs(get_origin(Required[int]), Required) + self.assertIs(get_origin(NotRequired[int]), NotRequired) + self.assertIs(get_origin(Unpack[Ts]), Unpack) + self.assertIs(get_origin(Unpack), None) def test_get_args(self): from typing_extensions import get_args T = TypeVar('T') + Ts = TypeVarTuple('Ts') class C(Generic[T]): pass self.assertEqual(get_args(C[int]), (int,)) self.assertEqual(get_args(C[T]), (T,)) @@ -616,6 +622,10 @@ class C(Generic[T]): pass self.assertIn(get_args(Callable[P, int]), [(P, int), ([P], int)]) self.assertEqual(get_args(Callable[Concatenate[int, P], int]), (Concatenate[int, P], int)) + self.assertEqual(get_args(Required[int]), (int,)) + self.assertEqual(get_args(NotRequired[int]), (int,)) + self.assertEqual(get_args(Unpack[Ts]), (Ts,)) + self.assertEqual(get_args(Unpack), ()) class CollectionsAbcTests(BaseTestCase): @@ -2305,7 +2315,6 @@ def test_basic_plain(self): def test_repr(self): Ts = TypeVarTuple('Ts') self.assertEqual(repr(Ts), 'Ts') - self.assertEqual(repr(Unpack[Ts]), 'typing_extensions.Unpack[Ts]') def test_no_redefinition(self): self.assertNotEqual(TypeVarTuple('Ts'), TypeVarTuple('Ts')) From b2c7a5ea0344566be197bb1ca9b2ad5a128acb82 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:20:29 -0800 Subject: [PATCH 08/19] fix lint --- typing_extensions/src/typing_extensions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 4566b9e4..f165cb98 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2346,9 +2346,9 @@ def _collect_type_vars(types, typevar_types=None): tvars = [] for t in types: if ( - isinstance(t, typevar_types) - and t not in tvars - and not isinstance(t, _UnpackAlias) + isinstance(t, typevar_types) and + t not in tvars and + not isinstance(t, _UnpackAlias) ): tvars.append(t) if isinstance(t, (typing._GenericAlias, _types.GenericAlias)): From c7d8823c3d01bd676369a413b04fe159cd25b102 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:21:40 -0800 Subject: [PATCH 09/19] fix name --- typing_extensions/src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index f165cb98..20b87cd5 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2372,7 +2372,7 @@ def __getitem__(self, parameters): return _UnpackAlias(self, (item,)) Unpack = _UnpackForm( - 'Required', + 'Unpack', doc="""A special typing construct to unpack a variadic type. For example: Shape = TypeVarTuple('Shape') From 8610e35029d6b3dfc0c46b5d5258c618233df67b Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:29:41 -0800 Subject: [PATCH 10/19] fix 3.6 --- typing_extensions/src/typing_extensions.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 20b87cd5..27ae726b 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2434,7 +2434,7 @@ def __eq__(self, other): Unpack = _Unpack(_root=True) -class TypeVarTuple(typing._Final, _root=True): +class TypeVarTuple: """Type variable tuple. Usage:: @@ -2488,9 +2488,9 @@ def __eq__(self, other): def __reduce__(self): return self.__name__ - # # Hack to get typing._type_check to pass. - # def __call__(self, *args, **kwargs): - # pass + def __init_subclass__(self, /, *args, **kwds): + if '_root' not in kwds: + raise TypeError("Cannot subclass special typing classes") if not PEP_560: # Only needed in 3.6. From 9e820f44651d345a2e49d2998cdbcf264d75d1db Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:30:31 -0800 Subject: [PATCH 11/19] that was not 3.6 --- typing_extensions/src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 27ae726b..052622f1 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2488,7 +2488,7 @@ def __eq__(self, other): def __reduce__(self): return self.__name__ - def __init_subclass__(self, /, *args, **kwds): + def __init_subclass__(self, *args, **kwds): if '_root' not in kwds: raise TypeError("Cannot subclass special typing classes") From f2cf02e6c67222edee9eafa4a9b136ae429471df Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:47:56 -0800 Subject: [PATCH 12/19] more thorough monkeypatching --- typing_extensions/src/typing_extensions.py | 146 +++++++++++++-------- 1 file changed, 88 insertions(+), 58 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 052622f1..111b2455 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -17,35 +17,6 @@ # 3.6 from typing import GenericMeta, _type_vars # noqa -# The two functions below are copies of typing internal helpers. -# They are needed by _ProtocolMeta - - -def _no_slots_copy(dct): - dict_copy = dict(dct) - if '__slots__' in dict_copy: - for slot in dict_copy['__slots__']: - dict_copy.pop(slot, None) - return dict_copy - - -def _check_generic(cls, parameters, elen): - """Check correct count for parameters of a generic cls (internal helper). - This gives a nice error message in case of count mismatch. - """ - if not elen: - raise TypeError(f"{cls} is not a generic class") - alen = len(parameters) - if alen != elen: - if hasattr(cls, "__parameters__"): - num_tv_tuples = sum( - isinstance(p, TypeVarTuple) for p in cls.__parameters__ - ) - if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: - return - raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" - f" actual {alen}, expected {elen}") - # Please keep __all__ alphabetized within each category. __all__ = [ @@ -98,6 +69,85 @@ def _check_generic(cls, parameters, elen): if PEP_560: __all__.extend(["get_args", "get_origin", "get_type_hints"]) +# The functions below are modified copies of typing internal helpers. +# They are needed by _ProtocolMeta and they provide support for PEP 646. + + +def _no_slots_copy(dct): + dict_copy = dict(dct) + if '__slots__' in dict_copy: + for slot in dict_copy['__slots__']: + dict_copy.pop(slot, None) + return dict_copy + + +_marker = object() + +def _check_generic(cls, parameters, elen=_marker): + """Check correct count for parameters of a generic cls (internal helper). + This gives a nice error message in case of count mismatch. + """ + if not elen: + raise TypeError(f"{cls} is not a generic class") + if elen is _marker: + if not hasattr(cls, "__parameters__") or not cls.__parameters__: + raise TypeError(f"{cls} is not a generic class") + elen = len(cls.__parameters__) + alen = len(parameters) + if alen != elen: + if hasattr(cls, "__parameters__"): + parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] + num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) + if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: + return + raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" + f" actual {alen}, expected {elen}") + + +if sys.version_info >= (3, 10): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)) +elif sys.version_info >= (3, 9): + def _should_collect_from_parameters(t): + return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) +else: + def _should_collect_from_parameters(t): + return isinstance(t, typing._GenericAlias) and not t._special + + +def _collect_type_vars(types, typevar_types=None): + """Collect all type variable contained in types in order of + first appearance (lexicographic order). For example:: + + _collect_type_vars((T, List[S, T])) == (T, S) + """ + if typevar_types is None: + typevar_types = typing.TypeVar + tvars = [] + for t in types: + if ( + isinstance(t, typevar_types) and + t not in tvars and + not isinstance(t, _UnpackAlias) + ): + tvars.append(t) + if _should_collect_from_parameters(t): + tvars.extend([t for t in t.__parameters__ if t not in tvars]) + return tuple(tvars) + + +# We have to do some monkey patching to deal with the dual nature of +# Unpack/TypeVarTuple: +# - We want Unpack to be a kind of TypeVar so it gets accepted in +# Generic[Unpack[Ts]] +# - We want it to *not* be treated as a TypeVar for the purposes of +# counting generic parameters, so that when we subscript a generic, +# the runtime doesn't try to substitute the Unpack with the subscripted type. +if not hasattr(typing, "TypeVarTuple"): + typing._collect_type_vars = _collect_type_vars + typing._check_generic = _check_generic + + # 3.6.2+ if hasattr(typing, 'NoReturn'): NoReturn = typing.NoReturn @@ -2328,35 +2378,8 @@ def add_batch_axis( item = typing._type_check(parameters, f'{self._name} accepts only single type') return _UnpackAlias(self, (item,)) - # We have to do some monkey patching to deal with the dual nature of - # Unpack/TypeVarTuple: - # - We want Unpack to be a kind of TypeVar so it gets accepted in - # Generic[Unpack[Ts]] - # - We want it to *not* be treated as a TypeVar for the purposes of - # counting generic parameters, so that when we subscript a generic, - # the runtime doesn't try to substitute the Unpack with the subscripted type. - def _collect_type_vars(types, typevar_types=None): - """Collect all type variable contained in types in order of - first appearance (lexicographic order). For example:: - - _collect_type_vars((T, List[S, T])) == (T, S) - """ - if typevar_types is None: - typevar_types = typing.TypeVar - tvars = [] - for t in types: - if ( - isinstance(t, typevar_types) and - t not in tvars and - not isinstance(t, _UnpackAlias) - ): - tvars.append(t) - if isinstance(t, (typing._GenericAlias, _types.GenericAlias)): - tvars.extend([t for t in t.__parameters__ if t not in tvars]) - return tuple(tvars) - - typing._collect_type_vars = _collect_type_vars - typing._check_generic = _check_generic + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) elif sys.version_info[:2] >= (3, 7): class _UnpackAlias(typing._GenericAlias, _root=True): @@ -2383,6 +2406,10 @@ def add_batch_axis( ) -> Array[Batch, Unpack[Shape]]: ... """) + + def _is_unpack(obj): + return isinstance(obj, _UnpackAlias) + else: # NOTE: Modeled after _Final's implementation when _FinalTypingBase available class _Unpack(typing._FinalTypingBase, _root=True): @@ -2433,6 +2460,9 @@ def __eq__(self, other): Unpack = _Unpack(_root=True) + def _is_unpack(obj): + return isinstance(obj, _Unpack) + class TypeVarTuple: """Type variable tuple. From 0f386e2e3dfdf40a21eb34840249f6ccbf67690d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:49:12 -0800 Subject: [PATCH 13/19] fix lint --- typing_extensions/src/typing_extensions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 111b2455..9511d325 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -83,6 +83,7 @@ def _no_slots_copy(dct): _marker = object() + def _check_generic(cls, parameters, elen=_marker): """Check correct count for parameters of a generic cls (internal helper). This gives a nice error message in case of count mismatch. @@ -106,7 +107,9 @@ def _check_generic(cls, parameters, elen=_marker): if sys.version_info >= (3, 10): def _should_collect_from_parameters(t): - return isinstance(t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)) + return isinstance( + t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType) + ) elif sys.version_info >= (3, 9): def _should_collect_from_parameters(t): return isinstance(t, (typing._GenericAlias, _types.GenericAlias)) From 55c4d20edd58172254060a4c59588b6c2065b129 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 30 Nov 2021 18:59:14 -0800 Subject: [PATCH 14/19] 3.6 hacks --- typing_extensions/src/typing_extensions.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 9511d325..8553bb61 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2427,6 +2427,7 @@ def add_batch_axis( """ __slots__ = ('__type__',) + __class__ = typing.TypeVar def __init__(self, tp=None, **kwds): self.__type__ = tp @@ -2461,6 +2462,10 @@ def __eq__(self, other): return self.__type__ == other.__type__ return self is other + # For 3.6 only + def _get_type_vars(self, tvars): + self.__type__._get_type_vars(tvars) + Unpack = _Unpack(_root=True) def _is_unpack(obj): From 9229373d9186aa92d4486c5cf405409a5781e291 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 15 Jan 2022 13:26:08 -0800 Subject: [PATCH 15/19] give up on 3.6 --- typing_extensions/src/test_typing_extensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index dd9b1884..bd4df49b 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -2273,6 +2273,7 @@ def test_union(self): Union ) + @skipUnless(PEP_560, "Unimplemented for 3.6") def test_concatenation(self): Xs = TypeVarTuple('Xs') Tuple[int, Unpack[Xs]] @@ -2283,6 +2284,7 @@ class C(Generic[Unpack[Xs]]): pass C[Unpack[Xs], int] C[int, Unpack[Xs], str] + @skipUnless(PEP_560, "Unimplemented for 3.6") def test_class(self): Ts = TypeVarTuple('Ts') From 0d164234bb398769a8e1ee280dcb5f2c55a5606d Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 15 Jan 2022 13:30:49 -0800 Subject: [PATCH 16/19] docs --- typing_extensions/CHANGELOG | 5 +++++ typing_extensions/README.rst | 11 ++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/typing_extensions/CHANGELOG b/typing_extensions/CHANGELOG index 24fe698d..ce126b19 100644 --- a/typing_extensions/CHANGELOG +++ b/typing_extensions/CHANGELOG @@ -1,3 +1,8 @@ +# Unreleased + +- Runtime support for PEP 646, adding `typing_extensions.TypeVarTuple` + and `typing_extensions.Unpack`. + # Release 4.0.1 (November 30, 2021) - Fix broken sdist in release 4.0.0. Patch by Adam Turner (@AA-Turner). diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst index 9ce1e7ee..0f624142 100644 --- a/typing_extensions/README.rst +++ b/typing_extensions/README.rst @@ -105,9 +105,14 @@ These changes are _not_ backported to prevent subtle compatibility issues when mixing the differing implementations of modified classes. Certain types have incorrect runtime behavior due to limitations of older -versions of the typing module. For example, ``ParamSpec`` and ``Concatenate`` -will not work with ``get_args``, ``get_origin``. Certain PEP 612 special cases -in user-defined ``Generic``\ s are also not available. +versions of the typing module: + +- ``ParamSpec`` and ``Concatenate`` will not work with ``get_args`` and + ``get_origin``. Certain PEP 612 special cases in user-defined + ``Generic``\ s are also not available. +- ``Unpack`` from PEP 646 does not work properly with user-defined + ``Generic``\ s in Python 3.6. + These types are only guaranteed to work for static type checking. Running tests From ff2f05c4e753d56189242f02615b69c3f0fa2d84 Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Sat, 15 Jan 2022 14:42:05 -0800 Subject: [PATCH 17/19] TypeVarTuple docstring (adapted from the PEP) --- typing_extensions/src/typing_extensions.py | 35 +++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 8553bb61..a2ebc1f6 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2479,7 +2479,40 @@ class TypeVarTuple: Ts = TypeVarTuple('Ts') - TODO docs + In the same way that a normal type variable is a stand-in for a single + type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* type such as + ``Tuple[int, str]``. + + Type variable tuples can be used in ``Generic`` declarations. + Consider the following example:: + + class Array(Generic[*Ts]): ... + + The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``, + where ``T1`` and ``T2`` are type variables. To use these type variables + as type parameters of ``Array``, we must *unpack* the type variable tuple using + the star operator: ``*Ts``. The signature of ``Array`` then behaves + as if we had simply written ``class Array(Generic[T1, T2]): ...``. + In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows + us to parameterise the class with an *arbitrary* number of type parameters. + + Type variable tuples can be used anywhere a normal ``TypeVar`` can. + This includes class definitions, as shown above, as well as function + signatures and variable annotations:: + + class Array(Generic[*Ts]): + + def __init__(self, shape: Tuple[*Ts]): + self._shape: Tuple[*Ts] = shape + + def get_shape(self) -> Tuple[*Ts]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + x.get_shape() # ... is tuple[Height, Width] """ From d3f77c2592404c99cb46909dd1c11d888c719dcf Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Tue, 1 Feb 2022 19:51:34 -0800 Subject: [PATCH 18/19] improve tests, docs, parens --- typing_extensions/README.rst | 3 ++- .../src/test_typing_extensions.py | 23 +++++++++++-------- typing_extensions/src/typing_extensions.py | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst index a7f5d22e..eae0abae 100644 --- a/typing_extensions/README.rst +++ b/typing_extensions/README.rst @@ -124,7 +124,8 @@ versions of the typing module: ``get_origin``. Certain PEP 612 special cases in user-defined ``Generic``\ s are also not available. - ``Unpack`` from PEP 646 does not work properly with user-defined - ``Generic``\ s in Python 3.6. + ``Generic``\ s in Python 3.6: ``class X(Generic[Unpack[Ts]]):`` does + not work. These types are only guaranteed to work for static type checking. diff --git a/typing_extensions/src/test_typing_extensions.py b/typing_extensions/src/test_typing_extensions.py index 35ca1e78..0e4fe60f 100644 --- a/typing_extensions/src/test_typing_extensions.py +++ b/typing_extensions/src/test_typing_extensions.py @@ -2341,21 +2341,23 @@ def test_union(self): @skipUnless(PEP_560, "Unimplemented for 3.6") def test_concatenation(self): Xs = TypeVarTuple('Xs') - Tuple[int, Unpack[Xs]] - Tuple[Unpack[Xs], int] - Tuple[int, Unpack[Xs], str] + self.assertEqual(Tuple[int, Unpack[Xs]].__args__, (int, Unpack[Xs])) + self.assertEqual(Tuple[Unpack[Xs], int].__args__, (Unpack[Xs], int)) + self.assertEqual(Tuple[int, Unpack[Xs], str].__args__, + (int, Unpack[Xs], str)) class C(Generic[Unpack[Xs]]): pass - C[int, Unpack[Xs]] - C[Unpack[Xs], int] - C[int, Unpack[Xs], str] + self.assertEqual(C[int, Unpack[Xs]].__args__, (int, Unpack[Xs])) + self.assertEqual(C[Unpack[Xs], int].__args__, (Unpack[Xs], int)) + self.assertEqual(C[int, Unpack[Xs], str].__args__, + (int, Unpack[Xs], str)) @skipUnless(PEP_560, "Unimplemented for 3.6") def test_class(self): Ts = TypeVarTuple('Ts') class C(Generic[Unpack[Ts]]): pass - C[int] - C[int, str] + self.assertEqual(C[int].__args__, (int,)) + self.assertEqual(C[int, str].__args__, (int, str)) with self.assertRaises(TypeError): class C(Generic[Unpack[Ts], int]): pass @@ -2363,8 +2365,9 @@ class C(Generic[Unpack[Ts], int]): pass T1 = TypeVar('T') T2 = TypeVar('T') class C(Generic[T1, T2, Unpack[Ts]]): pass - C[int, str] - C[int, str, float] + self.assertEqual(C[int, str].__args__, (int, str)) + self.assertEqual(C[int, str, float].__args__, (int, str, float)) + self.assertEqual(C[int, str, float, bool].__args__, (int, str, float, bool)) with self.assertRaises(TypeError): C[int] diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index 7d38ff3a..fda33b30 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -103,7 +103,7 @@ def _check_generic(cls, parameters, elen=_marker): if hasattr(cls, "__parameters__"): parameters = [p for p in cls.__parameters__ if not _is_unpack(p)] num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters) - if num_tv_tuples > 0 and alen >= elen - num_tv_tuples: + if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples): return raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};" f" actual {alen}, expected {elen}") From 5cd13f65e1ddbe63a484b678ede41200ad6b700b Mon Sep 17 00:00:00 2001 From: Jelle Zijlstra Date: Thu, 10 Feb 2022 18:44:30 -0800 Subject: [PATCH 19/19] Remove bound and variance from TypeVarTuple --- typing_extensions/src/typing_extensions.py | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/typing_extensions/src/typing_extensions.py b/typing_extensions/src/typing_extensions.py index fa3b90ef..f5a2b133 100644 --- a/typing_extensions/src/typing_extensions.py +++ b/typing_extensions/src/typing_extensions.py @@ -2701,14 +2701,8 @@ def get_shape(self) -> Tuple[*Ts]: def __iter__(self): yield self.__unpacked__ - def __init__(self, name, *, bound=None, covariant=False, contravariant=False): + def __init__(self, name): self.__name__ = name - self.__covariant__ = bool(covariant) - self.__contravariant__ = bool(contravariant) - if bound: - self.__bound__ = typing._type_check(bound, 'Bound must be a type.') - else: - self.__bound__ = None # for pickling: try: @@ -2721,13 +2715,7 @@ def __init__(self, name, *, bound=None, covariant=False, contravariant=False): self.__unpacked__ = Unpack[self] def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '' - return prefix + self.__name__ + return self.__name__ def __hash__(self): return object.__hash__(self)