Skip to content

Commit df4f237

Browse files
committed
Merge commit 'd5f9995' into read_bytes
Conflicts: msgpack/_msgpack.pyx
2 parents e7c51d9 + d5f9995 commit df4f237

16 files changed

+437
-142
lines changed

ChangeLog.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
0.3.0
2+
=====
3+
:release date: in development
4+
5+
Changes
6+
-------
7+
* Add ``.skip()`` method to ``Unpacker`` (thanks to jnothman)
8+
9+
10+
0.2.3
11+
=======
12+
:release date: in development
13+
14+
Changes
15+
-------
16+
* Warn when use_list is not specified. It's default value will be changed in 0.3.
17+
18+
Bugs fixed
19+
-----------
20+
* Can't pack subclass of dict.
21+
122
0.2.2
223
=======
324
:release date: 2012-09-21

ROADMAP.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
0.2 series
2+
==========
3+
Improve compatibility to simplejson.
4+
5+
0.3 series
6+
==========
7+
Add features msgpack-ruby has.

msgpack/_msgpack.pyx

Lines changed: 125 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
# coding: utf-8
22
#cython: embedsignature=True
33

4+
import warnings
5+
46
from cpython cimport *
57
cdef extern from "Python.h":
68
ctypedef char* const_char_ptr "const char*"
79
ctypedef char* const_void_ptr "const void*"
810
ctypedef struct PyObject
911
cdef int PyObject_AsReadBuffer(object o, const_void_ptr* buff, Py_ssize_t* buf_len) except -1
12+
char* __FILE__
13+
int __LINE__
1014

1115
from libc.stdlib cimport *
1216
from libc.string cimport *
@@ -139,11 +143,19 @@ cdef class Packer(object):
139143
ret = msgpack_pack_raw(&self.pk, len(o))
140144
if ret == 0:
141145
ret = msgpack_pack_raw_body(&self.pk, rawval, len(o))
142-
elif PyDict_Check(o):
146+
elif PyDict_CheckExact(o):
143147
d = <dict>o
144148
ret = msgpack_pack_map(&self.pk, len(d))
145149
if ret == 0:
146-
for k,v in d.iteritems():
150+
for k, v in d.iteritems():
151+
ret = self._pack(k, nest_limit-1)
152+
if ret != 0: break
153+
ret = self._pack(v, nest_limit-1)
154+
if ret != 0: break
155+
elif PyDict_Check(o):
156+
ret = msgpack_pack_map(&self.pk, len(o))
157+
if ret == 0:
158+
for k, v in o.items():
147159
ret = self._pack(k, nest_limit-1)
148160
if ret != 0: break
149161
ret = self._pack(v, nest_limit-1)
@@ -170,6 +182,17 @@ cdef class Packer(object):
170182
self.pk.length = 0
171183
return buf
172184

185+
cpdef pack_array_header(self, size_t size):
186+
msgpack_pack_array(&self.pk, size)
187+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
188+
self.pk.length = 0
189+
return buf
190+
191+
cpdef pack_map_header(self, size_t size):
192+
msgpack_pack_map(&self.pk, size)
193+
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
194+
self.pk.length = 0
195+
return buf
173196

174197
def pack(object o, object stream, default=None, encoding='utf-8', unicode_errors='strict'):
175198
"""
@@ -187,8 +210,9 @@ def packb(object o, default=None, encoding='utf-8', unicode_errors='strict', use
187210

188211
cdef extern from "unpack.h":
189212
ctypedef struct msgpack_user:
190-
int use_list
213+
bint use_list
191214
PyObject* object_hook
215+
bint has_pairs_hook # call object_hook with k-v pairs
192216
PyObject* list_hook
193217
char *encoding
194218
char *unicode_errors
@@ -200,71 +224,98 @@ cdef extern from "unpack.h":
200224
unsigned int ct
201225
PyObject* key
202226

203-
int template_execute(template_context* ctx, const_char_ptr data,
204-
size_t len, size_t* off) except -1
227+
ctypedef int (*execute_fn)(template_context* ctx, const_char_ptr data,
228+
size_t len, size_t* off) except -1
229+
execute_fn template_construct
230+
execute_fn template_skip
231+
execute_fn read_array_header
232+
execute_fn read_map_header
205233
void template_init(template_context* ctx)
206234
object template_data(template_context* ctx)
207235

236+
cdef inline init_ctx(template_context *ctx, object object_hook, object object_pairs_hook, object list_hook, bint use_list, encoding, unicode_errors):
237+
template_init(ctx)
238+
ctx.user.use_list = use_list
239+
ctx.user.object_hook = ctx.user.list_hook = <PyObject*>NULL
240+
241+
if object_hook is not None and object_pairs_hook is not None:
242+
raise ValueError("object_pairs_hook and object_hook are mutually exclusive.")
243+
244+
if object_hook is not None:
245+
if not PyCallable_Check(object_hook):
246+
raise TypeError("object_hook must be a callable.")
247+
ctx.user.object_hook = <PyObject*>object_hook
248+
249+
if object_pairs_hook is None:
250+
ctx.user.has_pairs_hook = False
251+
else:
252+
if not PyCallable_Check(object_pairs_hook):
253+
raise TypeError("object_pairs_hook must be a callable.")
254+
ctx.user.object_hook = <PyObject*>object_pairs_hook
255+
ctx.user.has_pairs_hook = True
256+
257+
if list_hook is not None:
258+
if not PyCallable_Check(list_hook):
259+
raise TypeError("list_hook must be a callable.")
260+
ctx.user.list_hook = <PyObject*>list_hook
261+
262+
if encoding is None:
263+
ctx.user.encoding = NULL
264+
ctx.user.unicode_errors = NULL
265+
else:
266+
if isinstance(encoding, unicode):
267+
_bencoding = encoding.encode('ascii')
268+
else:
269+
_bencoding = encoding
270+
ctx.user.encoding = PyBytes_AsString(_bencoding)
271+
if isinstance(unicode_errors, unicode):
272+
_berrors = unicode_errors.encode('ascii')
273+
else:
274+
_berrors = unicode_errors
275+
ctx.user.unicode_errors = PyBytes_AsString(_berrors)
208276

209277
def unpackb(object packed, object object_hook=None, object list_hook=None,
210-
bint use_list=0, encoding=None, unicode_errors="strict",
278+
bint use_list=1, encoding=None, unicode_errors="strict",
279+
object_pairs_hook=None,
211280
):
281+
"""Unpack packed_bytes to object. Returns an unpacked object.
282+
283+
Raises `ValueError` when `packed` contains extra bytes.
212284
"""
213-
Unpack packed_bytes to object. Returns an unpacked object."""
214285
cdef template_context ctx
215286
cdef size_t off = 0
216287
cdef int ret
217288

218289
cdef char* buf
219290
cdef Py_ssize_t buf_len
220-
PyObject_AsReadBuffer(packed, <const_void_ptr*>&buf, &buf_len)
221291

222-
if encoding is None:
223-
enc = NULL
224-
err = NULL
225-
else:
226-
if isinstance(encoding, unicode):
227-
bencoding = encoding.encode('ascii')
228-
else:
229-
bencoding = encoding
230-
if isinstance(unicode_errors, unicode):
231-
berrors = unicode_errors.encode('ascii')
232-
else:
233-
berrors = unicode_errors
234-
enc = PyBytes_AsString(bencoding)
235-
err = PyBytes_AsString(berrors)
292+
PyObject_AsReadBuffer(packed, <const_void_ptr*>&buf, &buf_len)
236293

237-
template_init(&ctx)
238-
ctx.user.use_list = use_list
239-
ctx.user.object_hook = ctx.user.list_hook = NULL
240-
ctx.user.encoding = <const_char_ptr>enc
241-
ctx.user.unicode_errors = <const_char_ptr>err
242-
if object_hook is not None:
243-
if not PyCallable_Check(object_hook):
244-
raise TypeError("object_hook must be a callable.")
245-
ctx.user.object_hook = <PyObject*>object_hook
246-
if list_hook is not None:
247-
if not PyCallable_Check(list_hook):
248-
raise TypeError("list_hook must be a callable.")
249-
ctx.user.list_hook = <PyObject*>list_hook
250-
ret = template_execute(&ctx, buf, buf_len, &off)
294+
init_ctx(&ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
295+
ret = template_construct(&ctx, buf, buf_len, &off)
251296
if ret == 1:
252-
return template_data(&ctx)
297+
obj = template_data(&ctx)
298+
if off < buf_len:
299+
raise ValueError("Extra data.")
300+
return obj
253301
else:
254302
return None
255303

256304

257305
def unpack(object stream, object object_hook=None, object list_hook=None,
258-
bint use_list=0, encoding=None, unicode_errors="strict",
306+
bint use_list=1, encoding=None, unicode_errors="strict",
307+
object_pairs_hook=None,
259308
):
260-
"""
261-
unpack an object from stream.
309+
"""Unpack an object from `stream`.
310+
311+
Raises `ValueError` when `stream` has extra bytes.
262312
"""
263313
return unpackb(stream.read(), use_list=use_list,
264-
object_hook=object_hook, list_hook=list_hook,
314+
object_hook=object_hook, object_pairs_hook=object_pairs_hook, list_hook=list_hook,
265315
encoding=encoding, unicode_errors=unicode_errors,
266316
)
267317

318+
268319
cdef class Unpacker(object):
269320
"""
270321
Streaming unpacker.
@@ -277,10 +328,13 @@ cdef class Unpacker(object):
277328
(default: min(1024**2, max_buffer_size))
278329
279330
If `use_list` is true, msgpack list is deserialized to Python list.
280-
Otherwise, it is deserialized to Python tuple. (default: False)
331+
Otherwise, it is deserialized to Python tuple.
281332
282333
`object_hook` is same to simplejson. If it is not None, it should be callable
283-
and Unpacker calls it when deserializing key-value.
334+
and Unpacker calls it with a dict argument after deserializing a map.
335+
336+
`object_pairs_hook` is same to simplejson. If it is not None, it should be callable
337+
and Unpacker calls it with a list of key-value pairs after deserializing a map.
284338
285339
`encoding` is encoding used for decoding msgpack bytes. If it is None (default),
286340
msgpack bytes is deserialized to Python bytes.
@@ -315,7 +369,6 @@ cdef class Unpacker(object):
315369
cdef object file_like
316370
cdef object file_like_read
317371
cdef Py_ssize_t read_size
318-
cdef bint use_list
319372
cdef object object_hook
320373
cdef object _bencoding
321374
cdef object _berrors
@@ -330,10 +383,10 @@ cdef class Unpacker(object):
330383
free(self.buf)
331384
self.buf = NULL
332385

333-
def __init__(self, file_like=None, Py_ssize_t read_size=0, bint use_list=0,
334-
object object_hook=None, object list_hook=None,
335-
encoding=None, unicode_errors='strict', int max_buffer_size=0):
336-
self.use_list = use_list
386+
def __init__(self, file_like=None, Py_ssize_t read_size=0, bint use_list=1,
387+
object object_hook=None, object object_pairs_hook=None, object list_hook=None,
388+
encoding=None, unicode_errors='strict', int max_buffer_size=0,
389+
):
337390
self.file_like = file_like
338391
if file_like:
339392
self.file_like_read = file_like.read
@@ -353,31 +406,7 @@ cdef class Unpacker(object):
353406
self.buf_size = read_size
354407
self.buf_head = 0
355408
self.buf_tail = 0
356-
template_init(&self.ctx)
357-
self.ctx.user.use_list = use_list
358-
self.ctx.user.object_hook = self.ctx.user.list_hook = <PyObject*>NULL
359-
if object_hook is not None:
360-
if not PyCallable_Check(object_hook):
361-
raise TypeError("object_hook must be a callable.")
362-
self.ctx.user.object_hook = <PyObject*>object_hook
363-
if list_hook is not None:
364-
if not PyCallable_Check(list_hook):
365-
raise TypeError("list_hook must be a callable.")
366-
self.ctx.user.list_hook = <PyObject*>list_hook
367-
if encoding is None:
368-
self.ctx.user.encoding = NULL
369-
self.ctx.user.unicode_errors = NULL
370-
else:
371-
if isinstance(encoding, unicode):
372-
self._bencoding = encoding.encode('ascii')
373-
else:
374-
self._bencoding = encoding
375-
self.ctx.user.encoding = PyBytes_AsString(self._bencoding)
376-
if isinstance(unicode_errors, unicode):
377-
self._berrors = unicode_errors.encode('ascii')
378-
else:
379-
self._berrors = unicode_errors
380-
self.ctx.user.unicode_errors = PyBytes_AsString(self._berrors)
409+
init_ctx(&self.ctx, object_hook, object_pairs_hook, list_hook, use_list, encoding, unicode_errors)
381410

382411
def feed(self, object next_bytes):
383412
cdef char* buf
@@ -438,20 +467,20 @@ cdef class Unpacker(object):
438467
else:
439468
self.file_like = None
440469

441-
cpdef unpack(self):
442-
"""unpack one object"""
470+
cdef object _unpack(self, execute_fn execute):
443471
cdef int ret
472+
cdef object obj
444473
while 1:
445-
ret = template_execute(&self.ctx, self.buf, self.buf_tail, &self.buf_head)
474+
ret = execute(&self.ctx, self.buf, self.buf_tail, &self.buf_head)
446475
if ret == 1:
447-
o = template_data(&self.ctx)
476+
obj = template_data(&self.ctx)
448477
template_init(&self.ctx)
449-
return o
478+
return obj
450479
elif ret == 0:
451480
if self.file_like is not None:
452481
self.read_from_file()
453482
continue
454-
raise StopIteration("No more unpack data.")
483+
raise StopIteration("No more data to unpack.")
455484
else:
456485
raise ValueError("Unpack failed: error = %d" % (ret,))
457486

@@ -465,11 +494,27 @@ cdef class Unpacker(object):
465494
ret += self.file_like.read(nbytes - len(ret))
466495
return ret
467496

497+
def unpack(self):
498+
"""unpack one object"""
499+
return self._unpack(template_construct)
500+
501+
def skip(self):
502+
"""read and ignore one object, returning None"""
503+
return self._unpack(template_skip)
504+
505+
def read_array_header(self):
506+
"""assuming the next object is an array, return its size n, such that the next n unpack() calls will iterate over its contents."""
507+
return self._unpack(read_array_header)
508+
509+
def read_map_header(self):
510+
"""assuming the next object is a map, return its size n, such that the next n * 2 unpack() calls will iterate over its key-value pairs."""
511+
return self._unpack(read_map_header)
512+
468513
def __iter__(self):
469514
return self
470515

471516
def __next__(self):
472-
return self.unpack()
517+
return self._unpack(template_construct)
473518

474519
# for debug.
475520
#def _buf(self):

msgpack/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version = (0, 2, 2)
1+
version = (0, 3, 0, 'dev1')

0 commit comments

Comments
 (0)