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
21 changes: 8 additions & 13 deletions Lib/test/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1991,19 +1991,14 @@ def test_pickle(self):
if type(fd) == FrozenDictSlots:
fd.slot_attr = 456
with self.subTest(fd=fd, proto=proto):
if proto >= 2:
p = pickle.dumps(fd, proto)
fd2 = pickle.loads(p)
self.assertEqual(fd2, fd)
self.assertEqual(type(fd2), type(fd))
if type(fd) == FrozenDict:
self.assertEqual(fd2.attr, 123)
if type(fd) == FrozenDictSlots:
self.assertEqual(fd2.slot_attr, 456)
else:
# protocol 0 and 1 don't support frozendict
with self.assertRaises(TypeError):
pickle.dumps(fd, proto)
p = pickle.dumps(fd, proto)
fd2 = pickle.loads(p)
self.assertEqual(fd2, fd)
self.assertEqual(type(fd2), type(fd))
if type(fd) == FrozenDict:
self.assertEqual(fd2.attr, 123)
if type(fd) == FrozenDictSlots:
self.assertEqual(fd2.slot_attr, 456)

def test_pickle_iter(self):
fd = frozendict(c=1, b=2, a=3, d=4, e=5, f=6)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
``frozendict`` can now be pickled with all pickle protocols, matching
:class:`frozenset`. Previously protocols 0 and 1 raised :exc:`TypeError`.
Patch by tonghuaroot.
24 changes: 20 additions & 4 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -8344,14 +8344,29 @@ _PyObject_InlineValuesConsistencyCheck(PyObject *obj)
// --- frozendict implementation ---------------------------------------------

static PyObject *
frozendict_getnewargs(PyObject *op, PyObject *Py_UNUSED(dummy))
frozendict_reduce(PyObject *op, PyObject *Py_UNUSED(dummy))
{
// Call dict(op): convert 'op' frozendict to a dict
// Mirror frozenset.__reduce__ so frozendict pickles at every protocol
// (__getnewargs__ alone only works at protocol 2 and above).
// Call dict(op) to convert the frozendict to a dict argument.
PyObject *arg = PyObject_CallOneArg((PyObject*)&PyDict_Type, op);
if (arg == NULL) {
return NULL;
}
return Py_BuildValue("(N)", arg);
PyObject *args = PyTuple_Pack(1, arg);
Py_DECREF(arg);
if (args == NULL) {
return NULL;
}
PyObject *state = _PyObject_GetState(op);
if (state == NULL) {
Py_DECREF(args);
return NULL;
}
PyObject *result = PyTuple_Pack(3, Py_TYPE(op), args, state);
Py_DECREF(args);
Py_DECREF(state);
return result;
}


Expand All @@ -8377,7 +8392,8 @@ static PyMethodDef frozendict_methods[] = {
DICT___REVERSED___METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS,
PyDoc_STR("frozendicts are generic over two types, signifying (respectively) the types of the frozendict's keys and values")},
{"__getnewargs__", frozendict_getnewargs, METH_NOARGS},
{"__reduce__", frozendict_reduce, METH_NOARGS,
PyDoc_STR("Return state information for pickling.")},
{NULL, NULL} /* sentinel */
};

Expand Down
Loading