Skip to content

Commit b877ce2

Browse files
committed
precise_mode instead of distinguish_tuple
When precise_mode flag is set, serialization will be as precise as possible - type checks will be exact (type(..) is ... instead of isinstance(..., ...) and tuple will be treated as undefined type. This mode is to make accurate object serialization possible.
1 parent 3b933f0 commit b877ce2

File tree

2 files changed

+51
-34
lines changed

2 files changed

+51
-34
lines changed

msgpack/_packer.pyx

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,13 @@ cdef class Packer(object):
5656
Convert unicode to bytes with this encoding. (default: 'utf-8')
5757
:param str unicode_errors:
5858
Error handler for encoding unicode. (default: 'strict')
59-
:param bool distinguish_tuple:
60-
If set to true, tuples will not be serialized as lists
61-
and will be treated as unsupported type. This is useful when trying
62-
to implement accurate serialization for python types.
59+
:param bool precise_mode:
60+
If set to true, types will be checked to be exact. Derived classes
61+
from serializeable types will not be serialized and will be
62+
treated as unsupported type and forwarded to default.
63+
Additionally tuples will not be serialized as lists.
64+
This is useful when trying to implement accurate serialization
65+
for python types.
6366
:param bool use_single_float:
6467
Use single precision float type for float. (default: False)
6568
:param bool autoreset:
@@ -75,7 +78,7 @@ cdef class Packer(object):
7578
cdef object _berrors
7679
cdef char *encoding
7780
cdef char *unicode_errors
78-
cdef bool distinguish_tuple
81+
cdef bint precise_mode
7982
cdef bool use_float
8083
cdef bint autoreset
8184

@@ -88,12 +91,12 @@ cdef class Packer(object):
8891
self.pk.length = 0
8992

9093
def __init__(self, default=None, encoding='utf-8', unicode_errors='strict',
91-
distinguish_tuple=False, use_single_float=False, bint autoreset=1,
92-
bint use_bin_type=0):
94+
use_single_float=False, bint autoreset=1, bint use_bin_type=0,
95+
bint precise_mode=0):
9396
"""
9497
"""
9598
self.use_float = use_single_float
96-
self.distinguish_tuple = distinguish_tuple
99+
self.precise_mode = precise_mode
97100
self.autoreset = autoreset
98101
self.pk.use_bin_type = use_bin_type
99102
if default is not None:
@@ -129,20 +132,20 @@ cdef class Packer(object):
129132
cdef dict d
130133
cdef size_t L
131134
cdef int default_used = 0
132-
cdef bool distinguish_tuple = self.distinguish_tuple
135+
cdef bint precise = self.precise_mode
133136

134137
if nest_limit < 0:
135138
raise PackValueError("recursion limit exceeded.")
136139

137140
while True:
138141
if o is None:
139142
ret = msgpack_pack_nil(&self.pk)
140-
elif isinstance(o, bool):
143+
elif PyBool_Check(o) if precise else isinstance(o, bool):
141144
if o:
142145
ret = msgpack_pack_true(&self.pk)
143146
else:
144147
ret = msgpack_pack_false(&self.pk)
145-
elif PyLong_Check(o):
148+
elif PyLong_CheckExact(o) if precise else PyLong_Check(o):
146149
# PyInt_Check(long) is True for Python 3.
147150
# Sow we should test long before int.
148151
if o > 0:
@@ -151,25 +154,25 @@ cdef class Packer(object):
151154
else:
152155
llval = o
153156
ret = msgpack_pack_long_long(&self.pk, llval)
154-
elif PyInt_Check(o):
157+
elif PyInt_CheckExact(o) if precise else PyInt_Check(o):
155158
longval = o
156159
ret = msgpack_pack_long(&self.pk, longval)
157-
elif PyFloat_Check(o):
160+
elif PyFloat_CheckExact(o) if precise else PyFloat_Check(o):
158161
if self.use_float:
159162
fval = o
160163
ret = msgpack_pack_float(&self.pk, fval)
161164
else:
162165
dval = o
163166
ret = msgpack_pack_double(&self.pk, dval)
164-
elif PyBytes_Check(o):
167+
elif PyBytes_CheckExact(o) if precise else PyBytes_Check(o):
165168
L = len(o)
166169
if L > (2**32)-1:
167170
raise ValueError("bytes is too large")
168171
rawval = o
169172
ret = msgpack_pack_bin(&self.pk, L)
170173
if ret == 0:
171174
ret = msgpack_pack_raw_body(&self.pk, rawval, L)
172-
elif PyUnicode_Check(o):
175+
elif PyUnicode_CheckExact(o) if precise else PyUnicode_Check(o):
173176
if not self.encoding:
174177
raise TypeError("Can't encode unicode string: no encoding is specified")
175178
o = PyUnicode_AsEncodedString(o, self.encoding, self.unicode_errors)
@@ -192,7 +195,7 @@ cdef class Packer(object):
192195
if ret != 0: break
193196
ret = self._pack(v, nest_limit-1)
194197
if ret != 0: break
195-
elif PyDict_Check(o):
198+
elif not precise and PyDict_Check(o):
196199
L = len(o)
197200
if L > (2**32)-1:
198201
raise ValueError("dict is too large")
@@ -212,7 +215,7 @@ cdef class Packer(object):
212215
raise ValueError("EXT data is too large")
213216
ret = msgpack_pack_ext(&self.pk, longval, L)
214217
ret = msgpack_pack_raw_body(&self.pk, rawval, L)
215-
elif (PyTuple_Check(o) and not distinguish_tuple) or PyList_Check(o):
218+
elif PyList_CheckExact(o) if precise else (PyTuple_Check(o) or PyList_Check(o)):
216219
L = len(o)
217220
if L > (2**32)-1:
218221
raise ValueError("list is too large")

msgpack/fallback.py

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -485,10 +485,13 @@ class Packer(object):
485485
Convert unicode to bytes with this encoding. (default: 'utf-8')
486486
:param str unicode_errors:
487487
Error handler for encoding unicode. (default: 'strict')
488-
:param bool distinguish_tuple:
489-
If set to true, tuples will not be serialized as lists
490-
and will be treated as unsupported type. This is useful when trying
491-
to implement accurate serialization for python types.
488+
:param bool precise_mode:
489+
If set to true, types will be checked to be exact. Derived classes
490+
from serializeable types will not be serialized and will be
491+
treated as unsupported type and forwarded to default.
492+
Additionally tuples will not be serialized as lists.
493+
This is useful when trying to implement accurate serialization
494+
for python types.
492495
:param bool use_single_float:
493496
Use single precision float type for float. (default: False)
494497
:param bool autoreset:
@@ -499,9 +502,9 @@ class Packer(object):
499502
It also enable str8 type for unicode.
500503
"""
501504
def __init__(self, default=None, encoding='utf-8', unicode_errors='strict',
502-
distinguish_tuple=False, use_single_float=False, autoreset=True,
505+
precise_mode=False, use_single_float=False, autoreset=True,
503506
use_bin_type=False):
504-
self._distinguish_tuple = distinguish_tuple
507+
self._precise_mode = precise_mode
505508
self._use_float = use_single_float
506509
self._autoreset = autoreset
507510
self._use_bin_type = use_bin_type
@@ -513,19 +516,30 @@ def __init__(self, default=None, encoding='utf-8', unicode_errors='strict',
513516
raise TypeError("default must be callable")
514517
self._default = default
515518

516-
def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
519+
def _check_precise(obj, t, type=type, tuple=tuple):
520+
if type(t) is tuple:
521+
return type(obj) in t
522+
else:
523+
return type(obj) is t
524+
525+
def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT,
526+
check=isinstance, check_precise=_check_precise):
517527
default_used = False
518-
list_type = list if self._distinguish_tuple else (list, tuple)
528+
if self._precise_mode:
529+
check = check_precise
530+
list_types = list
531+
else:
532+
list_types = (list, tuple)
519533
while True:
520534
if nest_limit < 0:
521535
raise PackValueError("recursion limit exceeded")
522536
if obj is None:
523537
return self._buffer.write(b"\xc0")
524-
if isinstance(obj, bool):
538+
if check(obj, bool):
525539
if obj:
526540
return self._buffer.write(b"\xc3")
527541
return self._buffer.write(b"\xc2")
528-
if isinstance(obj, int_types):
542+
if check(obj, int_types):
529543
if 0 <= obj < 0x80:
530544
return self._buffer.write(struct.pack("B", obj))
531545
if -0x20 <= obj < 0:
@@ -547,7 +561,7 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
547561
if -0x8000000000000000 <= obj < -0x80000000:
548562
return self._buffer.write(struct.pack(">Bq", 0xd3, obj))
549563
raise PackValueError("Integer value out of range")
550-
if self._use_bin_type and isinstance(obj, bytes):
564+
if self._use_bin_type and check(obj, bytes):
551565
n = len(obj)
552566
if n <= 0xff:
553567
self._buffer.write(struct.pack('>BB', 0xc4, n))
@@ -558,8 +572,8 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
558572
else:
559573
raise PackValueError("Bytes is too large")
560574
return self._buffer.write(obj)
561-
if isinstance(obj, (Unicode, bytes)):
562-
if isinstance(obj, Unicode):
575+
if check(obj, (Unicode, bytes)):
576+
if check(obj, Unicode):
563577
if self._encoding is None:
564578
raise TypeError(
565579
"Can't encode unicode string: "
@@ -577,11 +591,11 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
577591
else:
578592
raise PackValueError("String is too large")
579593
return self._buffer.write(obj)
580-
if isinstance(obj, float):
594+
if check(obj, float):
581595
if self._use_float:
582596
return self._buffer.write(struct.pack(">Bf", 0xca, obj))
583597
return self._buffer.write(struct.pack(">Bd", 0xcb, obj))
584-
if isinstance(obj, ExtType):
598+
if check(obj, ExtType):
585599
code = obj.code
586600
data = obj.data
587601
assert isinstance(code, int)
@@ -606,13 +620,13 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
606620
self._buffer.write(struct.pack("b", code))
607621
self._buffer.write(data)
608622
return
609-
if isinstance(obj, list_type):
623+
if check(obj, list_types):
610624
n = len(obj)
611625
self._fb_pack_array_header(n)
612626
for i in xrange(n):
613627
self._pack(obj[i], nest_limit - 1)
614628
return
615-
if isinstance(obj, dict):
629+
if check(obj, dict):
616630
return self._fb_pack_map_pairs(len(obj), dict_iteritems(obj),
617631
nest_limit - 1)
618632
if not default_used and self._default is not None:

0 commit comments

Comments
 (0)