summaryrefslogtreecommitdiff
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2016-09-27 15:20:12 -0700
committerGuido van Rossum <guido@python.org>2016-09-27 15:20:12 -0700
commit4cefe74aef34a86a9eb60cea9d7a775fc288004c (patch)
treede2a51619ad4adfdb139aa1a84f9e1b2887f2a8e /Lib/typing.py
parent63461bc384057febe552544608e77f2b9dcf817d (diff)
downloadcpython-git-4cefe74aef34a86a9eb60cea9d7a775fc288004c.tar.gz
Update typing.py and test_typing.py from upstream (https://github.com/python/typing)
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py641
1 files changed, 302 insertions, 339 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index 4676d28c8e..925d9e42d3 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -89,6 +89,13 @@ def _qualname(x):
return x.__name__
+def _trim_name(nm):
+ if nm.startswith('_') and nm not in ('_TypeAlias',
+ '_ForwardRef', '_TypingBase', '_FinalTypingBase'):
+ nm = nm[1:]
+ return nm
+
+
class TypingMeta(type):
"""Metaclass for every type defined below.
@@ -127,22 +134,69 @@ class TypingMeta(type):
pass
def __repr__(self):
- return '%s.%s' % (self.__module__, _qualname(self))
+ qname = _trim_name(_qualname(self))
+ return '%s.%s' % (self.__module__, qname)
+
+
+class _TypingBase(metaclass=TypingMeta, _root=True):
+ """Indicator of special typing constructs."""
+
+ __slots__ = ()
+
+
+ def __init__(self, *args, **kwds):
+ pass
+
+ def __new__(cls, *args, **kwds):
+ """Constructor.
+
+ This only exists to give a better error message in case
+ someone tries to subclass a special typing object (not a good idea).
+ """
+ if (len(args) == 3 and
+ isinstance(args[0], str) and
+ isinstance(args[1], tuple)):
+ # Close enough.
+ raise TypeError("Cannot subclass %r" % cls)
+ return object.__new__(cls)
+
+ # Things that are not classes also need these.
+ def _eval_type(self, globalns, localns):
+ return self
+
+ def _get_type_vars(self, tvars):
+ pass
+
+ def __repr__(self):
+ cls = type(self)
+ qname = _trim_name(_qualname(cls))
+ return '%s.%s' % (cls.__module__, qname)
+ def __call__(self, *args, **kwds):
+ raise TypeError("Cannot instantiate %r" % type(self))
-class Final:
+
+class _FinalTypingBase(_TypingBase, _root=True):
"""Mix-in class to prevent instantiation."""
__slots__ = ()
- def __new__(self, *args, **kwds):
- raise TypeError("Cannot instantiate %r" % self.__class__)
+ def __new__(cls, *args, _root=False, **kwds):
+ self = super().__new__(cls, *args, **kwds)
+ if _root is True:
+ return self
+ raise TypeError("Cannot instantiate %r" % cls)
-class _ForwardRef(TypingMeta):
+class _ForwardRef(_TypingBase, _root=True):
"""Wrapper to hold a forward reference."""
- def __new__(cls, arg):
+ __slots__ = ('__forward_arg__', '__forward_code__',
+ '__forward_evaluated__', '__forward_value__',
+ '__forward_frame__')
+
+ def __init__(self, arg):
+ super().__init__(arg)
if not isinstance(arg, str):
raise TypeError('ForwardRef must be a string -- got %r' % (arg,))
try:
@@ -150,7 +204,6 @@ class _ForwardRef(TypingMeta):
except SyntaxError:
raise SyntaxError('ForwardRef must be an expression -- got %r' %
(arg,))
- self = super().__new__(cls, arg, (), {}, _root=True)
self.__forward_arg__ = arg
self.__forward_code__ = code
self.__forward_evaluated__ = False
@@ -161,7 +214,6 @@ class _ForwardRef(TypingMeta):
frame = frame.f_back
assert frame is not None
self.__forward_frame__ = frame
- return self
def _eval_type(self, globalns, localns):
if not self.__forward_evaluated__:
@@ -177,49 +229,36 @@ class _ForwardRef(TypingMeta):
self.__forward_evaluated__ = True
return self.__forward_value__
+ def __eq__(self, other):
+ if not isinstance(other, _ForwardRef):
+ return NotImplemented
+ return (self.__forward_arg__ == other.__forward_arg__ and
+ self.__forward_frame__ == other.__forward_frame__)
+
+ def __hash__(self):
+ return hash((self.__forward_arg__, self.__forward_frame__))
+
def __instancecheck__(self, obj):
raise TypeError("Forward references cannot be used with isinstance().")
def __subclasscheck__(self, cls):
- if not self.__forward_evaluated__:
- globalns = self.__forward_frame__.f_globals
- localns = self.__forward_frame__.f_locals
- try:
- self._eval_type(globalns, localns)
- except NameError:
- return False # Too early.
- return issubclass(cls, self.__forward_value__)
+ raise TypeError("Forward references cannot be used with issubclass().")
def __repr__(self):
return '_ForwardRef(%r)' % (self.__forward_arg__,)
-class _TypeAlias:
+class _TypeAlias(_TypingBase, _root=True):
"""Internal helper class for defining generic variants of concrete types.
- Note that this is not a type; let's call it a pseudo-type. It can
- be used in instance and subclass checks, e.g. isinstance(m, Match)
- or issubclass(type(m), Match). However, it cannot be itself the
- target of an issubclass() call; e.g. issubclass(Match, C) (for
- some arbitrary class C) raises TypeError rather than returning
- False.
+ Note that this is not a type; let's call it a pseudo-type. It cannot
+ be used in instance and subclass checks in parameterized form, i.e.
+ ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning
+ ``False``.
"""
__slots__ = ('name', 'type_var', 'impl_type', 'type_checker')
- def __new__(cls, *args, **kwds):
- """Constructor.
-
- This only exists to give a better error message in case
- someone tries to subclass a type alias (not a good idea).
- """
- if (len(args) == 3 and
- isinstance(args[0], str) and
- isinstance(args[1], tuple)):
- # Close enough.
- raise TypeError("A type alias cannot be subclassed")
- return object.__new__(cls)
-
def __init__(self, name, type_var, impl_type, type_checker):
"""Initializer.
@@ -232,9 +271,9 @@ class _TypeAlias:
and returns a value that should be a type_var instance.
"""
assert isinstance(name, str), repr(name)
- assert isinstance(type_var, type), repr(type_var)
assert isinstance(impl_type, type), repr(impl_type)
assert not isinstance(impl_type, TypingMeta), repr(impl_type)
+ assert isinstance(type_var, (type, _TypingBase))
self.name = name
self.type_var = type_var
self.impl_type = impl_type
@@ -244,36 +283,41 @@ class _TypeAlias:
return "%s[%s]" % (self.name, _type_repr(self.type_var))
def __getitem__(self, parameter):
- assert isinstance(parameter, type), repr(parameter)
if not isinstance(self.type_var, TypeVar):
raise TypeError("%s cannot be further parameterized." % self)
- if self.type_var.__constraints__:
- if not issubclass(parameter, Union[self.type_var.__constraints__]):
+ if self.type_var.__constraints__ and isinstance(parameter, type):
+ if not issubclass(parameter, self.type_var.__constraints__):
raise TypeError("%s is not a valid substitution for %s." %
(parameter, self.type_var))
+ if isinstance(parameter, TypeVar):
+ raise TypeError("%s cannot be re-parameterized." % self.type_var)
return self.__class__(self.name, parameter,
self.impl_type, self.type_checker)
+ def __eq__(self, other):
+ if not isinstance(other, _TypeAlias):
+ return NotImplemented
+ return self.name == other.name and self.type_var == other.type_var
+
+ def __hash__(self):
+ return hash((self.name, self.type_var))
+
def __instancecheck__(self, obj):
- raise TypeError("Type aliases cannot be used with isinstance().")
+ if not isinstance(self.type_var, TypeVar):
+ raise TypeError("Parameterized type aliases cannot be used "
+ "with isinstance().")
+ return isinstance(obj, self.impl_type)
def __subclasscheck__(self, cls):
- if cls is Any:
- return True
- if isinstance(cls, _TypeAlias):
- # Covariance. For now, we compare by name.
- return (cls.name == self.name and
- issubclass(cls.type_var, self.type_var))
- else:
- # Note that this is too lenient, because the
- # implementation type doesn't carry information about
- # whether it is about bytes or str (for example).
- return issubclass(cls, self.impl_type)
+ if not isinstance(self.type_var, TypeVar):
+ raise TypeError("Parameterized type aliases cannot be used "
+ "with issubclass().")
+ return issubclass(cls, self.impl_type)
def _get_type_vars(types, tvars):
for t in types:
- if isinstance(t, TypingMeta) or isinstance(t, _ClassVar):
+ if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
t._get_type_vars(tvars)
@@ -284,7 +328,7 @@ def _type_vars(types):
def _eval_type(t, globalns, localns):
- if isinstance(t, TypingMeta) or isinstance(t, _ClassVar):
+ if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
return t._eval_type(globalns, localns)
else:
return t
@@ -306,7 +350,7 @@ def _type_check(arg, msg):
return type(None)
if isinstance(arg, str):
arg = _ForwardRef(arg)
- if not isinstance(arg, (type, _TypeAlias)) and not callable(arg):
+ if not isinstance(arg, (type, _TypingBase)) and not callable(arg):
raise TypeError(msg + " Got %.100r." % (arg,))
return arg
@@ -328,23 +372,7 @@ def _type_repr(obj):
return repr(obj)
-class AnyMeta(TypingMeta):
- """Metaclass for Any."""
-
- def __new__(cls, name, bases, namespace, _root=False):
- self = super().__new__(cls, name, bases, namespace, _root=_root)
- return self
-
- def __instancecheck__(self, obj):
- raise TypeError("Any cannot be used with isinstance().")
-
- def __subclasscheck__(self, cls):
- if not isinstance(cls, type):
- return super().__subclasscheck__(cls) # To TypeError.
- return True
-
-
-class Any(Final, metaclass=AnyMeta, _root=True):
+class _Any(_FinalTypingBase, _root=True):
"""Special type indicating an unconstrained type.
- Any object is an instance of Any.
@@ -354,8 +382,17 @@ class Any(Final, metaclass=AnyMeta, _root=True):
__slots__ = ()
+ def __instancecheck__(self, obj):
+ raise TypeError("Any cannot be used with isinstance().")
+
+ def __subclasscheck__(self, cls):
+ raise TypeError("Any cannot be used with issubclass().")
+
+
+Any = _Any(_root=True)
-class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
+
+class TypeVar(_TypingBase, _root=True):
"""Type variable.
Usage::
@@ -400,9 +437,14 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
A.__constraints__ == (str, bytes)
"""
- def __new__(cls, name, *constraints, bound=None,
+ __slots__ = ('__name__', '__bound__', '__constraints__',
+ '__covariant__', '__contravariant__')
+
+ def __init__(self, name, *constraints, bound=None,
covariant=False, contravariant=False):
- self = super().__new__(cls, name, (Final,), {}, _root=True)
+ super().__init__(name, *constraints, bound=bound,
+ covariant=covariant, contravariant=contravariant)
+ self.__name__ = name
if covariant and contravariant:
raise ValueError("Bivariant types are not supported.")
self.__covariant__ = bool(covariant)
@@ -417,7 +459,6 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
self.__bound__ = _type_check(bound, "Bound must be a type.")
else:
self.__bound__ = None
- return self
def _get_type_vars(self, tvars):
if self not in tvars:
@@ -436,16 +477,7 @@ class TypeVar(TypingMeta, metaclass=TypingMeta, _root=True):
raise TypeError("Type variables cannot be used with isinstance().")
def __subclasscheck__(self, cls):
- # TODO: Make this raise TypeError too?
- if cls is self:
- return True
- if cls is Any:
- return True
- if self.__bound__ is not None:
- return issubclass(cls, self.__bound__)
- if self.__constraints__:
- return any(issubclass(cls, c) for c in self.__constraints__)
- return True
+ raise TypeError("Type variables cannot be used with issubclass().")
# Some unconstrained type variables. These are used by the container types.
@@ -463,19 +495,85 @@ T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant.
AnyStr = TypeVar('AnyStr', bytes, str)
-class UnionMeta(TypingMeta):
- """Metaclass for Union."""
+def _tp_cache(func):
+ cached = functools.lru_cache()(func)
+ @functools.wraps(func)
+ def inner(*args, **kwds):
+ try:
+ return cached(*args, **kwds)
+ except TypeError:
+ pass # Do not duplicate real errors.
+ return func(*args, **kwds)
+ return inner
+
+
+class _Union(_FinalTypingBase, _root=True):
+ """Union type; Union[X, Y] means either X or Y.
+
+ To define a union, use e.g. Union[int, str]. Details:
+
+ - The arguments must be types and there must be at least one.
+
+ - None as an argument is a special case and is replaced by
+ type(None).
+
+ - Unions of unions are flattened, e.g.::
+
+ Union[Union[int, str], float] == Union[int, str, float]
+
+ - Unions of a single argument vanish, e.g.::
+
+ Union[int] == int # The constructor actually returns int
+
+ - Redundant arguments are skipped, e.g.::
+
+ Union[int, str, int] == Union[int, str]
+
+ - When comparing unions, the argument order is ignored, e.g.::
- def __new__(cls, name, bases, namespace, parameters=None, _root=False):
+ Union[int, str] == Union[str, int]
+
+ - When two arguments have a subclass relationship, the least
+ derived argument is kept, e.g.::
+
+ class Employee: pass
+ class Manager(Employee): pass
+ Union[int, Employee, Manager] == Union[int, Employee]
+ Union[Manager, int, Employee] == Union[int, Employee]
+ Union[Employee, Manager] == Employee
+
+ - Corollary: if Any is present it is the sole survivor, e.g.::
+
+ Union[int, Any] == Any
+
+ - Similar for object::
+
+ Union[int, object] == object
+
+ - To cut a tie: Union[object, Any] == Union[Any, object] == Any.
+
+ - 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__')
+
+ def __new__(cls, parameters=None, *args, _root=False):
+ self = super().__new__(cls, parameters, *args, _root=_root)
if parameters is None:
- return super().__new__(cls, name, bases, namespace, _root=_root)
+ self.__union_params__ = None
+ self.__union_set_params__ = None
+ 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, UnionMeta):
+ if isinstance(p, _Union):
params.extend(p.__union_params__)
else:
params.append(_type_check(p, msg))
@@ -499,22 +597,16 @@ class UnionMeta(TypingMeta):
for t1 in params:
if t1 is Any:
return Any
- if isinstance(t1, TypeVar):
- continue
- if isinstance(t1, _TypeAlias):
- # _TypeAlias is not a real class.
- continue
if not isinstance(t1, type):
- assert callable(t1) # A callable might sneak through.
continue
if any(isinstance(t2, type) and issubclass(t1, t2)
- for t2 in all_params - {t1} if not isinstance(t2, TypeVar)):
+ 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()
- # Create a new class with these params.
- self = super().__new__(cls, name, bases, {}, _root=True)
self.__union_params__ = tuple(t for t in params if t in all_params)
self.__union_set_params__ = frozenset(self.__union_params__)
return self
@@ -525,8 +617,7 @@ class UnionMeta(TypingMeta):
if p == self.__union_params__:
return self
else:
- return self.__class__(self.__name__, self.__bases__, {},
- p, _root=True)
+ return self.__class__(p, _root=True)
def _get_type_vars(self, tvars):
if self.__union_params__:
@@ -539,6 +630,7 @@ class UnionMeta(TypingMeta):
for t in self.__union_params__))
return r
+ @_tp_cache
def __getitem__(self, parameters):
if self.__union_params__ is not None:
raise TypeError(
@@ -547,11 +639,10 @@ class UnionMeta(TypingMeta):
raise TypeError("Cannot take a Union of no types.")
if not isinstance(parameters, tuple):
parameters = (parameters,)
- return self.__class__(self.__name__, self.__bases__,
- dict(self.__dict__), parameters, _root=True)
+ return self.__class__(parameters, _root=True)
def __eq__(self, other):
- if not isinstance(other, UnionMeta):
+ if not isinstance(other, _Union):
return NotImplemented
return self.__union_set_params__ == other.__union_set_params__
@@ -562,110 +653,45 @@ class UnionMeta(TypingMeta):
raise TypeError("Unions cannot be used with isinstance().")
def __subclasscheck__(self, cls):
- if cls is Any:
- return True
- if self.__union_params__ is None:
- return isinstance(cls, UnionMeta)
- elif isinstance(cls, UnionMeta):
- if cls.__union_params__ is None:
- return False
- return all(issubclass(c, self) for c in (cls.__union_params__))
- elif isinstance(cls, TypeVar):
- if cls in self.__union_params__:
- return True
- if cls.__constraints__:
- return issubclass(Union[cls.__constraints__], self)
- return False
- else:
- return any(issubclass(cls, t) for t in self.__union_params__)
+ raise TypeError("Unions cannot be used with issubclass().")
-class Union(Final, metaclass=UnionMeta, _root=True):
- """Union type; Union[X, Y] means either X or Y.
-
- To define a union, use e.g. Union[int, str]. Details:
-
- - The arguments must be types and there must be at least one.
-
- - None as an argument is a special case and is replaced by
- type(None).
-
- - Unions of unions are flattened, e.g.::
-
- Union[Union[int, str], float] == Union[int, str, float]
-
- - Unions of a single argument vanish, e.g.::
-
- Union[int] == int # The constructor actually returns int
-
- - Redundant arguments are skipped, e.g.::
-
- Union[int, str, int] == Union[int, str]
-
- - When comparing unions, the argument order is ignored, e.g.::
-
- Union[int, str] == Union[str, int]
-
- - When two arguments have a subclass relationship, the least
- derived argument is kept, e.g.::
-
- class Employee: pass
- class Manager(Employee): pass
- Union[int, Employee, Manager] == Union[int, Employee]
- Union[Manager, int, Employee] == Union[int, Employee]
- Union[Employee, Manager] == Employee
-
- - Corollary: if Any is present it is the sole survivor, e.g.::
-
- Union[int, Any] == Any
+Union = _Union(_root=True)
- - Similar for object::
- Union[int, object] == object
-
- - To cut a tie: Union[object, Any] == Union[Any, object] == Any.
-
- - You cannot subclass or instantiate a union.
-
- - You cannot write Union[X][Y] (what would it mean?).
+class _Optional(_FinalTypingBase, _root=True):
+ """Optional type.
- - You can use Optional[X] as a shorthand for Union[X, None].
+ Optional[X] is equivalent to Union[X, type(None)].
"""
- # Unsubscripted Union type has params set to None.
- __union_params__ = None
- __union_set_params__ = None
-
-
-class OptionalMeta(TypingMeta):
- """Metaclass for Optional."""
-
- def __new__(cls, name, bases, namespace, _root=False):
- return super().__new__(cls, name, bases, namespace, _root=_root)
+ __slots__ = ()
+ @_tp_cache
def __getitem__(self, arg):
arg = _type_check(arg, "Optional[t] requires a single type.")
return Union[arg, type(None)]
-class Optional(Final, metaclass=OptionalMeta, _root=True):
- """Optional type.
+Optional = _Optional(_root=True)
- Optional[X] is equivalent to Union[X, type(None)].
- """
- __slots__ = ()
+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, ...].
+ """
-class TupleMeta(TypingMeta):
- """Metaclass for Tuple."""
+ __slots__ = ('__tuple_params__', '__tuple_use_ellipsis__')
- def __new__(cls, name, bases, namespace, parameters=None,
+ def __init__(self, parameters=None,
use_ellipsis=False, _root=False):
- self = super().__new__(cls, name, bases, namespace, _root=_root)
self.__tuple_params__ = parameters
self.__tuple_use_ellipsis__ = use_ellipsis
- return self
def _get_type_vars(self, tvars):
if self.__tuple_params__:
@@ -679,8 +705,7 @@ class TupleMeta(TypingMeta):
if p == self.__tuple_params__:
return self
else:
- return self.__class__(self.__name__, self.__bases__, {},
- p, _root=True)
+ return self.__class__(p, _root=True)
def __repr__(self):
r = super().__repr__()
@@ -694,6 +719,7 @@ class TupleMeta(TypingMeta):
', '.join(params))
return r
+ @_tp_cache
def __getitem__(self, parameters):
if self.__tuple_params__ is not None:
raise TypeError("Cannot re-parameterize %r" % (self,))
@@ -707,64 +733,50 @@ class TupleMeta(TypingMeta):
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__(self.__name__, self.__bases__,
- dict(self.__dict__), parameters,
+ return self.__class__(parameters,
use_ellipsis=use_ellipsis, _root=True)
def __eq__(self, other):
- if not isinstance(other, TupleMeta):
+ 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__)
+ return hash((self.__tuple_params__, self.__tuple_use_ellipsis__))
def __instancecheck__(self, obj):
- raise TypeError("Tuples cannot be used with isinstance().")
+ if self.__tuple_params__ == None:
+ return isinstance(obj, tuple)
+ raise TypeError("Parameterized Tuple cannot be used "
+ "with isinstance().")
def __subclasscheck__(self, cls):
- if cls is Any:
- return True
- if not isinstance(cls, type):
- return super().__subclasscheck__(cls) # To TypeError.
- if issubclass(cls, tuple):
- return True # Special case.
- if not isinstance(cls, TupleMeta):
- return super().__subclasscheck__(cls) # False.
- if self.__tuple_params__ is None:
- return True
- if cls.__tuple_params__ is None:
- return False # ???
- if cls.__tuple_use_ellipsis__ != self.__tuple_use_ellipsis__:
- return False
- # Covariance.
- return (len(self.__tuple_params__) == len(cls.__tuple_params__) and
- all(issubclass(x, p)
- for x, p in zip(cls.__tuple_params__,
- self.__tuple_params__)))
-
-
-class Tuple(Final, metaclass=TupleMeta, _root=True):
- """Tuple type; Tuple[X, Y] is the cross-product type of X and Y.
+ if self.__tuple_params__ == None:
+ return issubclass(cls, tuple)
+ raise TypeError("Parameterized Tuple cannot be used "
+ "with issubclass().")
- 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 Sequence[T].
- """
+Tuple = _Tuple(_root=True)
- __slots__ = ()
+class _Callable(_FinalTypingBase, _root=True):
+ """Callable type; Callable[[int], str] is a function of (int) -> str.
-class CallableMeta(TypingMeta):
- """Metaclass for Callable."""
+ 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.
- def __new__(cls, name, bases, namespace, _root=False,
- args=None, result=None):
+ 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 # Must be 'class Callable'.
+ pass
else:
if args is not Ellipsis:
if not isinstance(args, list):
@@ -775,10 +787,8 @@ class CallableMeta(TypingMeta):
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 = super().__new__(cls, name, bases, namespace, _root=_root)
self.__args__ = args
self.__result__ = result
- return self
def _get_type_vars(self, tvars):
if self.__args__ and self.__args__ is not Ellipsis:
@@ -795,8 +805,7 @@ class CallableMeta(TypingMeta):
if args == self.__args__ and result == self.__result__:
return self
else:
- return self.__class__(self.__name__, self.__bases__, {},
- args=args, result=result, _root=True)
+ return self.__class__(args, result, _root=True)
def __repr__(self):
r = super().__repr__()
@@ -816,12 +825,10 @@ class CallableMeta(TypingMeta):
raise TypeError(
"Callable must be used as Callable[[arg, ...], result].")
args, result = parameters
- return self.__class__(self.__name__, self.__bases__,
- dict(self.__dict__), _root=True,
- args=args, result=result)
+ return self.__class__(args, result, _root=True)
def __eq__(self, other):
- if not isinstance(other, CallableMeta):
+ if not isinstance(other, _Callable):
return NotImplemented
return (self.__args__ == other.__args__ and
self.__result__ == other.__result__)
@@ -836,31 +843,18 @@ class CallableMeta(TypingMeta):
if self.__args__ is None and self.__result__ is None:
return isinstance(obj, collections_abc.Callable)
else:
- raise TypeError("Callable[] cannot be used with isinstance().")
+ raise TypeError("Parameterized Callable cannot be used "
+ "with isinstance().")
def __subclasscheck__(self, cls):
- if cls is Any:
- return True
- if not isinstance(cls, CallableMeta):
- return super().__subclasscheck__(cls)
if self.__args__ is None and self.__result__ is None:
- return True
- # We're not doing covariance or contravariance -- this is *invariance*.
- return self == cls
-
-
-class Callable(Final, metaclass=CallableMeta, _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.
+ return issubclass(cls, collections_abc.Callable)
+ else:
+ raise TypeError("Parameterized Callable cannot be used "
+ "with issubclass().")
- There is no syntax to indicate optional or keyword arguments,
- such function types are rarely used as callback types.
- """
- __slots__ = ()
+Callable = _Callable(_root=True)
def _gorg(a):
@@ -985,6 +979,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
def __hash__(self):
return hash((self.__name__, self.__parameters__))
+ @_tp_cache
def __getitem__(self, params):
if not isinstance(params, tuple):
params = (params,)
@@ -1040,44 +1035,18 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
return self.__subclasscheck__(instance.__class__)
def __subclasscheck__(self, cls):
- if cls is Any:
- return True
- if isinstance(cls, GenericMeta):
- # For a covariant class C(Generic[T]),
- # C[X] is a subclass of C[Y] iff X is a subclass of Y.
- origin = self.__origin__
- if origin is not None and origin is cls.__origin__:
- assert len(self.__args__) == len(origin.__parameters__)
- assert len(cls.__args__) == len(origin.__parameters__)
- for p_self, p_cls, p_origin in zip(self.__args__,
- cls.__args__,
- origin.__parameters__):
- if isinstance(p_origin, TypeVar):
- if p_origin.__covariant__:
- # Covariant -- p_cls must be a subclass of p_self.
- if not issubclass(p_cls, p_self):
- break
- elif p_origin.__contravariant__:
- # Contravariant. I think it's the opposite. :-)
- if not issubclass(p_self, p_cls):
- break
- else:
- # Invariant -- p_cls and p_self must equal.
- if p_self != p_cls:
- break
- else:
- # If the origin's parameter is not a typevar,
- # insist on invariance.
- if p_self != p_cls:
- break
- else:
- return True
- # If we break out of the loop, the superclass gets a chance.
+ if self is Generic:
+ raise TypeError("Class %r cannot be used with class "
+ "or instance checks" % self)
+ if (self.__origin__ is not None and
+ sys._getframe(1).f_globals['__name__'] != 'abc'):
+ raise TypeError("Parameterized generics cannot be used with class "
+ "or instance checks")
if super().__subclasscheck__(cls):
return True
- if self.__extra__ is None or isinstance(cls, GenericMeta):
- return False
- return issubclass(cls, self.__extra__)
+ if self.__extra__ is not None:
+ return issubclass(cls, self.__extra__)
+ return False
# Prevent checks for Generic to crash when defining Generic.
@@ -1117,7 +1086,7 @@ class Generic(metaclass=GenericMeta):
return obj
-class _ClassVar(metaclass=TypingMeta, _root=True):
+class _ClassVar(_FinalTypingBase, _root=True):
"""Special type construct to mark class variables.
An annotation wrapped in ClassVar indicates that a given
@@ -1134,36 +1103,35 @@ class _ClassVar(metaclass=TypingMeta, _root=True):
be used with isinstance() or issubclass().
"""
- def __init__(self, tp=None, _root=False):
- cls = type(self)
- if _root:
- self.__type__ = tp
- else:
- raise TypeError('Cannot initialize {}'.format(cls.__name__[1:]))
+ __slots__ = ('__type__',)
+
+ def __init__(self, tp=None, **kwds):
+ self.__type__ = tp
def __getitem__(self, item):
cls = type(self)
if self.__type__ is None:
return cls(_type_check(item,
- '{} accepts only types.'.format(cls.__name__[1:])),
+ '{} accepts only single type.'.format(cls.__name__[1:])),
_root=True)
raise TypeError('{} cannot be further subscripted'
.format(cls.__name__[1:]))
def _eval_type(self, globalns, localns):
- return type(self)(_eval_type(self.__type__, globalns, localns),
- _root=True)
+ new_tp = _eval_type(self.__type__, globalns, localns)
+ if new_tp == self.__type__:
+ 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):
- cls = type(self)
- if not self.__type__:
- return '{}.{}'.format(cls.__module__, cls.__name__[1:])
- return '{}.{}[{}]'.format(cls.__module__, cls.__name__[1:],
- _type_repr(self.__type__))
+ r = super().__repr__()
+ if self.__type__ is not None:
+ r += '[{}]'.format(_type_repr(self.__type__))
+ return r
def __hash__(self):
return hash((type(self).__name__, self.__type__))
@@ -1614,52 +1582,52 @@ if hasattr(collections_abc, 'Collection'):
if hasattr(collections_abc, 'Collection'):
class AbstractSet(Collection[T_co],
extra=collections_abc.Set):
- pass
+ __slots__ = ()
else:
class AbstractSet(Sized, Iterable[T_co], Container[T_co],
extra=collections_abc.Set):
- pass
+ __slots__ = ()
class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet):
- pass
+ __slots__ = ()
# NOTE: It is only covariant in the value type.
if hasattr(collections_abc, 'Collection'):
class Mapping(Collection[KT], Generic[KT, VT_co],
extra=collections_abc.Mapping):
- pass
+ __slots__ = ()
else:
class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co],
extra=collections_abc.Mapping):
- pass
+ __slots__ = ()
class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping):
- pass
+ __slots__ = ()
if hasattr(collections_abc, 'Reversible'):
if hasattr(collections_abc, 'Collection'):
class Sequence(Reversible[T_co], Collection[T_co],
extra=collections_abc.Sequence):
- pass
+ __slots__ = ()
else:
class Sequence(Sized, Reversible[T_co], Container[T_co],
extra=collections_abc.Sequence):
- pass
+ __slots__ = ()
else:
class Sequence(Sized, Iterable[T_co], Container[T_co],
extra=collections_abc.Sequence):
- pass
+ __slots__ = ()
class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence):
- pass
+ __slots__ = ()
class ByteString(Sequence[int], extra=collections_abc.ByteString):
- pass
+ __slots__ = ()
ByteString.register(type(memoryview(b'')))
@@ -1667,6 +1635,8 @@ ByteString.register(type(memoryview(b'')))
class List(list, MutableSequence[T], extra=list):
+ __slots__ = ()
+
def __new__(cls, *args, **kwds):
if _geqv(cls, List):
raise TypeError("Type List cannot be instantiated; "
@@ -1676,6 +1646,8 @@ class List(list, MutableSequence[T], extra=list):
class Set(set, MutableSet[T], extra=set):
+ __slots__ = ()
+
def __new__(cls, *args, **kwds):
if _geqv(cls, Set):
raise TypeError("Type Set cannot be instantiated; "
@@ -1683,22 +1655,7 @@ class Set(set, MutableSet[T], extra=set):
return set.__new__(cls, *args, **kwds)
-class _FrozenSetMeta(GenericMeta):
- """This metaclass ensures set is not a subclass of FrozenSet.
-
- Without this metaclass, set would be considered a subclass of
- FrozenSet, because FrozenSet.__extra__ is collections.abc.Set, and
- set is a subclass of that.
- """
-
- def __subclasscheck__(self, cls):
- if issubclass(cls, Set):
- return False
- return super().__subclasscheck__(cls)
-
-
-class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta,
- extra=frozenset):
+class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
__slots__ = ()
def __new__(cls, *args, **kwds):
@@ -1709,23 +1666,23 @@ class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta,
class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView):
- pass
+ __slots__ = ()
class KeysView(MappingView[KT], AbstractSet[KT],
extra=collections_abc.KeysView):
- pass
+ __slots__ = ()
class ItemsView(MappingView[Tuple[KT, VT_co]],
AbstractSet[Tuple[KT, VT_co]],
Generic[KT, VT_co],
extra=collections_abc.ItemsView):
- pass
+ __slots__ = ()
class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView):
- pass
+ __slots__ = ()
if hasattr(contextlib, 'AbstractContextManager'):
@@ -1736,6 +1693,8 @@ if hasattr(contextlib, 'AbstractContextManager'):
class Dict(dict, MutableMapping[KT, VT], extra=dict):
+ __slots__ = ()
+
def __new__(cls, *args, **kwds):
if _geqv(cls, Dict):
raise TypeError("Type Dict cannot be instantiated; "
@@ -1745,6 +1704,8 @@ class Dict(dict, MutableMapping[KT, VT], extra=dict):
class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
extra=collections.defaultdict):
+ __slots__ = ()
+
def __new__(cls, *args, **kwds):
if _geqv(cls, DefaultDict):
raise TypeError("Type DefaultDict cannot be instantiated; "
@@ -1800,6 +1761,8 @@ class Type(Generic[CT_co], extra=type):
At this point the type checker knows that joe has type BasicUser.
"""
+ __slots__ = ()
+
def _make_nmtuple(name, types):
nm_tpl = collections.namedtuple(name, [n for n, t in types])