mirror of
https://github.com/python/cpython.git
synced 2025-09-27 02:39:58 +00:00
Sync typing.py from upstream
This commit is contained in:
parent
3d67615a48
commit
7ef22d6b96
2 changed files with 170 additions and 33 deletions
|
@ -548,9 +548,9 @@ class GenericTests(BaseTestCase):
|
||||||
|
|
||||||
def test_repr(self):
|
def test_repr(self):
|
||||||
self.assertEqual(repr(SimpleMapping),
|
self.assertEqual(repr(SimpleMapping),
|
||||||
__name__ + '.' + 'SimpleMapping<~XK, ~XV>')
|
__name__ + '.' + 'SimpleMapping')
|
||||||
self.assertEqual(repr(MySimpleMapping),
|
self.assertEqual(repr(MySimpleMapping),
|
||||||
__name__ + '.' + 'MySimpleMapping<~XK, ~XV>')
|
__name__ + '.' + 'MySimpleMapping')
|
||||||
|
|
||||||
def test_chain_repr(self):
|
def test_chain_repr(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
@ -574,7 +574,36 @@ class GenericTests(BaseTestCase):
|
||||||
self.assertNotEqual(Z, Y[T])
|
self.assertNotEqual(Z, Y[T])
|
||||||
|
|
||||||
self.assertTrue(str(Z).endswith(
|
self.assertTrue(str(Z).endswith(
|
||||||
'.C<~T>[typing.Tuple[~S, ~T]]<~S, ~T>[~T, int]<~T>[str]'))
|
'.C[typing.Tuple[str, int]]'))
|
||||||
|
|
||||||
|
def test_new_repr(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
U = TypeVar('U', covariant=True)
|
||||||
|
S = TypeVar('S')
|
||||||
|
|
||||||
|
self.assertEqual(repr(List), 'typing.List')
|
||||||
|
self.assertEqual(repr(List[T]), 'typing.List[~T]')
|
||||||
|
self.assertEqual(repr(List[U]), 'typing.List[+U]')
|
||||||
|
self.assertEqual(repr(List[S][T][int]), 'typing.List[int]')
|
||||||
|
self.assertEqual(repr(List[int]), 'typing.List[int]')
|
||||||
|
|
||||||
|
def test_new_repr_complex(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
TS = TypeVar('TS')
|
||||||
|
|
||||||
|
self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]')
|
||||||
|
self.assertEqual(repr(List[Tuple[T, TS]][int, T]),
|
||||||
|
'typing.List[typing.Tuple[int, ~T]]')
|
||||||
|
self.assertEqual(repr(List[Tuple[T, T]][List[int]]),
|
||||||
|
'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]')
|
||||||
|
|
||||||
|
def test_new_repr_bare(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
self.assertEqual(repr(Generic[T]), 'typing.Generic[~T]')
|
||||||
|
self.assertEqual(repr(typing._Protocol[T]), 'typing.Protocol[~T]')
|
||||||
|
class C(typing.Dict[Any, Any]): ...
|
||||||
|
# this line should just work
|
||||||
|
repr(C.__mro__)
|
||||||
|
|
||||||
def test_dict(self):
|
def test_dict(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
@ -625,6 +654,63 @@ class GenericTests(BaseTestCase):
|
||||||
class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
|
class MM2(collections_abc.MutableMapping, MutableMapping[str, str]):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def test_orig_bases(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
class C(typing.Dict[str, T]): ...
|
||||||
|
self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],))
|
||||||
|
|
||||||
|
def test_naive_runtime_checks(self):
|
||||||
|
def naive_dict_check(obj, tp):
|
||||||
|
# Check if a dictionary conforms to Dict type
|
||||||
|
if len(tp.__parameters__) > 0:
|
||||||
|
raise NotImplementedError
|
||||||
|
if tp.__args__:
|
||||||
|
KT, VT = tp.__args__
|
||||||
|
return all(isinstance(k, KT) and isinstance(v, VT)
|
||||||
|
for k, v in obj.items())
|
||||||
|
self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int]))
|
||||||
|
self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int]))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
naive_dict_check({1: 'x'}, typing.Dict[str, T])
|
||||||
|
|
||||||
|
def naive_generic_check(obj, tp):
|
||||||
|
# Check if an instance conforms to the generic class
|
||||||
|
if not hasattr(obj, '__orig_class__'):
|
||||||
|
raise NotImplementedError
|
||||||
|
return obj.__orig_class__ == tp
|
||||||
|
class Node(Generic[T]): ...
|
||||||
|
self.assertTrue(naive_generic_check(Node[int](), Node[int]))
|
||||||
|
self.assertFalse(naive_generic_check(Node[str](), Node[int]))
|
||||||
|
self.assertFalse(naive_generic_check(Node[str](), List))
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
naive_generic_check([1,2,3], Node[int])
|
||||||
|
|
||||||
|
def naive_list_base_check(obj, tp):
|
||||||
|
# Check if list conforms to a List subclass
|
||||||
|
return all(isinstance(x, tp.__orig_bases__[0].__args__[0])
|
||||||
|
for x in obj)
|
||||||
|
class C(List[int]): ...
|
||||||
|
self.assertTrue(naive_list_base_check([1, 2, 3], C))
|
||||||
|
self.assertFalse(naive_list_base_check(['a', 'b'], C))
|
||||||
|
|
||||||
|
def test_multi_subscr_base(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
U = TypeVar('U')
|
||||||
|
V = TypeVar('V')
|
||||||
|
class C(List[T][U][V]): ...
|
||||||
|
class D(C, List[T][U][V]): ...
|
||||||
|
self.assertEqual(C.__parameters__, (V,))
|
||||||
|
self.assertEqual(D.__parameters__, (V,))
|
||||||
|
self.assertEqual(C[int].__parameters__, ())
|
||||||
|
self.assertEqual(D[int].__parameters__, ())
|
||||||
|
self.assertEqual(C[int].__args__, (int,))
|
||||||
|
self.assertEqual(D[int].__args__, (int,))
|
||||||
|
self.assertEqual(C.__bases__, (List,))
|
||||||
|
self.assertEqual(D.__bases__, (C, List))
|
||||||
|
self.assertEqual(C.__orig_bases__, (List[T][U][V],))
|
||||||
|
self.assertEqual(D.__orig_bases__, (C, List[T][U][V]))
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
|
@ -662,12 +748,12 @@ class GenericTests(BaseTestCase):
|
||||||
if not PY32:
|
if not PY32:
|
||||||
self.assertEqual(C.__qualname__,
|
self.assertEqual(C.__qualname__,
|
||||||
'GenericTests.test_repr_2.<locals>.C')
|
'GenericTests.test_repr_2.<locals>.C')
|
||||||
self.assertEqual(repr(C).split('.')[-1], 'C<~T>')
|
self.assertEqual(repr(C).split('.')[-1], 'C')
|
||||||
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.assertEqual(X.__qualname__, 'C')
|
||||||
self.assertEqual(repr(X).split('.')[-1], 'C<~T>[int]')
|
self.assertEqual(repr(X).split('.')[-1], 'C[int]')
|
||||||
|
|
||||||
class Y(C[int]):
|
class Y(C[int]):
|
||||||
pass
|
pass
|
||||||
|
|
107
Lib/typing.py
107
Lib/typing.py
|
@ -292,8 +292,8 @@ class _TypeAlias(_TypingBase, _root=True):
|
||||||
if not issubclass(parameter, self.type_var.__constraints__):
|
if not issubclass(parameter, self.type_var.__constraints__):
|
||||||
raise TypeError("%s is not a valid substitution for %s." %
|
raise TypeError("%s is not a valid substitution for %s." %
|
||||||
(parameter, self.type_var))
|
(parameter, self.type_var))
|
||||||
if isinstance(parameter, TypeVar):
|
if isinstance(parameter, TypeVar) and parameter is not self.type_var:
|
||||||
raise TypeError("%s cannot be re-parameterized." % self.type_var)
|
raise TypeError("%s cannot be re-parameterized." % self)
|
||||||
return self.__class__(self.name, parameter,
|
return self.__class__(self.name, parameter,
|
||||||
self.impl_type, self.type_checker)
|
self.impl_type, self.type_checker)
|
||||||
|
|
||||||
|
@ -622,9 +622,12 @@ class _Union(_FinalTypingBase, _root=True):
|
||||||
_get_type_vars(self.__union_params__, tvars)
|
_get_type_vars(self.__union_params__, 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.__union_params__:
|
if self.__union_params__:
|
||||||
r += '[%s]' % (', '.join(_type_repr(t)
|
r += '[%s]' % (', '.join(_replace_arg(t, tvars, args)
|
||||||
for t in self.__union_params__))
|
for t in self.__union_params__))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@ -706,9 +709,12 @@ class _Tuple(_FinalTypingBase, _root=True):
|
||||||
return self.__class__(p, _root=True)
|
return self.__class__(p, _root=True)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
return self._subs_repr([], [])
|
||||||
|
|
||||||
|
def _subs_repr(self, tvars, args):
|
||||||
r = super().__repr__()
|
r = super().__repr__()
|
||||||
if self.__tuple_params__ is not None:
|
if self.__tuple_params__ is not None:
|
||||||
params = [_type_repr(p) for p in self.__tuple_params__]
|
params = [_replace_arg(p, tvars, args) for p in self.__tuple_params__]
|
||||||
if self.__tuple_use_ellipsis__:
|
if self.__tuple_use_ellipsis__:
|
||||||
params.append('...')
|
params.append('...')
|
||||||
if not params:
|
if not params:
|
||||||
|
@ -791,6 +797,8 @@ class _Callable(_FinalTypingBase, _root=True):
|
||||||
def _get_type_vars(self, tvars):
|
def _get_type_vars(self, tvars):
|
||||||
if self.__args__ and self.__args__ is not Ellipsis:
|
if self.__args__ and self.__args__ is not Ellipsis:
|
||||||
_get_type_vars(self.__args__, tvars)
|
_get_type_vars(self.__args__, tvars)
|
||||||
|
if self.__result__:
|
||||||
|
_get_type_vars([self.__result__], tvars)
|
||||||
|
|
||||||
def _eval_type(self, globalns, localns):
|
def _eval_type(self, globalns, localns):
|
||||||
if self.__args__ is None and self.__result__ is None:
|
if self.__args__ is None and self.__result__ is None:
|
||||||
|
@ -806,14 +814,17 @@ class _Callable(_FinalTypingBase, _root=True):
|
||||||
return self.__class__(args, result, _root=True)
|
return self.__class__(args, result, _root=True)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
return self._subs_repr([], [])
|
||||||
|
|
||||||
|
def _subs_repr(self, tvars, args):
|
||||||
r = super().__repr__()
|
r = super().__repr__()
|
||||||
if self.__args__ is not None or self.__result__ is not None:
|
if self.__args__ is not None or self.__result__ is not None:
|
||||||
if self.__args__ is Ellipsis:
|
if self.__args__ is Ellipsis:
|
||||||
args_r = '...'
|
args_r = '...'
|
||||||
else:
|
else:
|
||||||
args_r = '[%s]' % ', '.join(_type_repr(t)
|
args_r = '[%s]' % ', '.join(_replace_arg(t, tvars, args)
|
||||||
for t in self.__args__)
|
for t in self.__args__)
|
||||||
r += '[%s, %s]' % (args_r, _type_repr(self.__result__))
|
r += '[%s, %s]' % (args_r, _replace_arg(self.__result__, tvars, args))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def __getitem__(self, parameters):
|
def __getitem__(self, parameters):
|
||||||
|
@ -878,6 +889,16 @@ 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.__name__ == tvar.__name__:
|
||||||
|
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__.
|
||||||
|
|
||||||
|
@ -938,11 +959,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
"""Metaclass for generic types."""
|
"""Metaclass for generic types."""
|
||||||
|
|
||||||
def __new__(cls, name, bases, namespace,
|
def __new__(cls, name, bases, namespace,
|
||||||
tvars=None, args=None, origin=None, extra=None):
|
tvars=None, args=None, origin=None, extra=None, orig_bases=None):
|
||||||
if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
|
|
||||||
bases = (extra,) + bases
|
|
||||||
self = super().__new__(cls, name, bases, namespace, _root=True)
|
|
||||||
|
|
||||||
if tvars is not None:
|
if tvars is not None:
|
||||||
# Called from __getitem__() below.
|
# Called from __getitem__() below.
|
||||||
assert origin is not None
|
assert origin is not None
|
||||||
|
@ -983,12 +1000,25 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
", ".join(str(g) for g in gvars)))
|
", ".join(str(g) for g in gvars)))
|
||||||
tvars = gvars
|
tvars = gvars
|
||||||
|
|
||||||
|
initial_bases = bases
|
||||||
|
if extra is not None and type(extra) is abc.ABCMeta and extra not in bases:
|
||||||
|
bases = (extra,) + bases
|
||||||
|
bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases)
|
||||||
|
|
||||||
|
# remove bare Generic from bases if there are other generic bases
|
||||||
|
if any(isinstance(b, GenericMeta) and b is not Generic for b in bases):
|
||||||
|
bases = tuple(b for b in bases if b is not Generic)
|
||||||
|
self = super().__new__(cls, name, bases, namespace, _root=True)
|
||||||
|
|
||||||
self.__parameters__ = tvars
|
self.__parameters__ = tvars
|
||||||
self.__args__ = args
|
self.__args__ = args
|
||||||
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).
|
||||||
self.__next_in_mro__ = _next_in_mro(self)
|
self.__next_in_mro__ = _next_in_mro(self)
|
||||||
|
# Preserve base classes on subclassing (__bases__ are type erased now).
|
||||||
|
if orig_bases is None:
|
||||||
|
self.__orig_bases__ = initial_bases
|
||||||
|
|
||||||
# This allows unparameterized generic collections to be used
|
# This allows unparameterized generic collections to be used
|
||||||
# with issubclass() and isinstance() in the same way as their
|
# with issubclass() and isinstance() in the same way as their
|
||||||
|
@ -1006,17 +1036,29 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
_get_type_vars(self.__parameters__, tvars)
|
_get_type_vars(self.__parameters__, tvars)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if self.__origin__ is not None:
|
if self.__origin__ is None:
|
||||||
r = repr(self.__origin__)
|
return super().__repr__()
|
||||||
else:
|
return self._subs_repr([], [])
|
||||||
r = super().__repr__()
|
|
||||||
if self.__args__:
|
def _subs_repr(self, tvars, args):
|
||||||
r += '[%s]' % (
|
assert len(tvars) == len(args)
|
||||||
', '.join(_type_repr(p) for p in self.__args__))
|
# Construct the chain of __origin__'s.
|
||||||
if self.__parameters__:
|
current = self.__origin__
|
||||||
r += '<%s>' % (
|
orig_chain = []
|
||||||
', '.join(_type_repr(p) for p in self.__parameters__))
|
while current.__origin__ is not None:
|
||||||
return r
|
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)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, GenericMeta):
|
if not isinstance(other, GenericMeta):
|
||||||
|
@ -1049,11 +1091,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
raise TypeError(
|
raise TypeError(
|
||||||
"Parameters to Generic[...] must all be unique")
|
"Parameters to Generic[...] must all be unique")
|
||||||
tvars = params
|
tvars = params
|
||||||
args = None
|
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
|
||||||
args = None
|
args = params
|
||||||
elif self.__origin__ in (Generic, _Protocol):
|
elif self.__origin__ in (Generic, _Protocol):
|
||||||
# Can't subscript Generic[...] or _Protocol[...].
|
# Can't subscript Generic[...] or _Protocol[...].
|
||||||
raise TypeError("Cannot subscript already-subscripted %s" %
|
raise TypeError("Cannot subscript already-subscripted %s" %
|
||||||
|
@ -1071,12 +1113,13 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
tvars = _type_vars(params)
|
tvars = _type_vars(params)
|
||||||
args = params
|
args = params
|
||||||
return self.__class__(self.__name__,
|
return self.__class__(self.__name__,
|
||||||
(self,) + self.__bases__,
|
self.__bases__,
|
||||||
dict(self.__dict__),
|
dict(self.__dict__),
|
||||||
tvars=tvars,
|
tvars=tvars,
|
||||||
args=args,
|
args=args,
|
||||||
origin=self,
|
origin=self,
|
||||||
extra=self.__extra__)
|
extra=self.__extra__,
|
||||||
|
orig_bases=self.__orig_bases__)
|
||||||
|
|
||||||
def __instancecheck__(self, instance):
|
def __instancecheck__(self, instance):
|
||||||
# Since we extend ABC.__subclasscheck__ and
|
# Since we extend ABC.__subclasscheck__ and
|
||||||
|
@ -1120,6 +1163,10 @@ class Generic(metaclass=GenericMeta):
|
||||||
else:
|
else:
|
||||||
origin = _gorg(cls)
|
origin = _gorg(cls)
|
||||||
obj = cls.__next_in_mro__.__new__(origin)
|
obj = cls.__next_in_mro__.__new__(origin)
|
||||||
|
try:
|
||||||
|
obj.__orig_class__ = cls
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
obj.__init__(*args, **kwds)
|
obj.__init__(*args, **kwds)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -1163,12 +1210,15 @@ class _ClassVar(_FinalTypingBase, _root=True):
|
||||||
|
|
||||||
def _get_type_vars(self, tvars):
|
def _get_type_vars(self, tvars):
|
||||||
if self.__type__:
|
if self.__type__:
|
||||||
_get_type_vars(self.__type__, tvars)
|
_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(_type_repr(self.__type__))
|
r += '[{}]'.format(_replace_arg(self.__type__, tvars, args))
|
||||||
return r
|
return r
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
|
@ -1485,6 +1535,7 @@ class _ProtocolMeta(GenericMeta):
|
||||||
attr != '__next_in_mro__' and
|
attr != '__next_in_mro__' and
|
||||||
attr != '__parameters__' and
|
attr != '__parameters__' and
|
||||||
attr != '__origin__' and
|
attr != '__origin__' and
|
||||||
|
attr != '__orig_bases__' and
|
||||||
attr != '__extra__' and
|
attr != '__extra__' and
|
||||||
attr != '__module__'):
|
attr != '__module__'):
|
||||||
attrs.add(attr)
|
attrs.add(attr)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue