From 01a5ae599fcd521da8f3c9145435d73e2b1ec06b Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 26 Mar 2026 14:51:59 +0200 Subject: [PATCH] gh-145056: Fix merging of collections.OrderedDict and frozendict --- Lib/collections/__init__.py | 4 ++-- Lib/test/test_ordered_dict.py | 6 ++++++ .../Library/2026-03-26-14-51-55.gh-issue-145056.QS-6l1.rst | 1 + Objects/odictobject.c | 4 ++-- 4 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-03-26-14-51-55.gh-issue-145056.QS-6l1.rst diff --git a/Lib/collections/__init__.py b/Lib/collections/__init__.py index 2eee4c70955513..ba60df037f28cb 100644 --- a/Lib/collections/__init__.py +++ b/Lib/collections/__init__.py @@ -328,14 +328,14 @@ def __ior__(self, other): return self def __or__(self, other): - if not isinstance(other, dict): + if not isinstance(other, (dict, frozendict)): return NotImplemented new = self.__class__(self) new.update(other) return new def __ror__(self, other): - if not isinstance(other, dict): + if not isinstance(other, (dict, frozendict)): return NotImplemented new = self.__class__(other) new.update(self) diff --git a/Lib/test/test_ordered_dict.py b/Lib/test/test_ordered_dict.py index 4204a6a47d2a81..642c2722711c7c 100644 --- a/Lib/test/test_ordered_dict.py +++ b/Lib/test/test_ordered_dict.py @@ -698,6 +698,7 @@ def test_merge_operator(self): d |= list(b.items()) expected = OrderedDict({0: 0, 1: 1, 2: 2, 3: 3}) self.assertEqual(a | dict(b), expected) + self.assertEqual(a | frozendict(b), expected) self.assertEqual(a | b, expected) self.assertEqual(c, expected) self.assertEqual(d, expected) @@ -706,12 +707,17 @@ def test_merge_operator(self): c |= a expected = OrderedDict({1: 1, 2: 1, 3: 3, 0: 0}) self.assertEqual(dict(b) | a, expected) + self.assertEqual(frozendict(b) | a, expected) + self.assertEqual(a.__ror__(frozendict(b)), expected) self.assertEqual(b | a, expected) self.assertEqual(c, expected) self.assertIs(type(a | b), OrderedDict) self.assertIs(type(dict(a) | b), OrderedDict) + self.assertIs(type(frozendict(a) | b), frozendict) + self.assertIs(type(b.__ror__(frozendict(a))), OrderedDict) self.assertIs(type(a | dict(b)), OrderedDict) + self.assertIs(type(a | frozendict(b)), OrderedDict) expected = a.copy() a |= () diff --git a/Misc/NEWS.d/next/Library/2026-03-26-14-51-55.gh-issue-145056.QS-6l1.rst b/Misc/NEWS.d/next/Library/2026-03-26-14-51-55.gh-issue-145056.QS-6l1.rst new file mode 100644 index 00000000000000..4eaabfbb9a87a0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-03-26-14-51-55.gh-issue-145056.QS-6l1.rst @@ -0,0 +1 @@ +Fix merging of :class:`collections.OrderedDict` and :class:`frozendict`. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 25928028919c9c..21486806653ad0 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -906,7 +906,7 @@ odict_or(PyObject *left, PyObject *right) type = Py_TYPE(right); other = left; } - if (!PyDict_Check(other)) { + if (!PyAnyDict_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } PyObject *new = PyObject_CallOneArg((PyObject*)type, left); @@ -2271,7 +2271,7 @@ static int mutablemapping_update_arg(PyObject *self, PyObject *arg) { int res = 0; - if (PyDict_CheckExact(arg)) { + if (PyAnyDict_CheckExact(arg)) { PyObject *items = PyDict_Items(arg); if (items == NULL) { return -1;