Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Lib/test/test_marshal.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions Python/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1502,6 +1502,11 @@ r_object(RFILE *p)
}
if (type == TYPE_FROZENDICT && v != NULL) {
Py_SETREF(v, PyFrozenDict_New(v));
/* frozendicts use delayed reference registration (like
* frozensets), so fill the slot reserved above now that the
* object exists; otherwise a later TYPE_REF to a shared
* frozendict resolves to an empty slot. */
v = r_ref_insert(v, idx, flag, p);
}
retval = v;
break;
Expand Down
Loading