Skip to content

Commit

Permalink
Issue python#28556: merge 5 more typing changes from upstream (python…
Browse files Browse the repository at this point in the history
  • Loading branch information
gvanrossum committed Jan 18, 2017
2 parents 8128d5a + 043a8bc commit 52f2959
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 12 deletions.
37 changes: 33 additions & 4 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
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
Expand All @@ -23,6 +23,7 @@
from typing import Pattern, Match
import abc
import typing
import weakref
try:
import collections.abc as collections_abc
except ImportError:
Expand Down Expand Up @@ -281,6 +282,15 @@ def test_union_generalization(self):
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]
Expand Down Expand Up @@ -718,6 +728,12 @@ class D(C, List[T][U][V]): ...
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')
Expand Down Expand Up @@ -896,6 +912,14 @@ class Node(Generic[T]): ...
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]):
Expand Down Expand Up @@ -1918,7 +1942,9 @@ def test_basics(self):
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):
Expand All @@ -1929,7 +1955,9 @@ def test_annotation_usage(self):
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):
Expand All @@ -1939,7 +1967,8 @@ def test_namedtuple_keyword_usage(self):
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):
Expand Down
51 changes: 43 additions & 8 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

# ABCs (from collections.abc).
'AbstractSet', # collections.abc.Set.
'GenericMeta', # subclass of abc.ABCMeta and a metaclass
# for 'Generic' and ABCs below.
'ByteString',
'Container',
'Hashable',
Expand Down Expand Up @@ -145,7 +147,7 @@ def __repr__(self):
class _TypingBase(metaclass=TypingMeta, _root=True):
"""Internal indicator of special typing constructs."""

__slots__ = ()
__slots__ = ('__weakref__',)

def __init__(self, *args, **kwds):
pass
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -757,9 +769,12 @@ def _subs_tree(self, tvars=None, args=None):
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__
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -1941,8 +1974,10 @@ class Employee(NamedTuple):
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::
Expand Down

0 comments on commit 52f2959

Please sign in to comment.