Skip to content

Commit 0d63c67

Browse files
committed
Merge pull request #37 from msgpack/exceptions
Split exceptions
2 parents dd5c76b + 1c0fe10 commit 0d63c67

File tree

5 files changed

+72
-18
lines changed

5 files changed

+72
-18
lines changed

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@ writing MessagePack data.
1919
NOTE for msgpack 0.2.x users
2020
----------------------------
2121

22+
The msgpack 0.3 have some incompatible changes.
23+
2224
The default value of ``use_list`` keyword argument is ``True`` from 0.3.x.
2325
You should pass the argument explicitly for backward compatibility.
2426

27+
`Unpacker.unpack()` and some unpack methods now raises `OutOfData`
28+
instead of `StopIteration`.
29+
`StopIteration` is used for iterator protocol only.
30+
2531

2632
HOW TO USE
2733
-----------

msgpack/_msgpack.pyx

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@ cdef extern from "pack.h":
3838
cdef int DEFAULT_RECURSE_LIMIT=511
3939

4040

41-
class BufferFull(Exception):
42-
pass
41+
from msgpack.exceptions import (
42+
BufferFull,
43+
OutOfData,
44+
UnpackValueError,
45+
ExtraData,
46+
)
4347

4448

4549
cdef class Packer(object):
@@ -102,7 +106,7 @@ cdef class Packer(object):
102106
cdef dict d
103107

104108
if nest_limit < 0:
105-
raise ValueError("Too deep.")
109+
raise UnpackValueError("recursion limit exceeded.")
106110

107111
if o is None:
108112
ret = msgpack_pack_nil(&self.pk)
@@ -174,7 +178,9 @@ cdef class Packer(object):
174178
cpdef pack(self, object obj):
175179
cdef int ret
176180
ret = self._pack(obj, DEFAULT_RECURSE_LIMIT)
177-
if ret:
181+
if ret == -1:
182+
raise MemoryError
183+
elif ret: # should not happen.
178184
raise TypeError
179185
buf = PyBytes_FromStringAndSize(self.pk.buf, self.pk.length)
180186
self.pk.length = 0
@@ -296,7 +302,7 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
296302
if ret == 1:
297303
obj = template_data(&ctx)
298304
if off < buf_len:
299-
raise ValueError("Extra data.")
305+
raise ExtraData(obj, PyBytes_FromStringAndSize(buf+off, buf_len-off))
300306
return obj
301307
else:
302308
return None
@@ -421,11 +427,12 @@ cdef class Unpacker(object):
421427
init_ctx(&self.ctx, object_hook, object_pairs_hook, list_hook, use_list, cenc, cerr)
422428

423429
def feed(self, object next_bytes):
430+
"""Append `next_bytes` to internal buffer."""
424431
cdef char* buf
425432
cdef Py_ssize_t buf_len
426433
if self.file_like is not None:
427434
raise AssertionError(
428-
"unpacker.feed() is not be able to use with`file_like`.")
435+
"unpacker.feed() is not be able to use with `file_like`.")
429436
PyObject_AsReadBuffer(next_bytes, <const_void_ptr*>&buf, &buf_len)
430437
self.append_buffer(buf, buf_len)
431438

@@ -479,7 +486,7 @@ cdef class Unpacker(object):
479486
else:
480487
self.file_like = None
481488

482-
cdef object _unpack(self, execute_fn execute, object write_bytes):
489+
cdef object _unpack(self, execute_fn execute, object write_bytes, bint iter=0):
483490
cdef int ret
484491
cdef object obj
485492
cdef size_t prev_head
@@ -497,7 +504,10 @@ cdef class Unpacker(object):
497504
if self.file_like is not None:
498505
self.read_from_file()
499506
continue
500-
raise StopIteration("No more data to unpack.")
507+
if iter:
508+
raise StopIteration("No more data to unpack.")
509+
else:
510+
raise OutOfData("No more data to unpack.")
501511
else:
502512
raise ValueError("Unpack failed: error = %d" % (ret,))
503513

@@ -515,31 +525,45 @@ cdef class Unpacker(object):
515525
"""
516526
unpack one object
517527
518-
If write_bytes is not None, it will be called with parts of the raw message as it is unpacked.
528+
If write_bytes is not None, it will be called with parts of the raw
529+
message as it is unpacked.
530+
531+
Raises `OutOfData` when there are no more bytes to unpack.
519532
"""
520533
return self._unpack(template_construct, write_bytes)
521534

522535
def skip(self, object write_bytes=None):
523536
"""
524537
read and ignore one object, returning None
525538
526-
If write_bytes is not None, it will be called with parts of the raw message as it is unpacked.
539+
If write_bytes is not None, it will be called with parts of the raw
540+
message as it is unpacked.
541+
542+
Raises `OutOfData` when there are no more bytes to unpack.
527543
"""
528544
return self._unpack(template_skip, write_bytes)
529545

530546
def read_array_header(self, object write_bytes=None):
531-
"""assuming the next object is an array, return its size n, such that the next n unpack() calls will iterate over its contents."""
547+
"""assuming the next object is an array, return its size n, such that
548+
the next n unpack() calls will iterate over its contents.
549+
550+
Raises `OutOfData` when there are no more bytes to unpack.
551+
"""
532552
return self._unpack(read_array_header, write_bytes)
533553

534554
def read_map_header(self, object write_bytes=None):
535-
"""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."""
555+
"""assuming the next object is a map, return its size n, such that the
556+
next n * 2 unpack() calls will iterate over its key-value pairs.
557+
558+
Raises `OutOfData` when there are no more bytes to unpack.
559+
"""
536560
return self._unpack(read_map_header, write_bytes)
537561

538562
def __iter__(self):
539563
return self
540564

541565
def __next__(self):
542-
return self._unpack(template_construct, None)
566+
return self._unpack(template_construct, None, 1)
543567

544568
# for debug.
545569
#def _buf(self):

msgpack/exceptions.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
class UnpackException(Exception):
2+
pass
3+
4+
5+
class BufferFull(UnpackException):
6+
pass
7+
8+
9+
class OutOfData(UnpackException):
10+
pass
11+
12+
13+
class UnpackValueError(UnpackException, ValueError):
14+
pass
15+
16+
17+
class ExtraData(ValueError):
18+
def __init__(self, unpacked, extra):
19+
self.unpacked = unpacked
20+
self.extra = extra
21+
22+
def __str__(self):
23+
return "unpack(b) recieved extra data."

test/test_read_size.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Test Unpacker's read_array_header and read_map_header methods"""
2-
from msgpack import packb, Unpacker
2+
from msgpack import packb, Unpacker, OutOfData
33
UnexpectedTypeException = ValueError
44

55
def test_read_array_header():
@@ -12,7 +12,7 @@ def test_read_array_header():
1212
try:
1313
unpacker.unpack()
1414
assert 0, 'should raise exception'
15-
except StopIteration:
15+
except OutOfData:
1616
assert 1, 'okay'
1717

1818

@@ -25,7 +25,7 @@ def test_read_map_header():
2525
try:
2626
unpacker.unpack()
2727
assert 0, 'should raise exception'
28-
except StopIteration:
28+
except OutOfData:
2929
assert 1, 'okay'
3030

3131
def test_incorrect_type_array():

test/test_sequnpack.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import six
55
from msgpack import Unpacker, BufferFull
6+
from msgpack.exceptions import OutOfData
67
import nose
78

89
def test_foobar():
@@ -17,7 +18,7 @@ def test_foobar():
1718
try:
1819
o = unpacker.unpack()
1920
assert 0, "should raise exception"
20-
except StopIteration:
21+
except OutOfData:
2122
assert 1, "ok"
2223

2324
unpacker.feed(b'foo')
@@ -41,7 +42,7 @@ def test_foobar_skip():
4142
try:
4243
o = unpacker.unpack()
4344
assert 0, "should raise exception"
44-
except StopIteration:
45+
except OutOfData:
4546
assert 1, "ok"
4647

4748
def test_maxbuffersize():

0 commit comments

Comments
 (0)