Merge pull request #5180 from dchiquito/3.12-collections

Update `_collections_abc.py` and `test_collections.py` to 3.12.2
This commit is contained in:
Jeong, YunWon
2024-02-26 13:20:36 +09:00
committed by GitHub
5 changed files with 151 additions and 33 deletions

View File

@@ -6,6 +6,32 @@
Unit tests are in test_collections.
"""
############ Maintenance notes #########################################
#
# ABCs are different from other standard library modules in that they
# specify compliance tests. In general, once an ABC has been published,
# new methods (either abstract or concrete) cannot be added.
#
# Though classes that inherit from an ABC would automatically receive a
# new mixin method, registered classes would become non-compliant and
# violate the contract promised by ``isinstance(someobj, SomeABC)``.
#
# Though irritating, the correct procedure for adding new abstract or
# mixin methods is to create a new ABC as a subclass of the previous
# ABC. For example, union(), intersection(), and difference() cannot
# be added to Set but could go into a new ABC that extends Set.
#
# Because they are so hard to change, new ABCs should have their APIs
# carefully thought through prior to publication.
#
# Since ABCMeta only checks for the presence of methods, it is possible
# to alter the signature of a method by adding optional arguments
# or changing parameters names. This is still a bit dubious but at
# least it won't cause isinstance() to return an incorrect result.
#
#
#######################################################################
from abc import ABCMeta, abstractmethod
import sys
@@ -23,7 +49,7 @@ __all__ = ["Awaitable", "Coroutine",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
"ByteString", "Buffer",
]
# This module has been renamed from collections.abc to _collections_abc to
@@ -413,6 +439,21 @@ class Collection(Sized, Iterable, Container):
return NotImplemented
class Buffer(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __buffer__(self, flags: int, /) -> memoryview:
raise NotImplementedError
@classmethod
def __subclasshook__(cls, C):
if cls is Buffer:
return _check_methods(C, "__buffer__")
return NotImplemented
class _CallableGenericAlias(GenericAlias):
""" Represent `Callable[argtypes, resulttype]`.
@@ -455,15 +496,8 @@ class _CallableGenericAlias(GenericAlias):
# rather than the default types.GenericAlias object. Most of the
# code is copied from typing's _GenericAlias and the builtin
# types.GenericAlias.
if not isinstance(item, tuple):
item = (item,)
# A special case in PEP 612 where if X = Callable[P, int],
# then X[int, str] == X[[int, str]].
if (len(self.__parameters__) == 1
and _is_param_expr(self.__parameters__[0])
and item and not _is_param_expr(item[0])):
item = (item,)
new_args = super().__getitem__(item).__args__
@@ -491,9 +525,8 @@ def _type_repr(obj):
Copied from :mod:`typing` since collections.abc
shouldn't depend on that module.
(Keep this roughly in sync with the typing version.)
"""
if isinstance(obj, GenericAlias):
return repr(obj)
if isinstance(obj, type):
if obj.__module__ == 'builtins':
return obj.__qualname__
@@ -1038,8 +1071,27 @@ Sequence.register(str)
Sequence.register(range)
Sequence.register(memoryview)
class _DeprecateByteStringMeta(ABCMeta):
def __new__(cls, name, bases, namespace, **kwargs):
if name != "ByteString":
import warnings
class ByteString(Sequence):
warnings._deprecated(
"collections.abc.ByteString",
remove=(3, 14),
)
return super().__new__(cls, name, bases, namespace, **kwargs)
def __instancecheck__(cls, instance):
import warnings
warnings._deprecated(
"collections.abc.ByteString",
remove=(3, 14),
)
return super().__instancecheck__(instance)
class ByteString(Sequence, metaclass=_DeprecateByteStringMeta):
"""This unifies bytes and bytearray.
XXX Should add all their methods.

View File

@@ -45,6 +45,11 @@ except ImportError:
else:
_collections_abc.MutableSequence.register(deque)
try:
from _collections import _deque_iterator
except ImportError:
pass
try:
from _collections import defaultdict
except ImportError:
@@ -94,17 +99,19 @@ class OrderedDict(dict):
# Individual links are kept alive by the hard reference in self.__map.
# Those hard references disappear when a key is deleted from an OrderedDict.
def __new__(cls, /, *args, **kwds):
"Create the ordered dict object and set up the underlying structures."
self = dict.__new__(cls)
self.__hardroot = _Link()
self.__root = root = _proxy(self.__hardroot)
root.prev = root.next = root
self.__map = {}
return self
def __init__(self, other=(), /, **kwds):
'''Initialize an ordered dictionary. The signature is the same as
regular dictionaries. Keyword argument order is preserved.
'''
try:
self.__root
except AttributeError:
self.__hardroot = _Link()
self.__root = root = _proxy(self.__hardroot)
root.prev = root.next = root
self.__map = {}
self.__update(other, **kwds)
def __setitem__(self, key, value,
@@ -271,7 +278,7 @@ class OrderedDict(dict):
'od.__repr__() <==> repr(od)'
if not self:
return '%s()' % (self.__class__.__name__,)
return '%s(%r)' % (self.__class__.__name__, list(self.items()))
return '%s(%r)' % (self.__class__.__name__, dict(self.items()))
def __reduce__(self):
'Return state information for pickling'
@@ -511,9 +518,12 @@ def namedtuple(typename, field_names, *, rename=False, defaults=None, module=Non
# specified a particular module.
if module is None:
try:
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
module = _sys._getframemodulename(1) or '__main__'
except AttributeError:
try:
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
except (AttributeError, ValueError):
pass
if module is not None:
result.__module__ = module
@@ -1015,8 +1025,8 @@ class ChainMap(_collections_abc.MutableMapping):
def __iter__(self):
d = {}
for mapping in reversed(self.maps):
d.update(dict.fromkeys(mapping)) # reuses stored hash values if possible
for mapping in map(dict.fromkeys, reversed(self.maps)):
d |= mapping # reuses stored hash values if possible
return iter(d)
def __contains__(self, key):
@@ -1136,10 +1146,17 @@ class UserDict(_collections_abc.MutableMapping):
def __iter__(self):
return iter(self.data)
# Modify __contains__ to work correctly when __missing__ is present
# Modify __contains__ and get() to work like dict
# does when __missing__ is present.
def __contains__(self, key):
return key in self.data
def get(self, key, default=None):
if key in self:
return self[key]
return default
# Now, add the methods in dicts but not in MutableMapping
def __repr__(self):
return repr(self.data)

View File

@@ -25,7 +25,7 @@ from collections.abc import Sized, Container, Callable, Collection
from collections.abc import Set, MutableSet
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
from collections.abc import Sequence, MutableSequence
from collections.abc import ByteString
from collections.abc import ByteString, Buffer
class TestUserObjects(unittest.TestCase):
@@ -71,6 +71,14 @@ class TestUserObjects(unittest.TestCase):
obj[123] = "abc"
self._copy_test(obj)
def test_dict_missing(self):
class A(UserDict):
def __missing__(self, key):
return 456
self.assertEqual(A()[123], 456)
# get() ignores __missing__ on dict
self.assertIs(A().get(123), None)
################################################################################
### ChainMap (helper class for configparser and the string module)
@@ -539,7 +547,7 @@ class TestNamedTuple(unittest.TestCase):
self.assertEqual(Dot(1)._replace(d=999), (999,))
self.assertEqual(Dot(1)._fields, ('d',))
n = 5000
n = support.EXCEEDS_RECURSION_LIMIT
names = list(set(''.join([choice(string.ascii_letters)
for j in range(10)]) for i in range(n)))
n = len(names)
@@ -1629,7 +1637,7 @@ class TestCollectionABCs(ABCTestCase):
class SetUsingInstanceFromIterable(MutableSet):
def __init__(self, values, created_by):
if not created_by:
raise ValueError(f'created_by must be specified')
raise ValueError('created_by must be specified')
self.created_by = created_by
self._values = set(values)
@@ -1949,13 +1957,38 @@ class TestCollectionABCs(ABCTestCase):
def test_ByteString(self):
for sample in [bytes, bytearray]:
self.assertIsInstance(sample(), ByteString)
with self.assertWarns(DeprecationWarning):
self.assertIsInstance(sample(), ByteString)
self.assertTrue(issubclass(sample, ByteString))
for sample in [str, list, tuple]:
self.assertNotIsInstance(sample(), ByteString)
with self.assertWarns(DeprecationWarning):
self.assertNotIsInstance(sample(), ByteString)
self.assertFalse(issubclass(sample, ByteString))
self.assertNotIsInstance(memoryview(b""), ByteString)
with self.assertWarns(DeprecationWarning):
self.assertNotIsInstance(memoryview(b""), ByteString)
self.assertFalse(issubclass(memoryview, ByteString))
with self.assertWarns(DeprecationWarning):
self.validate_abstract_methods(ByteString, '__getitem__', '__len__')
with self.assertWarns(DeprecationWarning):
class X(ByteString): pass
with self.assertWarns(DeprecationWarning):
# No metaclass conflict
class Z(ByteString, Awaitable): pass
# TODO: RUSTPYTHON
# Need to implement __buffer__ and __release_buffer__
# https://docs.python.org/3.13/reference/datamodel.html#emulating-buffer-types
@unittest.expectedFailure
def test_Buffer(self):
for sample in [bytes, bytearray, memoryview]:
self.assertIsInstance(sample(b"x"), Buffer)
self.assertTrue(issubclass(sample, Buffer))
for sample in [str, list, tuple]:
self.assertNotIsInstance(sample(), Buffer)
self.assertFalse(issubclass(sample, Buffer))
self.validate_abstract_methods(Buffer, '__buffer__')
# TODO: RUSTPYTHON
@unittest.expectedFailure

View File

@@ -122,6 +122,17 @@ class OrderedDictTests:
self.OrderedDict(Spam())
self.assertEqual(calls, ['keys'])
def test_overridden_init(self):
# Sync-up pure Python OD class with C class where
# a consistent internal state is created in __new__
# rather than __init__.
OrderedDict = self.OrderedDict
class ODNI(OrderedDict):
def __init__(*args, **kwargs):
pass
od = ODNI()
od['a'] = 1 # This used to fail because __init__ was bypassed
def test_fromkeys(self):
OrderedDict = self.OrderedDict
od = OrderedDict.fromkeys('abc')
@@ -370,7 +381,7 @@ class OrderedDictTests:
OrderedDict = self.OrderedDict
od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
self.assertEqual(repr(od),
"OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
"OrderedDict({'c': 1, 'b': 2, 'a': 3, 'd': 4, 'e': 5, 'f': 6})")
self.assertEqual(eval(repr(od)), od)
self.assertEqual(repr(OrderedDict()), "OrderedDict()")
@@ -380,7 +391,7 @@ class OrderedDictTests:
od = OrderedDict.fromkeys('abc')
od['x'] = od
self.assertEqual(repr(od),
"OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
"OrderedDict({'a': None, 'b': None, 'c': None, 'x': ...})")
def test_repr_recursive_values(self):
OrderedDict = self.OrderedDict

View File

@@ -705,6 +705,11 @@ class CollectionsCallableTests(BaseCallableTests, BaseTestCase):
def test_concatenate(self): # TODO: RUSTPYTHON, remove when this passes
super().test_concatenate() # TODO: RUSTPYTHON, remove when this passes
# TODO: RUSTPYTHON might be fixed by updating typing to 3.12
@unittest.expectedFailure
def test_repr(self): # TODO: RUSTPYTHON, remove when this passes
super().test_repr() # TODO: RUSTPYTHON, remove when this passes
class LiteralTests(BaseTestCase):
def test_basics(self):