@@ -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
@@ -213,13 +214,54 @@ cdef extern from "unpack.h":
213214 PyObject* key
214215
215216 int template_execute(template_context* ctx, const_char_ptr data,
216- size_t len , size_t* off) except - 1
217+ size_t len , size_t* off, bint construct ) except - 1
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 use_list = None , encoding = None , unicode_errors = " strict" ,
264+ object_pairs_hook = None ,
223265 ):
224266 """ Unpack packed_bytes to object. Returns an unpacked object.
225267
@@ -234,39 +276,11 @@ 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)
253279 if use_list is None :
254280 warnings.warn(" Set use_list explicitly." , category = DeprecationWarning , stacklevel = 1 )
255- ctx.user.use_list = 0
256- else :
257- ctx.user.use_list = use_list
258- ctx.user.object_hook = ctx.user.list_hook = NULL
259- ctx.user.encoding = < const_char_ptr> enc
260- ctx.user.unicode_errors = < const_char_ptr> err
261- if object_hook is not None :
262- if not PyCallable_Check(object_hook):
263- raise TypeError (" object_hook must be a callable." )
264- ctx.user.object_hook = < PyObject* > object_hook
265- if list_hook is not None :
266- if not PyCallable_Check(list_hook):
267- raise TypeError (" list_hook must be a callable." )
268- ctx.user.list_hook = < PyObject* > list_hook
269- ret = template_execute(& ctx, buf, buf_len, & off)
281+ use_list = 0
282+ init_ctx(& ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
283+ ret = template_execute(& ctx, buf, buf_len, & off, 1 )
270284 if ret == 1 :
271285 obj = template_data(& ctx)
272286 if off < buf_len:
@@ -278,6 +292,7 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
278292
279293def unpack (object stream , object object_hook = None , object list_hook = None ,
280294 use_list = None , encoding = None , unicode_errors = " strict" ,
295+ object_pairs_hook = None ,
281296 ):
282297 """ Unpack an object from `stream`.
283298
@@ -287,7 +302,7 @@ def unpack(object stream, object object_hook=None, object list_hook=None,
287302 warnings.warn(" Set use_list explicitly." , category = DeprecationWarning , stacklevel = 1 )
288303 use_list = 0
289304 return unpackb(stream.read(), use_list = use_list,
290- object_hook = object_hook, list_hook = list_hook,
305+ object_hook = object_hook, object_pairs_hook = object_pairs_hook, list_hook = list_hook,
291306 encoding = encoding, unicode_errors = unicode_errors,
292307 )
293308
@@ -307,7 +322,10 @@ cdef class Unpacker(object):
307322 Otherwise, it is deserialized to Python tuple.
308323
309324 `object_hook` is same to simplejson. If it is not None, it should be callable
310- and Unpacker calls it when deserializing key-value.
325+ and Unpacker calls it with a dict argument after deserializing a map.
326+
327+ `object_pairs_hook` is same to simplejson. If it is not None, it should be callable
328+ and Unpacker calls it with a list of key-value pairs after deserializing a map.
311329
312330 `encoding` is encoding used for decoding msgpack bytes. If it is None (default),
313331 msgpack bytes is deserialized to Python bytes.
@@ -357,9 +375,8 @@ cdef class Unpacker(object):
357375 self .buf = NULL
358376
359377 def __init__ (self , file_like = None , Py_ssize_t read_size = 0 , use_list = None ,
360- object object_hook = None , object list_hook = None ,
378+ object object_hook = None , object object_pairs_hook = None , object list_hook = None ,
361379 encoding = None , unicode_errors = ' strict' , int max_buffer_size = 0 ,
362- object object_pairs_hook = None ,
363380 ):
364381 if use_list is None :
365382 warnings.warn(" Set use_list explicitly." , category = DeprecationWarning , stacklevel = 1 )
@@ -384,31 +401,7 @@ cdef class Unpacker(object):
384401 self .buf_size = read_size
385402 self .buf_head = 0
386403 self .buf_tail = 0
387- template_init(& self .ctx)
388- self .ctx.user.use_list = use_list
389- self .ctx.user.object_hook = self .ctx.user.list_hook = < PyObject* > NULL
390- if object_hook is not None :
391- if not PyCallable_Check(object_hook):
392- raise TypeError (" object_hook must be a callable." )
393- self .ctx.user.object_hook = < PyObject* > object_hook
394- if list_hook is not None :
395- if not PyCallable_Check(list_hook):
396- raise TypeError (" list_hook must be a callable." )
397- self .ctx.user.list_hook = < PyObject* > list_hook
398- if encoding is None :
399- self .ctx.user.encoding = NULL
400- self .ctx.user.unicode_errors = NULL
401- else :
402- if isinstance (encoding, unicode ):
403- self ._bencoding = encoding.encode(' ascii' )
404- else :
405- self ._bencoding = encoding
406- self .ctx.user.encoding = PyBytes_AsString(self ._bencoding)
407- if isinstance (unicode_errors, unicode ):
408- self ._berrors = unicode_errors.encode(' ascii' )
409- else :
410- self ._berrors = unicode_errors
411- self .ctx.user.unicode_errors = PyBytes_AsString(self ._berrors)
404+ init_ctx(& self .ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
412405
413406 def feed (self , object next_bytes ):
414407 cdef char * buf
@@ -469,15 +462,18 @@ cdef class Unpacker(object):
469462 else :
470463 self .file_like = None
471464
472- cpdef unpack(self ):
473- """ unpack one object"""
465+ cdef object _unpack(self , bint construct):
474466 cdef int ret
467+ cdef object obj
475468 while 1 :
476- ret = template_execute(& self .ctx, self .buf, self .buf_tail, & self .buf_head)
469+ ret = template_execute(& self .ctx, self .buf, self .buf_tail, & self .buf_head, construct )
477470 if ret == 1 :
478- o = template_data(& self .ctx)
471+ if construct:
472+ obj = template_data(& self .ctx)
473+ else :
474+ obj = None
479475 template_init(& self .ctx)
480- return o
476+ return obj
481477 elif ret == 0 :
482478 if self .file_like is not None :
483479 self .read_from_file()
@@ -486,11 +482,19 @@ cdef class Unpacker(object):
486482 else :
487483 raise ValueError (" Unpack failed: error = %d " % (ret,))
488484
485+ def unpack (self ):
486+ """ unpack one object"""
487+ return self ._unpack(1 )
488+
489+ def skip (self ):
490+ """ read and ignore one object, returning None"""
491+ return self ._unpack(0 )
492+
489493 def __iter__ (self ):
490494 return self
491495
492496 def __next__ (self ):
493- return self .unpack( )
497+ return self ._unpack( 1 )
494498
495499 # for debug.
496500 # def _buf(self):
0 commit comments