bpo-40397: Refactor typing._GenericAlias (GH-19719)
Make the design more object-oriented. Split _GenericAlias on two almost independent classes: for special generic aliases like List and for parametrized generic aliases like List[int]. Add specialized subclasses for Callable, Callable[...], Tuple and Union[...].
This commit is contained in:
parent
470aac4d8e
commit
c1c7d8ead9
411
Lib/typing.py
411
Lib/typing.py
|
@ -181,34 +181,11 @@ def _collect_type_vars(types):
|
||||||
for t in types:
|
for t in types:
|
||||||
if isinstance(t, TypeVar) and t not in tvars:
|
if isinstance(t, TypeVar) and t not in tvars:
|
||||||
tvars.append(t)
|
tvars.append(t)
|
||||||
if ((isinstance(t, _GenericAlias) and not t._special)
|
if isinstance(t, (_GenericAlias, GenericAlias)):
|
||||||
or isinstance(t, GenericAlias)):
|
|
||||||
tvars.extend([t for t in t.__parameters__ if t not in tvars])
|
tvars.extend([t for t in t.__parameters__ if t not in tvars])
|
||||||
return tuple(tvars)
|
return tuple(tvars)
|
||||||
|
|
||||||
|
|
||||||
def _subs_tvars(tp, tvars, subs):
|
|
||||||
"""Substitute type variables 'tvars' with substitutions 'subs'.
|
|
||||||
These two must have the same length.
|
|
||||||
"""
|
|
||||||
if not isinstance(tp, (_GenericAlias, GenericAlias)):
|
|
||||||
return tp
|
|
||||||
new_args = list(tp.__args__)
|
|
||||||
for a, arg in enumerate(tp.__args__):
|
|
||||||
if isinstance(arg, TypeVar):
|
|
||||||
for i, tvar in enumerate(tvars):
|
|
||||||
if arg == tvar:
|
|
||||||
new_args[a] = subs[i]
|
|
||||||
else:
|
|
||||||
new_args[a] = _subs_tvars(arg, tvars, subs)
|
|
||||||
if tp.__origin__ is Union:
|
|
||||||
return Union[tuple(new_args)]
|
|
||||||
if isinstance(tp, GenericAlias):
|
|
||||||
return GenericAlias(tp.__origin__, tuple(new_args))
|
|
||||||
else:
|
|
||||||
return tp.copy_with(tuple(new_args))
|
|
||||||
|
|
||||||
|
|
||||||
def _check_generic(cls, parameters):
|
def _check_generic(cls, parameters):
|
||||||
"""Check correct count for parameters of a generic cls (internal helper).
|
"""Check correct count for parameters of a generic cls (internal helper).
|
||||||
This gives a nice error message in case of count mismatch.
|
This gives a nice error message in case of count mismatch.
|
||||||
|
@ -229,7 +206,7 @@ def _remove_dups_flatten(parameters):
|
||||||
# Flatten out Union[Union[...], ...].
|
# Flatten out Union[Union[...], ...].
|
||||||
params = []
|
params = []
|
||||||
for p in parameters:
|
for p in parameters:
|
||||||
if isinstance(p, _GenericAlias) and p.__origin__ is Union:
|
if isinstance(p, _UnionGenericAlias):
|
||||||
params.extend(p.__args__)
|
params.extend(p.__args__)
|
||||||
elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union:
|
elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union:
|
||||||
params.extend(p[1:])
|
params.extend(p[1:])
|
||||||
|
@ -274,18 +251,14 @@ def _eval_type(t, globalns, localns):
|
||||||
"""
|
"""
|
||||||
if isinstance(t, ForwardRef):
|
if isinstance(t, ForwardRef):
|
||||||
return t._evaluate(globalns, localns)
|
return t._evaluate(globalns, localns)
|
||||||
if isinstance(t, _GenericAlias):
|
if isinstance(t, (_GenericAlias, GenericAlias)):
|
||||||
ev_args = tuple(_eval_type(a, globalns, localns) for a in t.__args__)
|
ev_args = tuple(_eval_type(a, globalns, localns) for a in t.__args__)
|
||||||
if ev_args == t.__args__:
|
if ev_args == t.__args__:
|
||||||
return t
|
return t
|
||||||
res = t.copy_with(ev_args)
|
if isinstance(t, GenericAlias):
|
||||||
res._special = t._special
|
return GenericAlias(t.__origin__, ev_args)
|
||||||
return res
|
else:
|
||||||
if isinstance(t, GenericAlias):
|
return t.copy_with(ev_args)
|
||||||
ev_args = tuple(_eval_type(a, globalns, localns) for a in t.__args__)
|
|
||||||
if ev_args == t.__args__:
|
|
||||||
return t
|
|
||||||
return GenericAlias(t.__origin__, ev_args)
|
|
||||||
return t
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
@ -300,6 +273,7 @@ class _Final:
|
||||||
|
|
||||||
class _Immutable:
|
class _Immutable:
|
||||||
"""Mixin to indicate that object should not be copied."""
|
"""Mixin to indicate that object should not be copied."""
|
||||||
|
__slots__ = ()
|
||||||
|
|
||||||
def __copy__(self):
|
def __copy__(self):
|
||||||
return self
|
return self
|
||||||
|
@ -446,7 +420,7 @@ def Union(self, parameters):
|
||||||
parameters = _remove_dups_flatten(parameters)
|
parameters = _remove_dups_flatten(parameters)
|
||||||
if len(parameters) == 1:
|
if len(parameters) == 1:
|
||||||
return parameters[0]
|
return parameters[0]
|
||||||
return _GenericAlias(self, parameters)
|
return _UnionGenericAlias(self, parameters)
|
||||||
|
|
||||||
@_SpecialForm
|
@_SpecialForm
|
||||||
def Optional(self, parameters):
|
def Optional(self, parameters):
|
||||||
|
@ -579,7 +553,7 @@ class TypeVar(_Final, _Immutable, _root=True):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__slots__ = ('__name__', '__bound__', '__constraints__',
|
__slots__ = ('__name__', '__bound__', '__constraints__',
|
||||||
'__covariant__', '__contravariant__')
|
'__covariant__', '__contravariant__', '__dict__')
|
||||||
|
|
||||||
def __init__(self, name, *constraints, bound=None,
|
def __init__(self, name, *constraints, bound=None,
|
||||||
covariant=False, contravariant=False):
|
covariant=False, contravariant=False):
|
||||||
|
@ -629,23 +603,10 @@ class TypeVar(_Final, _Immutable, _root=True):
|
||||||
# e.g., Dict[T, int].__args__ == (T, int).
|
# e.g., Dict[T, int].__args__ == (T, int).
|
||||||
|
|
||||||
|
|
||||||
# Mapping from non-generic type names that have a generic alias in typing
|
|
||||||
# but with a different name.
|
|
||||||
_normalize_alias = {'list': 'List',
|
|
||||||
'tuple': 'Tuple',
|
|
||||||
'dict': 'Dict',
|
|
||||||
'set': 'Set',
|
|
||||||
'frozenset': 'FrozenSet',
|
|
||||||
'deque': 'Deque',
|
|
||||||
'defaultdict': 'DefaultDict',
|
|
||||||
'type': 'Type',
|
|
||||||
'Set': 'AbstractSet'}
|
|
||||||
|
|
||||||
def _is_dunder(attr):
|
def _is_dunder(attr):
|
||||||
return attr.startswith('__') and attr.endswith('__')
|
return attr.startswith('__') and attr.endswith('__')
|
||||||
|
|
||||||
|
class _BaseGenericAlias(_Final, _root=True):
|
||||||
class _GenericAlias(_Final, _root=True):
|
|
||||||
"""The central part of internal API.
|
"""The central part of internal API.
|
||||||
|
|
||||||
This represents a generic version of type 'origin' with type arguments 'params'.
|
This represents a generic version of type 'origin' with type arguments 'params'.
|
||||||
|
@ -654,12 +615,8 @@ class _GenericAlias(_Final, _root=True):
|
||||||
have 'name' always set. If 'inst' is False, then the alias can't be instantiated,
|
have 'name' always set. If 'inst' is False, then the alias can't be instantiated,
|
||||||
this is used by e.g. typing.List and typing.Dict.
|
this is used by e.g. typing.List and typing.Dict.
|
||||||
"""
|
"""
|
||||||
def __init__(self, origin, params, *, inst=True, special=False, name=None):
|
def __init__(self, origin, params, *, inst=True, name=None):
|
||||||
self._inst = inst
|
self._inst = inst
|
||||||
self._special = special
|
|
||||||
if special and name is None:
|
|
||||||
orig_name = origin.__name__
|
|
||||||
name = _normalize_alias.get(orig_name, orig_name)
|
|
||||||
self._name = name
|
self._name = name
|
||||||
if not isinstance(params, tuple):
|
if not isinstance(params, tuple):
|
||||||
params = (params,)
|
params = (params,)
|
||||||
|
@ -671,9 +628,61 @@ class _GenericAlias(_Final, _root=True):
|
||||||
self.__slots__ = None # This is not documented.
|
self.__slots__ = None # This is not documented.
|
||||||
if not name:
|
if not name:
|
||||||
self.__module__ = origin.__module__
|
self.__module__ = origin.__module__
|
||||||
if special:
|
|
||||||
self.__doc__ = f'A generic version of {origin.__module__}.{origin.__qualname__}'
|
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, _BaseGenericAlias):
|
||||||
|
return NotImplemented
|
||||||
|
return (self.__origin__ == other.__origin__
|
||||||
|
and self.__args__ == other.__args__)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self.__origin__, self.__args__))
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
if not self._inst:
|
||||||
|
raise TypeError(f"Type {self._name} cannot be instantiated; "
|
||||||
|
f"use {self.__origin__.__name__}() instead")
|
||||||
|
result = self.__origin__(*args, **kwargs)
|
||||||
|
try:
|
||||||
|
result.__orig_class__ = self
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return result
|
||||||
|
|
||||||
|
def __mro_entries__(self, bases):
|
||||||
|
res = []
|
||||||
|
if self.__origin__ not in bases:
|
||||||
|
res.append(self.__origin__)
|
||||||
|
i = bases.index(self)
|
||||||
|
for b in bases[i+1:]:
|
||||||
|
if isinstance(b, _BaseGenericAlias) or issubclass(b, Generic):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
res.append(Generic)
|
||||||
|
return tuple(res)
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
# We are careful for copy and pickle.
|
||||||
|
# Also for simplicity we just don't relay all dunder names
|
||||||
|
if '__origin__' in self.__dict__ and not _is_dunder(attr):
|
||||||
|
return getattr(self.__origin__, attr)
|
||||||
|
raise AttributeError(attr)
|
||||||
|
|
||||||
|
def __setattr__(self, attr, val):
|
||||||
|
if _is_dunder(attr) or attr in ('_name', '_inst'):
|
||||||
|
super().__setattr__(attr, val)
|
||||||
|
else:
|
||||||
|
setattr(self.__origin__, attr, val)
|
||||||
|
|
||||||
|
def __instancecheck__(self, obj):
|
||||||
|
return self.__subclasscheck__(type(obj))
|
||||||
|
|
||||||
|
def __subclasscheck__(self, cls):
|
||||||
|
raise TypeError("Subscripted generics cannot be used with"
|
||||||
|
" class and instance checks")
|
||||||
|
|
||||||
|
|
||||||
|
class _GenericAlias(_BaseGenericAlias, _root=True):
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem__(self, params):
|
def __getitem__(self, params):
|
||||||
if self.__origin__ in (Generic, Protocol):
|
if self.__origin__ in (Generic, Protocol):
|
||||||
|
@ -684,131 +693,109 @@ class _GenericAlias(_Final, _root=True):
|
||||||
msg = "Parameters to generic types must be types."
|
msg = "Parameters to generic types must be types."
|
||||||
params = tuple(_type_check(p, msg) for p in params)
|
params = tuple(_type_check(p, msg) for p in params)
|
||||||
_check_generic(self, params)
|
_check_generic(self, params)
|
||||||
return _subs_tvars(self, self.__parameters__, params)
|
|
||||||
|
subst = dict(zip(self.__parameters__, params))
|
||||||
|
new_args = []
|
||||||
|
for arg in self.__args__:
|
||||||
|
if isinstance(arg, TypeVar):
|
||||||
|
arg = subst[arg]
|
||||||
|
elif isinstance(arg, (_BaseGenericAlias, GenericAlias)):
|
||||||
|
subargs = tuple(subst[x] for x in arg.__parameters__)
|
||||||
|
arg = arg[subargs]
|
||||||
|
new_args.append(arg)
|
||||||
|
return self.copy_with(tuple(new_args))
|
||||||
|
|
||||||
def copy_with(self, params):
|
def copy_with(self, params):
|
||||||
# We don't copy self._special.
|
return self.__class__(self.__origin__, params, name=self._name, inst=self._inst)
|
||||||
return _GenericAlias(self.__origin__, params, name=self._name, inst=self._inst)
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if (self.__origin__ == Union and len(self.__args__) == 2
|
if self._name:
|
||||||
and type(None) in self.__args__):
|
name = 'typing.' + self._name
|
||||||
if self.__args__[0] is not type(None):
|
else:
|
||||||
arg = self.__args__[0]
|
name = _type_repr(self.__origin__)
|
||||||
else:
|
args = ", ".join([_type_repr(a) for a in self.__args__])
|
||||||
arg = self.__args__[1]
|
return f'{name}[{args}]'
|
||||||
return (f'typing.Optional[{_type_repr(arg)}]')
|
|
||||||
if (self._name != 'Callable' or
|
|
||||||
len(self.__args__) == 2 and self.__args__[0] is Ellipsis):
|
|
||||||
if self._name:
|
|
||||||
name = 'typing.' + self._name
|
|
||||||
else:
|
|
||||||
name = _type_repr(self.__origin__)
|
|
||||||
if not self._special:
|
|
||||||
args = f'[{", ".join([_type_repr(a) for a in self.__args__])}]'
|
|
||||||
else:
|
|
||||||
args = ''
|
|
||||||
return (f'{name}{args}')
|
|
||||||
if self._special:
|
|
||||||
return 'typing.Callable'
|
|
||||||
return (f'typing.Callable'
|
|
||||||
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
|
|
||||||
f'{_type_repr(self.__args__[-1])}]')
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __reduce__(self):
|
||||||
if not isinstance(other, _GenericAlias):
|
if self._name:
|
||||||
return NotImplemented
|
origin = globals()[self._name]
|
||||||
if self.__origin__ != other.__origin__:
|
else:
|
||||||
return False
|
origin = self.__origin__
|
||||||
if self.__origin__ is Union and other.__origin__ is Union:
|
args = tuple(self.__args__)
|
||||||
return frozenset(self.__args__) == frozenset(other.__args__)
|
if len(args) == 1 and not isinstance(args[0], tuple):
|
||||||
return self.__args__ == other.__args__
|
args, = args
|
||||||
|
return operator.getitem, (origin, args)
|
||||||
def __hash__(self):
|
|
||||||
if self.__origin__ is Union:
|
|
||||||
return hash((Union, frozenset(self.__args__)))
|
|
||||||
return hash((self.__origin__, self.__args__))
|
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
|
||||||
if not self._inst:
|
|
||||||
raise TypeError(f"Type {self._name} cannot be instantiated; "
|
|
||||||
f"use {self._name.lower()}() instead")
|
|
||||||
result = self.__origin__(*args, **kwargs)
|
|
||||||
try:
|
|
||||||
result.__orig_class__ = self
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
return result
|
|
||||||
|
|
||||||
def __mro_entries__(self, bases):
|
def __mro_entries__(self, bases):
|
||||||
if self._name: # generic version of an ABC or built-in class
|
if self._name: # generic version of an ABC or built-in class
|
||||||
res = []
|
return super().__mro_entries__(bases)
|
||||||
if self.__origin__ not in bases:
|
|
||||||
res.append(self.__origin__)
|
|
||||||
i = bases.index(self)
|
|
||||||
if not any(isinstance(b, _GenericAlias) or issubclass(b, Generic)
|
|
||||||
for b in bases[i+1:]):
|
|
||||||
res.append(Generic)
|
|
||||||
return tuple(res)
|
|
||||||
if self.__origin__ is Generic:
|
if self.__origin__ is Generic:
|
||||||
if Protocol in bases:
|
if Protocol in bases:
|
||||||
return ()
|
return ()
|
||||||
i = bases.index(self)
|
i = bases.index(self)
|
||||||
for b in bases[i+1:]:
|
for b in bases[i+1:]:
|
||||||
if isinstance(b, _GenericAlias) and b is not self:
|
if isinstance(b, _BaseGenericAlias) and b is not self:
|
||||||
return ()
|
return ()
|
||||||
return (self.__origin__,)
|
return (self.__origin__,)
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
|
||||||
# We are careful for copy and pickle.
|
|
||||||
# Also for simplicity we just don't relay all dunder names
|
|
||||||
if '__origin__' in self.__dict__ and not _is_dunder(attr):
|
|
||||||
return getattr(self.__origin__, attr)
|
|
||||||
raise AttributeError(attr)
|
|
||||||
|
|
||||||
def __setattr__(self, attr, val):
|
class _SpecialGenericAlias(_BaseGenericAlias, _root=True):
|
||||||
if _is_dunder(attr) or attr in ('_name', '_inst', '_special'):
|
def __init__(self, origin, params, *, inst=True, name=None):
|
||||||
super().__setattr__(attr, val)
|
if name is None:
|
||||||
else:
|
name = origin.__name__
|
||||||
setattr(self.__origin__, attr, val)
|
super().__init__(origin, params, inst=inst, name=name)
|
||||||
|
self.__doc__ = f'A generic version of {origin.__module__}.{origin.__qualname__}'
|
||||||
|
|
||||||
def __instancecheck__(self, obj):
|
@_tp_cache
|
||||||
return self.__subclasscheck__(type(obj))
|
def __getitem__(self, params):
|
||||||
|
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)
|
||||||
|
_check_generic(self, params)
|
||||||
|
assert self.__args__ == self.__parameters__
|
||||||
|
return self.copy_with(params)
|
||||||
|
|
||||||
|
def copy_with(self, params):
|
||||||
|
return _GenericAlias(self.__origin__, params,
|
||||||
|
name=self._name, inst=self._inst)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'typing.' + self._name
|
||||||
|
|
||||||
def __subclasscheck__(self, cls):
|
def __subclasscheck__(self, cls):
|
||||||
if self._special:
|
if isinstance(cls, _SpecialGenericAlias):
|
||||||
if not isinstance(cls, _GenericAlias):
|
return issubclass(cls.__origin__, self.__origin__)
|
||||||
return issubclass(cls, self.__origin__)
|
if not isinstance(cls, _GenericAlias):
|
||||||
if cls._special:
|
return issubclass(cls, self.__origin__)
|
||||||
return issubclass(cls.__origin__, self.__origin__)
|
return super().__subclasscheck__(cls)
|
||||||
raise TypeError("Subscripted generics cannot be used with"
|
|
||||||
" class and instance checks")
|
|
||||||
|
|
||||||
def __reduce__(self):
|
def __reduce__(self):
|
||||||
if self._special:
|
return self._name
|
||||||
return self._name
|
|
||||||
|
|
||||||
if self._name:
|
|
||||||
origin = globals()[self._name]
|
|
||||||
else:
|
|
||||||
origin = self.__origin__
|
|
||||||
if (origin is Callable and
|
|
||||||
not (len(self.__args__) == 2 and self.__args__[0] is Ellipsis)):
|
|
||||||
args = list(self.__args__[:-1]), self.__args__[-1]
|
|
||||||
else:
|
|
||||||
args = tuple(self.__args__)
|
|
||||||
if len(args) == 1 and not isinstance(args[0], tuple):
|
|
||||||
args, = args
|
|
||||||
return operator.getitem, (origin, args)
|
|
||||||
|
|
||||||
|
|
||||||
class _VariadicGenericAlias(_GenericAlias, _root=True):
|
class _CallableGenericAlias(_GenericAlias, _root=True):
|
||||||
"""Same as _GenericAlias above but for variadic aliases. Currently,
|
def __repr__(self):
|
||||||
this is used only by special internal aliases: Tuple and Callable.
|
assert self._name == 'Callable'
|
||||||
"""
|
if len(self.__args__) == 2 and self.__args__[0] is Ellipsis:
|
||||||
|
return super().__repr__()
|
||||||
|
return (f'typing.Callable'
|
||||||
|
f'[[{", ".join([_type_repr(a) for a in self.__args__[:-1]])}], '
|
||||||
|
f'{_type_repr(self.__args__[-1])}]')
|
||||||
|
|
||||||
|
def __reduce__(self):
|
||||||
|
args = self.__args__
|
||||||
|
if not (len(args) == 2 and args[0] is ...):
|
||||||
|
args = list(args[:-1]), args[-1]
|
||||||
|
return operator.getitem, (Callable, args)
|
||||||
|
|
||||||
|
|
||||||
|
class _CallableType(_SpecialGenericAlias, _root=True):
|
||||||
|
def copy_with(self, params):
|
||||||
|
return _CallableGenericAlias(self.__origin__, params,
|
||||||
|
name=self._name, inst=self._inst)
|
||||||
|
|
||||||
def __getitem__(self, params):
|
def __getitem__(self, params):
|
||||||
if self._name != 'Callable' or not self._special:
|
|
||||||
return self.__getitem_inner__(params)
|
|
||||||
if not isinstance(params, tuple) or len(params) != 2:
|
if not isinstance(params, tuple) or len(params) != 2:
|
||||||
raise TypeError("Callable must be used as "
|
raise TypeError("Callable must be used as "
|
||||||
"Callable[[arg, ...], result].")
|
"Callable[[arg, ...], result].")
|
||||||
|
@ -824,29 +811,53 @@ class _VariadicGenericAlias(_GenericAlias, _root=True):
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem_inner__(self, params):
|
def __getitem_inner__(self, params):
|
||||||
if self.__origin__ is tuple and self._special:
|
args, result = params
|
||||||
if params == ():
|
msg = "Callable[args, result]: result must be a type."
|
||||||
return self.copy_with((_TypingEmpty,))
|
result = _type_check(result, msg)
|
||||||
if not isinstance(params, tuple):
|
if args is Ellipsis:
|
||||||
params = (params,)
|
return self.copy_with((_TypingEllipsis, result))
|
||||||
if len(params) == 2 and params[1] is ...:
|
msg = "Callable[[arg, ...], result]: each arg must be a type."
|
||||||
msg = "Tuple[t, ...]: t must be a type."
|
args = tuple(_type_check(arg, msg) for arg in args)
|
||||||
p = _type_check(params[0], msg)
|
params = args + (result,)
|
||||||
return self.copy_with((p, _TypingEllipsis))
|
return self.copy_with(params)
|
||||||
msg = "Tuple[t0, t1, ...]: each t must be a type."
|
|
||||||
params = tuple(_type_check(p, msg) for p in params)
|
|
||||||
return self.copy_with(params)
|
class _TupleType(_SpecialGenericAlias, _root=True):
|
||||||
if self.__origin__ is collections.abc.Callable and self._special:
|
@_tp_cache
|
||||||
args, result = params
|
def __getitem__(self, params):
|
||||||
msg = "Callable[args, result]: result must be a type."
|
if params == ():
|
||||||
result = _type_check(result, msg)
|
return self.copy_with((_TypingEmpty,))
|
||||||
if args is Ellipsis:
|
if not isinstance(params, tuple):
|
||||||
return self.copy_with((_TypingEllipsis, result))
|
params = (params,)
|
||||||
msg = "Callable[[arg, ...], result]: each arg must be a type."
|
if len(params) == 2 and params[1] is ...:
|
||||||
args = tuple(_type_check(arg, msg) for arg in args)
|
msg = "Tuple[t, ...]: t must be a type."
|
||||||
params = args + (result,)
|
p = _type_check(params[0], msg)
|
||||||
return self.copy_with(params)
|
return self.copy_with((p, _TypingEllipsis))
|
||||||
return super().__getitem__(params)
|
msg = "Tuple[t0, t1, ...]: each t must be a type."
|
||||||
|
params = tuple(_type_check(p, msg) for p in params)
|
||||||
|
return self.copy_with(params)
|
||||||
|
|
||||||
|
|
||||||
|
class _UnionGenericAlias(_GenericAlias, _root=True):
|
||||||
|
def copy_with(self, params):
|
||||||
|
return Union[params]
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
if not isinstance(other, _UnionGenericAlias):
|
||||||
|
return NotImplemented
|
||||||
|
return set(self.__args__) == set(other.__args__)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(frozenset(self.__args__))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
args = self.__args__
|
||||||
|
if len(args) == 2:
|
||||||
|
if args[0] is type(None):
|
||||||
|
return f'typing.Optional[{_type_repr(args[1])}]'
|
||||||
|
elif args[1] is type(None):
|
||||||
|
return f'typing.Optional[{_type_repr(args[0])}]'
|
||||||
|
return super().__repr__()
|
||||||
|
|
||||||
|
|
||||||
class Generic:
|
class Generic:
|
||||||
|
@ -1162,9 +1173,8 @@ class _AnnotatedAlias(_GenericAlias, _root=True):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, _AnnotatedAlias):
|
if not isinstance(other, _AnnotatedAlias):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
if self.__origin__ != other.__origin__:
|
return (self.__origin__ == other.__origin__
|
||||||
return False
|
and self.__metadata__ == other.__metadata__)
|
||||||
return self.__metadata__ == other.__metadata__
|
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.__origin__, self.__metadata__))
|
return hash((self.__origin__, self.__metadata__))
|
||||||
|
@ -1380,9 +1390,7 @@ def _strip_annotations(t):
|
||||||
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
||||||
if stripped_args == t.__args__:
|
if stripped_args == t.__args__:
|
||||||
return t
|
return t
|
||||||
res = t.copy_with(stripped_args)
|
return t.copy_with(stripped_args)
|
||||||
res._special = t._special
|
|
||||||
return res
|
|
||||||
if isinstance(t, GenericAlias):
|
if isinstance(t, GenericAlias):
|
||||||
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
|
||||||
if stripped_args == t.__args__:
|
if stripped_args == t.__args__:
|
||||||
|
@ -1407,7 +1415,7 @@ def get_origin(tp):
|
||||||
"""
|
"""
|
||||||
if isinstance(tp, _AnnotatedAlias):
|
if isinstance(tp, _AnnotatedAlias):
|
||||||
return Annotated
|
return Annotated
|
||||||
if isinstance(tp, (_GenericAlias, GenericAlias)):
|
if isinstance(tp, (_BaseGenericAlias, GenericAlias)):
|
||||||
return tp.__origin__
|
return tp.__origin__
|
||||||
if tp is Generic:
|
if tp is Generic:
|
||||||
return Generic
|
return Generic
|
||||||
|
@ -1427,7 +1435,7 @@ def get_args(tp):
|
||||||
"""
|
"""
|
||||||
if isinstance(tp, _AnnotatedAlias):
|
if isinstance(tp, _AnnotatedAlias):
|
||||||
return (tp.__origin__,) + tp.__metadata__
|
return (tp.__origin__,) + tp.__metadata__
|
||||||
if isinstance(tp, _GenericAlias) and not tp._special:
|
if isinstance(tp, _GenericAlias):
|
||||||
res = tp.__args__
|
res = tp.__args__
|
||||||
if tp.__origin__ is collections.abc.Callable and res[0] is not Ellipsis:
|
if tp.__origin__ is collections.abc.Callable and res[0] is not Ellipsis:
|
||||||
res = (list(res[:-1]), res[-1])
|
res = (list(res[:-1]), res[-1])
|
||||||
|
@ -1561,8 +1569,7 @@ AnyStr = TypeVar('AnyStr', bytes, str)
|
||||||
|
|
||||||
|
|
||||||
# Various ABCs mimicking those in collections.abc.
|
# Various ABCs mimicking those in collections.abc.
|
||||||
def _alias(origin, params, inst=True):
|
_alias = _SpecialGenericAlias
|
||||||
return _GenericAlias(origin, params, special=True, inst=inst)
|
|
||||||
|
|
||||||
Hashable = _alias(collections.abc.Hashable, ()) # Not generic.
|
Hashable = _alias(collections.abc.Hashable, ()) # Not generic.
|
||||||
Awaitable = _alias(collections.abc.Awaitable, T_co)
|
Awaitable = _alias(collections.abc.Awaitable, T_co)
|
||||||
|
@ -1575,7 +1582,7 @@ Reversible = _alias(collections.abc.Reversible, T_co)
|
||||||
Sized = _alias(collections.abc.Sized, ()) # Not generic.
|
Sized = _alias(collections.abc.Sized, ()) # Not generic.
|
||||||
Container = _alias(collections.abc.Container, T_co)
|
Container = _alias(collections.abc.Container, T_co)
|
||||||
Collection = _alias(collections.abc.Collection, T_co)
|
Collection = _alias(collections.abc.Collection, T_co)
|
||||||
Callable = _VariadicGenericAlias(collections.abc.Callable, (), special=True)
|
Callable = _CallableType(collections.abc.Callable, ())
|
||||||
Callable.__doc__ = \
|
Callable.__doc__ = \
|
||||||
"""Callable type; Callable[[int], str] is a function of (int) -> str.
|
"""Callable type; Callable[[int], str] is a function of (int) -> str.
|
||||||
|
|
||||||
|
@ -1586,7 +1593,7 @@ Callable.__doc__ = \
|
||||||
There is no syntax to indicate optional or keyword arguments,
|
There is no syntax to indicate optional or keyword arguments,
|
||||||
such function types are rarely used as callback types.
|
such function types are rarely used as callback types.
|
||||||
"""
|
"""
|
||||||
AbstractSet = _alias(collections.abc.Set, T_co)
|
AbstractSet = _alias(collections.abc.Set, T_co, name='AbstractSet')
|
||||||
MutableSet = _alias(collections.abc.MutableSet, T)
|
MutableSet = _alias(collections.abc.MutableSet, T)
|
||||||
# NOTE: Mapping is only covariant in the value type.
|
# NOTE: Mapping is only covariant in the value type.
|
||||||
Mapping = _alias(collections.abc.Mapping, (KT, VT_co))
|
Mapping = _alias(collections.abc.Mapping, (KT, VT_co))
|
||||||
|
@ -1594,7 +1601,7 @@ MutableMapping = _alias(collections.abc.MutableMapping, (KT, VT))
|
||||||
Sequence = _alias(collections.abc.Sequence, T_co)
|
Sequence = _alias(collections.abc.Sequence, T_co)
|
||||||
MutableSequence = _alias(collections.abc.MutableSequence, T)
|
MutableSequence = _alias(collections.abc.MutableSequence, T)
|
||||||
ByteString = _alias(collections.abc.ByteString, ()) # Not generic
|
ByteString = _alias(collections.abc.ByteString, ()) # Not generic
|
||||||
Tuple = _VariadicGenericAlias(tuple, (), inst=False, special=True)
|
Tuple = _TupleType(tuple, (), inst=False, name='Tuple')
|
||||||
Tuple.__doc__ = \
|
Tuple.__doc__ = \
|
||||||
"""Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
|
"""Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
|
||||||
|
|
||||||
|
@ -1604,24 +1611,24 @@ Tuple.__doc__ = \
|
||||||
|
|
||||||
To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
|
To specify a variable-length tuple of homogeneous type, use Tuple[T, ...].
|
||||||
"""
|
"""
|
||||||
List = _alias(list, T, inst=False)
|
List = _alias(list, T, inst=False, name='List')
|
||||||
Deque = _alias(collections.deque, T)
|
Deque = _alias(collections.deque, T, name='Deque')
|
||||||
Set = _alias(set, T, inst=False)
|
Set = _alias(set, T, inst=False, name='Set')
|
||||||
FrozenSet = _alias(frozenset, T_co, inst=False)
|
FrozenSet = _alias(frozenset, T_co, inst=False, name='FrozenSet')
|
||||||
MappingView = _alias(collections.abc.MappingView, T_co)
|
MappingView = _alias(collections.abc.MappingView, T_co)
|
||||||
KeysView = _alias(collections.abc.KeysView, KT)
|
KeysView = _alias(collections.abc.KeysView, KT)
|
||||||
ItemsView = _alias(collections.abc.ItemsView, (KT, VT_co))
|
ItemsView = _alias(collections.abc.ItemsView, (KT, VT_co))
|
||||||
ValuesView = _alias(collections.abc.ValuesView, VT_co)
|
ValuesView = _alias(collections.abc.ValuesView, VT_co)
|
||||||
ContextManager = _alias(contextlib.AbstractContextManager, T_co)
|
ContextManager = _alias(contextlib.AbstractContextManager, T_co, name='ContextManager')
|
||||||
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co)
|
AsyncContextManager = _alias(contextlib.AbstractAsyncContextManager, T_co, name='AsyncContextManager')
|
||||||
Dict = _alias(dict, (KT, VT), inst=False)
|
Dict = _alias(dict, (KT, VT), inst=False, name='Dict')
|
||||||
DefaultDict = _alias(collections.defaultdict, (KT, VT))
|
DefaultDict = _alias(collections.defaultdict, (KT, VT), name='DefaultDict')
|
||||||
OrderedDict = _alias(collections.OrderedDict, (KT, VT))
|
OrderedDict = _alias(collections.OrderedDict, (KT, VT))
|
||||||
Counter = _alias(collections.Counter, T)
|
Counter = _alias(collections.Counter, T)
|
||||||
ChainMap = _alias(collections.ChainMap, (KT, VT))
|
ChainMap = _alias(collections.ChainMap, (KT, VT))
|
||||||
Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co))
|
Generator = _alias(collections.abc.Generator, (T_co, T_contra, V_co))
|
||||||
AsyncGenerator = _alias(collections.abc.AsyncGenerator, (T_co, T_contra))
|
AsyncGenerator = _alias(collections.abc.AsyncGenerator, (T_co, T_contra))
|
||||||
Type = _alias(type, CT_co, inst=False)
|
Type = _alias(type, CT_co, inst=False, name='Type')
|
||||||
Type.__doc__ = \
|
Type.__doc__ = \
|
||||||
"""A special construct usable to annotate class objects.
|
"""A special construct usable to annotate class objects.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue