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

This commit is contained in:
Guido van Rossum 2017-01-17 20:43:31 -08:00
commit 52f29591bc
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 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):

View File

@ -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::