8000 Update `_collections_abc.py` and `test_collections.py` to 3.12.2 by dchiquito · Pull Request #5180 · RustPython/RustPython · GitHub
[go: up one dir, main page]

Skip to content

Update _collections_abc.py and test_collections.py to 3.12.2 #5180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 63 additions & 11 deletions Lib/_collections_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -23,7 +49,7 @@ def _f(): pass
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
"ByteString", "Buffer",
]

# This module has been renamed from collections.abc to _collections_abc to
Expand Down Expand Up @@ -413,6 +439,21 @@ def __subclasshook__(cls, C):
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]`.

Expand Down Expand Up @@ -455,15 +496,8 @@ def __getitem__(self, item):
# 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__

Expand Down Expand Up @@ -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__
Expand Down Expand Up @@ -1038,8 +1071,27 @@ def count(self, value):
Sequence.register(range)
Sequence.register(memoryview)

class _DeprecateByteStringMeta(ABCMeta):
def __new__(cls, name, bases, namespace, **kwargs):
if name != "ByteString":
import warnings

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):
class ByteString(Sequence, metaclass=_DeprecateByteStringMeta):
"""This unifies bytes and bytearray.

XXX Should add all their methods.
Expand Down
45 changes: 31 additions & 14 deletions Lib/collections/__init__.py
8000
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@
else:
_collections_abc.MutableSequence.register(deque)

try:
from _collections import _deque_iterator
except ImportError:
pass

try:
from _collections import defaultdict
except ImportError:
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -271,7 +278,7 @@ def __repr__(self):
'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'
Expand Down Expand Up @@ -511,9 +518,12 @@ def __getnewargs__(self):
# 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

Expand Down Expand Up @@ -1015,8 +1025,8 @@ def __len__(self):

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):
Expand Down Expand Up @@ -1136,10 +1146,17 @@ def __delitem__(self, key):
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)
Expand Down
45 changes: 39 additions & 6 deletions Lib/test/test_collections.py
A93C
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
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):
Expand Down Expand Up @@ -71,6 +71,14 @@ def test_dict_copy(self):
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)
Expand Down Expand Up @@ -539,7 +547,7 @@ def test_odd_sizes(self):
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)
Expand Down Expand Up @@ -1629,7 +1637,7 @@ def test_Set_from_iterable(self):
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)

Expand Down Expand Up @@ -1949,13 +1957,38 @@ def assert_index_same(seq1, seq2, index_args):

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
Expand Down
15 changes: 13 additions & 2 deletions Lib/test/test_ordered_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,17 @@ def items(self):
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')
Expand Down Expand Up @@ -370,7 +381,7 @@ def test_repr(self):
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()")

Expand All @@ -380,7 +391,7 @@ def test_repr_recursive(self):
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
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,11 @@ def test_paramspec(self): # TODO: RUSTPYTHON, remove when this passes
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):
Expand Down
0