mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Extract the Python 3.14-compatible view representation from #8954. Snapshot view elements before invoking their repr methods, preserve recursion handling, and cover each view in the existing collections snippet. Leave migration-specific mapping changes out. Assisted-by: OpenAI Codex:model identifier unavailable
255 lines
7.1 KiB
Python
255 lines
7.1 KiB
Python
from collections import deque
|
|
|
|
d = deque([0, 1, 2])
|
|
|
|
d.append(1)
|
|
d.appendleft(3)
|
|
|
|
assert d == deque([3, 0, 1, 2, 1])
|
|
|
|
assert d <= deque([4])
|
|
|
|
assert d.copy() is not d
|
|
|
|
d = deque([1, 2, 3], 5)
|
|
|
|
d.extend([4, 5, 6])
|
|
|
|
assert d == deque([2, 3, 4, 5, 6]), d
|
|
|
|
d.remove(4)
|
|
|
|
assert d == deque([2, 3, 5, 6])
|
|
|
|
d.clear()
|
|
|
|
assert d == deque()
|
|
|
|
assert d == deque([], 4)
|
|
|
|
assert deque([1, 2, 3]) * 2 == deque([1, 2, 3, 1, 2, 3])
|
|
|
|
assert deque([1, 2, 3], 4) * 2 == deque([3, 1, 2, 3])
|
|
|
|
assert deque(maxlen=3) == deque()
|
|
|
|
assert deque([1, 2, 3, 4], maxlen=2) == deque([3, 4])
|
|
|
|
assert len(deque([1, 2, 3, 4])) == 4
|
|
|
|
assert d >= d
|
|
assert not (d > d)
|
|
assert d <= d
|
|
assert not (d < d)
|
|
assert d == d
|
|
assert not (d != d)
|
|
|
|
|
|
# Test that calling an evil __repr__ can't hang deque
|
|
class BadRepr:
|
|
def __repr__(self):
|
|
self.d.pop()
|
|
return ""
|
|
|
|
|
|
b = BadRepr()
|
|
d = deque([1, b, 2])
|
|
b.d = d
|
|
repr(d)
|
|
|
|
# namedtuple fields are `_tuplegetter` descriptors
|
|
import pickle
|
|
from _collections import _count_elements, _tuplegetter
|
|
from collections import Counter, OrderedDict, defaultdict, namedtuple
|
|
|
|
from testutils import assert_raises
|
|
|
|
Point = namedtuple("Point", "x y")
|
|
p = Point(1, 2)
|
|
assert type(Point.x) is _tuplegetter
|
|
assert (p.x, p.y) == (1, 2)
|
|
assert repr(Point.x) == "_tuplegetter(0, 'Alias for field number 0')"
|
|
assert Point.x.__get__(None, Point) is Point.x
|
|
assert Point.y.__get__((5, 6)) == 6
|
|
assert pickle.loads(pickle.dumps(Point.y)).__get__((5, 6)) == 6
|
|
with assert_raises(TypeError):
|
|
Point.x.__get__([1, 2])
|
|
with assert_raises(IndexError):
|
|
_tuplegetter(3, None).__get__((1, 2))
|
|
with assert_raises(AttributeError):
|
|
p.x = 3
|
|
with assert_raises(AttributeError):
|
|
del p.x
|
|
Point.x.__doc__ = "The x-coordinate"
|
|
assert repr(Point.x) == "_tuplegetter(0, 'The x-coordinate')"
|
|
|
|
# `_count_elements` behind Counter
|
|
counts = {}
|
|
_count_elements(counts, "abca")
|
|
assert counts == {"a": 2, "b": 1, "c": 1}
|
|
assert Counter("abracadabra").most_common(1) == [("a", 5)]
|
|
ordered = OrderedDict()
|
|
_count_elements(ordered, "bab")
|
|
assert list(ordered.items()) == [("b", 2), ("a", 1)]
|
|
|
|
|
|
# OrderedDict views show their contents in insertion order, including recursion.
|
|
def check_ordered_dict_view_repr():
|
|
ordered = OrderedDict([("b", 2), ("a", 1)])
|
|
for method, contents in (
|
|
("keys", "['b', 'a']"),
|
|
("values", "[2, 1]"),
|
|
("items", "[('b', 2), ('a', 1)]"),
|
|
):
|
|
assert repr(getattr(OrderedDict(), method)()) == "odict_" + method + "([])"
|
|
assert (
|
|
repr(getattr(ordered, method)()) == "odict_" + method + "(" + contents + ")"
|
|
)
|
|
|
|
recursive = OrderedDict()
|
|
values = recursive.values()
|
|
items = recursive.items()
|
|
recursive["self"] = values
|
|
assert repr(values) == "odict_values([...])"
|
|
recursive["self"] = items
|
|
assert repr(items) == "odict_items([('self', ...)])"
|
|
|
|
# Repr must snapshot the contents before arbitrary repr code mutates them.
|
|
for method in ("keys", "values", "items"):
|
|
|
|
class MutatingRepr:
|
|
def __repr__(self):
|
|
ordered.clear()
|
|
return "mutated"
|
|
|
|
item = MutatingRepr()
|
|
ordered = OrderedDict([(item, item), ("tail", "last")])
|
|
expected = {
|
|
"keys": "odict_keys([mutated, 'tail'])",
|
|
"values": "odict_values([mutated, 'last'])",
|
|
"items": "odict_items([(mutated, mutated), ('tail', 'last')])",
|
|
}
|
|
assert repr(getattr(ordered, method)()) == expected[method]
|
|
assert not ordered
|
|
|
|
class BadRepr:
|
|
def __repr__(self):
|
|
raise ValueError("view repr")
|
|
|
|
ordered = OrderedDict([(BadRepr(), BadRepr())])
|
|
for method in ("keys", "values", "items"):
|
|
with assert_raises(ValueError) as caught:
|
|
repr(getattr(ordered, method)())
|
|
assert str(caught.exception) == "view repr"
|
|
|
|
|
|
check_ordered_dict_view_repr()
|
|
|
|
factory = defaultdict(lambda: 100)
|
|
_count_elements(factory, "a")
|
|
assert factory == {"a": 1}
|
|
|
|
|
|
class Scaled(dict):
|
|
def __setitem__(self, key, value):
|
|
super().__setitem__(key, value * 10)
|
|
|
|
|
|
scaled = Scaled()
|
|
_count_elements(scaled, "aab")
|
|
assert scaled == {"a": 110, "b": 10}
|
|
|
|
|
|
class CustomGet(dict):
|
|
def get(self, key, default=None):
|
|
return 100
|
|
|
|
|
|
custom_get = CustomGet()
|
|
_count_elements(custom_get, "aab")
|
|
assert custom_get == {"a": 101, "b": 101}
|
|
with assert_raises(AttributeError):
|
|
_count_elements([], "a")
|
|
|
|
|
|
# defaultdict union constructs the subclass with its factory and left operand.
|
|
def check_defaultdict_subclass_union():
|
|
calls = []
|
|
|
|
class DefaultSubclass(defaultdict):
|
|
def __init__(self, factory=None, initial=()):
|
|
calls.append((factory, initial))
|
|
super().__init__(factory, initial)
|
|
|
|
def update(self, *args, **kwargs):
|
|
raise AssertionError("union must use native dict update")
|
|
|
|
def __setitem__(self, key, value):
|
|
raise AssertionError("union must not call overridden __setitem__")
|
|
|
|
for factory in (None, list):
|
|
left = DefaultSubclass(factory, {"shared": 1, "left": 2})
|
|
right = {"right": 3, "shared": 4}
|
|
calls.clear()
|
|
result = left | right
|
|
assert type(result) is DefaultSubclass
|
|
assert result.default_factory is factory
|
|
assert list(result.items()) == [("shared", 4), ("left", 2), ("right", 3)]
|
|
assert len(calls) == 1 and calls[0][0] is factory and calls[0][1] is left
|
|
|
|
calls.clear()
|
|
result = right | left
|
|
assert type(result) is DefaultSubclass
|
|
assert result.default_factory is factory
|
|
assert list(result.items()) == [("right", 3), ("shared", 1), ("left", 2)]
|
|
assert len(calls) == 1 and calls[0][0] is factory and calls[0][1] is right
|
|
assert left == {"shared": 1, "left": 2}
|
|
assert right == {"right": 3, "shared": 4}
|
|
|
|
calls.clear()
|
|
assert left.__or__([("x", 1)]) is NotImplemented
|
|
assert left.__ror__([("x", 1)]) is NotImplemented
|
|
assert calls == []
|
|
|
|
class FailingSubclass(defaultdict):
|
|
fail = False
|
|
|
|
def __init__(self, *args):
|
|
if self.fail:
|
|
raise ValueError("union constructor")
|
|
super().__init__(*args)
|
|
|
|
left = FailingSubclass(int, {"a": 1})
|
|
FailingSubclass.fail = True
|
|
with assert_raises(ValueError) as caught:
|
|
left | {}
|
|
assert str(caught.exception) == "union constructor"
|
|
with assert_raises(ValueError) as caught:
|
|
{} | left
|
|
assert str(caught.exception) == "union constructor"
|
|
|
|
class ReplacingSubclass(defaultdict):
|
|
replacement = None
|
|
|
|
def __new__(cls, *args):
|
|
if cls.replacement is not None:
|
|
return cls.replacement
|
|
return super().__new__(cls)
|
|
|
|
left = ReplacingSubclass(int, {"left": 1})
|
|
replacement = {"replacement": 2}
|
|
ReplacingSubclass.replacement = replacement
|
|
assert left | {"right": 3} is replacement
|
|
assert replacement == {"replacement": 2, "right": 3}
|
|
replacement.clear()
|
|
assert {"right": 3} | left is replacement
|
|
assert replacement == {"left": 1}
|
|
ReplacingSubclass.replacement = object()
|
|
with assert_raises(SystemError):
|
|
left | {}
|
|
with assert_raises(SystemError):
|
|
{} | left
|
|
|
|
|
|
check_defaultdict_subclass_union()
|