Issue #28556: updates to typing.py
This commit is contained in:
parent
84968b74c8
commit
5fc25a873c
|
@ -142,8 +142,9 @@ class TypeVarTests(BaseTestCase):
|
||||||
self.assertEqual(Union[X, X], X)
|
self.assertEqual(Union[X, X], X)
|
||||||
self.assertNotEqual(Union[X, int], Union[X])
|
self.assertNotEqual(Union[X, int], Union[X])
|
||||||
self.assertNotEqual(Union[X, int], Union[int])
|
self.assertNotEqual(Union[X, int], Union[int])
|
||||||
self.assertEqual(Union[X, int].__union_params__, (X, int))
|
self.assertEqual(Union[X, int].__args__, (X, int))
|
||||||
self.assertEqual(Union[X, int].__union_set_params__, {X, int})
|
self.assertEqual(Union[X, int].__parameters__, (X,))
|
||||||
|
self.assertIs(Union[X, int].__origin__, Union)
|
||||||
|
|
||||||
def test_union_constrained(self):
|
def test_union_constrained(self):
|
||||||
A = TypeVar('A', str, bytes)
|
A = TypeVar('A', str, bytes)
|
||||||
|
@ -311,8 +312,6 @@ class UnionTests(BaseTestCase):
|
||||||
class TupleTests(BaseTestCase):
|
class TupleTests(BaseTestCase):
|
||||||
|
|
||||||
def test_basics(self):
|
def test_basics(self):
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
issubclass(Tuple[int, str], Tuple)
|
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
issubclass(Tuple, Tuple[int, str])
|
issubclass(Tuple, Tuple[int, str])
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
|
@ -367,22 +366,6 @@ class CallableTests(BaseTestCase):
|
||||||
self.assertNotEqual(Callable[[int], int], Callable[[], int])
|
self.assertNotEqual(Callable[[int], int], Callable[[], int])
|
||||||
self.assertNotEqual(Callable[[int], int], Callable)
|
self.assertNotEqual(Callable[[int], int], Callable)
|
||||||
|
|
||||||
def test_cannot_subclass(self):
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
|
|
||||||
class C(Callable):
|
|
||||||
pass
|
|
||||||
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
|
|
||||||
class C(type(Callable)):
|
|
||||||
pass
|
|
||||||
|
|
||||||
with self.assertRaises(TypeError):
|
|
||||||
|
|
||||||
class C(Callable[[int], int]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_cannot_instantiate(self):
|
def test_cannot_instantiate(self):
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
Callable()
|
Callable()
|
||||||
|
@ -710,6 +693,138 @@ class GenericTests(BaseTestCase):
|
||||||
self.assertEqual(C.__orig_bases__, (List[T][U][V],))
|
self.assertEqual(C.__orig_bases__, (List[T][U][V],))
|
||||||
self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))
|
self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))
|
||||||
|
|
||||||
|
def test_extended_generic_rules_eq(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
U = TypeVar('U')
|
||||||
|
self.assertEqual(Tuple[T, T][int], Tuple[int, int])
|
||||||
|
self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Tuple[T, int][()]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Tuple[T, U][T, ...]
|
||||||
|
|
||||||
|
self.assertEqual(Union[T, int][int], int)
|
||||||
|
self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str])
|
||||||
|
class Base: ...
|
||||||
|
class Derived(Base): ...
|
||||||
|
self.assertEqual(Union[T, Base][Derived], Base)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Union[T, int][1]
|
||||||
|
|
||||||
|
self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT])
|
||||||
|
self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]])
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Callable[[T], U][..., int]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Callable[[T], U][[], int]
|
||||||
|
|
||||||
|
def test_extended_generic_rules_repr(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''),
|
||||||
|
'Union[Tuple, Callable]')
|
||||||
|
self.assertEqual(repr(Union[Tuple, Tuple[int]]).replace('typing.', ''),
|
||||||
|
'Tuple')
|
||||||
|
self.assertEqual(repr(Callable[..., Optional[T]][int]).replace('typing.', ''),
|
||||||
|
'Callable[..., Union[int, NoneType]]')
|
||||||
|
self.assertEqual(repr(Callable[[], List[T]][int]).replace('typing.', ''),
|
||||||
|
'Callable[[], List[int]]')
|
||||||
|
|
||||||
|
def test_generic_forvard_ref(self):
|
||||||
|
def foobar(x: List[List['T']]): ...
|
||||||
|
T = TypeVar('T')
|
||||||
|
self.assertEqual(get_type_hints(foobar, globals(), locals()), {'x': List[List[T]]})
|
||||||
|
def barfoo(x: Tuple[T, ...]): ...
|
||||||
|
self.assertIs(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...])
|
||||||
|
|
||||||
|
def test_extended_generic_rules_subclassing(self):
|
||||||
|
class T1(Tuple[T, KT]): ...
|
||||||
|
class T2(Tuple[T, ...]): ...
|
||||||
|
class C1(Callable[[T], T]): ...
|
||||||
|
class C2(Callable[..., int]):
|
||||||
|
def __call__(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.assertEqual(T1.__parameters__, (T, KT))
|
||||||
|
self.assertEqual(T1[int, str].__args__, (int, str))
|
||||||
|
self.assertEqual(T1[int, T].__origin__, T1)
|
||||||
|
|
||||||
|
self.assertEqual(T2.__parameters__, (T,))
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
T1[int]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
T2[int, str]
|
||||||
|
|
||||||
|
self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]')
|
||||||
|
self.assertEqual(C2.__parameters__, ())
|
||||||
|
self.assertIsInstance(C2(), collections_abc.Callable)
|
||||||
|
self.assertIsSubclass(C2, collections_abc.Callable)
|
||||||
|
self.assertIsSubclass(C1, collections_abc.Callable)
|
||||||
|
self.assertIsInstance(T1(), tuple)
|
||||||
|
self.assertIsSubclass(T2, tuple)
|
||||||
|
self.assertIsSubclass(Tuple[int, ...], typing.Sequence)
|
||||||
|
self.assertIsSubclass(Tuple[int, ...], typing.Iterable)
|
||||||
|
|
||||||
|
def test_fail_with_bare_union(self):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
List[Union]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Tuple[Optional]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
ClassVar[ClassVar]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
List[ClassVar[int]]
|
||||||
|
|
||||||
|
def test_fail_with_bare_generic(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
List[Generic]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
Tuple[Generic[T]]
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
List[typing._Protocol]
|
||||||
|
|
||||||
|
def test_type_erasure_special(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
class MyTup(Tuple[T, T]): ...
|
||||||
|
self.assertIs(MyTup[int]().__class__, MyTup)
|
||||||
|
self.assertIs(MyTup[int]().__orig_class__, MyTup[int])
|
||||||
|
class MyCall(Callable[..., T]):
|
||||||
|
def __call__(self): return None
|
||||||
|
self.assertIs(MyCall[T]().__class__, MyCall)
|
||||||
|
self.assertIs(MyCall[T]().__orig_class__, MyCall[T])
|
||||||
|
class MyDict(typing.Dict[T, T]): ...
|
||||||
|
self.assertIs(MyDict[int]().__class__, MyDict)
|
||||||
|
self.assertIs(MyDict[int]().__orig_class__, MyDict[int])
|
||||||
|
class MyDef(typing.DefaultDict[str, T]): ...
|
||||||
|
self.assertIs(MyDef[int]().__class__, MyDef)
|
||||||
|
self.assertIs(MyDef[int]().__orig_class__, MyDef[int])
|
||||||
|
|
||||||
|
def test_all_repr_eq_any(self):
|
||||||
|
objs = (getattr(typing, el) for el in typing.__all__)
|
||||||
|
for obj in objs:
|
||||||
|
self.assertNotEqual(repr(obj), '')
|
||||||
|
self.assertEqual(obj, obj)
|
||||||
|
if getattr(obj, '__parameters__', None) and len(obj.__parameters__) == 1:
|
||||||
|
self.assertEqual(obj[Any].__args__, (Any,))
|
||||||
|
if isinstance(obj, type):
|
||||||
|
for base in obj.__mro__:
|
||||||
|
self.assertNotEqual(repr(base), '')
|
||||||
|
self.assertEqual(base, base)
|
||||||
|
|
||||||
|
def test_substitution_helper(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
KT = TypeVar('KT')
|
||||||
|
VT = TypeVar('VT')
|
||||||
|
class Map(Generic[KT, VT]):
|
||||||
|
def meth(self, k: KT, v: VT): ...
|
||||||
|
StrMap = Map[str, T]
|
||||||
|
obj = StrMap[int]()
|
||||||
|
|
||||||
|
new_args = typing._subs_tree(obj.__orig_class__)
|
||||||
|
new_annots = {k: typing._replace_arg(v, type(obj).__parameters__, new_args)
|
||||||
|
for k, v in obj.meth.__annotations__.items()}
|
||||||
|
|
||||||
|
self.assertEqual(new_annots, {'k': str, 'v': int})
|
||||||
|
|
||||||
def test_pickle(self):
|
def test_pickle(self):
|
||||||
global C # pickle wants to reference the class by name
|
global C # pickle wants to reference the class by name
|
||||||
|
@ -752,7 +867,7 @@ class GenericTests(BaseTestCase):
|
||||||
X = C[int]
|
X = C[int]
|
||||||
self.assertEqual(X.__module__, __name__)
|
self.assertEqual(X.__module__, __name__)
|
||||||
if not PY32:
|
if not PY32:
|
||||||
self.assertEqual(X.__qualname__, 'C')
|
self.assertTrue(X.__qualname__.endswith('.<locals>.C'))
|
||||||
self.assertEqual(repr(X).split('.')[-1], 'C[int]')
|
self.assertEqual(repr(X).split('.')[-1], 'C[int]')
|
||||||
|
|
||||||
class Y(C[int]):
|
class Y(C[int]):
|
||||||
|
|
734
Lib/typing.py
734
Lib/typing.py
|
@ -333,8 +333,7 @@ def _type_vars(types):
|
||||||
def _eval_type(t, globalns, localns):
|
def _eval_type(t, globalns, localns):
|
||||||
if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
|
if isinstance(t, TypingMeta) or isinstance(t, _TypingBase):
|
||||||
return t._eval_type(globalns, localns)
|
return t._eval_type(globalns, localns)
|
||||||
else:
|
return t
|
||||||
return t
|
|
||||||
|
|
||||||
|
|
||||||
def _type_check(arg, msg):
|
def _type_check(arg, msg):
|
||||||
|
@ -353,8 +352,14 @@ def _type_check(arg, msg):
|
||||||
return type(None)
|
return type(None)
|
||||||
if isinstance(arg, str):
|
if isinstance(arg, str):
|
||||||
arg = _ForwardRef(arg)
|
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,))
|
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
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
@ -369,10 +374,12 @@ def _type_repr(obj):
|
||||||
if isinstance(obj, type) and not isinstance(obj, TypingMeta):
|
if isinstance(obj, type) and not isinstance(obj, TypingMeta):
|
||||||
if obj.__module__ == 'builtins':
|
if obj.__module__ == 'builtins':
|
||||||
return _qualname(obj)
|
return _qualname(obj)
|
||||||
else:
|
return '%s.%s' % (obj.__module__, _qualname(obj))
|
||||||
return '%s.%s' % (obj.__module__, _qualname(obj))
|
if obj is ...:
|
||||||
else:
|
return('...')
|
||||||
return repr(obj)
|
if isinstance(obj, types.FunctionType):
|
||||||
|
return obj.__name__
|
||||||
|
return repr(obj)
|
||||||
|
|
||||||
|
|
||||||
class _Any(_FinalTypingBase, _root=True):
|
class _Any(_FinalTypingBase, _root=True):
|
||||||
|
@ -502,7 +509,107 @@ T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant.
|
||||||
AnyStr = TypeVar('AnyStr', bytes, str)
|
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):
|
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)
|
cached = functools.lru_cache()(func)
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def inner(*args, **kwds):
|
def inner(*args, **kwds):
|
||||||
|
@ -555,100 +662,100 @@ class _Union(_FinalTypingBase, _root=True):
|
||||||
|
|
||||||
- You cannot subclass or instantiate a union.
|
- 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].
|
- 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):
|
def __new__(cls, parameters=None, origin=None, *args, _root=False):
|
||||||
self = super().__new__(cls, parameters, *args, _root=_root)
|
self = super().__new__(cls, parameters, origin, *args, _root=_root)
|
||||||
if parameters is None:
|
if origin is None:
|
||||||
self.__union_params__ = None
|
self.__parameters__ = None
|
||||||
self.__union_set_params__ = None
|
self.__args__ = None
|
||||||
|
self.__origin__ = None
|
||||||
|
self.__tree_hash__ = hash(frozenset(('Union',)))
|
||||||
return self
|
return self
|
||||||
if not isinstance(parameters, tuple):
|
if not isinstance(parameters, tuple):
|
||||||
raise TypeError("Expected parameters=<tuple>")
|
raise TypeError("Expected parameters=<tuple>")
|
||||||
# Flatten out Union[Union[...], ...] and type-check non-Union args.
|
if origin is Union:
|
||||||
params = []
|
parameters = _remove_dups_flatten(parameters)
|
||||||
msg = "Union[arg, ...]: each arg must be a type."
|
# It's not a union if there's only one type left.
|
||||||
for p in parameters:
|
if len(parameters) == 1:
|
||||||
if isinstance(p, _Union):
|
return parameters[0]
|
||||||
params.extend(p.__union_params__)
|
self.__parameters__ = _type_vars(parameters)
|
||||||
else:
|
self.__args__ = parameters
|
||||||
params.append(_type_check(p, msg))
|
self.__origin__ = origin
|
||||||
# Weed out strict duplicates, preserving the first of each occurrence.
|
# Pre-calculate the __hash__ on instantiation.
|
||||||
all_params = set(params)
|
# This improves speed for complex substitutions.
|
||||||
if len(all_params) < len(params):
|
subs_tree = self._subs_tree()
|
||||||
new_params = []
|
if isinstance(subs_tree, tuple):
|
||||||
for t in params:
|
self.__tree_hash__ = hash(frozenset(subs_tree))
|
||||||
if t in all_params:
|
else:
|
||||||
new_params.append(t)
|
self.__tree_hash__ = hash(subs_tree)
|
||||||
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__)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def _eval_type(self, globalns, localns):
|
def _eval_type(self, globalns, localns):
|
||||||
p = tuple(_eval_type(t, globalns, localns)
|
if self.__args__ is None:
|
||||||
for t in self.__union_params__)
|
|
||||||
if p == self.__union_params__:
|
|
||||||
return self
|
return self
|
||||||
else:
|
ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__)
|
||||||
return self.__class__(p, _root=True)
|
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):
|
def _get_type_vars(self, tvars):
|
||||||
if self.__union_params__:
|
if self.__origin__ and self.__parameters__:
|
||||||
_get_type_vars(self.__union_params__, tvars)
|
_get_type_vars(self.__parameters__, tvars)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self._subs_repr([], [])
|
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 _subs_repr(self, tvars, args):
|
def _tree_repr(self, tree):
|
||||||
r = super().__repr__()
|
arg_list = []
|
||||||
if self.__union_params__:
|
for arg in tree[1:]:
|
||||||
r += '[%s]' % (', '.join(_replace_arg(t, tvars, args)
|
if not isinstance(arg, tuple):
|
||||||
for t in self.__union_params__))
|
arg_list.append(_type_repr(arg))
|
||||||
return r
|
else:
|
||||||
|
arg_list.append(arg[0]._tree_repr(arg))
|
||||||
|
return super().__repr__() + '[%s]' % ', '.join(arg_list)
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem__(self, parameters):
|
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 == ():
|
if parameters == ():
|
||||||
raise TypeError("Cannot take a Union of no types.")
|
raise TypeError("Cannot take a Union of no types.")
|
||||||
if not isinstance(parameters, tuple):
|
if not isinstance(parameters, tuple):
|
||||||
parameters = (parameters,)
|
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):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, _Union):
|
if not isinstance(other, _Union):
|
||||||
return NotImplemented
|
return self._subs_tree() == other
|
||||||
return self.__union_set_params__ == other.__union_set_params__
|
return self.__tree_hash__ == other.__tree_hash__
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.__union_set_params__)
|
return self.__tree_hash__
|
||||||
|
|
||||||
def __instancecheck__(self, obj):
|
def __instancecheck__(self, obj):
|
||||||
raise TypeError("Unions cannot be used with isinstance().")
|
raise TypeError("Unions cannot be used with isinstance().")
|
||||||
|
@ -677,195 +784,6 @@ class _Optional(_FinalTypingBase, _root=True):
|
||||||
Optional = _Optional(_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):
|
def _gorg(a):
|
||||||
"""Return the farthest origin of a generic class."""
|
"""Return the farthest origin of a generic class."""
|
||||||
assert isinstance(a, GenericMeta)
|
assert isinstance(a, GenericMeta)
|
||||||
|
@ -889,16 +807,6 @@ def _geqv(a, b):
|
||||||
return _gorg(a) is _gorg(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):
|
def _next_in_mro(cls):
|
||||||
"""Helper for Generic.__new__.
|
"""Helper for Generic.__new__.
|
||||||
|
|
||||||
|
@ -1011,7 +919,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
self = super().__new__(cls, name, bases, namespace, _root=True)
|
self = super().__new__(cls, name, bases, namespace, _root=True)
|
||||||
|
|
||||||
self.__parameters__ = tvars
|
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.__origin__ = origin
|
||||||
self.__extra__ = extra
|
self.__extra__ = extra
|
||||||
# Speed hack (https://github.com/python/typing/issues/196).
|
# Speed hack (https://github.com/python/typing/issues/196).
|
||||||
|
@ -1029,55 +941,69 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
self.__subclasshook__ = _make_subclasshook(self)
|
self.__subclasshook__ = _make_subclasshook(self)
|
||||||
if isinstance(extra, abc.ABCMeta):
|
if isinstance(extra, abc.ABCMeta):
|
||||||
self._abc_registry = extra._abc_registry
|
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
|
return self
|
||||||
|
|
||||||
def _get_type_vars(self, tvars):
|
def _get_type_vars(self, tvars):
|
||||||
if self.__origin__ and self.__parameters__:
|
if self.__origin__ and self.__parameters__:
|
||||||
_get_type_vars(self.__parameters__, tvars)
|
_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):
|
def __repr__(self):
|
||||||
if self.__origin__ is None:
|
if self.__origin__ is None:
|
||||||
return super().__repr__()
|
return super().__repr__()
|
||||||
return self._subs_repr([], [])
|
return self._tree_repr(self._subs_tree())
|
||||||
|
|
||||||
def _subs_repr(self, tvars, args):
|
def _tree_repr(self, tree):
|
||||||
assert len(tvars) == len(args)
|
arg_list = []
|
||||||
# Construct the chain of __origin__'s.
|
for arg in tree[1:]:
|
||||||
current = self.__origin__
|
if arg == ():
|
||||||
orig_chain = []
|
arg_list.append('()')
|
||||||
while current.__origin__ is not None:
|
elif not isinstance(arg, tuple):
|
||||||
orig_chain.append(current)
|
arg_list.append(_type_repr(arg))
|
||||||
current = current.__origin__
|
else:
|
||||||
# Replace type variables in __args__ if asked ...
|
arg_list.append(arg[0]._tree_repr(arg))
|
||||||
str_args = []
|
return super().__repr__() + '[%s]' % ', '.join(arg_list)
|
||||||
for arg in self.__args__:
|
|
||||||
str_args.append(_replace_arg(arg, tvars, args))
|
def _subs_tree(self, tvars=None, args=None):
|
||||||
# ... then continue replacing down the origin chain.
|
if self.__origin__ is None:
|
||||||
for cls in orig_chain:
|
return self
|
||||||
new_str_args = []
|
tree_args = _subs_tree(self, tvars, args)
|
||||||
for i, arg in enumerate(cls.__args__):
|
return (_gorg(self),) + tuple(tree_args)
|
||||||
new_str_args.append(_replace_arg(arg, cls.__parameters__, str_args))
|
|
||||||
str_args = new_str_args
|
|
||||||
return super().__repr__() + '[%s]' % ', '.join(str_args)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, GenericMeta):
|
if not isinstance(other, GenericMeta):
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
if self.__origin__ is not None:
|
if self.__origin__ is None or other.__origin__ is None:
|
||||||
return (self.__origin__ is other.__origin__ and
|
|
||||||
self.__args__ == other.__args__ and
|
|
||||||
self.__parameters__ == other.__parameters__)
|
|
||||||
else:
|
|
||||||
return self is other
|
return self is other
|
||||||
|
return self.__tree_hash__ == other.__tree_hash__
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash((self.__name__, self.__parameters__))
|
return self.__tree_hash__
|
||||||
|
|
||||||
@_tp_cache
|
@_tp_cache
|
||||||
def __getitem__(self, params):
|
def __getitem__(self, params):
|
||||||
if not isinstance(params, tuple):
|
if not isinstance(params, tuple):
|
||||||
params = (params,)
|
params = (params,)
|
||||||
if not params:
|
if not params and not _gorg(self) is Tuple:
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Parameter list to %s[...] cannot be empty" % _qualname(self))
|
"Parameter list to %s[...] cannot be empty" % _qualname(self))
|
||||||
msg = "Parameters to generic types must be types."
|
msg = "Parameters to generic types must be types."
|
||||||
|
@ -1092,6 +1018,9 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
"Parameters to Generic[...] must all be unique")
|
"Parameters to Generic[...] must all be unique")
|
||||||
tvars = params
|
tvars = params
|
||||||
args = params
|
args = params
|
||||||
|
elif self in (Tuple, Callable):
|
||||||
|
tvars = _type_vars(params)
|
||||||
|
args = params
|
||||||
elif self is _Protocol:
|
elif self is _Protocol:
|
||||||
# _Protocol is internal, don't check anything.
|
# _Protocol is internal, don't check anything.
|
||||||
tvars = params
|
tvars = params
|
||||||
|
@ -1102,14 +1031,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
repr(self))
|
repr(self))
|
||||||
else:
|
else:
|
||||||
# Subscripting a regular Generic subclass.
|
# Subscripting a regular Generic subclass.
|
||||||
if not self.__parameters__:
|
_check_generic(self, params)
|
||||||
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))
|
|
||||||
tvars = _type_vars(params)
|
tvars = _type_vars(params)
|
||||||
args = params
|
args = params
|
||||||
return self.__class__(self.__name__,
|
return self.__class__(self.__name__,
|
||||||
|
@ -1134,6 +1056,22 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
Generic = None
|
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):
|
class Generic(metaclass=GenericMeta):
|
||||||
"""Abstract base class for generic types.
|
"""Abstract base class for generic types.
|
||||||
|
|
||||||
|
@ -1158,17 +1096,154 @@ class Generic(metaclass=GenericMeta):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
|
||||||
def __new__(cls, *args, **kwds):
|
def __new__(cls, *args, **kwds):
|
||||||
if cls.__origin__ is None:
|
return _generic_new(cls.__next_in_mro__, cls, *args, **kwds)
|
||||||
return cls.__next_in_mro__.__new__(cls)
|
|
||||||
|
|
||||||
|
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:
|
else:
|
||||||
origin = _gorg(cls)
|
if not isinstance(args, list):
|
||||||
obj = cls.__next_in_mro__.__new__(origin)
|
raise TypeError("Callable[args, result]: args must be a list."
|
||||||
try:
|
" Got %.100r." % (args,))
|
||||||
obj.__orig_class__ = cls
|
parameters = tuple(args) + (result,)
|
||||||
except AttributeError:
|
return self.__getitem_inner__(parameters)
|
||||||
pass
|
|
||||||
obj.__init__(*args, **kwds)
|
@_tp_cache
|
||||||
return obj
|
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):
|
class _ClassVar(_FinalTypingBase, _root=True):
|
||||||
|
@ -1208,17 +1283,10 @@ class _ClassVar(_FinalTypingBase, _root=True):
|
||||||
return self
|
return self
|
||||||
return type(self)(new_tp, _root=True)
|
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):
|
def __repr__(self):
|
||||||
return self._subs_repr([], [])
|
|
||||||
|
|
||||||
def _subs_repr(self, tvars, args):
|
|
||||||
r = super().__repr__()
|
r = super().__repr__()
|
||||||
if self.__type__ is not None:
|
if self.__type__ is not None:
|
||||||
r += '[{}]'.format(_replace_arg(self.__type__, tvars, args))
|
r += '[{}]'.format(_type_repr(self.__type__))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
@ -1231,6 +1299,7 @@ class _ClassVar(_FinalTypingBase, _root=True):
|
||||||
return self.__type__ == other.__type__
|
return self.__type__ == other.__type__
|
||||||
return self is other
|
return self is other
|
||||||
|
|
||||||
|
|
||||||
ClassVar = _ClassVar(_root=True)
|
ClassVar = _ClassVar(_root=True)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1533,6 +1602,7 @@ class _ProtocolMeta(GenericMeta):
|
||||||
attr != '__origin__' and
|
attr != '__origin__' and
|
||||||
attr != '__orig_bases__' and
|
attr != '__orig_bases__' and
|
||||||
attr != '__extra__' and
|
attr != '__extra__' and
|
||||||
|
attr != '__tree_hash__' and
|
||||||
attr != '__module__'):
|
attr != '__module__'):
|
||||||
attrs.add(attr)
|
attrs.add(attr)
|
||||||
|
|
||||||
|
@ -1723,7 +1793,7 @@ class List(list, MutableSequence[T], extra=list):
|
||||||
if _geqv(cls, List):
|
if _geqv(cls, List):
|
||||||
raise TypeError("Type List cannot be instantiated; "
|
raise TypeError("Type List cannot be instantiated; "
|
||||||
"use list() instead")
|
"use list() instead")
|
||||||
return list.__new__(cls, *args, **kwds)
|
return _generic_new(list, cls, *args, **kwds)
|
||||||
|
|
||||||
|
|
||||||
class Set(set, MutableSet[T], extra=set):
|
class Set(set, MutableSet[T], extra=set):
|
||||||
|
@ -1734,7 +1804,7 @@ class Set(set, MutableSet[T], extra=set):
|
||||||
if _geqv(cls, Set):
|
if _geqv(cls, Set):
|
||||||
raise TypeError("Type Set cannot be instantiated; "
|
raise TypeError("Type Set cannot be instantiated; "
|
||||||
"use set() instead")
|
"use set() instead")
|
||||||
return set.__new__(cls, *args, **kwds)
|
return _generic_new(set, cls, *args, **kwds)
|
||||||
|
|
||||||
|
|
||||||
class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
|
class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
|
||||||
|
@ -1744,7 +1814,7 @@ class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset):
|
||||||
if _geqv(cls, FrozenSet):
|
if _geqv(cls, FrozenSet):
|
||||||
raise TypeError("Type FrozenSet cannot be instantiated; "
|
raise TypeError("Type FrozenSet cannot be instantiated; "
|
||||||
"use frozenset() instead")
|
"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):
|
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):
|
if _geqv(cls, Dict):
|
||||||
raise TypeError("Type Dict cannot be instantiated; "
|
raise TypeError("Type Dict cannot be instantiated; "
|
||||||
"use dict() instead")
|
"use dict() instead")
|
||||||
return dict.__new__(cls, *args, **kwds)
|
return _generic_new(dict, cls, *args, **kwds)
|
||||||
|
|
||||||
class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
|
class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
|
||||||
extra=collections.defaultdict):
|
extra=collections.defaultdict):
|
||||||
|
@ -1792,7 +1862,7 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
|
||||||
if _geqv(cls, DefaultDict):
|
if _geqv(cls, DefaultDict):
|
||||||
raise TypeError("Type DefaultDict cannot be instantiated; "
|
raise TypeError("Type DefaultDict cannot be instantiated; "
|
||||||
"use collections.defaultdict() instead")
|
"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.
|
# Determine what base class to use for Generator.
|
||||||
if hasattr(collections_abc, '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):
|
if _geqv(cls, Generator):
|
||||||
raise TypeError("Type Generator cannot be instantiated; "
|
raise TypeError("Type Generator cannot be instantiated; "
|
||||||
"create a subclass instead")
|
"create a subclass instead")
|
||||||
return super().__new__(cls, *args, **kwds)
|
return _generic_new(_G_base, cls, *args, **kwds)
|
||||||
|
|
||||||
|
|
||||||
# Internal type variable used for Type[].
|
# Internal type variable used for Type[].
|
||||||
|
|
Loading…
Reference in New Issue