From 3fbbd58c28b9a519d255b1cd7f359c5144109286 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" Date: Wed, 4 Mar 2026 18:44:27 +0900 Subject: [PATCH] Restore defaultdict fallback and update test markers --- Lib/collections/__init__.py | 3 +- Lib/collections/_defaultdict.py | 58 +++++++++++++++++++++++++++++++++ Lib/test/test_collections.py | 4 +-- Lib/test/test_deque.py | 6 ++-- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 Lib/collections/_defaultdict.py diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 803de0c67..3d3bbd7a3 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -57,7 +57,8 @@ except ImportError: try: from _collections import defaultdict except ImportError: - pass + # TODO: RUSTPYTHON - implement defaultdict in Rust + from ._defaultdict import defaultdict heapq = None # Lazily imported diff --git a/Lib/collections/_defaultdict.py b/Lib/collections/_defaultdict.py new file mode 100644 index 000000000..b9c6c4961 --- /dev/null +++ b/Lib/collections/_defaultdict.py @@ -0,0 +1,58 @@ +from reprlib import recursive_repr as _recursive_repr + +class defaultdict(dict): + def __init__(self, *args, **kwargs): + if len(args) >= 1: + default_factory = args[0] + if default_factory is not None and not callable(default_factory): + raise TypeError("first argument must be callable or None") + args = args[1:] + else: + default_factory = None + super().__init__(*args, **kwargs) + self.default_factory = default_factory + + def __missing__(self, key): + if self.default_factory is not None: + val = self.default_factory() + else: + raise KeyError(key) + self[key] = val + return val + + @_recursive_repr() + def __repr_factory(factory): + return repr(factory) + + def __repr__(self): + return f"{type(self).__name__}({defaultdict.__repr_factory(self.default_factory)}, {dict.__repr__(self)})" + + def copy(self): + return type(self)(self.default_factory, self) + + __copy__ = copy + + def __reduce__(self): + if self.default_factory is not None: + args = self.default_factory, + else: + args = () + return type(self), args, None, None, iter(self.items()) + + def __or__(self, other): + if not isinstance(other, dict): + return NotImplemented + + new = defaultdict(self.default_factory, self) + new.update(other) + return new + + def __ror__(self, other): + if not isinstance(other, dict): + return NotImplemented + + new = defaultdict(self.default_factory, other) + new.update(self) + return new + +defaultdict.__module__ = 'collections' diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 0ed51c9f1..26d0dcb65 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -262,7 +262,7 @@ class TestChainMap(unittest.TestCase): d = c.new_child(b=20, c=30) self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}]) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: .Subclass'> is not def test_union_operators(self): cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4)) cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4)) @@ -2029,7 +2029,7 @@ class TestCollectionABCs(ABCTestCase): self.assertEqual(len(mss), len(mss2)) self.assertEqual(list(mss), list(mss2)) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: TypeError not raised def test_illegal_patma_flags(self): with self.assertRaises(TypeError): class Both(Collection): diff --git a/Lib/test/test_deque.py b/Lib/test/test_deque.py index cd7fc8afd..f798ab4b6 100644 --- a/Lib/test/test_deque.py +++ b/Lib/test/test_deque.py @@ -726,7 +726,7 @@ class TestBasic(unittest.TestCase): d.append(1) gc.collect() - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False is not true : Cycle was not collected def test_container_iterator(self): # Bug #3680: tp_traverse was not implemented for deque iterator objects class C(object): @@ -817,7 +817,7 @@ class TestSubclass(unittest.TestCase): d.clear() self.assertEqual(len(d), 0) - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'Deque' object has no attribute 'x' def test_copy_pickle(self): for cls in Deque, DequeWithSlots: for d in cls('abc'), cls('abcde', maxlen=4): @@ -842,7 +842,7 @@ class TestSubclass(unittest.TestCase): self.assertEqual(e.z, d.z) self.assertNotHasAttr(e, 'y') - @unittest.expectedFailure # TODO: RUSTPYTHON + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'Deque' object has no attribute 'x' def test_pickle_recursive(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): for d in Deque('abc'), Deque('abc', 3):