summaryrefslogtreecommitdiffstats
path: root/Lib/typing.py
diff options
context:
space:
mode:
authorIvan Levkivskyi <levkivskyi@gmail.com>2017-02-13 21:50:14 (GMT)
committerMariatta <Mariatta@users.noreply.github.com>2017-02-13 21:50:14 (GMT)
commitb692dc8475a032740576129d0990ddc3edccab2b (patch)
treebca0dc4276619ea45012e3dd1fb4159056d9fefc /Lib/typing.py
parent3cdbd68ce8230cff1afb67472b96fbfa7f047e32 (diff)
downloadcpython-b692dc8475a032740576129d0990ddc3edccab2b.zip
cpython-b692dc8475a032740576129d0990ddc3edccab2b.tar.gz
cpython-b692dc8475a032740576129d0990ddc3edccab2b.tar.bz2
bpo-28556: Various updates to typing (#28)
various updates from upstream python/typing repo: - Added typing.Counter and typing.ChainMap generics - More flexible typing.NamedTuple - Improved generic ABC caching - More tests - Bugfixes - Other updates * Add Misc/NEWS entry * Add issue number
Diffstat (limited to 'Lib/typing.py')
-rw-r--r--Lib/typing.py138
1 files changed, 105 insertions, 33 deletions
diff --git a/Lib/typing.py b/Lib/typing.py
index c9e3417..efe358f 100644
--- a/Lib/typing.py
+++ b/Lib/typing.py
@@ -10,6 +10,12 @@ try:
import collections.abc as collections_abc
except ImportError:
import collections as collections_abc # Fallback for PY3.2.
+try:
+ from types import SlotWrapperType, MethodWrapperType, MethodDescriptorType
+except ImportError:
+ SlotWrapperType = type(object.__init__)
+ MethodWrapperType = type(object().__str__)
+ MethodDescriptorType = type(str.join)
# Please keep __all__ alphabetized within each category.
@@ -62,6 +68,7 @@ __all__ = [
'SupportsRound',
# Concrete collection types.
+ 'Counter',
'Deque',
'Dict',
'DefaultDict',
@@ -849,19 +856,6 @@ def _next_in_mro(cls):
return next_in_mro
-def _valid_for_check(cls):
- """An internal helper to prohibit isinstance([1], List[str]) etc."""
- if cls is Generic:
- raise TypeError("Class %r cannot be used with class "
- "or instance checks" % cls)
- if (
- cls.__origin__ is not None and
- sys._getframe(3).f_globals['__name__'] not in ['abc', 'functools']
- ):
- raise TypeError("Parameterized generics cannot be used with class "
- "or instance checks")
-
-
def _make_subclasshook(cls):
"""Construct a __subclasshook__ callable that incorporates
the associated __extra__ class in subclass checks performed
@@ -872,7 +866,6 @@ def _make_subclasshook(cls):
# Registered classes need not be checked here because
# cls and its extra share the same _abc_registry.
def __extrahook__(subclass):
- _valid_for_check(cls)
res = cls.__extra__.__subclasshook__(subclass)
if res is not NotImplemented:
return res
@@ -887,7 +880,6 @@ def _make_subclasshook(cls):
else:
# For non-ABC extras we'll just call issubclass().
def __extrahook__(subclass):
- _valid_for_check(cls)
if cls.__extra__ and issubclass(subclass, cls.__extra__):
return True
return NotImplemented
@@ -974,6 +966,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
# 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)
+ namespace.update({'__origin__': origin, '__extra__': extra})
self = super().__new__(cls, name, bases, namespace, _root=True)
self.__parameters__ = tvars
@@ -982,8 +975,6 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
self.__args__ = tuple(... if a is _TypingEllipsis else
() if a is _TypingEmpty else
a for a in args) if args else None
- self.__origin__ = origin
- self.__extra__ = extra
# Speed hack (https://github.com/python/typing/issues/196).
self.__next_in_mro__ = _next_in_mro(self)
# Preserve base classes on subclassing (__bases__ are type erased now).
@@ -994,20 +985,56 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
# with issubclass() and isinstance() in the same way as their
# collections.abc counterparts (e.g., isinstance([], Iterable)).
if (
- # allow overriding
'__subclasshook__' not in namespace and extra or
- hasattr(self.__subclasshook__, '__name__') and
- self.__subclasshook__.__name__ == '__extrahook__'
+ # allow overriding
+ getattr(self.__subclasshook__, '__name__', '') == '__extrahook__'
):
self.__subclasshook__ = _make_subclasshook(self)
if isinstance(extra, abc.ABCMeta):
self._abc_registry = extra._abc_registry
+ self._abc_cache = extra._abc_cache
+ elif origin is not None:
+ self._abc_registry = origin._abc_registry
+ self._abc_cache = origin._abc_cache
if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2.
self.__qualname__ = origin.__qualname__
- self.__tree_hash__ = hash(self._subs_tree()) if origin else hash((self.__name__,))
+ self.__tree_hash__ = (hash(self._subs_tree()) if origin else
+ super(GenericMeta, self).__hash__())
return self
+ # _abc_negative_cache and _abc_negative_cache_version
+ # realised as descriptors, since GenClass[t1, t2, ...] always
+ # share subclass info with GenClass.
+ # This is an important memory optimization.
+ @property
+ def _abc_negative_cache(self):
+ if isinstance(self.__extra__, abc.ABCMeta):
+ return self.__extra__._abc_negative_cache
+ return _gorg(self)._abc_generic_negative_cache
+
+ @_abc_negative_cache.setter
+ def _abc_negative_cache(self, value):
+ if self.__origin__ is None:
+ if isinstance(self.__extra__, abc.ABCMeta):
+ self.__extra__._abc_negative_cache = value
+ else:
+ self._abc_generic_negative_cache = value
+
+ @property
+ def _abc_negative_cache_version(self):
+ if isinstance(self.__extra__, abc.ABCMeta):
+ return self.__extra__._abc_negative_cache_version
+ return _gorg(self)._abc_generic_negative_cache_version
+
+ @_abc_negative_cache_version.setter
+ def _abc_negative_cache_version(self, value):
+ if self.__origin__ is None:
+ if isinstance(self.__extra__, abc.ABCMeta):
+ self.__extra__._abc_negative_cache_version = value
+ else:
+ self._abc_generic_negative_cache_version = value
+
def _get_type_vars(self, tvars):
if self.__origin__ and self.__parameters__:
_get_type_vars(self.__parameters__, tvars)
@@ -1095,8 +1122,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
_check_generic(self, params)
tvars = _type_vars(params)
args = params
+
+ prepend = (self,) if self.__origin__ is None else ()
return self.__class__(self.__name__,
- self.__bases__,
+ prepend + self.__bases__,
_no_slots_copy(self.__dict__),
tvars=tvars,
args=args,
@@ -1104,6 +1133,17 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
extra=self.__extra__,
orig_bases=self.__orig_bases__)
+ def __subclasscheck__(self, cls):
+ if self.__origin__ is not None:
+ if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
+ raise TypeError("Parameterized generics cannot be used with class "
+ "or instance checks")
+ return False
+ if self is Generic:
+ raise TypeError("Class %r cannot be used with class "
+ "or instance checks" % self)
+ return super().__subclasscheck__(cls)
+
def __instancecheck__(self, instance):
# Since we extend ABC.__subclasscheck__ and
# ABC.__instancecheck__ inlines the cache checking done by the
@@ -1398,6 +1438,11 @@ def _get_defaults(func):
return res
+_allowed_types = (types.FunctionType, types.BuiltinFunctionType,
+ types.MethodType, types.ModuleType,
+ SlotWrapperType, MethodWrapperType, MethodDescriptorType)
+
+
def get_type_hints(obj, globalns=None, localns=None):
"""Return type hints for an object.
@@ -1452,12 +1497,7 @@ def get_type_hints(obj, globalns=None, localns=None):
hints = getattr(obj, '__annotations__', None)
if hints is None:
# Return empty annotations for something that _could_ have them.
- if (
- isinstance(obj, types.FunctionType) or
- isinstance(obj, types.BuiltinFunctionType) or
- isinstance(obj, types.MethodType) or
- isinstance(obj, types.ModuleType)
- ):
+ if isinstance(obj, _allowed_types):
return {}
else:
raise TypeError('{!r} is not a module, class, method, '
@@ -1824,8 +1864,7 @@ class Deque(collections.deque, MutableSequence[T], extra=collections.deque):
def __new__(cls, *args, **kwds):
if _geqv(cls, Deque):
- raise TypeError("Type Deque cannot be instantiated; "
- "use deque() instead")
+ return collections.deque(*args, **kwds)
return _generic_new(collections.deque, cls, *args, **kwds)
@@ -1894,11 +1933,35 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
def __new__(cls, *args, **kwds):
if _geqv(cls, DefaultDict):
- raise TypeError("Type DefaultDict cannot be instantiated; "
- "use collections.defaultdict() instead")
+ return collections.defaultdict(*args, **kwds)
return _generic_new(collections.defaultdict, cls, *args, **kwds)
+class Counter(collections.Counter, Dict[T, int], extra=collections.Counter):
+
+ __slots__ = ()
+
+ def __new__(cls, *args, **kwds):
+ if _geqv(cls, Counter):
+ return collections.Counter(*args, **kwds)
+ return _generic_new(collections.Counter, cls, *args, **kwds)
+
+
+if hasattr(collections, 'ChainMap'):
+ # ChainMap only exists in 3.3+
+ __all__.append('ChainMap')
+
+ class ChainMap(collections.ChainMap, MutableMapping[KT, VT],
+ extra=collections.ChainMap):
+
+ __slots__ = ()
+
+ def __new__(cls, *args, **kwds):
+ if _geqv(cls, ChainMap):
+ return collections.ChainMap(*args, **kwds)
+ return _generic_new(collections.ChainMap, cls, *args, **kwds)
+
+
# Determine what base class to use for Generator.
if hasattr(collections_abc, 'Generator'):
# Sufficiently recent versions of 3.5 have a Generator ABC.
@@ -1975,6 +2038,13 @@ def _make_nmtuple(name, types):
_PY36 = sys.version_info[:2] >= (3, 6)
+# attributes prohibited to set in NamedTuple class syntax
+_prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__',
+ '_fields', '_field_defaults', '_field_types',
+ '_make', '_replace', '_asdict')
+
+_special = ('__module__', '__name__', '__qualname__', '__annotations__')
+
class NamedTupleMeta(type):
@@ -2002,7 +2072,9 @@ class NamedTupleMeta(type):
nm_tpl._field_defaults = defaults_dict
# update from user namespace without overriding special namedtuple attributes
for key in ns:
- if not hasattr(nm_tpl, key):
+ if key in _prohibited:
+ raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
+ elif key not in _special and key not in nm_tpl._fields:
setattr(nm_tpl, key, ns[key])
return nm_tpl