diff options
author | Guido van Rossum <guido@python.org> | 2016-10-29 15:54:56 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2016-10-29 15:54:56 (GMT) |
commit | 5fc25a873cfdec27e46f71e62c9b65df5667c1b4 (patch) | |
tree | b44292a9602c96710411175df76909d7067b3396 /Lib/typing.py | |
parent | 84968b74c89aa22759b8e284673b46a4a69db852 (diff) | |
download | cpython-5fc25a873cfdec27e46f71e62c9b65df5667c1b4.zip cpython-5fc25a873cfdec27e46f71e62c9b65df5667c1b4.tar.gz cpython-5fc25a873cfdec27e46f71e62c9b65df5667c1b4.tar.bz2 |
Issue #28556: updates to typing.py
Diffstat (limited to 'Lib/typing.py')
-rw-r--r-- | Lib/typing.py | 738 |
1 files changed, 404 insertions, 334 deletions
diff --git a/Lib/typing.py b/Lib/typing.py index 261da5d..bda4ea5 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -333,8 +333,7 @@ def _type_vars(types): def _eval_type(t, globalns, localns): if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): return t._eval_type(globalns, localns) - else: - return t + return t def _type_check(arg, msg): @@ -353,8 +352,14 @@ def _type_check(arg, msg): return type(None) if isinstance(arg, str): arg = _ForwardRef(arg) - if not isinstance(arg, (type, _TypingBase)) and not callable(arg): + if (isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or + not isinstance(arg, (type, _TypingBase)) and not callable(arg)): raise TypeError(msg + " Got %.100r." % (arg,)) + # Bare Union etc. are not valid as type arguments + if (type(arg).__name__ in ('_Union', '_Optional') + and not getattr(arg, '__origin__', None) + or isinstance(arg, TypingMeta) and _gorg(arg) in (Generic, _Protocol)): + raise TypeError("Plain %s is not valid as type argument" % arg) return arg @@ -369,10 +374,12 @@ def _type_repr(obj): if isinstance(obj, type) and not isinstance(obj, TypingMeta): if obj.__module__ == 'builtins': return _qualname(obj) - else: - return '%s.%s' % (obj.__module__, _qualname(obj)) - else: - return repr(obj) + return '%s.%s' % (obj.__module__, _qualname(obj)) + if obj is ...: + return('...') + if isinstance(obj, types.FunctionType): + return obj.__name__ + return repr(obj) class _Any(_FinalTypingBase, _root=True): @@ -502,7 +509,107 @@ T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. AnyStr = TypeVar('AnyStr', bytes, str) +def _replace_arg(arg, tvars, args): + """ A helper fuunction: replace arg if it is a type variable + found in tvars with corresponding substitution from args or + with corresponding substitution sub-tree if arg is a generic type. + """ + + if tvars is None: + tvars = [] + if hasattr(arg, '_subs_tree'): + return arg._subs_tree(tvars, args) + if isinstance(arg, TypeVar): + for i, tvar in enumerate(tvars): + if arg == tvar: + return args[i] + return arg + + +def _subs_tree(cls, tvars=None, args=None): + """ Calculate substitution tree for generic cls after + replacing its type parameters with substitutions in tvars -> args (if any). + Repeat the same cyclicaly following __origin__'s. + """ + + if cls.__origin__ is None: + return cls + # Make of chain of origins (i.e. cls -> cls.__origin__) + current = cls.__origin__ + orig_chain = [] + while current.__origin__ is not None: + orig_chain.append(current) + current = current.__origin__ + # Replace type variables in __args__ if asked ... + tree_args = [] + for arg in cls.__args__: + tree_args.append(_replace_arg(arg, tvars, args)) + # ... then continue replacing down the origin chain. + for ocls in orig_chain: + new_tree_args = [] + for i, arg in enumerate(ocls.__args__): + new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args)) + tree_args = new_tree_args + return tree_args + + +def _remove_dups_flatten(parameters): + """ A helper for Union creation and substitution: flatten Union's + among parameters, then remove duplicates and strict subclasses. + """ + + # Flatten out Union[Union[...], ...]. + params = [] + for p in parameters: + if isinstance(p, _Union) and p.__origin__ is Union: + params.extend(p.__args__) + elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union: + params.extend(p[1:]) + else: + params.append(p) + # Weed out strict duplicates, preserving the first of each occurrence. + all_params = set(params) + if len(all_params) < len(params): + new_params = [] + for t in params: + if t in all_params: + new_params.append(t) + all_params.remove(t) + params = new_params + assert not all_params, all_params + # Weed out subclasses. + # E.g. Union[int, Employee, Manager] == Union[int, Employee]. + # If object is present it will be sole survivor among proper classes. + # Never discard type variables. + # (In particular, Union[str, AnyStr] != AnyStr.) + all_params = set(params) + for t1 in params: + if not isinstance(t1, type): + continue + if any(isinstance(t2, type) and issubclass(t1, t2) + for t2 in all_params - {t1} + if not (isinstance(t2, GenericMeta) and + t2.__origin__ is not None)): + all_params.remove(t1) + return tuple(t for t in params if t in all_params) + + +def _check_generic(cls, parameters): + # Check correct count for parameters of a generic cls. + if not cls.__parameters__: + raise TypeError("%s is not a generic class" % repr(cls)) + alen = len(parameters) + elen = len(cls.__parameters__) + if alen != elen: + raise TypeError("Too %s parameters for %s; actual %s, expected %s" % + ("many" if alen > elen else "few", repr(cls), alen, elen)) + + def _tp_cache(func): + """ Caching for __getitem__ of generic types with a fallback to + original function for non-hashable arguments. + """ + cached = functools.lru_cache()(func) @functools.wraps(func) def inner(*args, **kwds): @@ -555,100 +662,100 @@ class _Union(_FinalTypingBase, _root=True): - You cannot subclass or instantiate a union. - - You cannot write Union[X][Y] (what would it mean?). - - You can use Optional[X] as a shorthand for Union[X, None]. """ - __slots__ = ('__union_params__', '__union_set_params__') + __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__') - def __new__(cls, parameters=None, *args, _root=False): - self = super().__new__(cls, parameters, *args, _root=_root) - if parameters is None: - self.__union_params__ = None - self.__union_set_params__ = None + def __new__(cls, parameters=None, origin=None, *args, _root=False): + self = super().__new__(cls, parameters, origin, *args, _root=_root) + if origin is None: + self.__parameters__ = None + self.__args__ = None + self.__origin__ = None + self.__tree_hash__ = hash(frozenset(('Union',))) return self if not isinstance(parameters, tuple): raise TypeError("Expected parameters=<tuple>") - # Flatten out Union[Union[...], ...] and type-check non-Union args. - params = [] - msg = "Union[arg, ...]: each arg must be a type." - for p in parameters: - if isinstance(p, _Union): - params.extend(p.__union_params__) - else: - params.append(_type_check(p, msg)) - # Weed out strict duplicates, preserving the first of each occurrence. - all_params = set(params) - if len(all_params) < len(params): - new_params = [] - for t in params: - if t in all_params: - new_params.append(t) - all_params.remove(t) - params = new_params - assert not all_params, all_params - # Weed out subclasses. - # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If object is present it will be sole survivor among proper classes. - # Never discard type variables. - # (In particular, Union[str, AnyStr] != AnyStr.) - all_params = set(params) - for t1 in params: - if not isinstance(t1, type): - continue - if any(isinstance(t2, type) and issubclass(t1, t2) - for t2 in all_params - {t1} - if not (isinstance(t2, GenericMeta) and - t2.__origin__ is not None)): - all_params.remove(t1) - # It's not a union if there's only one type left. - if len(all_params) == 1: - return all_params.pop() - self.__union_params__ = tuple(t for t in params if t in all_params) - self.__union_set_params__ = frozenset(self.__union_params__) + if origin is Union: + parameters = _remove_dups_flatten(parameters) + # It's not a union if there's only one type left. + if len(parameters) == 1: + return parameters[0] + self.__parameters__ = _type_vars(parameters) + self.__args__ = parameters + self.__origin__ = origin + # Pre-calculate the __hash__ on instantiation. + # This improves speed for complex substitutions. + subs_tree = self._subs_tree() + if isinstance(subs_tree, tuple): + self.__tree_hash__ = hash(frozenset(subs_tree)) + else: + self.__tree_hash__ = hash(subs_tree) return self def _eval_type(self, globalns, localns): - p = tuple(_eval_type(t, globalns, localns) - for t in self.__union_params__) - if p == self.__union_params__: + if self.__args__ is None: return self - else: - return self.__class__(p, _root=True) + ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__) + ev_origin = _eval_type(self.__origin__, globalns, localns) + if ev_args == self.__args__ and ev_origin == self.__origin__: + # Everything is already evaluated. + return self + return self.__class__(ev_args, ev_origin, _root=True) def _get_type_vars(self, tvars): - if self.__union_params__: - _get_type_vars(self.__union_params__, tvars) + if self.__origin__ and self.__parameters__: + _get_type_vars(self.__parameters__, tvars) def __repr__(self): - return self._subs_repr([], []) - - def _subs_repr(self, tvars, args): - r = super().__repr__() - if self.__union_params__: - r += '[%s]' % (', '.join(_replace_arg(t, tvars, args) - for t in self.__union_params__)) - return r + if self.__origin__ is None: + return super().__repr__() + tree = self._subs_tree() + if not isinstance(tree, tuple): + return repr(tree) + return tree[0]._tree_repr(tree) + + def _tree_repr(self, tree): + arg_list = [] + for arg in tree[1:]: + if not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + return super().__repr__() + '[%s]' % ', '.join(arg_list) @_tp_cache def __getitem__(self, parameters): - if self.__union_params__ is not None: - raise TypeError( - "Cannot subscript an existing Union. Use Union[u, t] instead.") if parameters == (): raise TypeError("Cannot take a Union of no types.") if not isinstance(parameters, tuple): parameters = (parameters,) - return self.__class__(parameters, _root=True) + if self.__origin__ is None: + msg = "Union[arg, ...]: each arg must be a type." + else: + msg = "Parameters to generic types must be types." + parameters = tuple(_type_check(p, msg) for p in parameters) + if self is not Union: + _check_generic(self, parameters) + return self.__class__(parameters, origin=self, _root=True) + + def _subs_tree(self, tvars=None, args=None): + if self is Union: + return Union # Nothing to substitute + tree_args = _subs_tree(self, tvars, args) + tree_args = _remove_dups_flatten(tree_args) + if len(tree_args) == 1: + return tree_args[0] # Union of a single type is that type + return (Union,) + tree_args def __eq__(self, other): if not isinstance(other, _Union): - return NotImplemented - return self.__union_set_params__ == other.__union_set_params__ + return self._subs_tree() == other + return self.__tree_hash__ == other.__tree_hash__ def __hash__(self): - return hash(self.__union_set_params__) + return self.__tree_hash__ def __instancecheck__(self, obj): raise TypeError("Unions cannot be used with isinstance().") @@ -677,195 +784,6 @@ class _Optional(_FinalTypingBase, _root=True): Optional = _Optional(_root=True) -class _Tuple(_FinalTypingBase, _root=True): - """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. - - Example: Tuple[T1, T2] is a tuple of two elements corresponding - to type variables T1 and T2. Tuple[int, float, str] is a tuple - of an int, a float and a string. - - To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. - """ - - __slots__ = ('__tuple_params__', '__tuple_use_ellipsis__') - - def __init__(self, parameters=None, - use_ellipsis=False, _root=False): - self.__tuple_params__ = parameters - self.__tuple_use_ellipsis__ = use_ellipsis - - def _get_type_vars(self, tvars): - if self.__tuple_params__: - _get_type_vars(self.__tuple_params__, tvars) - - def _eval_type(self, globalns, localns): - tp = self.__tuple_params__ - if tp is None: - return self - p = tuple(_eval_type(t, globalns, localns) for t in tp) - if p == self.__tuple_params__: - return self - else: - return self.__class__(p, _root=True) - - def __repr__(self): - return self._subs_repr([], []) - - def _subs_repr(self, tvars, args): - r = super().__repr__() - if self.__tuple_params__ is not None: - params = [_replace_arg(p, tvars, args) for p in self.__tuple_params__] - if self.__tuple_use_ellipsis__: - params.append('...') - if not params: - params.append('()') - r += '[%s]' % ( - ', '.join(params)) - return r - - @_tp_cache - def __getitem__(self, parameters): - if self.__tuple_params__ is not None: - raise TypeError("Cannot re-parameterize %r" % (self,)) - if not isinstance(parameters, tuple): - parameters = (parameters,) - if len(parameters) == 2 and parameters[1] == Ellipsis: - parameters = parameters[:1] - use_ellipsis = True - msg = "Tuple[t, ...]: t must be a type." - else: - use_ellipsis = False - msg = "Tuple[t0, t1, ...]: each t must be a type." - parameters = tuple(_type_check(p, msg) for p in parameters) - return self.__class__(parameters, - use_ellipsis=use_ellipsis, _root=True) - - def __eq__(self, other): - if not isinstance(other, _Tuple): - return NotImplemented - return (self.__tuple_params__ == other.__tuple_params__ and - self.__tuple_use_ellipsis__ == other.__tuple_use_ellipsis__) - - def __hash__(self): - return hash((self.__tuple_params__, self.__tuple_use_ellipsis__)) - - def __instancecheck__(self, obj): - if self.__tuple_params__ == None: - return isinstance(obj, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with isinstance().") - - def __subclasscheck__(self, cls): - if self.__tuple_params__ == None: - return issubclass(cls, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with issubclass().") - - -Tuple = _Tuple(_root=True) - - -class _Callable(_FinalTypingBase, _root=True): - """Callable type; Callable[[int], str] is a function of (int) -> str. - - The subscription syntax must always be used with exactly two - values: the argument list and the return type. The argument list - must be a list of types; the return type must be a single type. - - There is no syntax to indicate optional or keyword arguments, - such function types are rarely used as callback types. - """ - - __slots__ = ('__args__', '__result__') - - def __init__(self, args=None, result=None, _root=False): - if args is None and result is None: - pass - else: - if args is not Ellipsis: - if not isinstance(args, list): - raise TypeError("Callable[args, result]: " - "args must be a list." - " Got %.100r." % (args,)) - msg = "Callable[[arg, ...], result]: each arg must be a type." - args = tuple(_type_check(arg, msg) for arg in args) - msg = "Callable[args, result]: result must be a type." - result = _type_check(result, msg) - self.__args__ = args - self.__result__ = result - - def _get_type_vars(self, tvars): - if self.__args__ and self.__args__ is not Ellipsis: - _get_type_vars(self.__args__, tvars) - if self.__result__: - _get_type_vars([self.__result__], tvars) - - def _eval_type(self, globalns, localns): - if self.__args__ is None and self.__result__ is None: - return self - if self.__args__ is Ellipsis: - args = self.__args__ - else: - args = [_eval_type(t, globalns, localns) for t in self.__args__] - result = _eval_type(self.__result__, globalns, localns) - if args == self.__args__ and result == self.__result__: - return self - else: - return self.__class__(args, result, _root=True) - - def __repr__(self): - return self._subs_repr([], []) - - def _subs_repr(self, tvars, args): - r = super().__repr__() - if self.__args__ is not None or self.__result__ is not None: - if self.__args__ is Ellipsis: - args_r = '...' - else: - args_r = '[%s]' % ', '.join(_replace_arg(t, tvars, args) - for t in self.__args__) - r += '[%s, %s]' % (args_r, _replace_arg(self.__result__, tvars, args)) - return r - - def __getitem__(self, parameters): - if self.__args__ is not None or self.__result__ is not None: - raise TypeError("This Callable type is already parameterized.") - if not isinstance(parameters, tuple) or len(parameters) != 2: - raise TypeError( - "Callable must be used as Callable[[arg, ...], result].") - args, result = parameters - return self.__class__(args, result, _root=True) - - def __eq__(self, other): - if not isinstance(other, _Callable): - return NotImplemented - return (self.__args__ == other.__args__ and - self.__result__ == other.__result__) - - def __hash__(self): - return hash(self.__args__) ^ hash(self.__result__) - - def __instancecheck__(self, obj): - # For unparametrized Callable we allow this, because - # typing.Callable should be equivalent to - # collections.abc.Callable. - if self.__args__ is None and self.__result__ is None: - return isinstance(obj, collections_abc.Callable) - else: - raise TypeError("Parameterized Callable cannot be used " - "with isinstance().") - - def __subclasscheck__(self, cls): - if self.__args__ is None and self.__result__ is None: - return issubclass(cls, collections_abc.Callable) - else: - raise TypeError("Parameterized Callable cannot be used " - "with issubclass().") - - -Callable = _Callable(_root=True) - - def _gorg(a): """Return the farthest origin of a generic class.""" assert isinstance(a, GenericMeta) @@ -889,16 +807,6 @@ def _geqv(a, b): return _gorg(a) is _gorg(b) -def _replace_arg(arg, tvars, args): - if hasattr(arg, '_subs_repr'): - return arg._subs_repr(tvars, args) - if isinstance(arg, TypeVar): - for i, tvar in enumerate(tvars): - if arg == tvar: - return args[i] - return _type_repr(arg) - - def _next_in_mro(cls): """Helper for Generic.__new__. @@ -1011,7 +919,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta): self = super().__new__(cls, name, bases, namespace, _root=True) self.__parameters__ = tvars - self.__args__ = args + # Be prepared that GenericMeta will be subclassed by TupleMeta + # and CallableMeta, those two allow ..., (), or [] in __args___. + self.__args__ = tuple(... if a is _TypingEllipsis else + () if a is _TypingEmpty else + a for a in args) if args else None self.__origin__ = origin self.__extra__ = extra # Speed hack (https://github.com/python/typing/issues/196). @@ -1029,55 +941,69 @@ class GenericMeta(TypingMeta, abc.ABCMeta): self.__subclasshook__ = _make_subclasshook(self) if isinstance(extra, abc.ABCMeta): self._abc_registry = extra._abc_registry + + if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2. + self.__qualname__ = origin.__qualname__ + self.__tree_hash__ = hash(self._subs_tree()) if origin else hash((self.__name__,)) return self def _get_type_vars(self, tvars): if self.__origin__ and self.__parameters__: _get_type_vars(self.__parameters__, tvars) + def _eval_type(self, globalns, localns): + ev_origin = (self.__origin__._eval_type(globalns, localns) + if self.__origin__ else None) + ev_args = tuple(_eval_type(a, globalns, localns) for a + in self.__args__) if self.__args__ else None + if ev_origin == self.__origin__ and ev_args == self.__args__: + return self + return self.__class__(self.__name__, + self.__bases__, + dict(self.__dict__), + tvars=_type_vars(ev_args) if ev_args else None, + args=ev_args, + origin=ev_origin, + extra=self.__extra__, + orig_bases=self.__orig_bases__) + def __repr__(self): if self.__origin__ is None: return super().__repr__() - return self._subs_repr([], []) - - def _subs_repr(self, tvars, args): - assert len(tvars) == len(args) - # Construct the chain of __origin__'s. - current = self.__origin__ - orig_chain = [] - while current.__origin__ is not None: - orig_chain.append(current) - current = current.__origin__ - # Replace type variables in __args__ if asked ... - str_args = [] - for arg in self.__args__: - str_args.append(_replace_arg(arg, tvars, args)) - # ... then continue replacing down the origin chain. - for cls in orig_chain: - new_str_args = [] - for i, arg in enumerate(cls.__args__): - new_str_args.append(_replace_arg(arg, cls.__parameters__, str_args)) - str_args = new_str_args - return super().__repr__() + '[%s]' % ', '.join(str_args) + return self._tree_repr(self._subs_tree()) + + def _tree_repr(self, tree): + arg_list = [] + for arg in tree[1:]: + if arg == (): + arg_list.append('()') + elif not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + return super().__repr__() + '[%s]' % ', '.join(arg_list) + + def _subs_tree(self, tvars=None, args=None): + if self.__origin__ is None: + return self + tree_args = _subs_tree(self, tvars, args) + return (_gorg(self),) + tuple(tree_args) def __eq__(self, other): if not isinstance(other, GenericMeta): return NotImplemented - if self.__origin__ is not None: - return (self.__origin__ is other.__origin__ and - self.__args__ == other.__args__ and - self.__parameters__ == other.__parameters__) - else: + if self.__origin__ is None or other.__origin__ is None: return self is other + return self.__tree_hash__ == other.__tree_hash__ def __hash__(self): - return hash((self.__name__, self.__parameters__)) + return self.__tree_hash__ @_tp_cache def __getitem__(self, params): if not isinstance(params, tuple): params = (params,) - if not params: + if not params and not _gorg(self) is Tuple: raise TypeError( "Parameter list to %s[...] cannot be empty" % _qualname(self)) msg = "Parameters to generic types must be types." @@ -1092,6 +1018,9 @@ class GenericMeta(TypingMeta, abc.ABCMeta): "Parameters to Generic[...] must all be unique") tvars = params args = params + elif self in (Tuple, Callable): + tvars = _type_vars(params) + args = params elif self is _Protocol: # _Protocol is internal, don't check anything. tvars = params @@ -1102,14 +1031,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): repr(self)) else: # Subscripting a regular Generic subclass. - if not self.__parameters__: - raise TypeError("%s is not a generic class" % repr(self)) - alen = len(params) - elen = len(self.__parameters__) - if alen != elen: - raise TypeError( - "Too %s parameters for %s; actual %s, expected %s" % - ("many" if alen > elen else "few", repr(self), alen, elen)) + _check_generic(self, params) tvars = _type_vars(params) args = params return self.__class__(self.__name__, @@ -1134,6 +1056,22 @@ class GenericMeta(TypingMeta, abc.ABCMeta): Generic = None +def _generic_new(base_cls, cls, *args, **kwds): + # Assure type is erased on instantiation, + # but attempt to store it in __orig_class__ + if cls.__origin__ is None: + return base_cls.__new__(cls) + else: + origin = _gorg(cls) + obj = base_cls.__new__(origin) + try: + obj.__orig_class__ = cls + except AttributeError: + pass + obj.__init__(*args, **kwds) + return obj + + class Generic(metaclass=GenericMeta): """Abstract base class for generic types. @@ -1158,17 +1096,154 @@ class Generic(metaclass=GenericMeta): __slots__ = () def __new__(cls, *args, **kwds): - if cls.__origin__ is None: - return cls.__next_in_mro__.__new__(cls) + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +class _TypingEmpty: + """Placeholder for () or []. Used by TupleMeta and CallableMeta + to allow empy list/tuple in specific places, without allowing them + to sneak in where prohibited. + """ + + +class _TypingEllipsis: + """Ditto for ...""" + + +class TupleMeta(GenericMeta): + """Metaclass for Tuple""" + + @_tp_cache + def __getitem__(self, parameters): + if self.__origin__ is not None or not _geqv(self, Tuple): + # Normal generic rules apply if this is not the first subscription + # or a subscription of a subclass. + return super().__getitem__(parameters) + if parameters == (): + return super().__getitem__((_TypingEmpty,)) + if not isinstance(parameters, tuple): + parameters = (parameters,) + if len(parameters) == 2 and parameters[1] is ...: + msg = "Tuple[t, ...]: t must be a type." + p = _type_check(parameters[0], msg) + return super().__getitem__((p, _TypingEllipsis)) + msg = "Tuple[t0, t1, ...]: each t must be a type." + parameters = tuple(_type_check(p, msg) for p in parameters) + return super().__getitem__(parameters) + + def __instancecheck__(self, obj): + if self.__args__ == None: + return isinstance(obj, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with isinstance().") + + def __subclasscheck__(self, cls): + if self.__args__ == None: + return issubclass(cls, tuple) + raise TypeError("Parameterized Tuple cannot be used " + "with issubclass().") + + +class Tuple(tuple, extra=tuple, metaclass=TupleMeta): + """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. + + Example: Tuple[T1, T2] is a tuple of two elements corresponding + to type variables T1 and T2. Tuple[int, float, str] is a tuple + of an int, a float and a string. + + To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. + """ + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, Tuple): + raise TypeError("Type Tuple cannot be instantiated; " + "use tuple() instead") + return _generic_new(tuple, cls, *args, **kwds) + + +class CallableMeta(GenericMeta): + """ Metaclass for Callable.""" + + def __repr__(self): + if self.__origin__ is None: + return super().__repr__() + return self._tree_repr(self._subs_tree()) + + def _tree_repr(self, tree): + if _gorg(self) is not Callable: + return super()._tree_repr(tree) + # For actual Callable (not its subclass) we override + # super()._tree_repr() for nice formatting. + arg_list = [] + for arg in tree[1:]: + if arg == (): + arg_list.append('[]') + elif not isinstance(arg, tuple): + arg_list.append(_type_repr(arg)) + else: + arg_list.append(arg[0]._tree_repr(arg)) + if len(arg_list) == 2: + return repr(tree[0]) + '[%s]' % ', '.join(arg_list) + return (repr(tree[0]) + + '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) + + def __getitem__(self, parameters): + """ A thin wrapper around __getitem_inner__ to provide the latter + with hashable arguments to improve speed. + """ + + if self.__origin__ is not None or not _geqv(self, Callable): + return super().__getitem__(parameters) + if not isinstance(parameters, tuple) or len(parameters) != 2: + raise TypeError("Callable must be used as " + "Callable[[arg, ...], result].") + args, result = parameters + if args is ...: + parameters = (..., result) + elif args == []: + parameters = ((), result) else: - origin = _gorg(cls) - obj = cls.__next_in_mro__.__new__(origin) - try: - obj.__orig_class__ = cls - except AttributeError: - pass - obj.__init__(*args, **kwds) - return obj + if not isinstance(args, list): + raise TypeError("Callable[args, result]: args must be a list." + " Got %.100r." % (args,)) + parameters = tuple(args) + (result,) + return self.__getitem_inner__(parameters) + + @_tp_cache + def __getitem_inner__(self, parameters): + *args, result = parameters + msg = "Callable[args, result]: result must be a type." + result = _type_check(result, msg) + if args == [...,]: + return super().__getitem__((_TypingEllipsis, result)) + if args == [(),]: + return super().__getitem__((_TypingEmpty, result)) + msg = "Callable[[arg, ...], result]: each arg must be a type." + args = tuple(_type_check(arg, msg) for arg in args) + parameters = args + (result,) + return super().__getitem__(parameters) + + +class Callable(extra=collections_abc.Callable, metaclass = CallableMeta): + """Callable type; Callable[[int], str] is a function of (int) -> str. + + The subscription syntax must always be used with exactly two + values: the argument list and the return type. The argument list + must be a list of types; the return type must be a single type. + + There is no syntax to indicate optional or keyword arguments, + such function types are rarely used as callback types. + """ + + __slots__ = () + + def __new__(cls, *args, **kwds): + if _geqv(cls, Callable): + raise TypeError("Type Callable cannot be instantiated; " + "use a non-abstract subclass instead") + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) class _ClassVar(_FinalTypingBase, _root=True): @@ -1208,17 +1283,10 @@ class _ClassVar(_FinalTypingBase, _root=True): return self return type(self)(new_tp, _root=True) - def _get_type_vars(self, tvars): - if self.__type__: - _get_type_vars([self.__type__], tvars) - def __repr__(self): - return self._subs_repr([], []) - - def _subs_repr(self, tvars, args): r = super().__repr__() if self.__type__ is not None: - r += '[{}]'.format(_replace_arg(self.__type__, tvars, args)) + r += '[{}]'.format(_type_repr(self.__type__)) return r def __hash__(self): @@ -1231,6 +1299,7 @@ class _ClassVar(_FinalTypingBase, _root=True): return self.__type__ == other.__type__ return self is other + ClassVar = _ClassVar(_root=True) @@ -1533,6 +1602,7 @@ class _ProtocolMeta(GenericMeta): attr != '__origin__' and attr != '__orig_bases__' and attr != '__extra__' and + attr != '__tree_hash__' and attr != '__module__'): attrs.add(attr) @@ -1723,7 +1793,7 @@ class List(list, MutableSequence[T], extra=list): if _geqv(cls, List): raise TypeError("Type List cannot be instantiated; " "use list() instead") - return list.__new__(cls, *args, **kwds) + return _generic_new(list, cls, *args, **kwds) class Set(set, MutableSet[T], extra=set): @@ -1734,7 +1804,7 @@ class Set(set, MutableSet[T], extra=set): if _geqv(cls, Set): raise TypeError("Type Set cannot be instantiated; " "use set() instead") - return set.__new__(cls, *args, **kwds) + return _generic_new(set, cls, *args, **kwds) class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset): @@ -1744,7 +1814,7 @@ class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset): if _geqv(cls, FrozenSet): raise TypeError("Type FrozenSet cannot be instantiated; " "use frozenset() instead") - return frozenset.__new__(cls, *args, **kwds) + return _generic_new(frozenset, cls, *args, **kwds) class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView): @@ -1781,7 +1851,7 @@ class Dict(dict, MutableMapping[KT, VT], extra=dict): if _geqv(cls, Dict): raise TypeError("Type Dict cannot be instantiated; " "use dict() instead") - return dict.__new__(cls, *args, **kwds) + return _generic_new(dict, cls, *args, **kwds) class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], extra=collections.defaultdict): @@ -1792,7 +1862,7 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], if _geqv(cls, DefaultDict): raise TypeError("Type DefaultDict cannot be instantiated; " "use collections.defaultdict() instead") - return collections.defaultdict.__new__(cls, *args, **kwds) + return _generic_new(collections.defaultdict, cls, *args, **kwds) # Determine what base class to use for Generator. if hasattr(collections_abc, 'Generator'): @@ -1811,7 +1881,7 @@ class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co], if _geqv(cls, Generator): raise TypeError("Type Generator cannot be instantiated; " "create a subclass instead") - return super().__new__(cls, *args, **kwds) + return _generic_new(_G_base, cls, *args, **kwds) # Internal type variable used for Type[]. |