Files
youknowdot b725188965 Represent OrderedDict views with their contents (#9038)
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
2026-10-10 19:32:22 +09:00

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()