This commit is contained in:
commit
52f29591bc
|
@ -12,7 +12,7 @@ from typing import T, KT, VT # Not in __all__.
|
||||||
from typing import Union, Optional
|
from typing import Union, Optional
|
||||||
from typing import Tuple, List, MutableMapping
|
from typing import Tuple, List, MutableMapping
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import Generic, ClassVar
|
from typing import Generic, ClassVar, GenericMeta
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing import get_type_hints
|
from typing import get_type_hints
|
||||||
from typing import no_type_check, no_type_check_decorator
|
from typing import no_type_check, no_type_check_decorator
|
||||||
|
@ -23,6 +23,7 @@ from typing import IO, TextIO, BinaryIO
|
||||||
from typing import Pattern, Match
|
from typing import Pattern, Match
|
||||||
import abc
|
import abc
|
||||||
import typing
|
import typing
|
||||||
|
import weakref
|
||||||
try:
|
try:
|
||||||
import collections.abc as collections_abc
|
import collections.abc as collections_abc
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
@ -281,6 +282,15 @@ class UnionTests(BaseTestCase):
|
||||||
self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
|
self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
|
||||||
self.assertTrue(Union[str, typing.Iterable] == typing.Iterable)
|
self.assertTrue(Union[str, typing.Iterable] == typing.Iterable)
|
||||||
|
|
||||||
|
def test_union_compare_other(self):
|
||||||
|
self.assertNotEqual(Union, object)
|
||||||
|
self.assertNotEqual(Union, Any)
|
||||||
|
self.assertNotEqual(ClassVar, Union)
|
||||||
|
self.assertNotEqual(Optional, Union)
|
||||||
|
self.assertNotEqual([None], Optional)
|
||||||
|
self.assertNotEqual(Optional, typing.Mapping)
|
||||||
|
self.assertNotEqual(Optional[typing.MutableMapping], Union)
|
||||||
|
|
||||||
def test_optional(self):
|
def test_optional(self):
|
||||||
o = Optional[int]
|
o = Optional[int]
|
||||||
u = Union[int, None]
|
u = Union[int, None]
|
||||||
|
@ -718,6 +728,12 @@ 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_subscript_meta(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
self.assertEqual(Type[GenericMeta], Type[GenericMeta])
|
||||||
|
self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int])
|
||||||
|
self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta))
|
||||||
|
|
||||||
def test_extended_generic_rules_eq(self):
|
def test_extended_generic_rules_eq(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
U = TypeVar('U')
|
U = TypeVar('U')
|
||||||
|
@ -896,6 +912,14 @@ class GenericTests(BaseTestCase):
|
||||||
self.assertEqual(t, copy(t))
|
self.assertEqual(t, copy(t))
|
||||||
self.assertEqual(t, deepcopy(t))
|
self.assertEqual(t, deepcopy(t))
|
||||||
|
|
||||||
|
def test_weakref_all(self):
|
||||||
|
T = TypeVar('T')
|
||||||
|
things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any],
|
||||||
|
Optional[List[int]], typing.Mapping[int, str],
|
||||||
|
typing.re.Match[bytes], typing.Iterable['whatever']]
|
||||||
|
for t in things:
|
||||||
|
self.assertEqual(weakref.ref(t)(), t)
|
||||||
|
|
||||||
def test_parameterized_slots(self):
|
def test_parameterized_slots(self):
|
||||||
T = TypeVar('T')
|
T = TypeVar('T')
|
||||||
class C(Generic[T]):
|
class C(Generic[T]):
|
||||||
|
@ -1918,7 +1942,9 @@ class NamedTupleTests(BaseTestCase):
|
||||||
self.assertEqual(jim.id, 1)
|
self.assertEqual(jim.id, 1)
|
||||||
self.assertEqual(Emp.__name__, 'Emp')
|
self.assertEqual(Emp.__name__, 'Emp')
|
||||||
self.assertEqual(Emp._fields, ('name', 'id'))
|
self.assertEqual(Emp._fields, ('name', 'id'))
|
||||||
self.assertEqual(Emp._field_types, dict(name=str, id=int))
|
self.assertEqual(Emp.__annotations__,
|
||||||
|
collections.OrderedDict([('name', str), ('id', int)]))
|
||||||
|
self.assertIs(Emp._field_types, Emp.__annotations__)
|
||||||
|
|
||||||
@skipUnless(PY36, 'Python 3.6 required')
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
def test_annotation_usage(self):
|
def test_annotation_usage(self):
|
||||||
|
@ -1929,7 +1955,9 @@ class NamedTupleTests(BaseTestCase):
|
||||||
self.assertEqual(tim.cool, 9000)
|
self.assertEqual(tim.cool, 9000)
|
||||||
self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
|
self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
|
||||||
self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
|
self.assertEqual(CoolEmployee._fields, ('name', 'cool'))
|
||||||
self.assertEqual(CoolEmployee._field_types, dict(name=str, cool=int))
|
self.assertEqual(CoolEmployee.__annotations__,
|
||||||
|
collections.OrderedDict(name=str, cool=int))
|
||||||
|
self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__)
|
||||||
|
|
||||||
@skipUnless(PY36, 'Python 3.6 required')
|
@skipUnless(PY36, 'Python 3.6 required')
|
||||||
def test_namedtuple_keyword_usage(self):
|
def test_namedtuple_keyword_usage(self):
|
||||||
|
@ -1939,7 +1967,8 @@ class NamedTupleTests(BaseTestCase):
|
||||||
self.assertEqual(nick.name, 'Nick')
|
self.assertEqual(nick.name, 'Nick')
|
||||||
self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
|
self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
|
||||||
self.assertEqual(LocalEmployee._fields, ('name', 'age'))
|
self.assertEqual(LocalEmployee._fields, ('name', 'age'))
|
||||||
self.assertEqual(LocalEmployee._field_types, dict(name=str, age=int))
|
self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int))
|
||||||
|
self.assertIs(LocalEmployee._field_types, LocalEmployee.__annotations__)
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
NamedTuple('Name', [('x', int)], y=str)
|
NamedTuple('Name', [('x', int)], y=str)
|
||||||
with self.assertRaises(TypeError):
|
with self.assertRaises(TypeError):
|
||||||
|
|
|
@ -27,6 +27,8 @@ __all__ = [
|
||||||
|
|
||||||
# ABCs (from collections.abc).
|
# ABCs (from collections.abc).
|
||||||
'AbstractSet', # collections.abc.Set.
|
'AbstractSet', # collections.abc.Set.
|
||||||
|
'GenericMeta', # subclass of abc.ABCMeta and a metaclass
|
||||||
|
# for 'Generic' and ABCs below.
|
||||||
'ByteString',
|
'ByteString',
|
||||||
'Container',
|
'Container',
|
||||||
'Hashable',
|
'Hashable',
|
||||||
|
@ -145,7 +147,7 @@ class TypingMeta(type):
|
||||||
class _TypingBase(metaclass=TypingMeta, _root=True):
|
class _TypingBase(metaclass=TypingMeta, _root=True):
|
||||||
"""Internal indicator of special typing constructs."""
|
"""Internal indicator of special typing constructs."""
|
||||||
|
|
||||||
__slots__ = ()
|
__slots__ = ('__weakref__',)
|
||||||
|
|
||||||
def __init__(self, *args, **kwds):
|
def __init__(self, *args, **kwds):
|
||||||
pass
|
pass
|
||||||
|
@ -514,7 +516,7 @@ def _replace_arg(arg, tvars, args):
|
||||||
|
|
||||||
if tvars is None:
|
if tvars is None:
|
||||||
tvars = []
|
tvars = []
|
||||||
if hasattr(arg, '_subs_tree'):
|
if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)):
|
||||||
return arg._subs_tree(tvars, args)
|
return arg._subs_tree(tvars, args)
|
||||||
if isinstance(arg, TypeVar):
|
if isinstance(arg, TypeVar):
|
||||||
for i, tvar in enumerate(tvars):
|
for i, tvar in enumerate(tvars):
|
||||||
|
@ -523,6 +525,16 @@ def _replace_arg(arg, tvars, args):
|
||||||
return arg
|
return arg
|
||||||
|
|
||||||
|
|
||||||
|
# Special typing constructs Union, Optional, Generic, Callable and Tuple
|
||||||
|
# use three special attributes for internal bookkeeping of generic types:
|
||||||
|
# * __parameters__ is a tuple of unique free type parameters of a generic
|
||||||
|
# type, for example, Dict[T, T].__parameters__ == (T,);
|
||||||
|
# * __origin__ keeps a reference to a type that was subscripted,
|
||||||
|
# e.g., Union[T, int].__origin__ == Union;
|
||||||
|
# * __args__ is a tuple of all arguments used in subscripting,
|
||||||
|
# e.g., Dict[T, int].__args__ == (T, int).
|
||||||
|
|
||||||
|
|
||||||
def _subs_tree(cls, tvars=None, args=None):
|
def _subs_tree(cls, tvars=None, args=None):
|
||||||
"""An internal helper function: calculate substitution tree
|
"""An internal helper function: calculate substitution tree
|
||||||
for generic cls after replacing its type parameters with
|
for generic cls after replacing its type parameters with
|
||||||
|
@ -757,9 +769,12 @@ class _Union(_FinalTypingBase, _root=True):
|
||||||
return (Union,) + tree_args
|
return (Union,) + tree_args
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
if not isinstance(other, _Union):
|
if isinstance(other, _Union):
|
||||||
|
return self.__tree_hash__ == other.__tree_hash__
|
||||||
|
elif self is not Union:
|
||||||
return self._subs_tree() == other
|
return self._subs_tree() == other
|
||||||
return self.__tree_hash__ == other.__tree_hash__
|
else:
|
||||||
|
return self is other
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return self.__tree_hash__
|
return self.__tree_hash__
|
||||||
|
@ -883,10 +898,26 @@ def _no_slots_copy(dct):
|
||||||
|
|
||||||
|
|
||||||
class GenericMeta(TypingMeta, abc.ABCMeta):
|
class GenericMeta(TypingMeta, abc.ABCMeta):
|
||||||
"""Metaclass for generic types."""
|
"""Metaclass for generic types.
|
||||||
|
|
||||||
|
This is a metaclass for typing.Generic and generic ABCs defined in
|
||||||
|
typing module. User defined subclasses of GenericMeta can override
|
||||||
|
__new__ and invoke super().__new__. Note that GenericMeta.__new__
|
||||||
|
has strict rules on what is allowed in its bases argument:
|
||||||
|
* plain Generic is disallowed in bases;
|
||||||
|
* Generic[...] should appear in bases at most once;
|
||||||
|
* if Generic[...] is present, then it should list all type variables
|
||||||
|
that appear in other bases.
|
||||||
|
In addition, type of all generic bases is erased, e.g., C[int] is
|
||||||
|
stripped to plain C.
|
||||||
|
"""
|
||||||
|
|
||||||
def __new__(cls, name, bases, namespace,
|
def __new__(cls, name, bases, namespace,
|
||||||
tvars=None, args=None, origin=None, extra=None, orig_bases=None):
|
tvars=None, args=None, origin=None, extra=None, orig_bases=None):
|
||||||
|
"""Create a new generic class. GenericMeta.__new__ accepts
|
||||||
|
keyword arguments that are used for internal bookkeeping, therefore
|
||||||
|
an override should pass unused keyword arguments to super().
|
||||||
|
"""
|
||||||
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
|
||||||
|
@ -1906,7 +1937,9 @@ def _make_nmtuple(name, types):
|
||||||
msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
|
msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type"
|
||||||
types = [(n, _type_check(t, msg)) for n, t in types]
|
types = [(n, _type_check(t, msg)) for n, t in types]
|
||||||
nm_tpl = collections.namedtuple(name, [n for n, t in types])
|
nm_tpl = collections.namedtuple(name, [n for n, t in types])
|
||||||
nm_tpl._field_types = dict(types)
|
# Prior to PEP 526, only _field_types attribute was assigned.
|
||||||
|
# Now, both __annotations__ and _field_types are used to maintain compatibility.
|
||||||
|
nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types)
|
||||||
try:
|
try:
|
||||||
nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
|
nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
|
@ -1941,8 +1974,10 @@ class NamedTuple(metaclass=NamedTupleMeta):
|
||||||
|
|
||||||
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
Employee = collections.namedtuple('Employee', ['name', 'id'])
|
||||||
|
|
||||||
The resulting class has one extra attribute: _field_types,
|
The resulting class has extra __annotations__ and _field_types
|
||||||
giving a dict mapping field names to types. (The field names
|
attributes, giving an ordered dict mapping field names to types.
|
||||||
|
__annotations__ should be preferred, while _field_types
|
||||||
|
is kept to maintain pre PEP 526 compatibility. (The field names
|
||||||
are in the _fields attribute, which is part of the namedtuple
|
are in the _fields attribute, which is part of the namedtuple
|
||||||
API.) Alternative equivalent keyword syntax is also accepted::
|
API.) Alternative equivalent keyword syntax is also accepted::
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue