summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorInada Naoki <songofacandy@gmail.com>2018-11-29 22:29:38 +0900
committerInada Naoki <songofacandy@gmail.com>2018-11-29 22:29:38 +0900
commite9086a34e4b3d64df78314339f152c800e79c8e1 (patch)
treed4b46ab4c67c7195d3370829b57e1ac692f2cedb
parent3c9c6edbc88908fceb3c69ff3d6455be8b5914c8 (diff)
downloadmsgpack-python-e9086a34e4b3d64df78314339f152c800e79c8e1.tar.gz
Add strict_map_key option to unpacker
-rw-r--r--msgpack/_unpacker.pyx17
-rw-r--r--msgpack/unpack.h5
-rw-r--r--test/test_except.py11
3 files changed, 28 insertions, 5 deletions
diff --git a/msgpack/_unpacker.pyx b/msgpack/_unpacker.pyx
index a5403d8..2163425 100644
--- a/msgpack/_unpacker.pyx
+++ b/msgpack/_unpacker.pyx
@@ -27,6 +27,7 @@ cdef extern from "unpack.h":
bint use_list
bint raw
bint has_pairs_hook # call object_hook with k-v pairs
+ bint strict_map_key
PyObject* object_hook
PyObject* list_hook
PyObject* ext_hook
@@ -56,7 +57,7 @@ cdef extern from "unpack.h":
cdef inline init_ctx(unpack_context *ctx,
object object_hook, object object_pairs_hook,
object list_hook, object ext_hook,
- bint use_list, bint raw,
+ bint use_list, bint raw, bint strict_map_key,
const char* encoding, const char* unicode_errors,
Py_ssize_t max_str_len, Py_ssize_t max_bin_len,
Py_ssize_t max_array_len, Py_ssize_t max_map_len,
@@ -64,6 +65,7 @@ cdef inline init_ctx(unpack_context *ctx,
unpack_init(ctx)
ctx.user.use_list = use_list
ctx.user.raw = raw
+ ctx.user.strict_map_key = strict_map_key
ctx.user.object_hook = ctx.user.list_hook = <PyObject*>NULL
ctx.user.max_str_len = max_str_len
ctx.user.max_bin_len = max_bin_len
@@ -140,7 +142,7 @@ cdef inline int get_data_from_buffer(object obj,
return 1
def unpackb(object packed, object object_hook=None, object list_hook=None,
- bint use_list=True, bint raw=True,
+ bint use_list=True, bint raw=True, bint strict_map_key=False,
encoding=None, unicode_errors=None,
object_pairs_hook=None, ext_hook=ExtType,
Py_ssize_t max_str_len=1024*1024,
@@ -180,7 +182,7 @@ def unpackb(object packed, object object_hook=None, object list_hook=None,
get_data_from_buffer(packed, &view, &buf, &buf_len, &new_protocol)
try:
init_ctx(&ctx, object_hook, object_pairs_hook, list_hook, ext_hook,
- use_list, raw, cenc, cerr,
+ use_list, raw, strict_map_key, cenc, cerr,
max_str_len, max_bin_len, max_array_len, max_map_len, max_ext_len)
ret = unpack_construct(&ctx, buf, buf_len, &off)
finally:
@@ -236,6 +238,11 @@ cdef class Unpacker(object):
*encoding* option which is deprecated overrides this option.
+ :param bool strict_map_key:
+ If true, only str or bytes are accepted for map (dict) keys.
+ It's False by default for backward-compatibility.
+ But it will be True from msgpack 1.0.
+
:param callable object_hook:
When specified, it should be callable.
Unpacker calls it with a dict argument after unpacking msgpack map.
@@ -318,7 +325,7 @@ cdef class Unpacker(object):
self.buf = NULL
def __init__(self, file_like=None, Py_ssize_t read_size=0,
- bint use_list=True, bint raw=True,
+ bint use_list=True, bint raw=True, bint strict_map_key=False,
object object_hook=None, object object_pairs_hook=None, object list_hook=None,
encoding=None, unicode_errors=None, Py_ssize_t max_buffer_size=0,
object ext_hook=ExtType,
@@ -366,7 +373,7 @@ cdef class Unpacker(object):
cerr = unicode_errors
init_ctx(&self.ctx, object_hook, object_pairs_hook, list_hook,
- ext_hook, use_list, raw, cenc, cerr,
+ ext_hook, use_list, raw, strict_map_key, cenc, cerr,
max_str_len, max_bin_len, max_array_len,
max_map_len, max_ext_len)
diff --git a/msgpack/unpack.h b/msgpack/unpack.h
index 63e5543..85dbbed 100644
--- a/msgpack/unpack.h
+++ b/msgpack/unpack.h
@@ -23,6 +23,7 @@ typedef struct unpack_user {
bool use_list;
bool raw;
bool has_pairs_hook;
+ bool strict_map_key;
PyObject *object_hook;
PyObject *list_hook;
PyObject *ext_hook;
@@ -188,6 +189,10 @@ static inline int unpack_callback_map(unpack_user* u, unsigned int n, msgpack_un
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)
{
+ if (u->strict_map_key && !PyUnicode_CheckExact(k) && !PyBytes_CheckExact(k)) {
+ PyErr_Format(PyExc_ValueError, "%.100s is not allowed for map key", Py_TYPE(k)->tp_name);
+ return -1;
+ }
if (u->has_pairs_hook) {
msgpack_unpack_object item = PyTuple_Pack(2, k, v);
if (!item)
diff --git a/test/test_except.py b/test/test_except.py
index 626c8be..40ca3ee 100644
--- a/test/test_except.py
+++ b/test/test_except.py
@@ -50,3 +50,14 @@ def test_invalidvalue():
with raises(StackError):
unpackb(b"\x91" * 3000) # nested fixarray(len=1)
+
+
+def test_strict_map_key():
+ valid = {u"unicode": 1, b"bytes": 2}
+ packed = packb(valid, use_bin_type=True)
+ assert valid == unpackb(packed, raw=True)
+
+ invalid = {42: 1}
+ packed = packb(invalid, use_bin_type=True)
+ with raises(ValueError):
+ unpackb(packed, raw=True)