gh-155315: Fix marshal round-trip of shared frozendict references (GH-155316)

serhiy-storchaka <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/b6c11feb677f88f8367e80892b1c1979e16b68ee
commit: b6c11feb677f88f8367e80892b1c1979e16b68ee
branch: main
author: tonghuaroot (童话) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-08-17T10:36:48+03:00
summary:

gh-155315: Fix marshal round-trip of shared frozendict references (GH-155316)

The TYPE_FROZENDICT reader reserved a reference slot but never filled it with
r_ref_insert, unlike TYPE_FROZENSET, so a frozendict referenced more than once
failed to load with ValueError.

files:
A Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-14-45-02.gh-issue-155315.Mk9Fd2.rst
M Lib/test/test_marshal.py
M Python/marshal.c

diff --git a/Lib/test/test_marshal.py b/Lib/test/test_marshal.py
index 9c4d91c456dc5d9..0e71d65f22b4f0d 100644
--- a/Lib/test/test_marshal.py
+++ b/Lib/test/test_marshal.py
@@ -386,6 +386,16 @@ def test_reference_loop_frozendict(self):
         for v in range(marshal.version + 1):
             self.assertRaises(ValueError, marshal.dumps, a, v)
 
+    def test_shared_reference_frozendict(self):
+        # A frozendict referenced more than once must round-trip with the
+        # shared identity preserved, like frozenset.
+        fd = frozendict({'a': 1, 'b': 2})
+        out = marshal.loads(marshal.dumps([fd, fd]))
+        self.assertEqual(out[0], fd)
+        self.assertIs(out[0], out[1])
+        nested = marshal.loads(marshal.dumps(frozendict({'x': fd, 'y': fd})))
+        self.assertIs(nested['x'], nested['y'])
+
     def test_loads_reference_loop_list(self):
         data = b'\xdb\x01\x00\x00\x00r\x00\x00\x00\x00' # [<R>]
         a = marshal.loads(data)
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-14-45-02.gh-issue-155315.Mk9Fd2.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-14-45-02.gh-issue-155315.Mk9Fd2.rst
new file mode 100644
index 000000000000000..a62059c0fa26160
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-08-07-14-45-02.gh-issue-155315.Mk9Fd2.rst
@@ -0,0 +1,3 @@
+Fix :mod:`marshal` so that a ``frozendict`` referenced more than once in the
+serialized data round-trips correctly, instead of failing to load with
+:exc:`ValueError`. Patch by tonghuaroot.
diff --git a/Python/marshal.c b/Python/marshal.c
index 603697e9081c59a..b11f2dca57a226c 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -1505,6 +1505,7 @@ r_object(RFILE *p)
         }
         if (type == TYPE_FROZENDICT && v != NULL) {
             Py_SETREF(v, PyFrozenDict_New(v));
+            v = r_ref_insert(v, idx, flag, p);
         }
         retval = v;
         break;

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.