@@ -201,6 +201,7 @@ cdef extern from "unpack.h":
201201 ctypedef struct msgpack_user:
202202 bint use_list
203203 PyObject* object_hook
204+ bint has_pairs_hook # call object_hook with k-v pairs
204205 PyObject* list_hook
205206 char * encoding
206207 char * unicode_errors
@@ -217,9 +218,50 @@ cdef extern from "unpack.h":
217218 void template_init(template_context* ctx)
218219 object template_data(template_context* ctx)
219220
221+ cdef inline init_ctx(template_context * ctx, object object_hook, object object_pairs_hook, object list_hook, bint use_list, encoding, unicode_errors):
222+ template_init(ctx)
223+ ctx.user.use_list = use_list
224+ ctx.user.object_hook = ctx.user.list_hook = < PyObject* > NULL
225+
226+ if object_hook is not None and object_pairs_hook is not None :
227+ raise ValueError (" object_pairs_hook and object_hook are mutually exclusive." )
228+
229+ if object_hook is not None :
230+ if not PyCallable_Check(object_hook):
231+ raise TypeError (" object_hook must be a callable." )
232+ ctx.user.object_hook = < PyObject* > object_hook
233+
234+ if object_pairs_hook is None :
235+ ctx.user.has_pairs_hook = False
236+ else :
237+ if not PyCallable_Check(object_pairs_hook):
238+ raise TypeError (" object_pairs_hook must be a callable." )
239+ ctx.user.object_hook = < PyObject* > object_pairs_hook
240+ ctx.user.has_pairs_hook = True
241+
242+ if list_hook is not None :
243+ if not PyCallable_Check(list_hook):
244+ raise TypeError (" list_hook must be a callable." )
245+ ctx.user.list_hook = < PyObject* > list_hook
246+
247+ if encoding is None :
248+ ctx.user.encoding = NULL
249+ ctx.user.unicode_errors = NULL
250+ else :
251+ if isinstance (encoding, unicode ):
252+ _bencoding = encoding.encode(' ascii' )
253+ else :
254+ _bencoding = encoding
255+ ctx.user.encoding = PyBytes_AsString(_bencoding)
256+ if isinstance (unicode_errors, unicode ):
257+ _berrors = unicode_errors.encode(' ascii' )
258+ else :
259+ _berrors = unicode_errors
260+ ctx.user.unicode_errors = PyBytes_AsString(_berrors)
220261
221262def unpackb (object packed , object object_hook = None , object list_hook = None ,
222263 bint use_list = 1 , encoding = None , unicode_errors = " strict" ,
264+ object_pairs_hook = None ,
223265 ):
224266 """ Unpack packed_bytes to object. Returns an unpacked object.
225267
@@ -234,34 +276,7 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
234276
235277 PyObject_AsReadBuffer(packed, < const_void_ptr* > & buf, & buf_len)
236278
237- if encoding is None :
238- enc = NULL
239- err = NULL
240- else :
241- if isinstance (encoding, unicode ):
242- bencoding = encoding.encode(' ascii' )
243- else :
244- bencoding = encoding
245- if isinstance (unicode_errors, unicode ):
246- berrors = unicode_errors.encode(' ascii' )
247- else :
248- berrors = unicode_errors
249- enc = PyBytes_AsString(bencoding)
250- err = PyBytes_AsString(berrors)
251-
252- template_init(& ctx)
253- ctx.user.use_list = use_list
254- ctx.user.object_hook = ctx.user.list_hook = NULL
255- ctx.user.encoding = < const_char_ptr> enc
256- ctx.user.unicode_errors = < const_char_ptr> err
257- if object_hook is not None :
258- if not PyCallable_Check(object_hook):
259- raise TypeError (" object_hook must be a callable." )
260- ctx.user.object_hook = < PyObject* > object_hook
261- if list_hook is not None :
262- if not PyCallable_Check(list_hook):
263- raise TypeError (" list_hook must be a callable." )
264- ctx.user.list_hook = < PyObject* > list_hook
279+ init_ctx(& ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
265280 ret = template_execute(& ctx, buf, buf_len, & off, 1 )
266281 if ret == 1 :
267282 obj = template_data(& ctx)
@@ -274,13 +289,14 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
274289
275290def unpack (object stream , object object_hook = None , object list_hook = None ,
276291 bint use_list = 1 , encoding = None , unicode_errors = " strict" ,
292+ object_pairs_hook = None ,
277293 ):
278294 """ Unpack an object from `stream`.
279295
280296 Raises `ValueError` when `stream` has extra bytes.
281297 """
282298 return unpackb(stream.read(), use_list = use_list,
283- object_hook = object_hook, list_hook = list_hook,
299+ object_hook = object_hook, object_pairs_hook = object_pairs_hook, list_hook = list_hook,
284300 encoding = encoding, unicode_errors = unicode_errors,
285301 )
286302
@@ -300,7 +316,10 @@ cdef class Unpacker(object):
300316 Otherwise, it is deserialized to Python tuple.
301317
302318 `object_hook` is same to simplejson. If it is not None, it should be callable
303- and Unpacker calls it when deserializing key-value.
319+ and Unpacker calls it with a dict argument after deserializing a map.
320+
321+ `object_pairs_hook` is same to simplejson. If it is not None, it should be callable
322+ and Unpacker calls it with a list of key-value pairs after deserializing a map.
304323
305324 `encoding` is encoding used for decoding msgpack bytes. If it is None (default),
306325 msgpack bytes is deserialized to Python bytes.
@@ -350,9 +369,8 @@ cdef class Unpacker(object):
350369 self .buf = NULL
351370
352371 def __init__ (self , file_like = None , Py_ssize_t read_size = 0 , bint use_list = 1 ,
353- object object_hook = None , object list_hook = None ,
372+ object object_hook = None , object object_pairs_hook = None , object list_hook = None ,
354373 encoding = None , unicode_errors = ' strict' , int max_buffer_size = 0 ,
355- object object_pairs_hook = None ,
356374 ):
357375 self .file_like = file_like
358376 if file_like:
@@ -373,31 +391,7 @@ cdef class Unpacker(object):
373391 self .buf_size = read_size
374392 self .buf_head = 0
375393 self .buf_tail = 0
376- template_init(& self .ctx)
377- self .ctx.user.use_list = use_list
378- self .ctx.user.object_hook = self .ctx.user.list_hook = < PyObject* > NULL
379- if object_hook is not None :
380- if not PyCallable_Check(object_hook):
381- raise TypeError (" object_hook must be a callable." )
382- self .ctx.user.object_hook = < PyObject* > object_hook
383- if list_hook is not None :
384- if not PyCallable_Check(list_hook):
385- raise TypeError (" list_hook must be a callable." )
386- self .ctx.user.list_hook = < PyObject* > list_hook
387- if encoding is None :
388- self .ctx.user.encoding = NULL
389- self .ctx.user.unicode_errors = NULL
390- else :
391- if isinstance (encoding, unicode ):
392- self ._bencoding = encoding.encode(' ascii' )
393- else :
394- self ._bencoding = encoding
395- self .ctx.user.encoding = PyBytes_AsString(self ._bencoding)
396- if isinstance (unicode_errors, unicode ):
397- self ._berrors = unicode_errors.encode(' ascii' )
398- else :
399- self ._berrors = unicode_errors
400- self .ctx.user.unicode_errors = PyBytes_AsString(self ._berrors)
394+ init_ctx(& self .ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
401395
402396 def feed (self , object next_bytes ):
403397 cdef char * buf
0 commit comments