Fix #27014 -- infinite recursion using typing.py.

This commit is contained in:
Guido van Rossum 2016-05-18 08:35:00 -07:00
parent e5ea1abf91
commit 1cea70f08c
3 changed files with 33 additions and 14 deletions

View File

@ -1,4 +1,5 @@
import contextlib import contextlib
import collections
import pickle import pickle
import re import re
import sys import sys
@ -1218,13 +1219,17 @@ class CollectionsAbcTests(BaseTestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
typing.List[int]() typing.List[int]()
def test_list_subclass_instantiation(self): def test_list_subclass(self):
class MyList(typing.List[int]): class MyList(typing.List[int]):
pass pass
a = MyList() a = MyList()
self.assertIsInstance(a, MyList) self.assertIsInstance(a, MyList)
self.assertIsInstance(a, typing.Sequence)
self.assertIsSubclass(MyList, list)
self.assertNotIsSubclass(list, MyList)
def test_no_dict_instantiation(self): def test_no_dict_instantiation(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
@ -1234,13 +1239,17 @@ class CollectionsAbcTests(BaseTestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
typing.Dict[str, int]() typing.Dict[str, int]()
def test_dict_subclass_instantiation(self): def test_dict_subclass(self):
class MyDict(typing.Dict[str, int]): class MyDict(typing.Dict[str, int]):
pass pass
d = MyDict() d = MyDict()
self.assertIsInstance(d, MyDict) self.assertIsInstance(d, MyDict)
self.assertIsInstance(d, typing.MutableMapping)
self.assertIsSubclass(MyDict, dict)
self.assertNotIsSubclass(dict, MyDict)
def test_no_defaultdict_instantiation(self): def test_no_defaultdict_instantiation(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
@ -1250,7 +1259,7 @@ class CollectionsAbcTests(BaseTestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
typing.DefaultDict[str, int]() typing.DefaultDict[str, int]()
def test_defaultdict_subclass_instantiation(self): def test_defaultdict_subclass(self):
class MyDefDict(typing.DefaultDict[str, int]): class MyDefDict(typing.DefaultDict[str, int]):
pass pass
@ -1258,6 +1267,9 @@ class CollectionsAbcTests(BaseTestCase):
dd = MyDefDict() dd = MyDefDict()
self.assertIsInstance(dd, MyDefDict) self.assertIsInstance(dd, MyDefDict)
self.assertIsSubclass(MyDefDict, collections.defaultdict)
self.assertNotIsSubclass(collections.defaultdict, MyDefDict)
def test_no_set_instantiation(self): def test_no_set_instantiation(self):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
typing.Set() typing.Set()
@ -1338,6 +1350,13 @@ class CollectionsAbcTests(BaseTestCase):
self.assertEqual(len(MMB[str, str]()), 0) self.assertEqual(len(MMB[str, str]()), 0)
self.assertEqual(len(MMB[KT, VT]()), 0) self.assertEqual(len(MMB[KT, VT]()), 0)
self.assertNotIsSubclass(dict, MMA)
self.assertNotIsSubclass(dict, MMB)
self.assertIsSubclass(MMA, typing.Mapping)
self.assertIsSubclass(MMB, typing.Mapping)
self.assertIsSubclass(MMC, typing.Mapping)
class OtherABCTests(BaseTestCase): class OtherABCTests(BaseTestCase):

View File

@ -894,8 +894,6 @@ def _next_in_mro(cls):
class GenericMeta(TypingMeta, abc.ABCMeta): class GenericMeta(TypingMeta, abc.ABCMeta):
"""Metaclass for generic types.""" """Metaclass for generic types."""
__extra__ = None
def __new__(cls, name, bases, namespace, def __new__(cls, name, bases, namespace,
tvars=None, args=None, origin=None, extra=None): tvars=None, args=None, origin=None, extra=None):
self = super().__new__(cls, name, bases, namespace, _root=True) self = super().__new__(cls, name, bases, namespace, _root=True)
@ -943,10 +941,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta):
self.__parameters__ = tvars self.__parameters__ = tvars
self.__args__ = args self.__args__ = args
self.__origin__ = origin self.__origin__ = origin
if extra is not None:
self.__extra__ = extra self.__extra__ = extra
# Else __extra__ is inherited, eventually from the
# (meta-)class default above.
# Speed hack (https://github.com/python/typing/issues/196). # Speed hack (https://github.com/python/typing/issues/196).
self.__next_in_mro__ = _next_in_mro(self) self.__next_in_mro__ = _next_in_mro(self)
return self return self
@ -1307,6 +1302,7 @@ class _ProtocolMeta(GenericMeta):
attr != '__next_in_mro__' and attr != '__next_in_mro__' and
attr != '__parameters__' and attr != '__parameters__' and
attr != '__origin__' and attr != '__origin__' and
attr != '__extra__' and
attr != '__module__'): attr != '__module__'):
attrs.add(attr) attrs.add(attr)
@ -1470,7 +1466,7 @@ class ByteString(Sequence[int], extra=collections_abc.ByteString):
ByteString.register(type(memoryview(b''))) ByteString.register(type(memoryview(b'')))
class List(list, MutableSequence[T]): class List(list, MutableSequence[T], extra=list):
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
if _geqv(cls, List): if _geqv(cls, List):
@ -1479,7 +1475,7 @@ class List(list, MutableSequence[T]):
return list.__new__(cls, *args, **kwds) return list.__new__(cls, *args, **kwds)
class Set(set, MutableSet[T]): class Set(set, MutableSet[T], extra=set):
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
if _geqv(cls, Set): if _geqv(cls, Set):
@ -1502,7 +1498,8 @@ class _FrozenSetMeta(GenericMeta):
return super().__subclasscheck__(cls) return super().__subclasscheck__(cls)
class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta): class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta,
extra=frozenset):
__slots__ = () __slots__ = ()
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
@ -1538,7 +1535,7 @@ if hasattr(contextlib, 'AbstractContextManager'):
__all__.append('ContextManager') __all__.append('ContextManager')
class Dict(dict, MutableMapping[KT, VT]): class Dict(dict, MutableMapping[KT, VT], extra=dict):
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
if _geqv(cls, Dict): if _geqv(cls, Dict):
@ -1546,7 +1543,8 @@ class Dict(dict, MutableMapping[KT, VT]):
"use dict() instead") "use dict() instead")
return dict.__new__(cls, *args, **kwds) return dict.__new__(cls, *args, **kwds)
class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): class DefaultDict(collections.defaultdict, MutableMapping[KT, VT],
extra=collections.defaultdict):
def __new__(cls, *args, **kwds): def __new__(cls, *args, **kwds):
if _geqv(cls, DefaultDict): if _geqv(cls, DefaultDict):

View File

@ -123,6 +123,8 @@ Core and Builtins
Library Library
------- -------
- Issue #27014: Fix infinite recursion using typing.py. Thanks to Kalle Tuure!
- Issue #14132: Fix urllib.request redirect handling when the target only has - Issue #14132: Fix urllib.request redirect handling when the target only has
a query string. Original fix by Ján Janech. a query string. Original fix by Ján Janech.