8000 Merge pull request #5180 from dchiquito/3.12-collections · RustPython/RustPython@e6c6f96 · GitHub
[go: up one dir, main page]

Skip to content

Commit e6c6f96

Browse files
authored
Merge pull request #5180 from dchiquito/3.12-collections
Update `_collections_abc.py` and `test_collections.py` to 3.12.2
2 parents a8ab7dd + 407f251 commit e6c6f96

File tree

5 files changed

+151
-33
lines changed

5 files changed

+151
-33
lines changed

Lib/_collections_abc.py

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,32 @@
66
Unit tests are in test_collections.
77
"""
88

9+
############ Maintenance notes #########################################
10+
#
11+
# ABCs are different from other standard library modules in that they
12+
# specify compliance tests. In general, once an ABC has been published,
13+
# new methods (either abstract or concrete) cannot be added.
14+
#
15+
# Though classes that inherit from an ABC would automatically receive a
16+
# new mixin method, registered classes would become non-compliant and
17+
# violate the contract promised by ``isinstance(someobj, SomeABC)``.
18+
#
19+
# Though irritating, the correct procedure for adding new abstract or
20+
# mixin methods is to create a new ABC as a subclass of the previous
21+
# ABC. For example, union(), intersection(), and difference() cannot
22+
# be added to Set but could go into a new ABC that extends Set.
23+
#
24+
# Because they are so hard to change, new ABCs should have their APIs
25+
# carefully thought through prior to publication.
26+
#
27+
# Since ABCMeta only checks for the presence of methods, it is possible
28+
# to alter the signature of a method by adding optional arguments
29+
# or changing parameters names. This is still a bit dubious but at
30+
# least it won't cause isinstance() to return an incorrect result.
31+
#
32+
#
33+
#######################################################################
34+
935
from abc import ABCMeta, abstractmethod
1036
import sys
1137

@@ -23,7 +49,7 @@ def _f(): pass
2349
"Mapping", "MutableMapping",
2450
"MappingView", "KeysView", "ItemsView", "ValuesView",
2551
"Sequence", "MutableSequence",
26-
"ByteString",
52+
"ByteString", "Buffer",
2753
]
2854

2955
# This module has been renamed from collections.abc to _collections_abc to
@@ -413,6 +439,21 @@ def __subclasshook__(cls, C):
413439
return NotImplemented
414440

415441

442+
class Buffer(metaclass=ABCMeta):
443+
444+
__slots__ = ()
445+
446+
@abstractmethod
447+
def __buffer__(self, flags: int, /) -> memoryview:
448+
raise NotImplementedError
449+
450+
@classmethod
451+
def __subclasshook__(cls, C):
452+
if cls is Buffer:
453+
return _check_methods(C, "__buffer__")
454+
return NotImplemented
455+
456+
416457
class _CallableGenericAlias(GenericAlias):
417458
""" Represent `Callable[argtypes, resulttype]`.
418459
@@ -455,15 +496,8 @@ def __getitem__(self, item):
455496
# rather than the default types.GenericAlias object. Most of the
456497
# code is copied from typing's _GenericAlias and the builtin
457498
# types.GenericAlias.
458-
459499
if not isinstance(item, tuple):
460500
item = (item,)
461-
# A special case in PEP 612 where if X = Callable[P, int],
462-
# then X[int, str] == X[[int, str]].
463-
if (len(self.__parameters__) == 1
464-
and _is_param_expr(self.__parameters__[0])
465-
and item and not _is_param_expr(item[0])):
466-
item = (item,)
467501

468502
new_args = super().__getitem__(item).__args__
469503

@@ -491,9 +525,8 @@ def _type_repr(obj):
491525
492526
Copied from :mod:`typing` since collections.abc
493527
shouldn't depend on that module.
528+
(Keep this roughly in sync with the typing version.)
494529
"""
495-
if isinstance(obj, GenericAlias):
496-
return repr(obj)
497530
if isinstance(obj, type):
498531
if obj.__module__ == 'builtins':
499532
return obj.__qualname__
@@ -1038,8 +1071,27 @@ def count(self, value):
10381071
Sequence.register(range)
10391072
Sequence.register(memoryview)
10401073

1074+
class _DeprecateByteStringMeta(ABCMeta):
1075+
def __new__(cls, name, bases, namespace, **kwargs):
1076+
if name != "ByteString":
1077+
import warnings
1078+
1079+
warnings._deprecated(
1080+
"collections.abc.ByteString",
1081+
remove=(3, 14),
1082+
)
1083+
return super().__new__(cls, name, bases, namespace, **kwargs)
1084+
1085+
def __instancecheck__(cls, instance):
1086+
import warnings
1087+
1088+
warnings._deprecated(
1089+
"collections.abc.ByteString",
1090+
remove=(3, 14),
1091+
)
1092+
return super().__instancecheck__(instance)
10411093

1042-
class ByteString(Sequence):
1094+
class ByteString(Sequence, metaclass=_DeprecateByteStringMeta):
10431095
"""This unifies bytes and bytearray.
10441096
10451097
XXX Should add all their methods.

Lib/collections/__init__.py

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
else:
4646
_collections_abc.MutableSequence.register(deque)
4747

48+
try:
49+
from _collections import _deque_iterator
50+
except ImportError:
51+
pass
52+
4853
try:
4954
from _collections import defaultdict
5055
except ImportError:
@@ -94,17 +99,19 @@ class OrderedDict(dict):
9499
# Individual links are kept alive by the hard reference in self.__map.
95100
# Those hard references disappear when a key is deleted from an OrderedDict.
96101

102+
def __new__(cls, /, *args, **kwds):
103+
"Create the ordered dict object and set up the underlying structures."
104+
self = dict.__new__(cls)
105+
self.__hardroot = _Link()
106+
self.__root = root = _proxy(self.__hardroot)
107+
root.prev = root.next = root
108+
self.__map = {}
109+
return self
110+
97111
def __init__(self, other=(), /, **kwds):
98112
'''Initialize an ordered dictionary. The signature is the same as
99113
regular dictionaries. Keyword argument order is preserved.
100114
'''
101-
try:
102-
self.__root
103-
except AttributeError:
104-
self.__hardroot = _Link()
105-
self.__root = root = _proxy(self.__hardroot)
106-
root.prev = root.next = root
107-
self.__map = {}
108115
self.__update(other, **kwds)
109116

110117
def __setitem__(self, key, value,
@@ -271,7 +278,7 @@ def __repr__(self):
271278
'od.__repr__() <==> repr(od)'
272279
if not self:
273280
return '%s()' % (self.__class__.__name__,)
274-
return '%s(%r)' % (self.__class__.__name__, list(self.items()))
281+
return '%s(%r)' % (self.__class__.__name__, dict(self.items()))
275282

276283
def __reduce__(self):
277284
'Return state information for pickling'
@@ -511,9 +518,12 @@ def __getnewargs__(self):
511518
# specified a particular module.
512519
if module is None:
513520
try:
514-
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
515-
except (AttributeError, ValueError):
516-
pass
521+
module = _sys._getframemodulename(1) or '__main__'
522+
except AttributeError:
523+
try:
524+
module = _sys._getframe(1).f_globals.get('__name__', '__main__')
525+
except (AttributeError, ValueError):
526+
pass
517527
if module is not None:
518528
result.__module__ = module
519529

@@ -1015,8 +1025,8 @@ def __len__(self):
10151025

10161026
def __iter__(self):
10171027
d = {}
1018-
for mapping in reversed(self.maps):
1019-
d.update(dict.fromkeys(mapping)) # reuses stored hash values if possible
1028+
for mapping in map(dict.fromkeys, reversed(self.maps)):
1029+
d |= mapping # reuses stored hash values if possible
10201030
return iter(d)
10211031

10221032
def __contains__(self, key):
@@ -1136,10 +1146,17 @@ def __delitem__(self, key):
11361146
def __iter__(self):
11371147
return iter(self.data)
11381148

1139-
# Modify __contains__ to work correctly when __missing__ is present
1149+
# Modify __contains__ and get() to work like dict
1150+
# does when __missing__ is present.
11401151
def __contains__(self, key):
11411152
return key in self.data
11421153

1154+
def get(self, key, default=None):
1155+
if key in self:
1156+
return self[key]
1157+
return default
1158+
1159+
11431160
# Now, add the methods in dicts but not in MutableMapping
11441161
def __repr__(self):
11451162
return repr(self.data)

Lib/test/test_collections.py

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from collections.abc import Set, MutableSet
2626
from collections.abc import Mapping, MutableMapping, KeysView, ItemsView, ValuesView
2727
from collections.abc import Sequence, MutableSequence
28-
from collections.abc import ByteString
28+
from collections.abc import ByteString, Buffer
2929

3030

3131
class TestUserObjects(unittest.TestCase):
@@ -71,6 +71,14 @@ def test_dict_copy(self):
7171
obj[123] = "abc"
7272
self._copy_test(obj)
7373

74+
def test_dict_missing(self):
75+
class A(UserDict):
76+
def __missing__(self, key):
77+
return 456
78+
self.assertEqual(A()[123], 456)
79+
# get() ignores __missing__ on dict
80+
self.assertIs(A().get(123), None)
81+
7482

7583
################################################################################
7684
### ChainMap (helper class for configparser and the string module)
@@ -539,7 +547,7 @@ def test_odd_sizes(self):
539547
self.assertEqual(Dot(1)._replace(d=999), (999,))
540548
self.assertEqual(Dot(1)._fields, ('d',))
541549

542-
n = 5000
550+
n = support.EXCEEDS_RECURSION_LIMIT
543551
names = list(set(''.join([choice(string.ascii_letters)
544552
for j in range(10)]) for i in range(n)))
545553
n = len(names)
@@ -1629,7 +1637,7 @@ def test_Set_from_iterable(self):
16291637
class SetUsingInstanceFromIterable(MutableSet):
16301638
def __init__(self, values, created_by):
16311639
if not created_by:
1632-
raise ValueError(f'created_by must be specified')
1640+
raise ValueError('created_by must be specified')
16331641
self.created_by = created_by
16341642
self._values = set(values)
16351643

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

19501958
def test_ByteString(self):
19511959
for sample in [bytes, bytearray]:
1952-
self.assertIsInstance(sample(), ByteString)
1960+
with self.assertWarns(DeprecationWarning):
1961+
self.assertIsInstance(sample(), ByteString)
19531962
self.assertTrue(issubclass(sample, ByteString))
19541963
for sample in [str, list, tuple]:
1955-
self.assertNotIsInstance(sample(), ByteString)
1964+
with self.assertWarns(DeprecationWarning):
1965+
self.assertNotIsInstance(sample(), ByteString)
19561966
self.assertFalse(issubclass(sample, ByteString))
1957-
self.assertNotIsInstance(memoryview(b""), ByteString)
1967+
with self.assertWarns(DeprecationWarning):
1968+
self.assertNotIsInstance(memoryview(b""), ByteString)
19581969
self.assertFalse(issubclass(memoryview, ByteString))
1970+
with self.assertWarns(DeprecationWarning):
1971+
self.validate_abstract_methods(ByteString, '__getitem__', '__len__')
1972+
1973+
with self.assertWarns(DeprecationWarning):
1974+
class X(ByteString): pass
1975+
1976+
with self.assertWarns(DeprecationWarning):
1977+
# No metaclass conflict
1978+
class Z(ByteString, Awaitable): pass
1979+
1980+
# TODO: RUSTPYTHON
1981+
# Need to implement __buffer__ and __release_buffer__
1982+
# https://docs.python.org/3.13/reference/datamodel.html#emulating-buffer-types
1983+
@unittest.expectedFailure
1984+
def test_Buffer(self):
1985+
for sample in [bytes, bytearray, memoryview]:
1986+
self.assertIsInstance(sample(b"x"), Buffer)
1987+
self.assertTrue(issubclass(sample, Buffer))
1988+
for sample in [str, list, tuple]:
1989+
self.assertNotIsInstance(sample(), Buffer)
1990+
self.assertFalse(issubclass(sample, Buffer))
1991+
self.validate_abstract_methods(Buffer, '__buffer__')
19591992

19601993
# TODO: RUSTPYTHON
19611994
@unittest.expectedFailure

Lib/test/test_ordered_dict.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,17 @@ def items(self):
122122
self.OrderedDict(Spam())
123123
self.assertEqual(calls, ['keys'])
124124

125+
def test_overridden_init(self):
126+
# Sync-up pure Python OD class with C class where
127+
# a consistent internal state is created in __new__
128+
# rather than __init__.
129+
OrderedDict = self.OrderedDict
130+
class ODNI(OrderedDict):
131+
def __init__(*args, **kwargs):
132+
pass
133+
od = ODNI()
134+
od['a'] = 1 # This used to fail because __init__ was bypassed
135+
125136
def test_fromkeys(self):
126137
OrderedDict = self.OrderedDict
127138
od = OrderedDict.fromkeys('abc')
@@ -370,7 +381,7 @@ def test_repr(self):
370381
OrderedDict = self.OrderedDict
371382
od = OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])
372383
self.assertEqual(repr(od),
373-
"OrderedDict([('c', 1), ('b', 2), ('a', 3), ('d', 4), ('e', 5), ('f', 6)])")
384+
"OrderedDict({'c': 1, 'b': 2, 'a': 3, 'd': 4, 'e': 5, 'f': 6})")
374385
self.assertEqual(eval(repr(od)), od)
375386
self.assertEqual(repr(OrderedDict()), "OrderedDict()")
376387

@@ -380,7 +391,7 @@ def test_repr_recursive(self):
380391
od = OrderedDict.fromkeys('abc')
381392
od['x'] = od
382393
self.assertEqual(repr(od),
383-
"OrderedDict([('a', None), ('b', None), ('c', None), ('x', ...)])")
394+
"OrderedDict({'a': None, 'b': None, 'c': None, 'x': ...})")
384395

385396
def test_repr_recursive_values(self):
386397
OrderedDict = self.OrderedDict

Lib/test/test_typing.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,11 @@ def test_paramspec(self): # TODO: RUSTPYTHON, remove when this passes
705705
def test_concatenate(self): # TODO: RUSTPYTHON, remove when this passes
706706
super().test_concatenate() # TODO: RUSTPYTHON, remove when this passes
707707

708+
# TODO: RUSTPYTHON might be fixed by updating typing to 3.12
709+
@unittest.expectedFailure
710+
def test_repr(self): # TODO: RUSTPYTHON, remove when this passes
711+
super().test_repr() # TODO: RUSTPYTHON, remove when this passes
712+
708713

709714
class LiteralTests(BaseTestCase):
710715
def test_basics(self):

0 commit comments

Comments
 (0)
0