summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2016-10-29 15:54:56 (GMT)
committerGuido van Rossum <guido@python.org>2016-10-29 15:54:56 (GMT)
commit5fc25a873cfdec27e46f71e62c9b65df5667c1b4 (patch)
treeb44292a9602c96710411175df76909d7067b3396 /Lib/typing.py
parent84968b74c89aa22759b8e284673b46a4a69db852 (diff)
downloadcpython-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.py738
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[].