summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorkj <28750310+Fidget-Spinner@users.noreply.github.com>2020-12-24 04:33:48 (GMT)
committerGitHub <noreply@github.com>2020-12-24 04:33:48 (GMT)
commit73607be68668ab7f4bee53507c8dc7b5a46c9cb4 (patch)
tree4ec6bb3f178e61b7fc61d979ec65519a4ef1d4bc /Lib
parentcc3467a57b61b0e7ef254b36790a1c44b13f2228 (diff)
downloadcpython-73607be68668ab7f4bee53507c8dc7b5a46c9cb4.zip
cpython-73607be68668ab7f4bee53507c8dc7b5a46c9cb4.tar.gz
cpython-73607be68668ab7f4bee53507c8dc7b5a46c9cb4.tar.bz2
bpo-41559: Implement PEP 612 - Add ParamSpec and Concatenate to typing (#23702)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/_collections_abc.py28
-rw-r--r--Lib/test/test_genericalias.py21
-rw-r--r--Lib/test/test_typing.py120
-rw-r--r--Lib/typing.py209
4 files changed, 317 insertions, 61 deletions
diff --git a/Lib/_collections_abc.py b/Lib/_collections_abc.py
index e4eac79..87302ac 100644
--- a/Lib/_collections_abc.py
+++ b/Lib/_collections_abc.py
@@ -416,7 +416,7 @@ class Collection(Sized, Iterable, Container):
class _CallableGenericAlias(GenericAlias):
""" Represent `Callable[argtypes, resulttype]`.
- This sets ``__args__`` to a tuple containing the flattened``argtypes``
+ This sets ``__args__`` to a tuple containing the flattened ``argtypes``
followed by ``resulttype``.
Example: ``Callable[[int, str], float]`` sets ``__args__`` to
@@ -444,7 +444,7 @@ class _CallableGenericAlias(GenericAlias):
return super().__new__(cls, origin, ga_args)
def __repr__(self):
- if len(self.__args__) == 2 and self.__args__[0] is Ellipsis:
+ if _has_special_args(self.__args__):
return super().__repr__()
return (f'collections.abc.Callable'
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
@@ -452,7 +452,7 @@ class _CallableGenericAlias(GenericAlias):
def __reduce__(self):
args = self.__args__
- if not (len(args) == 2 and args[0] is Ellipsis):
+ if not _has_special_args(args):
args = list(args[:-1]), args[-1]
return _CallableGenericAlias, (Callable, args)
@@ -461,12 +461,28 @@ class _CallableGenericAlias(GenericAlias):
# rather than the default types.GenericAlias object.
ga = super().__getitem__(item)
args = ga.__args__
- t_result = args[-1]
- t_args = args[:-1]
- args = (t_args, t_result)
+ # args[0] occurs due to things like Z[[int, str, bool]] from PEP 612
+ if not isinstance(ga.__args__[0], tuple):
+ t_result = ga.__args__[-1]
+ t_args = ga.__args__[:-1]
+ args = (t_args, t_result)
return _CallableGenericAlias(Callable, args)
+def _has_special_args(args):
+ """Checks if args[0] matches either ``...``, ``ParamSpec`` or
+ ``_ConcatenateGenericAlias`` from typing.py
+ """
+ if len(args) != 2:
+ return False
+ obj = args[0]
+ if obj is Ellipsis:
+ return True
+ obj = type(obj)
+ names = ('ParamSpec', '_ConcatenateGenericAlias')
+ return obj.__module__ == 'typing' and any(obj.__name__ == name for name in names)
+
+
def _type_repr(obj):
"""Return the repr() of an object, special-casing types (internal helper).
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index ccf40b1..fd024dc 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -369,6 +369,27 @@ class BaseTest(unittest.TestCase):
self.assertEqual(c1.__args__, c2.__args__)
self.assertEqual(hash(c1.__args__), hash(c2.__args__))
+ with self.subTest("Testing ParamSpec uses"):
+ P = typing.ParamSpec('P')
+ C1 = Callable[P, T]
+ # substitution
+ self.assertEqual(C1[int, str], Callable[[int], str])
+ self.assertEqual(C1[[int, str], str], Callable[[int, str], str])
+ self.assertEqual(repr(C1).split(".")[-1], "Callable[~P, ~T]")
+ self.assertEqual(repr(C1[int, str]).split(".")[-1], "Callable[[int], str]")
+
+ C2 = Callable[P, int]
+ # special case in PEP 612 where
+ # X[int, str, float] == X[[int, str, float]]
+ self.assertEqual(C2[int, str, float], C2[[int, str, float]])
+ self.assertEqual(repr(C2).split(".")[-1], "Callable[~P, int]")
+ self.assertEqual(repr(C2[int, str]).split(".")[-1], "Callable[[int, str], int]")
+
+ with self.subTest("Testing Concatenate uses"):
+ P = typing.ParamSpec('P')
+ C1 = Callable[typing.Concatenate[int, P], int]
+ self.assertEqual(repr(C1), "collections.abc.Callable"
+ "[typing.Concatenate[int, ~P], int]")
if __name__ == "__main__":
unittest.main()
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 8e86e76..c340c8a 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -25,6 +25,7 @@ from typing import IO, TextIO, BinaryIO
from typing import Pattern, Match
from typing import Annotated, ForwardRef
from typing import TypeAlias
+from typing import ParamSpec, Concatenate
import abc
import typing
import weakref
@@ -1130,10 +1131,6 @@ class ProtocolTests(BaseTestCase):
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
@@ -1155,8 +1152,6 @@ class ProtocolTests(BaseTestCase):
self.assertIsSubclass(P, PR)
with self.assertRaises(TypeError):
PR[int]
- with self.assertRaises(TypeError):
- PR[int, 1]
class P1(Protocol, Generic[T]):
def bar(self, x: T) -> str: ...
@@ -1175,8 +1170,6 @@ class ProtocolTests(BaseTestCase):
return x
self.assertIsInstance(Test(), PSub)
- with self.assertRaises(TypeError):
- PR[int, ClassVar]
def test_init_called(self):
T = TypeVar('T')
@@ -1746,8 +1739,6 @@ class GenericTests(BaseTestCase):
self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]])
with self.assertRaises(TypeError):
Tuple[T, int][()]
- with self.assertRaises(TypeError):
- Tuple[T, U][T, ...]
self.assertEqual(Union[T, int][int], int)
self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str])
@@ -1759,10 +1750,6 @@ class GenericTests(BaseTestCase):
self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT])
self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]])
- with self.assertRaises(TypeError):
- Callable[[T], U][..., int]
- with self.assertRaises(TypeError):
- Callable[[T], U][[], int]
def test_extended_generic_rules_repr(self):
T = TypeVar('T')
@@ -4243,6 +4230,111 @@ class TypeAliasTests(BaseTestCase):
TypeAlias[int]
+class ParamSpecTests(BaseTestCase):
+
+ def test_basic_plain(self):
+ P = ParamSpec('P')
+ self.assertEqual(P, P)
+ self.assertIsInstance(P, ParamSpec)
+
+ def test_valid_uses(self):
+ P = ParamSpec('P')
+ T = TypeVar('T')
+ C1 = Callable[P, int]
+ self.assertEqual(C1.__args__, (P, int))
+ self.assertEqual(C1.__parameters__, (P,))
+ C2 = Callable[P, T]
+ self.assertEqual(C2.__args__, (P, T))
+ self.assertEqual(C2.__parameters__, (P, T))
+ # Test collections.abc.Callable too.
+ C3 = collections.abc.Callable[P, int]
+ self.assertEqual(C3.__args__, (P, int))
+ self.assertEqual(C3.__parameters__, (P,))
+ C4 = collections.abc.Callable[P, T]
+ self.assertEqual(C4.__args__, (P, T))
+ self.assertEqual(C4.__parameters__, (P, T))
+
+ # ParamSpec instances should also have args and kwargs attributes.
+ self.assertIn('args', dir(P))
+ self.assertIn('kwargs', dir(P))
+ P.args
+ P.kwargs
+
+ def test_user_generics(self):
+ T = TypeVar("T")
+ P = ParamSpec("P")
+ P_2 = ParamSpec("P_2")
+
+ class X(Generic[T, P]):
+ f: Callable[P, int]
+ x: T
+ G1 = X[int, P_2]
+ self.assertEqual(G1.__args__, (int, P_2))
+ self.assertEqual(G1.__parameters__, (P_2,))
+
+ G2 = X[int, Concatenate[int, P_2]]
+ self.assertEqual(G2.__args__, (int, Concatenate[int, P_2]))
+ self.assertEqual(G2.__parameters__, (P_2,))
+
+ G3 = X[int, [int, bool]]
+ self.assertEqual(G3.__args__, (int, (int, bool)))
+ self.assertEqual(G3.__parameters__, ())
+
+ G4 = X[int, ...]
+ self.assertEqual(G4.__args__, (int, Ellipsis))
+ self.assertEqual(G4.__parameters__, ())
+
+ class Z(Generic[P]):
+ f: Callable[P, int]
+
+ G5 = Z[[int, str, bool]]
+ self.assertEqual(G5.__args__, ((int, str, bool),))
+ self.assertEqual(G5.__parameters__, ())
+
+ G6 = Z[int, str, bool]
+ self.assertEqual(G6.__args__, ((int, str, bool),))
+ self.assertEqual(G6.__parameters__, ())
+
+ # G5 and G6 should be equivalent according to the PEP
+ self.assertEqual(G5.__args__, G6.__args__)
+ self.assertEqual(G5.__origin__, G6.__origin__)
+ self.assertEqual(G5.__parameters__, G6.__parameters__)
+ self.assertEqual(G5, G6)
+
+ def test_var_substitution(self):
+ T = TypeVar("T")
+ P = ParamSpec("P")
+ C1 = Callable[P, T]
+ self.assertEqual(C1[int, str], Callable[[int], str])
+ self.assertEqual(C1[[int, str, dict], float], Callable[[int, str, dict], float])
+
+
+class ConcatenateTests(BaseTestCase):
+ def test_basics(self):
+ P = ParamSpec('P')
+ class MyClass: ...
+ c = Concatenate[MyClass, P]
+ self.assertNotEqual(c, Concatenate)
+
+ def test_valid_uses(self):
+ P = ParamSpec('P')
+ T = TypeVar('T')
+ C1 = Callable[Concatenate[int, P], int]
+ self.assertEqual(C1.__args__, (Concatenate[int, P], int))
+ self.assertEqual(C1.__parameters__, (P,))
+ C2 = Callable[Concatenate[int, T, P], T]
+ self.assertEqual(C2.__args__, (Concatenate[int, T, P], T))
+ self.assertEqual(C2.__parameters__, (T, P))
+
+ # Test collections.abc.Callable too.
+ C3 = collections.abc.Callable[Concatenate[int, P], int]
+ self.assertEqual(C3.__args__, (Concatenate[int, P], int))
+ self.assertEqual(C3.__parameters__, (P,))
+ C4 = collections.abc.Callable[Concatenate[int, T, P], T]
+ self.assertEqual(C4.__args__, (Concatenate[int, T, P], T))
+ self.assertEqual(C4.__parameters__, (T, P))
+
+
class AllTests(BaseTestCase):
"""Tests for __all__."""
diff --git a/Lib/typing.py b/Lib/typing.py
index 7f07321..7b79876 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -4,8 +4,10 @@ The typing module: Support for gradual typing as defined by PEP 484.
At large scale, the structure of the module is following:
* Imports and exports, all public names should be explicitly added to __all__.
* Internal helper functions: these should never be used in code outside this module.
-* _SpecialForm and its instances (special forms): Any, NoReturn, ClassVar, Union, Optional
-* Two classes whose instances can be type arguments in addition to types: ForwardRef and TypeVar
+* _SpecialForm and its instances (special forms):
+ Any, NoReturn, ClassVar, Union, Optional, Concatenate
+* Classes whose instances can be type arguments in addition to types:
+ ForwardRef, TypeVar and ParamSpec
* The core of internal generics API: _GenericAlias and _VariadicGenericAlias, the latter is
currently only used by Tuple and Callable. All subscripted types like X[int], Union[int, str],
etc., are instances of either of these classes.
@@ -36,11 +38,13 @@ __all__ = [
'Any',
'Callable',
'ClassVar',
+ 'Concatenate',
'Final',
'ForwardRef',
'Generic',
'Literal',
'Optional',
+ 'ParamSpec',
'Protocol',
'Tuple',
'Type',
@@ -154,7 +158,7 @@ def _type_check(arg, msg, is_argument=True):
return arg
if isinstance(arg, _SpecialForm) or arg in (Generic, Protocol):
raise TypeError(f"Plain {arg} is not valid as type argument")
- if isinstance(arg, (type, TypeVar, ForwardRef, types.Union)):
+ if isinstance(arg, (type, TypeVar, ForwardRef, types.Union, ParamSpec)):
return arg
if not callable(arg):
raise TypeError(f"{msg} Got {arg!r:.100}.")
@@ -183,14 +187,14 @@ def _type_repr(obj):
def _collect_type_vars(types):
- """Collect all type variable contained in types in order of
- first appearance (lexicographic order). For example::
+ """Collect all type variable-like variables 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, TypeVar) and t not in tvars:
+ if isinstance(t, _TypeVarLike) 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])
@@ -208,6 +212,21 @@ def _check_generic(cls, parameters, elen):
raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
f" actual {alen}, expected {elen}")
+def _prepare_paramspec_params(cls, params):
+ """Prepares the parameters for a Generic containing ParamSpec
+ variables (internal helper).
+ """
+ # Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.
+ if len(cls.__parameters__) == 1 and len(params) > 1:
+ return (params,)
+ else:
+ _params = []
+ # Convert lists to tuples to help other libraries cache the results.
+ for p, tvar in zip(params, cls.__parameters__):
+ if isinstance(tvar, ParamSpec) and isinstance(p, list):
+ p = tuple(p)
+ _params.append(p)
+ return tuple(_params)
def _deduplicate(params):
# Weed out strict duplicates, preserving the first of each occurrence.
@@ -523,6 +542,29 @@ def TypeAlias(self, parameters):
raise TypeError(f"{self} is not subscriptable")
+@_SpecialForm
+def Concatenate(self, parameters):
+ """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.
+ """
+ if parameters == ():
+ raise TypeError("Cannot take a Concatenate of no types.")
+ if not isinstance(parameters, tuple):
+ parameters = (parameters,)
+ if not isinstance(parameters[-1], ParamSpec):
+ raise TypeError("The last parameter to Concatenate should be a "
+ "ParamSpec variable.")
+ msg = "Concatenate[arg, ...]: each arg must be a type."
+ parameters = tuple(_type_check(p, msg) for p in parameters)
+ return _ConcatenateGenericAlias(self, parameters)
+
+
class ForwardRef(_Final, _root=True):
"""Internal wrapper to hold a forward reference."""
@@ -585,8 +627,41 @@ class ForwardRef(_Final, _root=True):
def __repr__(self):
return f'ForwardRef({self.__forward_arg__!r})'
+class _TypeVarLike:
+ """Mixin for TypeVar-like types (TypeVar and ParamSpec)."""
+ def __init__(self, bound, covariant, contravariant):
+ """Used to setup TypeVars and ParamSpec's bound, covariant and
+ contravariant attributes.
+ """
+ if covariant and contravariant:
+ raise ValueError("Bivariant types are not supported.")
+ self.__covariant__ = bool(covariant)
+ self.__contravariant__ = bool(contravariant)
+ if bound:
+ self.__bound__ = _type_check(bound, "Bound must be a type.")
+ else:
+ self.__bound__ = None
+
+ def __or__(self, right):
+ return Union[self, right]
+
+ def __ror__(self, right):
+ return Union[self, right]
+
+ def __repr__(self):
+ if self.__covariant__:
+ prefix = '+'
+ elif self.__contravariant__:
+ prefix = '-'
+ else:
+ prefix = '~'
+ return prefix + self.__name__
+
+ def __reduce__(self):
+ return self.__name__
+
-class TypeVar(_Final, _Immutable, _root=True):
+class TypeVar( _Final, _Immutable, _TypeVarLike, _root=True):
"""Type variable.
Usage::
@@ -636,20 +711,13 @@ class TypeVar(_Final, _Immutable, _root=True):
def __init__(self, name, *constraints, bound=None,
covariant=False, contravariant=False):
self.__name__ = name
- if covariant and contravariant:
- raise ValueError("Bivariant types are not supported.")
- self.__covariant__ = bool(covariant)
- self.__contravariant__ = bool(contravariant)
+ super().__init__(bound, covariant, contravariant)
if constraints and bound is not None:
raise TypeError("Constraints cannot be combined with bound=...")
if constraints and len(constraints) == 1:
raise TypeError("A single constraint is not allowed")
msg = "TypeVar(name, constraint, ...): constraints must be types."
self.__constraints__ = tuple(_type_check(t, msg) for t in constraints)
- if bound:
- self.__bound__ = _type_check(bound, "Bound must be a type.")
- else:
- self.__bound__ = None
try:
def_mod = sys._getframe(1).f_globals.get('__name__', '__main__') # for pickling
except (AttributeError, ValueError):
@@ -657,23 +725,68 @@ class TypeVar(_Final, _Immutable, _root=True):
if def_mod != 'typing':
self.__module__ = def_mod
- def __or__(self, right):
- return Union[self, right]
- def __ror__(self, right):
- return Union[self, right]
+class ParamSpec(_Final, _Immutable, _TypeVarLike, _root=True):
+ """Parameter specification variable.
- def __repr__(self):
- if self.__covariant__:
- prefix = '+'
- elif self.__contravariant__:
- prefix = '-'
- else:
- prefix = '~'
- return prefix + self.__name__
+ Usage::
- def __reduce__(self):
- return self.__name__
+ P = ParamSpec('P')
+
+ Parameter specification variables exist primarily for the benefit of static
+ type checkers. They are used to forward the parameter types of one
+ Callable to another Callable, a pattern commonly found in higher order
+ functions and decorators. They are only valid when used in Concatenate, or
+ as the first argument to Callable, or as parameters for user-defined Generics.
+ See class Generic for more information on generic types. An example for
+ annotating a decorator::
+
+ T = TypeVar('T')
+ P = ParamSpec('P')
+
+ def add_logging(f: Callable[P, T]) -> Callable[P, T]:
+ '''A type-safe decorator to add logging to a function.'''
+ def inner(*args: P.args, **kwargs: P.kwargs) -> T:
+ logging.info(f'{f.__name__} was called')
+ return f(*args, **kwargs)
+ return inner
+
+ @add_logging
+ def add_two(x: float, y: float) -> float:
+ '''Add two numbers together.'''
+ return x + y
+
+ Parameter specification variables defined with covariant=True or
+ contravariant=True can be used to declare covariant or contravariant
+ generic types. These keyword arguments are valid, but their actual semantics
+ are yet to be decided. See PEP 612 for details.
+
+ Parameter specification variables can be introspected. e.g.:
+
+ P.__name__ == 'T'
+ P.__bound__ == None
+ P.__covariant__ == False
+ P.__contravariant__ == False
+
+ Note that only parameter specification variables defined in global scope can
+ be pickled.
+ """
+
+ __slots__ = ('__name__', '__bound__', '__covariant__', '__contravariant__',
+ '__dict__')
+
+ args = object()
+ kwargs = object()
+
+ def __init__(self, name, bound=None, covariant=False, contravariant=False):
+ self.__name__ = name
+ super().__init__(bound, covariant, contravariant)
+ try:
+ def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
+ except (AttributeError, ValueError):
+ def_mod = None
+ if def_mod != 'typing':
+ self.__module__ = def_mod
def _is_dunder(attr):
@@ -783,21 +896,26 @@ class _GenericAlias(_BaseGenericAlias, _root=True):
raise TypeError(f"Cannot subscript already-subscripted {self}")
if not isinstance(params, tuple):
params = (params,)
- msg = "Parameters to generic types must be types."
- params = tuple(_type_check(p, msg) for p in 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)
_check_generic(self, params, len(self.__parameters__))
subst = dict(zip(self.__parameters__, params))
new_args = []
for arg in self.__args__:
- if isinstance(arg, TypeVar):
+ if isinstance(arg, _TypeVarLike):
arg = subst[arg]
elif isinstance(arg, (_GenericAlias, GenericAlias)):
subparams = arg.__parameters__
if subparams:
subargs = tuple(subst[x] for x in subparams)
arg = arg[subargs]
- new_args.append(arg)
+ # Required to flatten out the args for CallableGenericAlias
+ if self.__origin__ == collections.abc.Callable and isinstance(arg, tuple):
+ new_args.extend(arg)
+ else:
+ new_args.append(arg)
return self.copy_with(tuple(new_args))
def copy_with(self, params):
@@ -884,15 +1002,18 @@ class _SpecialGenericAlias(_BaseGenericAlias, _root=True):
class _CallableGenericAlias(_GenericAlias, _root=True):
def __repr__(self):
assert self._name == 'Callable'
- if len(self.__args__) == 2 and self.__args__[0] is Ellipsis:
+ args = self.__args__
+ if len(args) == 2 and (args[0] is Ellipsis
+ or isinstance(args[0], (ParamSpec, _ConcatenateGenericAlias))):
return super().__repr__()
return (f'typing.Callable'
- f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
- f'{_type_repr(self.__args__[-1])}]')
+ f'[[{", ".join([_type_repr(a) for a in args[:-1]])}], '
+ f'{_type_repr(args[-1])}]')
def __reduce__(self):
args = self.__args__
- if not (len(args) == 2 and args[0] is ...):
+ if not (len(args) == 2 and (args[0] is Ellipsis
+ or isinstance(args[0], (ParamSpec, _ConcatenateGenericAlias)))):
args = list(args[:-1]), args[-1]
return operator.getitem, (Callable, args)
@@ -992,6 +1113,10 @@ class _LiteralGenericAlias(_GenericAlias, _root=True):
return hash(frozenset(_value_and_type_iter(self.__args__)))
+class _ConcatenateGenericAlias(_GenericAlias, _root=True):
+ pass
+
+
class Generic:
"""Abstract base class for generic types.
@@ -1022,18 +1147,20 @@ class Generic:
if not params and cls is not Tuple:
raise TypeError(
f"Parameter list to {cls.__qualname__}[...] cannot be empty")
- msg = "Parameters to generic types must be types."
- params = tuple(_type_check(p, msg) for p in 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, TypeVar) for p in params):
+ if not all(isinstance(p, _TypeVarLike) for p in params):
raise TypeError(
- f"Parameters to {cls.__name__}[...] must all be type variables")
+ f"Parameters to {cls.__name__}[...] must all be type variables "
+ f"or parameter specification variables.")
if len(set(params)) != len(params):
raise TypeError(
f"Parameters to {cls.__name__}[...] must all be unique")
else:
# Subscripting a regular Generic subclass.
+ 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)