Updated umsgpack to 2.7.1

This commit is contained in:
Mark Qvist 2021-12-01 19:20:24 +01:00
parent d2232f19ba
commit 1443f4c104
1 changed files with 252 additions and 128 deletions

378
RNS/vendor/umsgpack.py vendored
View File

@ -1,4 +1,4 @@
# u-msgpack-python v2.5.0 - v at sergeev.io # u-msgpack-python v2.7.1 - v at sergeev.io
# https://github.com/vsergeev/u-msgpack-python # https://github.com/vsergeev/u-msgpack-python
# #
# u-msgpack-python is a lightweight MessagePack serializer and deserializer # u-msgpack-python is a lightweight MessagePack serializer and deserializer
@ -10,7 +10,7 @@
# #
# MIT License # MIT License
# #
# Copyright (c) 2013-2016 vsergeev / Ivan (Vanya) A. Sergeev # Copyright (c) 2013-2020 vsergeev / Ivan (Vanya) A. Sergeev
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
@ -31,7 +31,7 @@
# THE SOFTWARE. # THE SOFTWARE.
# #
""" """
u-msgpack-python v2.5.0 - v at sergeev.io u-msgpack-python v2.7.1 - v at sergeev.io
https://github.com/vsergeev/u-msgpack-python https://github.com/vsergeev/u-msgpack-python
u-msgpack-python is a lightweight MessagePack serializer and deserializer u-msgpack-python is a lightweight MessagePack serializer and deserializer
@ -49,10 +49,15 @@ import datetime
import sys import sys
import io import io
__version__ = "2.5.0" if sys.version_info[0:2] >= (3, 3):
from collections.abc import Hashable
else:
from collections import Hashable
__version__ = "2.7.1"
"Module version string" "Module version string"
version = (2, 5, 0) version = (2, 7, 1)
"Module version tuple" "Module version tuple"
@ -61,7 +66,7 @@ version = (2, 5, 0)
############################################################################## ##############################################################################
# Extension type for application-defined types and data # Extension type for application-defined types and data
class Ext: class Ext(object):
""" """
The Ext class facilitates creating a serializable extension object to store The Ext class facilitates creating a serializable extension object to store
an application-defined type and data byte array. an application-defined type and data byte array.
@ -75,23 +80,33 @@ class Ext:
type: application-defined type integer type: application-defined type integer
data: application-defined data byte array data: application-defined data byte array
TypeError:
Type is not an integer.
ValueError:
Type is out of range of -128 to 127.
TypeError::
Data is not type 'bytes' (Python 3) or not type 'str' (Python 2).
Example: Example:
>>> foo = umsgpack.Ext(0x05, b"\x01\x02\x03") >>> foo = umsgpack.Ext(5, b"\x01\x02\x03")
>>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) >>> umsgpack.packb({u"special stuff": foo, u"awesome": True})
'\x82\xa7awesome\xc3\xadspecial stuff\xc7\x03\x05\x01\x02\x03' '\x82\xa7awesome\xc3\xadspecial stuff\xc7\x03\x05\x01\x02\x03'
>>> bar = umsgpack.unpackb(_) >>> bar = umsgpack.unpackb(_)
>>> print(bar["special stuff"]) >>> print(bar["special stuff"])
Ext Object (Type: 0x05, Data: 01 02 03) Ext Object (Type: 5, Data: 01 02 03)
>>> >>>
""" """
# Check type is type int # Check type is type int and in range
if not isinstance(type, int): if not isinstance(type, int):
raise TypeError("ext type is not type integer") raise TypeError("ext type is not type integer")
# Check data is type bytes elif not (-2**7 <= type <= 2**7 - 1):
raise ValueError("ext type value {:d} is out of range (-128 to 127)".format(type))
# Check data is type bytes or str
elif sys.version_info[0] == 3 and not isinstance(data, bytes): elif sys.version_info[0] == 3 and not isinstance(data, bytes):
raise TypeError("ext data is not type \'bytes\'") raise TypeError("ext data is not type \'bytes\'")
elif sys.version_info[0] == 2 and not isinstance(data, str): elif sys.version_info[0] == 2 and not isinstance(data, str):
raise TypeError("ext data is not type \'str\'") raise TypeError("ext data is not type \'str\'")
self.type = type self.type = type
self.data = data self.data = data
@ -99,9 +114,8 @@ class Ext:
""" """
Compare this Ext object with another for equality. Compare this Ext object with another for equality.
""" """
return (isinstance(other, self.__class__) and return isinstance(other, self.__class__) \
self.type == other.type and and self.type == other.type and self.data == other.data
self.data == other.data)
def __ne__(self, other): def __ne__(self, other):
""" """
@ -113,8 +127,8 @@ class Ext:
""" """
String representation of this Ext object. String representation of this Ext object.
""" """
s = "Ext Object (Type: 0x%02x, Data: " % self.type s = "Ext Object (Type: {:d}, Data: ".format(self.type)
s += " ".join(["0x%02x" % ord(self.data[i:i + 1]) s += " ".join(["0x{:02}".format(ord(self.data[i:i + 1]))
for i in xrange(min(len(self.data), 8))]) for i in xrange(min(len(self.data), 8))])
if len(self.data) > 8: if len(self.data) > 8:
s += " ..." s += " ..."
@ -130,7 +144,52 @@ class Ext:
class InvalidString(bytes): class InvalidString(bytes):
"""Subclass of bytes to hold invalid UTF-8 strings.""" """Subclass of bytes to hold invalid UTF-8 strings."""
pass
##############################################################################
# Ext Serializable Decorator
##############################################################################
_ext_class_to_type = {}
_ext_type_to_class = {}
def ext_serializable(ext_type):
"""
Return a decorator to register a class for automatic packing and unpacking
with the specified Ext type code. The application class should implement a
`packb()` method that returns serialized bytes, and an `unpackb()` class
method or static method that accepts serialized bytes and returns an
instance of the application class.
Args:
ext_type: application-defined Ext type code
Raises:
TypeError:
Ext type is not an integer.
ValueError:
Ext type is out of range of -128 to 127.
ValueError:
Ext type or class already registered.
"""
def wrapper(cls):
if not isinstance(ext_type, int):
raise TypeError("Ext type is not type integer")
elif not (-2**7 <= ext_type <= 2**7 - 1):
raise ValueError("Ext type value {:d} is out of range of -128 to 127".format(ext_type))
elif ext_type in _ext_type_to_class:
raise ValueError("Ext type {:d} already registered with class {:s}".format(ext_type, repr(_ext_type_to_class[ext_type])))
elif cls in _ext_class_to_type:
raise ValueError("Class {:s} already registered with Ext type {:d}".format(repr(cls), ext_type))
_ext_type_to_class[ext_type] = cls
_ext_class_to_type[cls] = ext_type
return cls
return wrapper
############################################################################## ##############################################################################
# Exceptions # Exceptions
@ -140,39 +199,32 @@ class InvalidString(bytes):
# Base Exception classes # Base Exception classes
class PackException(Exception): class PackException(Exception):
"Base class for exceptions encountered during packing." "Base class for exceptions encountered during packing."
pass
class UnpackException(Exception): class UnpackException(Exception):
"Base class for exceptions encountered during unpacking." "Base class for exceptions encountered during unpacking."
pass
# Packing error # Packing error
class UnsupportedTypeException(PackException): class UnsupportedTypeException(PackException):
"Object type not supported for packing." "Object type not supported for packing."
pass
# Unpacking error # Unpacking error
class InsufficientDataException(UnpackException): class InsufficientDataException(UnpackException):
"Insufficient data to unpack the serialized object." "Insufficient data to unpack the serialized object."
pass
class InvalidStringException(UnpackException): class InvalidStringException(UnpackException):
"Invalid UTF-8 string encountered during unpacking." "Invalid UTF-8 string encountered during unpacking."
pass
class UnsupportedTimestampException(UnpackException): class UnsupportedTimestampException(UnpackException):
"Unsupported timestamp format encountered during unpacking." "Unsupported timestamp format encountered during unpacking."
pass
class ReservedCodeException(UnpackException): class ReservedCodeException(UnpackException):
"Reserved code encountered during unpacking." "Reserved code encountered during unpacking."
pass
class UnhashableKeyException(UnpackException): class UnhashableKeyException(UnpackException):
@ -180,12 +232,10 @@ class UnhashableKeyException(UnpackException):
Unhashable key encountered during map unpacking. Unhashable key encountered during map unpacking.
The serialized map cannot be deserialized into a Python dictionary. The serialized map cannot be deserialized into a Python dictionary.
""" """
pass
class DuplicateKeyException(UnpackException): class DuplicateKeyException(UnpackException):
"Duplicate key encountered during map unpacking." "Duplicate key encountered during map unpacking."
pass
# Backwards compatibility # Backwards compatibility
@ -250,15 +300,15 @@ def _pack_integer(obj, fp, options):
else: else:
raise UnsupportedTypeException("huge signed int") raise UnsupportedTypeException("huge signed int")
else: else:
if obj <= 127: if obj < 128:
fp.write(struct.pack("B", obj)) fp.write(struct.pack("B", obj))
elif obj <= 2**8 - 1: elif obj < 2**8:
fp.write(b"\xcc" + struct.pack("B", obj)) fp.write(b"\xcc" + struct.pack("B", obj))
elif obj <= 2**16 - 1: elif obj < 2**16:
fp.write(b"\xcd" + struct.pack(">H", obj)) fp.write(b"\xcd" + struct.pack(">H", obj))
elif obj <= 2**32 - 1: elif obj < 2**32:
fp.write(b"\xce" + struct.pack(">I", obj)) fp.write(b"\xce" + struct.pack(">I", obj))
elif obj <= 2**64 - 1: elif obj < 2**64:
fp.write(b"\xcf" + struct.pack(">Q", obj)) fp.write(b"\xcf" + struct.pack(">Q", obj))
else: else:
raise UnsupportedTypeException("huge unsigned int") raise UnsupportedTypeException("huge unsigned int")
@ -285,94 +335,99 @@ def _pack_float(obj, fp, options):
def _pack_string(obj, fp, options): def _pack_string(obj, fp, options):
obj = obj.encode('utf-8') obj = obj.encode('utf-8')
if len(obj) <= 31: obj_len = len(obj)
fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) if obj_len < 32:
elif len(obj) <= 2**8 - 1: fp.write(struct.pack("B", 0xa0 | obj_len) + obj)
fp.write(b"\xd9" + struct.pack("B", len(obj)) + obj) elif obj_len < 2**8:
elif len(obj) <= 2**16 - 1: fp.write(b"\xd9" + struct.pack("B", obj_len) + obj)
fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) elif obj_len < 2**16:
elif len(obj) <= 2**32 - 1: fp.write(b"\xda" + struct.pack(">H", obj_len) + obj)
fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) elif obj_len < 2**32:
fp.write(b"\xdb" + struct.pack(">I", obj_len) + obj)
else: else:
raise UnsupportedTypeException("huge string") raise UnsupportedTypeException("huge string")
def _pack_binary(obj, fp, options): def _pack_binary(obj, fp, options):
if len(obj) <= 2**8 - 1: obj_len = len(obj)
fp.write(b"\xc4" + struct.pack("B", len(obj)) + obj) if obj_len < 2**8:
elif len(obj) <= 2**16 - 1: fp.write(b"\xc4" + struct.pack("B", obj_len) + obj)
fp.write(b"\xc5" + struct.pack(">H", len(obj)) + obj) elif obj_len < 2**16:
elif len(obj) <= 2**32 - 1: fp.write(b"\xc5" + struct.pack(">H", obj_len) + obj)
fp.write(b"\xc6" + struct.pack(">I", len(obj)) + obj) elif obj_len < 2**32:
fp.write(b"\xc6" + struct.pack(">I", obj_len) + obj)
else: else:
raise UnsupportedTypeException("huge binary string") raise UnsupportedTypeException("huge binary string")
def _pack_oldspec_raw(obj, fp, options): def _pack_oldspec_raw(obj, fp, options):
if len(obj) <= 31: obj_len = len(obj)
fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) if obj_len < 32:
elif len(obj) <= 2**16 - 1: fp.write(struct.pack("B", 0xa0 | obj_len) + obj)
fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) elif obj_len < 2**16:
elif len(obj) <= 2**32 - 1: fp.write(b"\xda" + struct.pack(">H", obj_len) + obj)
fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) elif obj_len < 2**32:
fp.write(b"\xdb" + struct.pack(">I", obj_len) + obj)
else: else:
raise UnsupportedTypeException("huge raw string") raise UnsupportedTypeException("huge raw string")
def _pack_ext(obj, fp, options): def _pack_ext(obj, fp, options):
if len(obj.data) == 1: obj_len = len(obj.data)
if obj_len == 1:
fp.write(b"\xd4" + struct.pack("B", obj.type & 0xff) + obj.data) fp.write(b"\xd4" + struct.pack("B", obj.type & 0xff) + obj.data)
elif len(obj.data) == 2: elif obj_len == 2:
fp.write(b"\xd5" + struct.pack("B", obj.type & 0xff) + obj.data) fp.write(b"\xd5" + struct.pack("B", obj.type & 0xff) + obj.data)
elif len(obj.data) == 4: elif obj_len == 4:
fp.write(b"\xd6" + struct.pack("B", obj.type & 0xff) + obj.data) fp.write(b"\xd6" + struct.pack("B", obj.type & 0xff) + obj.data)
elif len(obj.data) == 8: elif obj_len == 8:
fp.write(b"\xd7" + struct.pack("B", obj.type & 0xff) + obj.data) fp.write(b"\xd7" + struct.pack("B", obj.type & 0xff) + obj.data)
elif len(obj.data) == 16: elif obj_len == 16:
fp.write(b"\xd8" + struct.pack("B", obj.type & 0xff) + obj.data) fp.write(b"\xd8" + struct.pack("B", obj.type & 0xff) + obj.data)
elif len(obj.data) <= 2**8 - 1: elif obj_len < 2**8:
fp.write(b"\xc7" + fp.write(b"\xc7" + struct.pack("BB", obj_len, obj.type & 0xff) + obj.data)
struct.pack("BB", len(obj.data), obj.type & 0xff) + obj.data) elif obj_len < 2**16:
elif len(obj.data) <= 2**16 - 1: fp.write(b"\xc8" + struct.pack(">HB", obj_len, obj.type & 0xff) + obj.data)
fp.write(b"\xc8" + elif obj_len < 2**32:
struct.pack(">HB", len(obj.data), obj.type & 0xff) + obj.data) fp.write(b"\xc9" + struct.pack(">IB", obj_len, obj.type & 0xff) + obj.data)
elif len(obj.data) <= 2**32 - 1:
fp.write(b"\xc9" +
struct.pack(">IB", len(obj.data), obj.type & 0xff) + obj.data)
else: else:
raise UnsupportedTypeException("huge ext data") raise UnsupportedTypeException("huge ext data")
def _pack_ext_timestamp(obj, fp, options): def _pack_ext_timestamp(obj, fp, options):
if not obj.tzinfo:
# Object is naive datetime, convert to aware date time,
# assuming UTC timezone
delta = obj.replace(tzinfo=_utc_tzinfo) - _epoch
else:
# Object is aware datetime
delta = obj - _epoch delta = obj - _epoch
seconds = delta.seconds + delta.days * 86400 seconds = delta.seconds + delta.days * 86400
microseconds = delta.microseconds microseconds = delta.microseconds
if microseconds == 0 and 0 <= seconds <= 2**32 - 1: if microseconds == 0 and 0 <= seconds <= 2**32 - 1:
# 32-bit timestamp # 32-bit timestamp
fp.write(b"\xd6\xff" + fp.write(b"\xd6\xff" + struct.pack(">I", seconds))
struct.pack(">I", seconds))
elif 0 <= seconds <= 2**34 - 1: elif 0 <= seconds <= 2**34 - 1:
# 64-bit timestamp # 64-bit timestamp
value = ((microseconds * 1000) << 34) | seconds value = ((microseconds * 1000) << 34) | seconds
fp.write(b"\xd7\xff" + fp.write(b"\xd7\xff" + struct.pack(">Q", value))
struct.pack(">Q", value))
elif -2**63 <= abs(seconds) <= 2**63 - 1: elif -2**63 <= abs(seconds) <= 2**63 - 1:
# 96-bit timestamp # 96-bit timestamp
fp.write(b"\xc7\x0c\xff" + fp.write(b"\xc7\x0c\xff" + struct.pack(">Iq", microseconds * 1000, seconds))
struct.pack(">I", microseconds * 1000) +
struct.pack(">q", seconds))
else: else:
raise UnsupportedTypeException("huge timestamp") raise UnsupportedTypeException("huge timestamp")
def _pack_array(obj, fp, options): def _pack_array(obj, fp, options):
if len(obj) <= 15: obj_len = len(obj)
fp.write(struct.pack("B", 0x90 | len(obj))) if obj_len < 16:
elif len(obj) <= 2**16 - 1: fp.write(struct.pack("B", 0x90 | obj_len))
fp.write(b"\xdc" + struct.pack(">H", len(obj))) elif obj_len < 2**16:
elif len(obj) <= 2**32 - 1: fp.write(b"\xdc" + struct.pack(">H", obj_len))
fp.write(b"\xdd" + struct.pack(">I", len(obj))) elif obj_len < 2**32:
fp.write(b"\xdd" + struct.pack(">I", obj_len))
else: else:
raise UnsupportedTypeException("huge array") raise UnsupportedTypeException("huge array")
@ -381,12 +436,13 @@ def _pack_array(obj, fp, options):
def _pack_map(obj, fp, options): def _pack_map(obj, fp, options):
if len(obj) <= 15: obj_len = len(obj)
fp.write(struct.pack("B", 0x80 | len(obj))) if obj_len < 16:
elif len(obj) <= 2**16 - 1: fp.write(struct.pack("B", 0x80 | obj_len))
fp.write(b"\xde" + struct.pack(">H", len(obj))) elif obj_len < 2**16:
elif len(obj) <= 2**32 - 1: fp.write(b"\xde" + struct.pack(">H", obj_len))
fp.write(b"\xdf" + struct.pack(">I", len(obj))) elif obj_len < 2**32:
fp.write(b"\xdf" + struct.pack(">I", obj_len))
else: else:
raise UnsupportedTypeException("huge array") raise UnsupportedTypeException("huge array")
@ -435,9 +491,14 @@ def _pack2(obj, fp, **options):
_pack_nil(obj, fp, options) _pack_nil(obj, fp, options)
elif ext_handlers and obj.__class__ in ext_handlers: elif ext_handlers and obj.__class__ in ext_handlers:
_pack_ext(ext_handlers[obj.__class__](obj), fp, options) _pack_ext(ext_handlers[obj.__class__](obj), fp, options)
elif obj.__class__ in _ext_class_to_type:
try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__)))
elif isinstance(obj, bool): elif isinstance(obj, bool):
_pack_boolean(obj, fp, options) _pack_boolean(obj, fp, options)
elif isinstance(obj, int) or isinstance(obj, long): elif isinstance(obj, (int, long)):
_pack_integer(obj, fp, options) _pack_integer(obj, fp, options)
elif isinstance(obj, float): elif isinstance(obj, float):
_pack_float(obj, fp, options) _pack_float(obj, fp, options)
@ -449,7 +510,7 @@ def _pack2(obj, fp, **options):
_pack_string(obj, fp, options) _pack_string(obj, fp, options)
elif isinstance(obj, str): elif isinstance(obj, str):
_pack_binary(obj, fp, options) _pack_binary(obj, fp, options)
elif isinstance(obj, list) or isinstance(obj, tuple): elif isinstance(obj, (list, tuple)):
_pack_array(obj, fp, options) _pack_array(obj, fp, options)
elif isinstance(obj, dict): elif isinstance(obj, dict):
_pack_map(obj, fp, options) _pack_map(obj, fp, options)
@ -464,9 +525,19 @@ def _pack2(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options) _pack_ext(ext_handlers[t](obj), fp, options)
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: %s" % str(type(obj))) "unsupported type: {:s}".format(str(type(obj))))
elif _ext_class_to_type:
# Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
if t:
try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t)))
else: else:
raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
# Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type # Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type
@ -507,6 +578,11 @@ def _pack3(obj, fp, **options):
_pack_nil(obj, fp, options) _pack_nil(obj, fp, options)
elif ext_handlers and obj.__class__ in ext_handlers: elif ext_handlers and obj.__class__ in ext_handlers:
_pack_ext(ext_handlers[obj.__class__](obj), fp, options) _pack_ext(ext_handlers[obj.__class__](obj), fp, options)
elif obj.__class__ in _ext_class_to_type:
try:
_pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__)))
elif isinstance(obj, bool): elif isinstance(obj, bool):
_pack_boolean(obj, fp, options) _pack_boolean(obj, fp, options)
elif isinstance(obj, int): elif isinstance(obj, int):
@ -521,7 +597,7 @@ def _pack3(obj, fp, **options):
_pack_string(obj, fp, options) _pack_string(obj, fp, options)
elif isinstance(obj, bytes): elif isinstance(obj, bytes):
_pack_binary(obj, fp, options) _pack_binary(obj, fp, options)
elif isinstance(obj, list) or isinstance(obj, tuple): elif isinstance(obj, (list, tuple)):
_pack_array(obj, fp, options) _pack_array(obj, fp, options)
elif isinstance(obj, dict): elif isinstance(obj, dict):
_pack_map(obj, fp, options) _pack_map(obj, fp, options)
@ -536,10 +612,20 @@ def _pack3(obj, fp, **options):
_pack_ext(ext_handlers[t](obj), fp, options) _pack_ext(ext_handlers[t](obj), fp, options)
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: %s" % str(type(obj))) "unsupported type: {:s}".format(str(type(obj))))
elif _ext_class_to_type:
# Linear search for superclass
t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None)
if t:
try:
_pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t)))
else:
raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj))))
else: else:
raise UnsupportedTypeException( raise UnsupportedTypeException(
"unsupported type: %s" % str(type(obj))) "unsupported type: {:s}".format(str(type(obj))))
def _packb2(obj, **options): def _packb2(obj, **options):
@ -613,9 +699,20 @@ def _packb3(obj, **options):
def _read_except(fp, n): def _read_except(fp, n):
if n == 0:
return b""
data = fp.read(n) data = fp.read(n)
if len(data) < n: if len(data) == 0:
raise InsufficientDataException() raise InsufficientDataException()
while len(data) < n:
chunk = fp.read(n - len(data))
if len(chunk) == 0:
raise InsufficientDataException()
data += chunk
return data return data
@ -640,21 +737,21 @@ def _unpack_integer(code, fp, options):
return struct.unpack(">I", _read_except(fp, 4))[0] return struct.unpack(">I", _read_except(fp, 4))[0]
elif code == b'\xcf': elif code == b'\xcf':
return struct.unpack(">Q", _read_except(fp, 8))[0] return struct.unpack(">Q", _read_except(fp, 8))[0]
raise Exception("logic error, not int: 0x%02x" % ord(code)) raise Exception("logic error, not int: 0x{:02x}".format(ord(code)))
def _unpack_reserved(code, fp, options): def _unpack_reserved(code, fp, options):
if code == b'\xc1': if code == b'\xc1':
raise ReservedCodeException( raise ReservedCodeException(
"encountered reserved code: 0x%02x" % ord(code)) "encountered reserved code: 0x{:02x}".format(ord(code)))
raise Exception( raise Exception(
"logic error, not reserved code: 0x%02x" % ord(code)) "logic error, not reserved code: 0x{:02x}".format(ord(code)))
def _unpack_nil(code, fp, options): def _unpack_nil(code, fp, options):
if code == b'\xc0': if code == b'\xc0':
return None return None
raise Exception("logic error, not nil: 0x%02x" % ord(code)) raise Exception("logic error, not nil: 0x{:02x}".format(ord(code)))
def _unpack_boolean(code, fp, options): def _unpack_boolean(code, fp, options):
@ -662,7 +759,7 @@ def _unpack_boolean(code, fp, options):
return False return False
elif code == b'\xc3': elif code == b'\xc3':
return True return True
raise Exception("logic error, not boolean: 0x%02x" % ord(code)) raise Exception("logic error, not boolean: 0x{:02x}".format(ord(code)))
def _unpack_float(code, fp, options): def _unpack_float(code, fp, options):
@ -670,7 +767,7 @@ def _unpack_float(code, fp, options):
return struct.unpack(">f", _read_except(fp, 4))[0] return struct.unpack(">f", _read_except(fp, 4))[0]
elif code == b'\xcb': elif code == b'\xcb':
return struct.unpack(">d", _read_except(fp, 8))[0] return struct.unpack(">d", _read_except(fp, 8))[0]
raise Exception("logic error, not float: 0x%02x" % ord(code)) raise Exception("logic error, not float: 0x{:02x}".format(ord(code)))
def _unpack_string(code, fp, options): def _unpack_string(code, fp, options):
@ -683,7 +780,7 @@ def _unpack_string(code, fp, options):
elif code == b'\xdb': elif code == b'\xdb':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not string: 0x%02x" % ord(code)) raise Exception("logic error, not string: 0x{:02x}".format(ord(code)))
# Always return raw bytes in compatibility mode # Always return raw bytes in compatibility mode
global compatibility global compatibility
@ -707,7 +804,7 @@ def _unpack_binary(code, fp, options):
elif code == b'\xc6': elif code == b'\xc6':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not binary: 0x%02x" % ord(code)) raise Exception("logic error, not binary: 0x{:02x}".format(ord(code)))
return _read_except(fp, length) return _read_except(fp, length)
@ -730,43 +827,48 @@ def _unpack_ext(code, fp, options):
elif code == b'\xc9': elif code == b'\xc9':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not ext: 0x%02x" % ord(code)) raise Exception("logic error, not ext: 0x{:02x}".format(ord(code)))
ext_type = struct.unpack("b", _read_except(fp, 1))[0] ext_type = struct.unpack("b", _read_except(fp, 1))[0]
ext_data = _read_except(fp, length) ext_data = _read_except(fp, length)
# Create extension object
ext = Ext(ext_type, ext_data)
# Unpack with ext handler, if we have one # Unpack with ext handler, if we have one
ext_handlers = options.get("ext_handlers") ext_handlers = options.get("ext_handlers")
if ext_handlers and ext.type in ext_handlers: if ext_handlers and ext_type in ext_handlers:
return ext_handlers[ext.type](ext) return ext_handlers[ext_type](Ext(ext_type, ext_data))
# Unpack with ext classes, if type is registered
if ext_type in _ext_type_to_class:
try:
return _ext_type_to_class[ext_type].unpackb(ext_data)
except AttributeError:
raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_type_to_class[ext_type])))
# Timestamp extension # Timestamp extension
if ext.type == -1: if ext_type == -1:
return _unpack_ext_timestamp(ext, options) return _unpack_ext_timestamp(ext_data, options)
return ext return Ext(ext_type, ext_data)
def _unpack_ext_timestamp(ext, options): def _unpack_ext_timestamp(ext_data, options):
if len(ext.data) == 4: obj_len = len(ext_data)
if obj_len == 4:
# 32-bit timestamp # 32-bit timestamp
seconds = struct.unpack(">I", ext.data)[0] seconds = struct.unpack(">I", ext_data)[0]
microseconds = 0 microseconds = 0
elif len(ext.data) == 8: elif obj_len == 8:
# 64-bit timestamp # 64-bit timestamp
value = struct.unpack(">Q", ext.data)[0] value = struct.unpack(">Q", ext_data)[0]
seconds = value & 0x3ffffffff seconds = value & 0x3ffffffff
microseconds = (value >> 34) // 1000 microseconds = (value >> 34) // 1000
elif len(ext.data) == 12: elif obj_len == 12:
# 96-bit timestamp # 96-bit timestamp
seconds = struct.unpack(">q", ext.data[4:12])[0] seconds = struct.unpack(">q", ext_data[4:12])[0]
microseconds = struct.unpack(">I", ext.data[0:4])[0] // 1000 microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000
else: else:
raise UnsupportedTimestampException( raise UnsupportedTimestampException(
"unsupported timestamp with data length %d" % len(ext.data)) "unsupported timestamp with data length {:d}".format(len(ext_data)))
return _epoch + datetime.timedelta(seconds=seconds, return _epoch + datetime.timedelta(seconds=seconds,
microseconds=microseconds) microseconds=microseconds)
@ -780,7 +882,10 @@ def _unpack_array(code, fp, options):
elif code == b'\xdd': elif code == b'\xdd':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not array: 0x%02x" % ord(code)) raise Exception("logic error, not array: 0x{:02x}".format(ord(code)))
if options.get('use_tuple'):
return tuple((_unpack(fp, options) for i in xrange(length)))
return [_unpack(fp, options) for i in xrange(length)] return [_unpack(fp, options) for i in xrange(length)]
@ -799,10 +904,9 @@ def _unpack_map(code, fp, options):
elif code == b'\xdf': elif code == b'\xdf':
length = struct.unpack(">I", _read_except(fp, 4))[0] length = struct.unpack(">I", _read_except(fp, 4))[0]
else: else:
raise Exception("logic error, not map: 0x%02x" % ord(code)) raise Exception("logic error, not map: 0x{:02x}".format(ord(code)))
d = {} if not options.get('use_ordered_dict') \ d = {} if not options.get('use_ordered_dict') else collections.OrderedDict()
else collections.OrderedDict()
for _ in xrange(length): for _ in xrange(length):
# Unpack key # Unpack key
k = _unpack(fp, options) k = _unpack(fp, options)
@ -810,12 +914,12 @@ def _unpack_map(code, fp, options):
if isinstance(k, list): if isinstance(k, list):
# Attempt to convert list into a hashable tuple # Attempt to convert list into a hashable tuple
k = _deep_list_to_tuple(k) k = _deep_list_to_tuple(k)
elif not isinstance(k, collections.Hashable): elif not isinstance(k, Hashable):
raise UnhashableKeyException( raise UnhashableKeyException(
"encountered unhashable key: %s, %s" % (str(k), str(type(k)))) "encountered unhashable key: \"{:s}\" ({:s})".format(str(k), str(type(k))))
elif k in d: elif k in d:
raise DuplicateKeyException( raise DuplicateKeyException(
"encountered duplicate key: %s, %s" % (str(k), str(type(k)))) "encountered duplicate key: \"{:s}\" ({:s})".format(str(k), str(type(k))))
# Unpack value # Unpack value
v = _unpack(fp, options) v = _unpack(fp, options)
@ -824,7 +928,7 @@ def _unpack_map(code, fp, options):
d[k] = v d[k] = v
except TypeError: except TypeError:
raise UnhashableKeyException( raise UnhashableKeyException(
"encountered unhashable key: %s" % str(k)) "encountered unhashable key: \"{:s}\"".format(str(k)))
return d return d
@ -848,6 +952,8 @@ def _unpack2(fp, **options):
Ext into an object Ext into an object
use_ordered_dict (bool): unpack maps into OrderedDict, instead of use_ordered_dict (bool): unpack maps into OrderedDict, instead of
unordered dict (default False) unordered dict (default False)
use_tuple (bool): unpacks arrays into tuples, instead of lists (default
False)
allow_invalid_utf8 (bool): unpack invalid strings into instances of allow_invalid_utf8 (bool): unpack invalid strings into instances of
InvalidString, for access to the bytes InvalidString, for access to the bytes
(default False) (default False)
@ -892,6 +998,8 @@ def _unpack3(fp, **options):
Ext into an object Ext into an object
use_ordered_dict (bool): unpack maps into OrderedDict, instead of use_ordered_dict (bool): unpack maps into OrderedDict, instead of
unordered dict (default False) unordered dict (default False)
use_tuple (bool): unpacks arrays into tuples, instead of lists (default
False)
allow_invalid_utf8 (bool): unpack invalid strings into instances of allow_invalid_utf8 (bool): unpack invalid strings into instances of
InvalidString, for access to the bytes InvalidString, for access to the bytes
(default False) (default False)
@ -937,6 +1045,8 @@ def _unpackb2(s, **options):
Ext into an object Ext into an object
use_ordered_dict (bool): unpack maps into OrderedDict, instead of use_ordered_dict (bool): unpack maps into OrderedDict, instead of
unordered dict (default False) unordered dict (default False)
use_tuple (bool): unpacks arrays into tuples, instead of lists (default
False)
allow_invalid_utf8 (bool): unpack invalid strings into instances of allow_invalid_utf8 (bool): unpack invalid strings into instances of
InvalidString, for access to the bytes InvalidString, for access to the bytes
(default False) (default False)
@ -985,6 +1095,8 @@ def _unpackb3(s, **options):
Ext into an object Ext into an object
use_ordered_dict (bool): unpack maps into OrderedDict, instead of use_ordered_dict (bool): unpack maps into OrderedDict, instead of
unordered dict (default False) unordered dict (default False)
use_tuple (bool): unpacks arrays into tuples, instead of lists (default
False)
allow_invalid_utf8 (bool): unpack invalid strings into instances of allow_invalid_utf8 (bool): unpack invalid strings into instances of
InvalidString, for access to the bytes InvalidString, for access to the bytes
(default False) (default False)
@ -1045,9 +1157,21 @@ def __init():
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
_utc_tzinfo = datetime.timezone.utc _utc_tzinfo = datetime.timezone.utc
else: else:
_utc_tzinfo = None class UTC(datetime.tzinfo):
ZERO = datetime.timedelta(0)
# Calculate epoch datetime def utcoffset(self, dt):
return UTC.ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return UTC.ZERO
_utc_tzinfo = UTC()
# Calculate an aware epoch datetime
_epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc_tzinfo) _epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc_tzinfo)
# Auto-detect system float precision # Auto-detect system float precision