Skip to content

Commit 9a2739d

Browse files
thehesiodasvetlov
authored andcommitted
fix pickling of multidicts (#77)
* fix pickling of multidicts * add unit test * fix unittest and simplify code * disable pickling of proxies as we can't unpickle them correctly * flake * changes based on review use py3.6 exception type * fix test * fix unittest * remove old import * pep fix
1 parent 2a6e20c commit 9a2739d

3 files changed

Lines changed: 36 additions & 20 deletions

File tree

multidict/_multidict.pyx

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ from __future__ import absolute_import
33
import sys
44
from collections import abc
55
from collections.abc import Iterable, Set
6-
from operator import itemgetter
76

87
from cpython.object cimport PyObject_Str
98

@@ -216,37 +215,34 @@ cdef class _Base:
216215

217216

218217
cdef class MultiDictProxy(_Base):
218+
_proxy_classes = (MultiDict, MultiDictProxy)
219+
_base_class = MultiDict
219220

220221
def __init__(self, arg):
221222
cdef _Base base
222-
if not isinstance(arg, (MultiDict, MultiDictProxy)):
223+
if not isinstance(arg, self._proxy_classes):
223224
raise TypeError(
224-
'ctor requires MultiDict or MultiDictProxy instance'
225+
'ctor requires {} instance'
225226
', not {}'.format(
227+
' or '.join(self._proxy_classes),
226228
type(arg)))
227229

228230
base = arg
229231
self._impl = base._impl
230232

233+
def __reduce__(self):
234+
raise TypeError("can't pickle {} objects".format(self.__class__.__name__))
235+
231236
def copy(self):
232237
"""Return a copy of itself."""
233-
return MultiDict(self)
238+
return self._base_class(self)
234239

235240
abc.Mapping.register(MultiDictProxy)
236241

237242

238243
cdef class CIMultiDictProxy(MultiDictProxy):
239-
240-
def __init__(self, arg):
241-
cdef _Base base
242-
if not isinstance(arg, (CIMultiDict, CIMultiDictProxy)):
243-
raise TypeError(
244-
'ctor requires CIMultiDict or CIMultiDictProxy instance'
245-
', not {}'.format(
246-
type(arg)))
247-
248-
base = arg
249-
self._impl = base._impl
244+
_proxy_classes = (CIMultiDict, CIMultiDictProxy)
245+
_base_class = CIMultiDict
250246

251247
cdef str _title(self, s):
252248
typ = type(s)
@@ -256,10 +252,6 @@ cdef class CIMultiDictProxy(MultiDictProxy):
256252
return PyObject_Str(s)
257253
return s.title()
258254

259-
def copy(self):
260-
"""Return a copy of itself."""
261-
return CIMultiDict(self)
262-
263255

264256
abc.Mapping.register(CIMultiDictProxy)
265257

@@ -282,9 +274,14 @@ cdef class MultiDict(_Base):
282274

283275
def __init__(self, *args, **kwargs):
284276
self._impl = _Impl()
285-
286277
self._extend(args, kwargs, 'MultiDict', True)
287278

279+
def __reduce__(self):
280+
return (
281+
self.__class__,
282+
tuple(self.items()),
283+
)
284+
288285
cdef _extend(self, tuple args, dict kwargs, name, bint do_add):
289286
cdef _Pair item
290287
cdef object key

multidict/_multidict_py.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ def __init__(self, arg):
155155

156156
self._impl = arg._impl
157157

158+
def __reduce__(self):
159+
raise TypeError("can't pickle {} objects".format(
160+
self.__class__.__name__))
161+
158162
def copy(self):
159163
"""Return a copy of itself."""
160164
return MultiDict(self.items())

tests/test_multidict.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import sys
22
import unittest
3+
import pickle
34

45
import multidict
56
from multidict._multidict import (MultiDictProxy,
@@ -327,6 +328,20 @@ def test_values__repr__(self):
327328
self.assertEqual(repr(d.values()),
328329
"_ValuesView('value1', 'value2')")
329330

331+
def test_pickle(self):
332+
d = self.make_dict([('a', 1), ('a', 2)])
333+
334+
if isinstance(d, (MultiDictProxy, _MultiDictProxy)):
335+
with self.assertRaises(TypeError):
336+
pbytes = pickle.dumps(d)
337+
338+
return
339+
else:
340+
pbytes = pickle.dumps(d)
341+
342+
obj = pickle.loads(pbytes)
343+
self.assertEqual(dict(d), dict(obj))
344+
330345

331346
class _CIMultiDictTests(_Root):
332347

0 commit comments

Comments
 (0)