Issue #28556: merge 5 more typing changes from upstream (#340, #344, #348, #349, #350)

This commit is contained in:
Guido van Rossum 2017-01-17 20:43:28 -08:00
parent e741e4f88e
commit 83ec302e52
2 changed files with 76 additions and 12 deletions

View File

@ -12,7 +12,7 @@ from typing import T, KT, VT # Not in __all__.
from typing import Union, Optional
from typing import Tuple, List, MutableMapping
from typing import Callable
from typing import Generic, ClassVar
from typing import Generic, ClassVar, GenericMeta
from typing import cast
from typing import get_type_hints
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
import abc
import typing
import weakref
try:
import collections.abc as collections_abc
except ImportError:
@ -281,6 +282,15 @@ class UnionTests(BaseTestCase):
self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int])
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):
o = Optional[int]
u = Union[int, None]
@ -718,6 +728,12 @@ class GenericTests(BaseTestCase):
self.assertEqual(C.__orig_bases__, (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):
T = TypeVar('T')
U = TypeVar('U')
@ -896,6 +912,14 @@ class GenericTests(BaseTestCase):
self.assertEqual(t, copy(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):
T = TypeVar('T')
class C(Generic[T]):
@ -1918,7 +1942,9 @@ class NamedTupleTests(BaseTestCase):
self.assertEqual(jim.id, 1)
self.assertEqual(Emp.__name__, 'Emp')
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')
def test_annotation_usage(self):
@ -1929,7 +1955,9 @@ class NamedTupleTests(BaseTestCase):
self.assertEqual(tim.cool, 9000)
self.assertEqual(CoolEmployee.__name__, 'CoolEmployee')
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')
def test_namedtuple_keyword_usage(self):
@ -1939,7 +1967,8 @@ class NamedTupleTests(BaseTestCase):
self.assertEqual(nick.name, 'Nick')
self.assertEqual(LocalEmployee.__name__, 'LocalEmployee')
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):
NamedTuple('Name', [('x', int)], y=str)
with self.assertRaises(TypeError):

View File

@ -27,6 +27,8 @@ __all__ = [
# ABCs (from collections.abc).
'AbstractSet', # collections.abc.Set.
'GenericMeta', # subclass of abc.ABCMeta and a metaclass
# for 'Generic' and ABCs below.
'ByteString',
'Container',
'Hashable',
@ -145,7 +147,7 @@ class TypingMeta(type):
class _TypingBase(metaclass=TypingMeta, _root=True):
"""Internal indicator of special typing constructs."""
__slots__ = ()
__slots__ = ('__weakref__',)
def __init__(self, *args, **kwds):
pass
@ -514,7 +516,7 @@ def _replace_arg(arg, tvars, args):
if tvars is None:
tvars = []
if hasattr(arg, '_subs_tree'):
if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)):
return arg._subs_tree(tvars, args)
if isinstance(arg, TypeVar):
for i, tvar in enumerate(tvars):
@ -523,6 +525,16 @@ def _replace_arg(arg, tvars, args):
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):
"""An internal helper function: calculate substitution tree
for generic cls after replacing its type parameters with
@ -757,9 +769,12 @@ class _Union(_FinalTypingBase, _root=True):
return (Union,) + tree_args
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.__tree_hash__ == other.__tree_hash__
else:
return self is other
def __hash__(self):
return self.__tree_hash__
@ -883,10 +898,26 @@ def _no_slots_copy(dct):
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,
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:
# Called from __getitem__() below.
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"
types = [(n, _type_check(t, msg)) 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:
nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
@ -1941,8 +1974,10 @@ class NamedTuple(metaclass=NamedTupleMeta):
Employee = collections.namedtuple('Employee', ['name', 'id'])
The resulting class has one extra attribute: _field_types,
giving a dict mapping field names to types. (The field names
The resulting class has extra __annotations__ and _field_types
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
API.) Alternative equivalent keyword syntax is also accepted::