Skip to content

Commit a0887e6

Browse files
committed
better exception handling
1 parent 14bda11 commit a0887e6

File tree

2 files changed

+111
-139
lines changed

2 files changed

+111
-139
lines changed

msgpack/_packer.pyx

Lines changed: 111 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ cdef extern from "pack.h":
3838
int msgpack_pack_raw_body(msgpack_packer* pk, char* body, size_t l) except -1
3939
int msgpack_pack_ext(msgpack_packer* pk, char typecode, size_t l) except -1
4040
int msgpack_pack_timestamp(msgpack_packer* x, long long seconds, unsigned long nanoseconds) except -1
41-
int msgpack_pack_unicode(msgpack_packer* pk, object o, long long limit) except -1
4241

4342

4443
cdef int DEFAULT_RECURSE_LIMIT=511
@@ -143,139 +142,133 @@ cdef class Packer:
143142
PyMem_Free(self.pk.buf)
144143
self.pk.buf = NULL
145144

146-
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT) except -1:
145+
cdef int _pack_inner(self, object o, bint will_default, int nest_limit) except -1:
147146
cdef long long llval
148147
cdef unsigned long long ullval
149148
cdef unsigned long ulval
150149
cdef long longval
151150
cdef float fval
152151
cdef double dval
153-
cdef char* rawval
154-
cdef int ret
155-
cdef dict d
152+
cdef const char* rawval
156153
cdef Py_ssize_t L
157-
cdef int default_used = 0
158-
cdef bint strict_types = self.strict_types
154+
cdef bool strict_types = self.strict_types
159155
cdef Py_buffer view
160156

161-
if nest_limit < 0:
162-
raise ValueError("recursion limit exceeded.")
163-
164-
while True:
165-
if o is None:
166-
msgpack_pack_nil(&self.pk)
167-
elif o is True:
168-
msgpack_pack_true(&self.pk)
169-
elif o is False:
170-
msgpack_pack_false(&self.pk)
171-
elif PyLong_CheckExact(o) if strict_types else PyLong_Check(o):
172-
# PyInt_Check(long) is True for Python 3.
173-
# So we should test long before int.
174-
try:
175-
if o > 0:
176-
ullval = o
177-
msgpack_pack_unsigned_long_long(&self.pk, ullval)
178-
else:
179-
llval = o
180-
msgpack_pack_long_long(&self.pk, llval)
181-
except OverflowError as oe:
182-
if not default_used and self._default is not None:
183-
o = self._default(o)
184-
default_used = True
185-
continue
186-
else:
187-
raise OverflowError("Integer value out of range")
188-
elif PyFloat_CheckExact(o) if strict_types else PyFloat_Check(o):
189-
if self.use_float:
190-
fval = o
191-
msgpack_pack_float(&self.pk, fval)
157+
if o is None:
158+
msgpack_pack_nil(&self.pk)
159+
elif o is True:
160+
msgpack_pack_true(&self.pk)
161+
elif o is False:
162+
msgpack_pack_false(&self.pk)
163+
elif PyLong_CheckExact(o) if strict_types else PyLong_Check(o):
164+
try:
165+
if o > 0:
166+
ullval = o
167+
msgpack_pack_unsigned_long_long(&self.pk, ullval)
168+
else:
169+
llval = o
170+
msgpack_pack_long_long(&self.pk, llval)
171+
except OverflowError as oe:
172+
if will_default:
173+
return -2
192174
else:
193-
dval = o
194-
msgpack_pack_double(&self.pk, dval)
195-
elif PyBytesLike_CheckExact(o) if strict_types else PyBytesLike_Check(o):
175+
raise OverflowError("Integer value out of range")
176+
elif PyFloat_CheckExact(o) if strict_types else PyFloat_Check(o):
177+
if self.use_float:
178+
fval = o
179+
msgpack_pack_float(&self.pk, fval)
180+
else:
181+
dval = o
182+
msgpack_pack_double(&self.pk, dval)
183+
elif PyBytesLike_CheckExact(o) if strict_types else PyBytesLike_Check(o):
184+
L = Py_SIZE(o)
185+
if L > ITEM_LIMIT:
186+
PyErr_Format(ValueError, b"%.200s object is too large", Py_TYPE(o).tp_name)
187+
return -1
188+
rawval = o
189+
msgpack_pack_bin(&self.pk, L)
190+
msgpack_pack_raw_body(&self.pk, rawval, L)
191+
elif PyUnicode_CheckExact(o) if strict_types else PyUnicode_Check(o):
192+
if self.unicode_errors == NULL:
193+
rawval = PyUnicode_AsUTF8AndSize(o, &L)
194+
if L >ITEM_LIMIT:
195+
raise ValueError("unicode string is too large")
196+
else:
197+
o = PyUnicode_AsEncodedString(o, NULL, self.unicode_errors)
196198
L = Py_SIZE(o)
197199
if L > ITEM_LIMIT:
198-
PyErr_Format(ValueError, b"%.200s object is too large", Py_TYPE(o).tp_name)
200+
raise ValueError("unicode string is too large")
199201
rawval = o
202+
msgpack_pack_raw(&self.pk, L)
203+
msgpack_pack_raw_body(&self.pk, rawval, L)
204+
elif PyDict_CheckExact(o) if strict_types else PyDict_Check(o):
205+
L = len(o)
206+
if L > ITEM_LIMIT:
207+
raise ValueError("dict is too large")
208+
msgpack_pack_map(&self.pk, L)
209+
for k, v in o.items():
210+
self._pack(k, nest_limit)
211+
self._pack(v, nest_limit)
212+
elif type(o) is ExtType if strict_types else isinstance(o, ExtType):
213+
# This should be before Tuple because ExtType is namedtuple.
214+
longval = o.code
215+
rawval = o.data
216+
L = len(o.data)
217+
if L > ITEM_LIMIT:
218+
raise ValueError("EXT data is too large")
219+
msgpack_pack_ext(&self.pk, longval, L)
220+
msgpack_pack_raw_body(&self.pk, rawval, L)
221+
elif type(o) is Timestamp:
222+
llval = o.seconds
223+
ulval = o.nanoseconds
224+
msgpack_pack_timestamp(&self.pk, llval, ulval)
225+
elif PyList_CheckExact(o) if strict_types else (PyTuple_Check(o) or PyList_Check(o)):
226+
L = Py_SIZE(o)
227+
if L > ITEM_LIMIT:
228+
raise ValueError("list is too large")
229+
msgpack_pack_array(&self.pk, L)
230+
for v in o:
231+
self._pack(v, nest_limit)
232+
elif PyMemoryView_Check(o):
233+
PyObject_GetBuffer(o, &view, PyBUF_SIMPLE)
234+
L = view.len
235+
if L > ITEM_LIMIT:
236+
PyBuffer_Release(&view);
237+
raise ValueError("memoryview is too large")
238+
try:
200239
msgpack_pack_bin(&self.pk, L)
201-
msgpack_pack_raw_body(&self.pk, rawval, L)
202-
elif PyUnicode_CheckExact(o) if strict_types else PyUnicode_Check(o):
203-
if self.unicode_errors == NULL:
204-
ret = msgpack_pack_unicode(&self.pk, o, ITEM_LIMIT);
205-
if ret == -2:
206-
raise ValueError("unicode string is too large")
207-
else:
208-
o = PyUnicode_AsEncodedString(o, NULL, self.unicode_errors)
209-
L = Py_SIZE(o)
210-
if L > ITEM_LIMIT:
211-
raise ValueError("unicode string is too large")
212-
msgpack_pack_raw(&self.pk, L)
213-
rawval = o
214-
msgpack_pack_raw_body(&self.pk, rawval, L)
215-
elif PyDict_CheckExact(o):
216-
d = <dict>o
217-
L = len(d)
218-
if L > ITEM_LIMIT:
219-
raise ValueError("dict is too large")
220-
msgpack_pack_map(&self.pk, L)
221-
for k, v in d.items():
222-
self._pack(k, nest_limit-1)
223-
self._pack(v, nest_limit-1)
224-
elif not strict_types and PyDict_Check(o):
225-
L = len(o)
226-
if L > ITEM_LIMIT:
227-
raise ValueError("dict is too large")
228-
msgpack_pack_map(&self.pk, L)
229-
for k, v in o.items():
230-
self._pack(k, nest_limit-1)
231-
self._pack(v, nest_limit-1)
232-
elif type(o) is ExtType if strict_types else isinstance(o, ExtType):
233-
# This should be before Tuple because ExtType is namedtuple.
234-
longval = o.code
235-
rawval = o.data
236-
L = len(o.data)
237-
if L > ITEM_LIMIT:
238-
raise ValueError("EXT data is too large")
239-
msgpack_pack_ext(&self.pk, longval, L)
240-
msgpack_pack_raw_body(&self.pk, rawval, L)
241-
elif type(o) is Timestamp:
242-
llval = o.seconds
243-
ulval = o.nanoseconds
244-
msgpack_pack_timestamp(&self.pk, llval, ulval)
245-
elif PyList_CheckExact(o) if strict_types else (PyTuple_Check(o) or PyList_Check(o)):
246-
L = Py_SIZE(o)
247-
if L > ITEM_LIMIT:
248-
raise ValueError("list is too large")
249-
msgpack_pack_array(&self.pk, L)
250-
for v in o:
251-
ret = self._pack(v, nest_limit-1)
252-
elif PyMemoryView_Check(o):
253-
PyObject_GetBuffer(o, &view, PyBUF_SIMPLE)
254-
L = view.len
255-
if L > ITEM_LIMIT:
256-
PyBuffer_Release(&view);
257-
raise ValueError("memoryview is too large")
258-
try:
259-
msgpack_pack_bin(&self.pk, L)
260-
msgpack_pack_raw_body(&self.pk, <char*>view.buf, L)
261-
finally:
262-
PyBuffer_Release(&view);
263-
elif self.datetime and PyDateTime_CheckExact(o) and datetime_tzinfo(o) is not None:
264-
delta = o - epoch
265-
if not PyDelta_CheckExact(delta):
266-
raise ValueError("failed to calculate delta")
267-
llval = timedelta_days(delta) * <long long>(24*60*60) + timedelta_seconds(delta)
268-
ulval = timedelta_microseconds(delta) * 1000
269-
msgpack_pack_timestamp(&self.pk, llval, ulval)
270-
elif not default_used and self._default:
240+
msgpack_pack_raw_body(&self.pk, <char*>view.buf, L)
241+
finally:
242+
PyBuffer_Release(&view);
243+
elif self.datetime and PyDateTime_CheckExact(o) and datetime_tzinfo(o) is not None:
244+
delta = o - epoch
245+
if not PyDelta_CheckExact(delta):
246+
raise ValueError("failed to calculate delta")
247+
llval = timedelta_days(delta) * <long long>(24*60*60) + timedelta_seconds(delta)
248+
ulval = timedelta_microseconds(delta) * 1000
249+
msgpack_pack_timestamp(&self.pk, llval, ulval)
250+
elif will_default:
251+
return -2
252+
elif self.datetime and PyDateTime_CheckExact(o):
253+
# this should be later than will_default
254+
PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name)
255+
return -1
256+
else:
257+
PyErr_Format(TypeError, b"can not serialize '%.200s' object", Py_TYPE(o).tp_name)
258+
return -1
259+
260+
cdef int _pack(self, object o, int nest_limit=DEFAULT_RECURSE_LIMIT) except -1:
261+
cdef int ret
262+
if nest_limit < 0:
263+
raise ValueError("recursion limit exceeded.")
264+
nest_limit -= 1
265+
if self._default is not None:
266+
ret = self._pack_inner(o, 1, nest_limit)
267+
if ret == -2:
271268
o = self._default(o)
272-
default_used = 1
273-
continue
274-
elif self.datetime and PyDateTime_CheckExact(o):
275-
PyErr_Format(ValueError, b"can not serialize '%.200s' object where tzinfo=None", Py_TYPE(o).tp_name)
276269
else:
277-
PyErr_Format(TypeError, b"can not serialize '%.200s' object", Py_TYPE(o).tp_name)
278-
return ret
270+
return ret
271+
return self._pack_inner(o, 0, nest_limit)
279272

280273
cpdef pack(self, object obj):
281274
cdef int ret

msgpack/pack.h

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,27 +64,6 @@ static inline int msgpack_pack_write(msgpack_packer* pk, const char *data, size_
6464

6565
#include "pack_template.h"
6666

67-
// return -2 when o is too long
68-
static inline int
69-
msgpack_pack_unicode(msgpack_packer *pk, PyObject *o, long long limit)
70-
{
71-
assert(PyUnicode_Check(o));
72-
73-
Py_ssize_t len;
74-
const char* buf = PyUnicode_AsUTF8AndSize(o, &len);
75-
if (buf == NULL)
76-
return -1;
77-
78-
if (len > limit) {
79-
return -2;
80-
}
81-
82-
int ret = msgpack_pack_raw(pk, len);
83-
if (ret) return ret;
84-
85-
return msgpack_pack_raw_body(pk, buf, len);
86-
}
87-
8867
#ifdef __cplusplus
8968
}
9069
#endif

0 commit comments

Comments
 (0)