diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 90bad775859..f9e54b2725f 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -1,4 +1,5 @@ import contextlib +import collections import pickle import re import sys @@ -1218,13 +1219,17 @@ class CollectionsAbcTests(BaseTestCase): with self.assertRaises(TypeError): typing.List[int]() - def test_list_subclass_instantiation(self): + def test_list_subclass(self): class MyList(typing.List[int]): pass 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): with self.assertRaises(TypeError): @@ -1234,13 +1239,17 @@ class CollectionsAbcTests(BaseTestCase): with self.assertRaises(TypeError): typing.Dict[str, int]() - def test_dict_subclass_instantiation(self): + def test_dict_subclass(self): class MyDict(typing.Dict[str, int]): pass 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): with self.assertRaises(TypeError): @@ -1250,7 +1259,7 @@ class CollectionsAbcTests(BaseTestCase): with self.assertRaises(TypeError): typing.DefaultDict[str, int]() - def test_defaultdict_subclass_instantiation(self): + def test_defaultdict_subclass(self): class MyDefDict(typing.DefaultDict[str, int]): pass @@ -1258,6 +1267,9 @@ class CollectionsAbcTests(BaseTestCase): dd = MyDefDict() self.assertIsInstance(dd, MyDefDict) + self.assertIsSubclass(MyDefDict, collections.defaultdict) + self.assertNotIsSubclass(collections.defaultdict, MyDefDict) + def test_no_set_instantiation(self): with self.assertRaises(TypeError): typing.Set() @@ -1338,6 +1350,13 @@ class CollectionsAbcTests(BaseTestCase): self.assertEqual(len(MMB[str, str]()), 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): diff --git a/Lib/typing.py b/Lib/typing.py index d2750111d45..841e778648a 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -894,8 +894,6 @@ def _next_in_mro(cls): class GenericMeta(TypingMeta, abc.ABCMeta): """Metaclass for generic types.""" - __extra__ = None - def __new__(cls, name, bases, namespace, tvars=None, args=None, origin=None, extra=None): self = super().__new__(cls, name, bases, namespace, _root=True) @@ -943,10 +941,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): self.__parameters__ = tvars self.__args__ = args self.__origin__ = origin - if extra is not None: - self.__extra__ = extra - # Else __extra__ is inherited, eventually from the - # (meta-)class default above. + self.__extra__ = extra # Speed hack (https://github.com/python/typing/issues/196). self.__next_in_mro__ = _next_in_mro(self) return self @@ -1307,6 +1302,7 @@ class _ProtocolMeta(GenericMeta): attr != '__next_in_mro__' and attr != '__parameters__' and attr != '__origin__' and + attr != '__extra__' and attr != '__module__'): attrs.add(attr) @@ -1470,7 +1466,7 @@ class ByteString(Sequence[int], extra=collections_abc.ByteString): ByteString.register(type(memoryview(b''))) -class List(list, MutableSequence[T]): +class List(list, MutableSequence[T], extra=list): def __new__(cls, *args, **kwds): if _geqv(cls, List): @@ -1479,7 +1475,7 @@ class List(list, MutableSequence[T]): return list.__new__(cls, *args, **kwds) -class Set(set, MutableSet[T]): +class Set(set, MutableSet[T], extra=set): def __new__(cls, *args, **kwds): if _geqv(cls, Set): @@ -1502,7 +1498,8 @@ class _FrozenSetMeta(GenericMeta): return super().__subclasscheck__(cls) -class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta): +class FrozenSet(frozenset, AbstractSet[T_co], metaclass=_FrozenSetMeta, + extra=frozenset): __slots__ = () def __new__(cls, *args, **kwds): @@ -1538,7 +1535,7 @@ if hasattr(contextlib, 'AbstractContextManager'): __all__.append('ContextManager') -class Dict(dict, MutableMapping[KT, VT]): +class Dict(dict, MutableMapping[KT, VT], extra=dict): def __new__(cls, *args, **kwds): if _geqv(cls, Dict): @@ -1546,7 +1543,8 @@ class Dict(dict, MutableMapping[KT, VT]): "use dict() instead") 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): if _geqv(cls, DefaultDict): diff --git a/Misc/NEWS b/Misc/NEWS index 97ddbb12f3d..8d91d7882a9 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -123,6 +123,8 @@ Core and Builtins 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 a query string. Original fix by Ján Janech.