From 1fe92f2ba0488727e1b9519b5523c4e8705273e4 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Mon, 10 May 2021 17:45:31 +0800 Subject: [PATCH 1/2] Drop ParamSpec from most ``__parameters__`` --- Lib/test/test_typing.py | 25 +++++++++++ Lib/typing.py | 41 +++++++++++++------ .../2021-05-10-17-45-00.bpo-44098._MoxuZ.rst | 5 +++ 3 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-05-10-17-45-00.bpo-44098._MoxuZ.rst diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 47dc0b9358d7de..21dc29c0617aec 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -4351,6 +4351,31 @@ def test_var_substitution(self): self.assertEqual(C1[int, str], Callable[[int], str]) self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float]) + def test_no_paramspec_in__parameters__(self): + # ParamSpec should not be found in __parameters__ + # of generics. Usages outside Callable, Concatenate + # and Generic are invalid. + T = TypeVar("T") + P = ParamSpec("P") + self.assertNotIn(P, List[P].__parameters__) + self.assertIn(T, Tuple[T, P].__parameters__) + + # Test for consistency with builtin generics. + self.assertNotIn(P, list[P].__parameters__) + self.assertIn(T, tuple[T, P].__parameters__) + + def test_paramspec_in_nested_generics(self): + # Although ParamSpec should not be found in __parameters__ of most + # generics, they probably should be found when nested in + # a valid location. + T = TypeVar("T") + P = ParamSpec("P") + C1 = Callable[P, T] + G1 = List[C1] + G2 = list[C1] + self.assertEqual(G1.__parameters__, (P, T)) + self.assertEqual(G2.__parameters__, (P, T)) + class ConcatenateTests(BaseTestCase): def test_basics(self): diff --git a/Lib/typing.py b/Lib/typing.py index a51452bfec357e..a0ae35b262f97e 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -195,15 +195,17 @@ def _type_repr(obj): return repr(obj) -def _collect_type_vars(types): - """Collect all type variable-like variables contained +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 = TypeVar tvars = [] for t in types: - if isinstance(t, _TypeVarLike) and t not in tvars: + if isinstance(t, typevar_types) and t not in tvars: tvars.append(t) if isinstance(t, (_GenericAlias, GenericAlias)): tvars.extend([t for t in t.__parameters__ if t not in tvars]) @@ -932,7 +934,8 @@ def __getattr__(self, attr): raise AttributeError(attr) def __setattr__(self, attr, val): - if _is_dunder(attr) or attr in ('_name', '_inst', '_nparams'): + if _is_dunder(attr) or attr in {'_name', '_inst', '_nparams', + '_typevar_types', '_paramspec_tvars'}: super().__setattr__(attr, val) else: setattr(self.__origin__, attr, val) @@ -957,14 +960,18 @@ def __subclasscheck__(self, cls): class _GenericAlias(_BaseGenericAlias, _root=True): - def __init__(self, origin, params, *, inst=True, name=None): + def __init__(self, origin, params, *, inst=True, name=None, + _typevar_types=TypeVar, + _paramspec_tvars=False): super().__init__(origin, inst=inst, name=name) if not isinstance(params, tuple): params = (params,) self.__args__ = tuple(... if a is _TypingEllipsis else () if a is _TypingEmpty else a for a in params) - self.__parameters__ = _collect_type_vars(params) + self.__parameters__ = _collect_type_vars(params, typevar_types=_typevar_types) + self._typevar_types = _typevar_types + self._paramspec_tvars = _paramspec_tvars if not name: self.__module__ = origin.__module__ @@ -991,14 +998,15 @@ def __getitem__(self, params): if not isinstance(params, tuple): params = (params,) params = tuple(_type_convert(p) for p in params) - if any(isinstance(t, ParamSpec) for t in self.__parameters__): - params = _prepare_paramspec_params(self, params) + if self._paramspec_tvars: + if any(isinstance(t, ParamSpec) for t in self.__parameters__): + params = _prepare_paramspec_params(self, params) _check_generic(self, params, len(self.__parameters__)) subst = dict(zip(self.__parameters__, params)) new_args = [] for arg in self.__args__: - if isinstance(arg, _TypeVarLike): + if isinstance(arg, self._typevar_types): arg = subst[arg] elif isinstance(arg, (_GenericAlias, GenericAlias)): subparams = arg.__parameters__ @@ -1115,7 +1123,9 @@ def __reduce__(self): class _CallableType(_SpecialGenericAlias, _root=True): def copy_with(self, params): return _CallableGenericAlias(self.__origin__, params, - name=self._name, inst=self._inst) + name=self._name, inst=self._inst, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) def __getitem__(self, params): if not isinstance(params, tuple) or len(params) != 2: @@ -1208,7 +1218,10 @@ def __hash__(self): class _ConcatenateGenericAlias(_GenericAlias, _root=True): - pass + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) class Generic: @@ -1256,7 +1269,9 @@ def __class_getitem__(cls, params): if any(isinstance(t, ParamSpec) for t in cls.__parameters__): params = _prepare_paramspec_params(cls, params) _check_generic(cls, params, len(cls.__parameters__)) - return _GenericAlias(cls, params) + return _GenericAlias(cls, params, + _typevar_types=(TypeVar, ParamSpec), + _paramspec_tvars=True) def __init_subclass__(cls, *args, **kwargs): super().__init_subclass__(*args, **kwargs) @@ -1268,7 +1283,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 = _collect_type_vars(cls.__orig_bases__, typevar_types=_TypeVarLike) # Look for Generic[T1, ..., Tn]. # If found, tvars must be a subset of it. # If not found, tvars is it. diff --git a/Misc/NEWS.d/next/Library/2021-05-10-17-45-00.bpo-44098._MoxuZ.rst b/Misc/NEWS.d/next/Library/2021-05-10-17-45-00.bpo-44098._MoxuZ.rst new file mode 100644 index 00000000000000..2aaa8b695af465 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-05-10-17-45-00.bpo-44098._MoxuZ.rst @@ -0,0 +1,5 @@ +``typing.ParamSpec`` will no longer be found in the ``__parameters__`` of +most :mod:`typing` generics except in valid use locations specified by +:pep:`612`. This prevents incorrect usage like ``typing.List[P][int]``. This +change means incorrect usage which may have passed silently in 3.10 beta 1 +and earlier will now error. From 6724d76dd79aa1df223b1d6cd0d6ca815472a8a0 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 13 May 2021 13:01:26 +0800 Subject: [PATCH 2/2] TypeVarLIke->(TypeVar, ParamSpec) --- Lib/typing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/typing.py b/Lib/typing.py index a0ae35b262f97e..b580750173cdde 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -1257,7 +1257,7 @@ def __class_getitem__(cls, params): params = tuple(_type_convert(p) for p in params) if cls in (Generic, Protocol): # Generic and Protocol can only be subscripted with unique type variables. - if not all(isinstance(p, _TypeVarLike) for p in params): + if not all(isinstance(p, (TypeVar, ParamSpec)) for p in params): raise TypeError( f"Parameters to {cls.__name__}[...] must all be type variables " f"or parameter specification variables.") @@ -1283,7 +1283,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__, typevar_types=_TypeVarLike) + tvars = _collect_type_vars(cls.__orig_bases__, (TypeVar, ParamSpec)) # Look for Generic[T1, ..., Tn]. # If found, tvars must be a subset of it. # If not found, tvars is it. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy