Skip to content

Commit 3d89784

Browse files
committed
Add list_hook option to unpacker.
1 parent 0076d42 commit 3d89784

4 files changed

Lines changed: 43 additions & 13 deletions

File tree

msgpack/_msgpack.pyx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ cdef extern from "pack.h":
5252
int msgpack_pack_raw(msgpack_packer* pk, size_t l)
5353
int msgpack_pack_raw_body(msgpack_packer* pk, char* body, size_t l)
5454

55+
cdef int DEFAULT_RECURSE_LIMIT=511
5556

5657
cdef class Packer(object):
5758
"""MessagePack Packer
@@ -80,7 +81,8 @@ cdef class Packer(object):
8081
def __dealloc__(self):
8182
free(self.pk.buf);
8283

83-
cdef int _pack(self, object o, int nest_limit=511, default=None) except -1:
84+
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT,
85+
default=None) except -1:
8486
cdef long long llval
8587
cdef unsigned long long ullval
8688
cdef long longval
@@ -148,7 +150,7 @@ cdef class Packer(object):
148150

149151
def pack(self, object obj):
150152
cdef int ret
151-
ret = self._pack(obj, self.default)
153+
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT, self.default)
152154
if ret:
153155
raise TypeError
154156
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
@@ -172,6 +174,7 @@ cdef extern from "unpack.h":
172174
ctypedef struct msgpack_user:
173175
int use_list
174176
PyObject* object_hook
177+
PyObject* list_hook
175178

176179
ctypedef struct template_context:
177180
msgpack_user user
@@ -186,19 +189,23 @@ cdef extern from "unpack.h":
186189
object template_data(template_context* ctx)
187190

188191

189-
def unpackb(bytes packed_bytes, object object_hook=None):
192+
def unpackb(bytes packed_bytes, object object_hook=None, object list_hook=None):
190193
"""Unpack packed_bytes to object. Returns an unpacked object."""
191194
cdef const_char_ptr p = packed_bytes
192195
cdef template_context ctx
193196
cdef size_t off = 0
194197
cdef int ret
195198
template_init(&ctx)
196199
ctx.user.use_list = 0
197-
ctx.user.object_hook = NULL
200+
ctx.user.object_hook = ctx.user.list_hook = NULL
198201
if object_hook is not None:
199202
if not PyCallable_Check(object_hook):
200203
raise TypeError("object_hook must be a callable.")
201204
ctx.user.object_hook = <PyObject*>object_hook
205+
if list_hook is not None:
206+
if not PyCallable_Check(list_hook):
207+
raise TypeError("list_hook must be a callable.")
208+
ctx.user.list_hook = <PyObject*>list_hook
202209
ret = template_execute(&ctx, p, len(packed_bytes), &off)
203210
if ret == 1:
204211
return template_data(&ctx)
@@ -207,10 +214,10 @@ def unpackb(bytes packed_bytes, object object_hook=None):
207214

208215
unpacks = unpackb
209216

210-
def unpack(object stream, object object_hook=None):
217+
def unpack(object stream, object object_hook=None, object list_hook=None):
211218
"""unpack an object from stream."""
212219
packed = stream.read()
213-
return unpackb(packed, object_hook=object_hook)
220+
return unpackb(packed, object_hook=object_hook, list_hook=list_hook)
214221

215222
cdef class UnpackIterator(object):
216223
cdef object unpacker
@@ -265,7 +272,7 @@ cdef class Unpacker(object):
265272
free(self.buf);
266273

267274
def __init__(self, file_like=None, int read_size=0, bint use_list=0,
268-
object object_hook=None):
275+
object object_hook=None, object list_hook=None):
269276
if read_size == 0:
270277
read_size = 1024*1024
271278
self.use_list = use_list
@@ -278,11 +285,15 @@ cdef class Unpacker(object):
278285
self.buf_tail = 0
279286
template_init(&self.ctx)
280287
self.ctx.user.use_list = use_list
281-
self.ctx.user.object_hook = <PyObject*>NULL
288+
self.ctx.user.object_hook = self.ctx.user.list_hook = <PyObject*>NULL
282289
if object_hook is not None:
283290
if not PyCallable_Check(object_hook):
284291
raise TypeError("object_hook must be a callable.")
285292
self.ctx.user.object_hook = <PyObject*>object_hook
293+
if list_hook is not None:
294+
if not PyCallable_Check(list_hook):
295+
raise TypeError("object_hook must be a callable.")
296+
self.ctx.user.list_hook = <PyObject*>list_hook
286297

287298
def feed(self, bytes next_bytes):
288299
self.waiting_bytes.append(next_bytes)

msgpack/unpack.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
typedef struct unpack_user {
2323
int use_list;
2424
PyObject *object_hook;
25+
PyObject *list_hook;
2526
} unpack_user;
2627

2728

@@ -154,6 +155,16 @@ static inline int template_callback_array_item(unpack_user* u, unsigned int curr
154155
return 0;
155156
}
156157

158+
static inline int template_callback_array_end(unpack_user* u, msgpack_unpack_object* c)
159+
{
160+
if (u->list_hook) {
161+
PyObject *arglist = Py_BuildValue("(O)", *c);
162+
*c = PyEval_CallObject(u->list_hook, arglist);
163+
Py_DECREF(arglist);
164+
}
165+
return 0;
166+
}
167+
157168
static inline int template_callback_map(unpack_user* u, unsigned int n, msgpack_unpack_object* o)
158169
{
159170
PyObject *p = PyDict_New();
@@ -173,16 +184,14 @@ static inline int template_callback_map_item(unpack_user* u, msgpack_unpack_obje
173184
return -1;
174185
}
175186

176-
//static inline int template_callback_map_end(unpack_user* u, msgpack_unpack_object* c)
177-
int template_callback_map_end(unpack_user* u, msgpack_unpack_object* c)
187+
static inline int template_callback_map_end(unpack_user* u, msgpack_unpack_object* c)
178188
{
179189
if (u->object_hook) {
180190
PyObject *arglist = Py_BuildValue("(O)", *c);
181191
*c = PyEval_CallObject(u->object_hook, arglist);
182192
Py_DECREF(arglist);
183-
return 0;
184193
}
185-
return -1;
194+
return 0;
186195
}
187196

188197
static inline int template_callback_raw(unpack_user* u, const char* b, const char* p, unsigned int l, msgpack_unpack_object* o)

msgpack/unpack_template.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const c
304304
case CT_ARRAY_ITEM:
305305
if(msgpack_unpack_callback(_array_item)(user, c->curr, &c->obj, obj) < 0) { goto _failed; }
306306
if(++c->curr == c->count) {
307+
msgpack_unpack_callback(_array_end)(user, &c->obj);
307308
obj = c->obj;
308309
--top;
309310
/*printf("stack pop %d\n", top);*/
@@ -317,7 +318,7 @@ msgpack_unpack_func(int, _execute)(msgpack_unpack_struct(_context)* ctx, const c
317318
case CT_MAP_VALUE:
318319
if(msgpack_unpack_callback(_map_item)(user, &c->obj, c->map_key, obj) < 0) { goto _failed; }
319320
if(--c->count == 0) {
320-
msgpack_unpack_callback(_map_end)(user, &c->obj);
321+
msgpack_unpack_callback(_map_end)(user, &c->obj);
321322
obj = c->obj;
322323
--top;
323324
/*printf("stack pop %d\n", top);*/

test/test_obj.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,16 @@ def test_bad_hook():
3131
packed = packs([3, 1+2j], default=lambda o: o)
3232
unpacked = unpacks(packed)
3333

34+
def _arr_to_str(arr):
35+
return ''.join(str(c) for c in arr)
36+
37+
def test_array_hook():
38+
packed = packs([1,2,3])
39+
unpacked = unpacks(packed, list_hook=_arr_to_str)
40+
eq_(unpacked, '123')
41+
3442
if __name__ == '__main__':
3543
test_decode_hook()
3644
test_encode_hook()
3745
test_bad_hook()
46+
test_array_hook()

0 commit comments

Comments
 (0)