8000 unpacker: Add `strict_map_key` option (#334) · loude/msgpack-python@760e30b · GitHub
[go: up one dir, main page]

Skip to content

Commit 760e30b

Browse files
authored
unpacker: Add strict_map_key option (msgpack#334)
2 parents 3c9c6ed + 8ae6320 commit 760e30b

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

msgpack/_unpacker.pyx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ cdef extern from "unpack.h":
2727
bint use_list
2828
bint raw
2929
bint has_pairs_hook # call object_hook with k-v pairs
30+
bint strict_map_key
3031
PyObject* object_hook
3132
PyObject* list_hook
3233
PyObject* ext_hook
@@ -56,14 +57,15 @@ cdef extern from "unpack.h":
5657
cdef inline init_ctx(unpack_context *ctx,
5758
object object_hook, object object_pairs_hook,
5859
object list_hook, object ext_hook,
59-
bint use_list, bint raw,
60+
bint use_list, bint raw, bint strict_map_key,
6061
const char* encoding, const char* unicode_errors,
6162
Py_ssize_t max_str_len, Py_ssize_t max_bin_len,
6263
Py_ssize_t max_array_len, Py_ssize_t max_map_len,
6364
Py_ssize_t max_ext_len):
6465
unpack_init(ctx)
6566
ctx.user.use_list = use_list
6667
ctx.user.raw = raw
68+
ctx.user.strict_map_key = strict_map_key
6769
ctx.user.object_hook = ctx.user.list_hook = <PyObject*>NULL
6870
ctx.user.max_str_len = max_str_len
6971
ctx.user.max_bin_len = max_bin_len
@@ -140,7 +142,7 @@ cdef inline int get_data_from_buffer(object obj,
140142
return 1
141143

142144
def unpackb(object packed, object object_hook=None, object list_hook=None,
143-
bint use_list=True, bint raw=True,
145+
bint use_list=True, bint raw=True, bint strict_map_key=False,
144146
encoding=None, unicode_errors=None,
145147
object_pairs_hook=None, ext_hook=ExtType,
146148
Py_ssize_t max_str_len=1024*1024,
@@ -180,7 +182,7 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
180182
get_data_from_buffer(packed, &view, &buf, &buf_len, &new_protocol)
181183
try:
182184
init_ctx(&ctx, object_hook, object_pairs_hook, list_hook, ext_hook,
183-
use_list, raw, cenc, cerr,
185+
use_list, raw, strict_map_key, cenc, cerr,
184186
max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
185187
ret = unpack_construct(&ctx, buf, buf_len, &off)
186188
finally:
@@ -236,6 +238,11 @@ cdef class Unpacker(object):
236238
237239
*encoding* option which is deprecated overrides this option.
238240
241+
:param bool strict_map_key:
242+
If true, only str or bytes are accepted for map (dict) keys.
243+
It's False by default for backward-compatibility.
244+
But it will be True from msgpack 1.0.
245+
239246
:param callable object_hook:
240247
When specified, it should be callable.
241248
Unpacker calls it with a dict argument after unpacking msgpack map.
@@ -318,7 +325,7 @@ cdef class Unpacker(object):
318325
self.buf = NULL
319326

320327
def __init__(self, file_like=None, Py_ssize_t read_size=0,
321-
bint use_list=True, bint raw=True,
328+
bint use_list=True, bint raw=True, bint strict_map_key=False,
322329
object object_hook=None, object object_pairs_hook=None, object list_hook=None,
323330
encoding=None, unicode_errors=None, Py_ssize_t max_buffer_size=0,
324331
object ext_hook=ExtType,
@@ -366,7 +373,7 @@ cdef class Unpacker(object):
366373
cerr = unicode_errors
367374

368375
init_ctx(&self.ctx, object_hook, object_pairs_hook, list_hook,
369-
ext_hook, use_list, raw, cenc, cerr,
376+
ext_hook, use_list, raw, strict_map_key, cenc, cerr,
370377
max_str_len, max_bin_len, max_array_len,
371378
max_map_len, max_ext_len)
372379

msgpack/fallback.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ class Unpacker(object):
179179
180180
*encoding* option which is deprecated overrides this option.
181181
182+
:param bool strict_map_key:
183+
If true, only str or bytes are accepted for map (dict) keys.
184+
It's False by default for backward-compatibility.
185+
But it will be True from msgpack 1.0.
186+
182187
:param callable object_hook:
183188
When specified, it should be callable.
184189
Unpacker calls it with a dict argument after unpacking msgpack map.
@@ -241,7 +246,7 @@ class Unpacker(object):
241246
Other exceptions can be raised during unpacking.
242247
"""
243248

244-
def __init__(self, file_like=None, read_size=0, use_list=True, raw=True,
249+
def __init__(self, file_like=None, read_size=0, use_list=True, raw=True, strict_map_key=False,
245250
object_hook=None, object_pairs_hook=None, list_hook=None,
246251
encoding=None, unicode_errors=None, max_buffer_size=0,
247252
ext_hook=ExtType,
@@ -286,6 +291,7 @@ def __init__(self, file_like=None, read_size=0, use_list=True, raw=True,
286291
raise ValueError("read_size must be smaller than max_buffer_size")
287292
self._read_size = read_size or min(self._max_buffer_size, 16*1024)
288293
self._raw = bool(raw)
294+
self._strict_map_key = bool(strict_map_key)
289295
self._encoding = encoding
290296
self._unicode_errors = unicode_errors
291297
self._use_list = use_list
@@ -633,6 +639,8 @@ def _unpack(self, execute=EX_CONSTRUCT):
633639
ret = {}
634640
for _ in xrange(n):
635641
key = self._unpack(EX_CONSTRUCT)
642+
if self._strict_map_key and type(key) not in (Unicode, bytes):
643+
raise ValueError("%s is not allowed for map key" % str(type(key)))
636644
ret[key] = self._unpack(EX_CONSTRUCT)
637645
if self._object_hook is not None:
638646
ret = self._object_hook(ret)

msgpack/unpack.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ typedef struct unpack_user {
2323
bool use_list;
2424
bool raw;
2525
bool has_pairs_hook;
26+
bool strict_map_key;
2627
PyObject *object_hook;
2728
PyObject *list_hook;
2829
PyObject *ext_hook;
@@ -188,6 +189,10 @@ static inline int unpack_callback_map(unpack_user* u, unsigned int n, msgpack_un
188189

189190
static inline int unpack_callback_map_item(unpack_user* u, unsigned int current, msgpack_unpack_object* c, msgpack_unpack_object k, msgpack_unpack_object v)
190191
{
192+
if (u->strict_map_key && !PyUnicode_CheckExact(k) && !PyBytes_CheckExact(k)) {
193+
PyErr_Format(PyExc_ValueError, "%.100s is not allowed for map key", Py_TYPE(k)->tp_name);
194+
return -1;
195+
}
191196
if (u->has_pairs_hook) {
192197
msgpack_unpack_object item = PyTuple_Pack(2, k, v);
193198
if (!item)

test/test_except.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,14 @@ def test_invalidvalue():
5050

5151
with raises(StackError):
5252
unpackb(b"\x91" * 3000) # nested fixarray(len=1)
53+
54+
55+
def test_strict_map_key():
56+
valid = {u"unicode": 1, b"bytes": 2}
57+
packed = packb(valid, use_bin_type=True)
58+
assert valid == unpackb(packed, raw=False, strict_map_key=True)
59+
60+
invalid = {42: 1}
61+
packed = packb(invalid, use_bin_type=True)
62+
with raises(ValueError):
63+
unpackb(packed, raw=False, strict_map_key=True)

0 commit comments

Comments
 (0)
0