@@ -601,6 +601,13 @@ class Packer(object):
601601 Convert unicode to bytes with this encoding. (default: 'utf-8')
602602 :param str unicode_errors:
603603 Error handler for encoding unicode. (default: 'strict')
604+ :param bool precise_mode:
605+ If set to true, types will be checked to be exact. Derived classes
606+ from serializeable types will not be serialized and will be
607+ treated as unsupported type and forwarded to default.
608+ Additionally tuples will not be serialized as lists.
609+ This is useful when trying to implement accurate serialization
610+ for python types.
604611 :param bool use_single_float:
605612 Use single precision float type for float. (default: False)
606613 :param bool autoreset:
@@ -611,7 +618,9 @@ class Packer(object):
611618 It also enable str8 type for unicode.
612619 """
613620 def __init__ (self , default = None , encoding = 'utf-8' , unicode_errors = 'strict' ,
614- use_single_float = False , autoreset = True , use_bin_type = False ):
621+ precise_mode = False , use_single_float = False , autoreset = True ,
622+ use_bin_type = False ):
623+ self ._precise_mode = precise_mode
615624 self ._use_float = use_single_float
616625 self ._autoreset = autoreset
617626 self ._use_bin_type = use_bin_type
@@ -623,18 +632,30 @@ def __init__(self, default=None, encoding='utf-8', unicode_errors='strict',
623632 raise TypeError ("default must be callable" )
624633 self ._default = default
625634
626- def _pack (self , obj , nest_limit = DEFAULT_RECURSE_LIMIT , isinstance = isinstance ):
635+ def _check_precise (obj , t , type = type , tuple = tuple ):
636+ if type (t ) is tuple :
637+ return type (obj ) in t
638+ else :
639+ return type (obj ) is t
640+
641+ def _pack (self , obj , nest_limit = DEFAULT_RECURSE_LIMIT ,
642+ check = isinstance , check_precise = _check_precise ):
627643 default_used = False
644+ if self ._precise_mode :
645+ check = check_precise
646+ list_types = list
647+ else :
648+ list_types = (list , tuple )
628649 while True :
629650 if nest_limit < 0 :
630651 raise PackValueError ("recursion limit exceeded" )
631652 if obj is None :
632653 return self ._buffer .write (b"\xc0 " )
633- if isinstance (obj , bool ):
654+ if check (obj , bool ):
634655 if obj :
635656 return self ._buffer .write (b"\xc3 " )
636657 return self ._buffer .write (b"\xc2 " )
637- if isinstance (obj , int_types ):
658+ if check (obj , int_types ):
638659 if 0 <= obj < 0x80 :
639660 return self ._buffer .write (struct .pack ("B" , obj ))
640661 if - 0x20 <= obj < 0 :
@@ -660,7 +681,7 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
660681 default_used = True
661682 continue
662683 raise PackValueError ("Integer value out of range" )
663- if self ._use_bin_type and isinstance (obj , bytes ):
684+ if self ._use_bin_type and check (obj , bytes ):
664685 n = len (obj )
665686 if n <= 0xff :
666687 self ._buffer .write (struct .pack ('>BB' , 0xc4 , n ))
@@ -671,8 +692,8 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
671692 else :
672693 raise PackValueError ("Bytes is too large" )
673694 return self ._buffer .write (obj )
674- if isinstance (obj , (Unicode , bytes )):
675- if isinstance (obj , Unicode ):
695+ if check (obj , (Unicode , bytes )):
696+ if check (obj , Unicode ):
676697 if self ._encoding is None :
677698 raise TypeError (
678699 "Can't encode unicode string: "
@@ -690,11 +711,11 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
690711 else :
691712 raise PackValueError ("String is too large" )
692713 return self ._buffer .write (obj )
693- if isinstance (obj , float ):
714+ if check (obj , float ):
694715 if self ._use_float :
695716 return self ._buffer .write (struct .pack (">Bf" , 0xca , obj ))
696717 return self ._buffer .write (struct .pack (">Bd" , 0xcb , obj ))
697- if isinstance (obj , ExtType ):
718+ if check (obj , ExtType ):
698719 code = obj .code
699720 data = obj .data
700721 assert isinstance (code , int )
@@ -719,13 +740,13 @@ def _pack(self, obj, nest_limit=DEFAULT_RECURSE_LIMIT, isinstance=isinstance):
719740 self ._buffer .write (struct .pack ("b" , code ))
720741 self ._buffer .write (data )
721742 return
722- if isinstance (obj , ( list , tuple ) ):
743+ if check (obj , list_types ):
723744 n = len (obj )
724745 self ._fb_pack_array_header (n )
725746 for i in xrange (n ):
726747 self ._pack (obj [i ], nest_limit - 1 )
727748 return
728- if isinstance (obj , dict ):
749+ if check (obj , dict ):
729750 return self ._fb_pack_map_pairs (len (obj ), dict_iteritems (obj ),
730751 nest_limit - 1 )
731752 if not default_used and self ._default is not None :
0 commit comments