-
-
Notifications
You must be signed in to change notification settings - Fork 33.8k
bpo-44796: Unify TypeVar and ParamSpec substitution #31143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
a03c0d1
af9a60c
e4345da
845676d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,11 +174,13 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms= | |
| if (isinstance(arg, _GenericAlias) and | ||
| arg.__origin__ in invalid_generic_forms): | ||
| raise TypeError(f"{arg} is not valid as type argument") | ||
| if arg in (Any, NoReturn, ClassVar, Final): | ||
| if arg in (Any, NoReturn): | ||
| return arg | ||
| if allow_special_forms and arg in (ClassVar, Final): | ||
| 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.UnionType, ParamSpec)): | ||
| if isinstance(arg, (type, TypeVar, ForwardRef, types.UnionType)): | ||
|
||
| return arg | ||
| if not callable(arg): | ||
| raise TypeError(f"{msg} Got {arg!r:.100}.") | ||
|
|
@@ -211,21 +213,22 @@ def _type_repr(obj): | |
| return repr(obj) | ||
|
|
||
|
|
||
| def _collect_type_vars(types_, typevar_types=None): | ||
| """Collect all type variable contained | ||
| in types in order of first appearance (lexicographic order). For example:: | ||
| def _collect_parameters(args): | ||
| """Collect all type variables and parameter specifications in args | ||
| in order of first appearance (lexicographic order). For example:: | ||
|
|
||
| _collect_type_vars((T, List[S, T])) == (T, S) | ||
| _collect_parameters((T, Callable[P, T])) == (T, P) | ||
| """ | ||
| if typevar_types is None: | ||
| typevar_types = TypeVar | ||
| tvars = [] | ||
| for t in types_: | ||
| if isinstance(t, typevar_types) and t not in tvars: | ||
| tvars.append(t) | ||
| if isinstance(t, (_GenericAlias, GenericAlias, types.UnionType)): | ||
| tvars.extend([t for t in t.__parameters__ if t not in tvars]) | ||
| return tuple(tvars) | ||
| parameters = [] | ||
| for t in args: | ||
| if hasattr(t, '__typing_subst__'): | ||
| if t not in parameters: | ||
| parameters.append(t) | ||
| else: | ||
| for x in getattr(t, '__parameters__', ()): | ||
| if x not in parameters: | ||
| parameters.append(x) | ||
| return tuple(parameters) | ||
|
|
||
|
|
||
| def _check_generic(cls, parameters, elen): | ||
|
|
@@ -818,6 +821,11 @@ def __init__(self, name, *constraints, bound=None, | |
| if def_mod != 'typing': | ||
| self.__module__ = def_mod | ||
|
|
||
| def __typing_subst__(self, arg): | ||
| msg = "Parameters to generic types must be types." | ||
| arg = _type_check(arg, msg, is_argument=True) | ||
| return arg | ||
|
|
||
|
|
||
| class ParamSpecArgs(_Final, _Immutable, _root=True): | ||
| """The args for a ParamSpec object. | ||
|
|
@@ -918,6 +926,14 @@ def __init__(self, name, *, bound=None, covariant=False, contravariant=False): | |
| if def_mod != 'typing': | ||
| self.__module__ = def_mod | ||
|
|
||
| def __typing_subst__(self, arg): | ||
| if isinstance(arg, (list, tuple)): | ||
| arg = tuple(_type_check(a, "Expected a type.") for a in arg) | ||
| elif not _is_param_expr(arg): | ||
| raise TypeError(f"Expected a list of types, an ellipsis, " | ||
| f"ParamSpec, or Concatenate. Got {arg}") | ||
| return arg | ||
|
|
||
|
|
||
| def _is_dunder(attr): | ||
| return attr.startswith('__') and attr.endswith('__') | ||
|
|
@@ -972,7 +988,7 @@ def __getattr__(self, attr): | |
|
|
||
| def __setattr__(self, attr, val): | ||
| if _is_dunder(attr) or attr in {'_name', '_inst', '_nparams', | ||
| '_typevar_types', '_paramspec_tvars'}: | ||
| '_paramspec_tvars'}: | ||
| super().__setattr__(attr, val) | ||
| else: | ||
| setattr(self.__origin__, attr, val) | ||
|
|
@@ -1001,16 +1017,14 @@ def __dir__(self): | |
|
|
||
| class _GenericAlias(_BaseGenericAlias, _root=True): | ||
| 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, typevar_types=_typevar_types) | ||
| self._typevar_types = _typevar_types | ||
| self.__parameters__ = _collect_parameters(params) | ||
| self._paramspec_tvars = _paramspec_tvars | ||
| if not name: | ||
| self.__module__ = origin.__module__ | ||
|
|
@@ -1047,16 +1061,11 @@ def __getitem__(self, params): | |
| subst = dict(zip(self.__parameters__, params)) | ||
| new_args = [] | ||
| for arg in self.__args__: | ||
| if isinstance(arg, self._typevar_types): | ||
| if isinstance(arg, ParamSpec): | ||
| arg = subst[arg] | ||
| if not _is_param_expr(arg): | ||
| raise TypeError(f"Expected a list of types, an ellipsis, " | ||
| f"ParamSpec, or Concatenate. Got {arg}") | ||
| else: | ||
| arg = subst[arg] | ||
| elif isinstance(arg, (_GenericAlias, GenericAlias, types.UnionType)): | ||
| subparams = arg.__parameters__ | ||
| substfunc = getattr(arg, '__typing_subst__', None) | ||
| if substfunc: | ||
| arg = substfunc(subst[arg]) | ||
| else: | ||
| subparams = getattr(arg, '__parameters__', ()) | ||
| if subparams: | ||
| subargs = tuple(subst[x] for x in subparams) | ||
| arg = arg[subargs] | ||
|
|
@@ -1172,7 +1181,6 @@ class _CallableType(_SpecialGenericAlias, _root=True): | |
| def copy_with(self, params): | ||
| return _CallableGenericAlias(self.__origin__, params, | ||
| name=self._name, inst=self._inst, | ||
| _typevar_types=(TypeVar, ParamSpec), | ||
| _paramspec_tvars=True) | ||
|
|
||
| def __getitem__(self, params): | ||
|
|
@@ -1272,7 +1280,6 @@ def __hash__(self): | |
| class _ConcatenateGenericAlias(_GenericAlias, _root=True): | ||
| def __init__(self, *args, **kwargs): | ||
| super().__init__(*args, **kwargs, | ||
| _typevar_types=(TypeVar, ParamSpec), | ||
| _paramspec_tvars=True) | ||
|
|
||
| def copy_with(self, params): | ||
|
|
@@ -1333,7 +1340,6 @@ def __class_getitem__(cls, params): | |
| else: | ||
| _check_generic(cls, params, len(cls.__parameters__)) | ||
| return _GenericAlias(cls, params, | ||
| _typevar_types=(TypeVar, ParamSpec), | ||
| _paramspec_tvars=True) | ||
|
|
||
| def __init_subclass__(cls, *args, **kwargs): | ||
|
|
@@ -1346,7 +1352,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, ParamSpec)) | ||
| tvars = _collect_parameters(cls.__orig_bases__) | ||
| # Look for Generic[T1, ..., Tn]. | ||
| # If found, tvars must be a subset of it. | ||
| # If not found, tvars is it. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we have to keep
__parameters__, it's de facto a public API by now.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is inherited from GenericAlias now.