Skip to content

Commit 41d8757

Browse files
committed
gh-155315: Fix marshal round-trip of shared frozendict references
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.
1 parent 8b048eb commit 41d8757

3 files changed

Lines changed: 18 additions & 0 deletions

File tree

Lib/test/test_marshal.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,16 @@ def test_reference_loop_frozendict(self):
386386
for v in range(marshal.version + 1):
387387
self.assertRaises(ValueError, marshal.dumps, a, v)
388388

389+
def test_shared_reference_frozendict(self):
390+
# A frozendict referenced more than once must round-trip with the
391+
# shared identity preserved, like frozenset.
392+
fd = frozendict({'a': 1, 'b': 2})
393+
out = marshal.loads(marshal.dumps([fd, fd]))
394+
self.assertEqual(out[0], fd)
395+
self.assertIs(out[0], out[1])
396+
nested = marshal.loads(marshal.dumps(frozendict({'x': fd, 'y': fd})))
397+
self.assertIs(nested['x'], nested['y'])
398+
389399
def test_loads_reference_loop_list(self):
390400
data = b'\xdb\x01\x00\x00\x00r\x00\x00\x00\x00' # [<R>]
391401
a = marshal.loads(data)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :mod:`marshal` so that a ``frozendict`` referenced more than once in the
2+
serialized data round-trips correctly, instead of failing to load with
3+
:exc:`ValueError`. Patch by tonghuaroot.

Python/marshal.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,11 @@ r_object(RFILE *p)
15021502
}
15031503
if (type == TYPE_FROZENDICT && v != NULL) {
15041504
Py_SETREF(v, PyFrozenDict_New(v));
1505+
/* frozendicts use delayed reference registration (like
1506+
* frozensets), so fill the slot reserved above now that the
1507+
* object exists; otherwise a later TYPE_REF to a shared
1508+
* frozendict resolves to an empty slot. */
1509+
v = r_ref_insert(v, idx, flag, p);
15051510
}
15061511
retval = v;
15071512
break;

0 commit comments

Comments
 (0)