From 620f6ef14e735a60b38a81cbabecd215c994c912 Mon Sep 17 00:00:00 2001 From: Gabe Appleton Date: Sun, 3 May 2020 19:35:25 -0400 Subject: [PATCH 01/41] add support for packing subclasses of ext_serializable() classes resolves #45. Signed-off-by: Vanya A. Sergeev --- umsgpack.py | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index c7dddf5..f5f152b 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -140,7 +140,8 @@ class InvalidString(bytes): # Ext Serializable Decorator ############################################################################## -_ext_classes = {} +_ext_classes_to_code = {} +_ext_codes_to_class = {} def ext_serializable(ext_type): @@ -159,13 +160,13 @@ def ext_serializable(ext_type): Ext type or class already registered. """ def wrapper(cls): - if ext_type in _ext_classes: - raise ValueError("Ext type 0x{:02x} already registered with class {:s}".format(ext_type, repr(_ext_classes[ext_type]))) - elif cls in _ext_classes: + if ext_type in _ext_codes_to_class: + raise ValueError("Ext type 0x{:02x} already registered with class {:s}".format(ext_type, repr(_ext_codes_to_class[ext_type]))) + elif cls in _ext_classes_to_code: raise ValueError("Class {:s} already registered with Ext type 0x{:02x}".format(repr(cls), ext_type)) - _ext_classes[ext_type] = cls - _ext_classes[cls] = ext_type + _ext_codes_to_class[ext_type] = cls + _ext_classes_to_code[cls] = ext_type return cls @@ -472,11 +473,19 @@ def _pack2(obj, fp, **options): _pack_nil(obj, fp, options) elif ext_handlers and obj.__class__ in ext_handlers: _pack_ext(ext_handlers[obj.__class__](obj), fp, options) - elif obj.__class__ in _ext_classes: + elif obj.__class__ in _ext_classes_to_code: try: - _pack_ext(Ext(_ext_classes[obj.__class__], obj.packb()), fp, options) + _pack_ext(Ext(_ext_classes_to_code[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, tuple(_ext_classes_to_code)): + for cls in _ext_classes_to_code: + if isinstance(obj, cls): + try: + _pack_ext(Ext(_ext_classes_to_code[cls], obj.packb()), fp, options) + break + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, (int, long)): @@ -549,11 +558,19 @@ def _pack3(obj, fp, **options): _pack_nil(obj, fp, options) elif ext_handlers and obj.__class__ in ext_handlers: _pack_ext(ext_handlers[obj.__class__](obj), fp, options) - elif obj.__class__ in _ext_classes: + elif obj.__class__ in _ext_classes_to_code: try: - _pack_ext(Ext(_ext_classes[obj.__class__], obj.packb()), fp, options) + _pack_ext(Ext(_ext_classes_to_code[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, tuple(_ext_classes_to_code)): + for cls in _ext_classes_to_code: + if isinstance(obj, cls): + try: + _pack_ext(Ext(_ext_classes_to_code[cls], obj.packb()), fp, options) + break + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, int): @@ -799,11 +816,11 @@ def _unpack_ext(code, fp, options): return ext_handlers[ext_type](Ext(ext_type, ext_data)) # Unpack with ext classes, if type is registered - if ext_type in _ext_classes: + if ext_type in _ext_codes_to_class: try: - return _ext_classes[ext_type].unpackb(ext_data) + return _ext_codes_to_class[ext_type].unpackb(ext_data) except AttributeError: - raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_classes[ext_type]))) + raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_codes_to_class[ext_type]))) # Timestamp extension if ext_type == -1: From fce7f63fbcb2f5449fd7799d980d5c58f01c66f9 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 31 Jul 2020 03:34:16 -0500 Subject: [PATCH 02/41] rename ext_serializable() mapping dictionaries --- umsgpack.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index f5f152b..b4a1cd6 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -140,8 +140,8 @@ class InvalidString(bytes): # Ext Serializable Decorator ############################################################################## -_ext_classes_to_code = {} -_ext_codes_to_class = {} +_ext_class_to_type = {} +_ext_type_to_class = {} def ext_serializable(ext_type): @@ -160,13 +160,13 @@ def ext_serializable(ext_type): Ext type or class already registered. """ def wrapper(cls): - if ext_type in _ext_codes_to_class: - raise ValueError("Ext type 0x{:02x} already registered with class {:s}".format(ext_type, repr(_ext_codes_to_class[ext_type]))) - elif cls in _ext_classes_to_code: + if ext_type in _ext_type_to_class: + raise ValueError("Ext type 0x{:02x} 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 0x{:02x}".format(repr(cls), ext_type)) - _ext_codes_to_class[ext_type] = cls - _ext_classes_to_code[cls] = ext_type + _ext_type_to_class[ext_type] = cls + _ext_class_to_type[cls] = ext_type return cls @@ -473,16 +473,16 @@ def _pack2(obj, fp, **options): _pack_nil(obj, fp, options) elif ext_handlers and obj.__class__ in ext_handlers: _pack_ext(ext_handlers[obj.__class__](obj), fp, options) - elif obj.__class__ in _ext_classes_to_code: + elif obj.__class__ in _ext_class_to_type: try: - _pack_ext(Ext(_ext_classes_to_code[obj.__class__], obj.packb()), fp, options) + _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, tuple(_ext_classes_to_code)): - for cls in _ext_classes_to_code: + elif isinstance(obj, tuple(_ext_class_to_type)): + for cls in _ext_class_to_type: if isinstance(obj, cls): try: - _pack_ext(Ext(_ext_classes_to_code[cls], obj.packb()), fp, options) + _pack_ext(Ext(_ext_class_to_type[cls], obj.packb()), fp, options) break except AttributeError: raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) @@ -558,16 +558,16 @@ def _pack3(obj, fp, **options): _pack_nil(obj, fp, options) elif ext_handlers and obj.__class__ in ext_handlers: _pack_ext(ext_handlers[obj.__class__](obj), fp, options) - elif obj.__class__ in _ext_classes_to_code: + elif obj.__class__ in _ext_class_to_type: try: - _pack_ext(Ext(_ext_classes_to_code[obj.__class__], obj.packb()), fp, options) + _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, tuple(_ext_classes_to_code)): - for cls in _ext_classes_to_code: + elif isinstance(obj, tuple(_ext_class_to_type)): + for cls in _ext_class_to_type: if isinstance(obj, cls): try: - _pack_ext(Ext(_ext_classes_to_code[cls], obj.packb()), fp, options) + _pack_ext(Ext(_ext_class_to_type[cls], obj.packb()), fp, options) break except AttributeError: raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) @@ -816,11 +816,11 @@ def _unpack_ext(code, fp, options): return ext_handlers[ext_type](Ext(ext_type, ext_data)) # Unpack with ext classes, if type is registered - if ext_type in _ext_codes_to_class: + if ext_type in _ext_type_to_class: try: - return _ext_codes_to_class[ext_type].unpackb(ext_data) + 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_codes_to_class[ext_type]))) + raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_type_to_class[ext_type]))) # Timestamp extension if ext_type == -1: From b36181ef62f05a857d56bdc8552dfa58362b9607 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 31 Jul 2020 03:37:30 -0500 Subject: [PATCH 03/41] refactor ext_serializable() superclass search in packing --- umsgpack.py | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index b4a1cd6..9e63605 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -478,14 +478,16 @@ def _pack2(obj, fp, **options): _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, tuple(_ext_class_to_type)): - for cls in _ext_class_to_type: - if isinstance(obj, cls): - try: - _pack_ext(Ext(_ext_class_to_type[cls], obj.packb()), fp, options) - break - except AttributeError: - raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) + 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" % str(type(obj))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, (int, long)): @@ -563,14 +565,16 @@ def _pack3(obj, fp, **options): _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, tuple(_ext_class_to_type)): - for cls in _ext_class_to_type: - if isinstance(obj, cls): - try: - _pack_ext(Ext(_ext_class_to_type[cls], obj.packb()), fp, options) - break - except AttributeError: - raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(cls))) + 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" % str(type(obj))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, int): From e6e8566cadb4054b7f93ed810b555ff3f72007d2 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 31 Jul 2020 03:38:16 -0500 Subject: [PATCH 04/41] reorder priority of ext_serializable() superclass search in packing after ext_handlers superclass search. --- umsgpack.py | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index 9e63605..8313e77 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -478,16 +478,6 @@ def _pack2(obj, fp, **options): _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 _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" % str(type(obj))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, (int, long)): @@ -518,6 +508,16 @@ def _pack2(obj, fp, **options): else: raise UnsupportedTypeException( "unsupported type: %s" % 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" % str(type(obj))) else: raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) @@ -565,16 +565,6 @@ def _pack3(obj, fp, **options): _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 _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" % str(type(obj))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, int): @@ -605,6 +595,16 @@ def _pack3(obj, fp, **options): else: raise UnsupportedTypeException( "unsupported type: %s" % 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" % str(type(obj))) else: raise UnsupportedTypeException( "unsupported type: %s" % str(type(obj))) From a5d7d40be6072b7df00de31c37725a7ace4c9b6f Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 31 Jul 2020 03:39:32 -0500 Subject: [PATCH 05/41] fix deregistration of Ext serializable classes in unit tests --- test_umsgpack.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test_umsgpack.py b/test_umsgpack.py index 512bf6b..8476f08 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -669,8 +669,10 @@ class IncompleteClass: with self.assertRaises(NotImplementedError): umsgpack.unpackb(b"\xd4\x21\x00") - # Unregister Ext serializable classes for future tests - umsgpack._ext_classes = {} + # Unregister Ext serializable classes to prevent interference with + # subsequent tests + umsgpack._ext_classes_to_code = {} + umsgpack._ext_code_to_classes = {} def test_streaming_writer(self): # Try first composite test vector From c9c5c7557396f921ef82b2585e8a323d1c3421ae Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 1 Aug 2020 00:22:47 -0500 Subject: [PATCH 06/41] add ext handler subclass test to unit tests --- test_umsgpack.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test_umsgpack.py b/test_umsgpack.py index 8476f08..fcb70a7 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -610,6 +610,31 @@ def test_unpack_ext_override(self): unpacked = umsgpack.unpackb(data, ext_handlers=override_ext_handlers) self.assertEqual(unpacked, obj) + def test_ext_handlers_subclass(self): + class Rectangle: + def __init__(self, length, width): + self.length = length + self.width = width + + def __eq__(self, other): + return self.length == other.length and self.width == other.width + + class Square(Rectangle): + def __init__(self, width): + Rectangle.__init__(self, width, width) + + # Test pack (packs base class) + packed = umsgpack.packb(Square(5), ext_handlers={ + Rectangle: lambda obj: umsgpack.Ext(0x10, umsgpack.packb([obj.length, obj.width])), + }) + self.assertEqual(packed, b"\xc7\x03\x10\x92\x05\x05") + + # Test unpack (unpacks base class) + unpacked = umsgpack.unpackb(packed, ext_handlers={ + 0x10: lambda ext: Rectangle(*umsgpack.unpackb(ext.data)), + }) + self.assertEqual(unpacked, Rectangle(5, 5)) + def test_ext_serializable(self): # Register test class @umsgpack.ext_serializable(0x20) From 77d322d77988affe52d43ca76a954d1efdf4cb58 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 1 Aug 2020 00:23:07 -0500 Subject: [PATCH 07/41] add ext_serializable() subclass test to unit tests --- test_umsgpack.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test_umsgpack.py b/test_umsgpack.py index fcb70a7..2f53dde 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -699,6 +699,40 @@ class IncompleteClass: umsgpack._ext_classes_to_code = {} umsgpack._ext_code_to_classes = {} + def test_ext_serializable_subclass(self): + @umsgpack.ext_serializable(0x10) + class Rectangle: + def __init__(self, length, width): + self.length = length + self.width = width + + def __eq__(self, other): + return self.length == other.length and self.width == other.width + + def packb(self): + return umsgpack.packb([self.length, self.width]) + + @classmethod + def unpackb(cls, data): + return cls(*umsgpack.unpackb(data)) + + class Square(Rectangle): + def __init__(self, width): + Rectangle.__init__(self, width, width) + + # Test pack (packs base class) + packed = umsgpack.packb(Square(5)) + self.assertEqual(packed, b"\xc7\x03\x10\x92\x05\x05") + + # Test unpack (unpacks base class) + unpacked = umsgpack.unpackb(packed) + self.assertEqual(unpacked, Rectangle(5, 5)) + + # Unregister Ext serializable classes to prevent interference with + # subsequent tests + umsgpack._ext_classes_to_code = {} + umsgpack._ext_code_to_classes = {} + def test_streaming_writer(self): # Try first composite test vector (_, obj, data) = composite_test_vectors[0] From 80fe3cc2b9472751adc5d6f3ab2c7712adc92e0b Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 1 Aug 2020 00:30:47 -0500 Subject: [PATCH 08/41] update version and changelog to v2.7.0 --- CHANGELOG.md | 5 +++++ setup.py | 2 +- umsgpack.py | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec0da9..355e351 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +* Version 2.7.0 - 08/01/2020 + * Add support for packing subclasses of `ext_serializable()` application classes. + * Contributors + * Gabe Appleton, @gappleto97 - 620f6ef + * Version 2.6.0 - 04/25/2020 * Add `use_tuple` option to unpacking functions for unpacking MessagePack arrays into tuples. * Add `ext_serializable()` decorator for registration of application classes with Ext types for automatic packing and unpacking. diff --git a/setup.py b/setup.py index abcf225..7fdafa4 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='u-msgpack-python', - version='2.6.0', + version='2.7.0', description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.', author='vsergeev', author_email='v@sergeev.io', diff --git a/umsgpack.py b/umsgpack.py index 8313e77..ff53036 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.6.0 - v at sergeev.io +# u-msgpack-python v2.7.0 - v at sergeev.io # https://github.com/vsergeev/u-msgpack-python # # u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.6.0 - v at sergeev.io +u-msgpack-python v2.7.0 - v at sergeev.io https://github.com/vsergeev/u-msgpack-python u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -54,10 +54,10 @@ else: from collections import Hashable -__version__ = "2.6.0" +__version__ = "2.7.0" "Module version string" -version = (2, 6, 0) +version = (2, 7, 0) "Module version tuple" From 6a1771f3760909d112124d1626877e3dbe49a61f Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:03:46 -0500 Subject: [PATCH 09/41] format ext type value as signed decimal instead of hex --- umsgpack.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index ff53036..1596d35 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -81,12 +81,12 @@ def __init__(self, type, data): data: application-defined data byte array 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}) '\x82\xa7awesome\xc3\xadspecial stuff\xc7\x03\x05\x01\x02\x03' >>> bar = umsgpack.unpackb(_) >>> 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 @@ -117,7 +117,7 @@ def __str__(self): """ String representation of this Ext object. """ - s = "Ext Object (Type: 0x%02x, Data: " % self.type + s = "Ext Object (Type: %d, Data: " % self.type s += " ".join(["0x%02x" % ord(self.data[i:i + 1]) for i in xrange(min(len(self.data), 8))]) if len(self.data) > 8: @@ -161,9 +161,9 @@ def ext_serializable(ext_type): """ def wrapper(cls): if ext_type in _ext_type_to_class: - raise ValueError("Ext type 0x{:02x} already registered with class {:s}".format(ext_type, repr(_ext_type_to_class[ext_type]))) + 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 0x{:02x}".format(repr(cls), ext_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 From 9d511bcb355d8cfc59957c09ac705924c6ffb507 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:06:54 -0500 Subject: [PATCH 10/41] update ext type value formatting in readme and msgpack.org.md --- README.md | 6 +++--- msgpack.org.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e8a6a7f..bb04cbb 100644 --- a/README.md +++ b/README.md @@ -64,14 +64,14 @@ Streaming serialization with file-like objects: Serializing and deserializing a raw Ext type: ``` python ->>> # Create an Ext object with type 0x05 and data b"\x01\x02\x03" -... foo = umsgpack.Ext(0x05, b"\x01\x02\x03") +>>> # Create an Ext object with type 5 and data b"\x01\x02\x03" +... foo = umsgpack.Ext(5, b"\x01\x02\x03") >>> umsgpack.packb({u"stuff": foo, u"awesome": True}) b'\x82\xa5stuff\xc7\x03\x05\x01\x02\x03\xa7awesome\xc3' >>> >>> bar = umsgpack.unpackb(_) >>> print(bar['stuff']) -Ext Object (Type: 0x05, Data: 0x01 0x02 0x03) +Ext Object (Type: 5, Data: 0x01 0x02 0x03) >>> bar['stuff'].type 5 >>> bar['stuff'].data diff --git a/msgpack.org.md b/msgpack.org.md index 227c7ea..3117777 100644 --- a/msgpack.org.md +++ b/msgpack.org.md @@ -65,14 +65,14 @@ Streaming serialization with file-like objects: Serializing and deserializing a raw Ext type: ``` python ->>> # Create an Ext object with type 0x05 and data b"\x01\x02\x03" -... foo = umsgpack.Ext(0x05, b"\x01\x02\x03") +>>> # Create an Ext object with type 5 and data b"\x01\x02\x03" +... foo = umsgpack.Ext(5, b"\x01\x02\x03") >>> umsgpack.packb({u"stuff": foo, u"awesome": True}) b'\x82\xa5stuff\xc7\x03\x05\x01\x02\x03\xa7awesome\xc3' >>> >>> bar = umsgpack.unpackb(_) >>> print(bar['stuff']) -Ext Object (Type: 0x05, Data: 0x01 0x02 0x03) +Ext Object (Type: 5, Data: 0x01 0x02 0x03) >>> bar['stuff'].type 5 >>> bar['stuff'].data From c3a6a9529d396280739a60cd891f29edba201753 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:07:08 -0500 Subject: [PATCH 11/41] add ext type value validation to Ext class and ext_serializable() decorator resolves #46. --- test_umsgpack.py | 18 ++++++++++++++++++ umsgpack.py | 24 +++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/test_umsgpack.py b/test_umsgpack.py index 2f53dde..f6bedd2 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -539,12 +539,20 @@ def test_unpack_tuple(self): self.assertEqual(umsgpack.unpackb(data, use_tuple=True), obj_tuple) def test_ext_exceptions(self): + # Test invalid Ext type type with self.assertRaises(TypeError): _ = umsgpack.Ext(5.0, b"") + # Test invalid data type with self.assertRaises(TypeError): _ = umsgpack.Ext(0, u"unicode string") + # Test out of range Ext type value + with self.assertRaises(ValueError): + _ = umsgpack.Ext(-129, b"data") + with self.assertRaises(ValueError): + _ = umsgpack.Ext(128, b"data") + def test_pack_ext_handler(self): for (name, obj, data) in ext_handlers_test_vectors: obj_repr = repr(obj) @@ -681,6 +689,16 @@ def unpackb(cls, data): class DummyClass: pass + # Test out of range Ext type value + with self.assertRaises(ValueError): + @umsgpack.ext_serializable(-129) + class DummyClass2: + pass + with self.assertRaises(ValueError): + @umsgpack.ext_serializable(128) + class DummyClass3: + pass + # Register class with missing packb() and unpackb() @umsgpack.ext_serializable(0x21) class IncompleteClass: diff --git a/umsgpack.py b/umsgpack.py index 1596d35..e6487ab 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -80,6 +80,13 @@ def __init__(self, type, data): type: application-defined type integer 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: >>> foo = umsgpack.Ext(5, b"\x01\x02\x03") >>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) @@ -89,14 +96,17 @@ def __init__(self, type, data): 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): 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): raise TypeError("ext data is not type \'bytes\'") elif sys.version_info[0] == 2 and not isinstance(data, str): raise TypeError("ext data is not type \'str\'") + self.type = type self.data = data @@ -156,11 +166,19 @@ def ext_serializable(ext_type): 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 ext_type in _ext_type_to_class: + 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)) From e1a16d4c3c1efce7fdeeba2216e02cd29c7c3f66 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:26:49 -0500 Subject: [PATCH 12/41] change to .format() strings in Ext string representation --- umsgpack.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index e6487ab..fde2342 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -127,8 +127,8 @@ def __str__(self): """ String representation of this Ext object. """ - s = "Ext Object (Type: %d, Data: " % self.type - s += " ".join(["0x%02x" % ord(self.data[i:i + 1]) + s = "Ext Object (Type: {:d}, Data: ".format(self.type) + s += " ".join(["0x{:02}".format(ord(self.data[i:i + 1])) for i in xrange(min(len(self.data), 8))]) if len(self.data) > 8: s += " ..." From 570c082bf92376da051903326a0f5a5278b70de2 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:27:04 -0500 Subject: [PATCH 13/41] change to .format() strings in exceptions --- umsgpack.py | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index fde2342..4d63a1e 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -525,7 +525,7 @@ def _pack2(obj, fp, **options): _pack_ext(ext_handlers[t](obj), fp, options) else: 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) @@ -535,9 +535,9 @@ def _pack2(obj, fp, **options): except AttributeError: raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) # Pack for Python 3, with unicode 'str' type, 'bytes' type, and no 'long' type @@ -612,7 +612,7 @@ def _pack3(obj, fp, **options): _pack_ext(ext_handlers[t](obj), fp, options) else: 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) @@ -622,10 +622,10 @@ def _pack3(obj, fp, **options): except AttributeError: raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) else: raise UnsupportedTypeException( - "unsupported type: %s" % str(type(obj))) + "unsupported type: {:s}".format(str(type(obj)))) def _packb2(obj, **options): @@ -737,21 +737,21 @@ def _unpack_integer(code, fp, options): return struct.unpack(">I", _read_except(fp, 4))[0] elif code == b'\xcf': 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): if code == b'\xc1': raise ReservedCodeException( - "encountered reserved code: 0x%02x" % ord(code)) + "encountered reserved code: 0x{:02x}".format(ord(code))) 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): if code == b'\xc0': 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): @@ -759,7 +759,7 @@ def _unpack_boolean(code, fp, options): return False elif code == b'\xc3': 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): @@ -767,7 +767,7 @@ def _unpack_float(code, fp, options): return struct.unpack(">f", _read_except(fp, 4))[0] elif code == b'\xcb': 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): @@ -780,7 +780,7 @@ def _unpack_string(code, fp, options): elif code == b'\xdb': length = struct.unpack(">I", _read_except(fp, 4))[0] 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 global compatibility @@ -804,7 +804,7 @@ def _unpack_binary(code, fp, options): elif code == b'\xc6': length = struct.unpack(">I", _read_except(fp, 4))[0] 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) @@ -827,7 +827,7 @@ def _unpack_ext(code, fp, options): elif code == b'\xc9': length = struct.unpack(">I", _read_except(fp, 4))[0] 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_data = _read_except(fp, length) @@ -868,7 +868,7 @@ def _unpack_ext_timestamp(ext_data, options): microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000 else: 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, microseconds=microseconds) @@ -882,7 +882,7 @@ def _unpack_array(code, fp, options): elif code == b'\xdd': length = struct.unpack(">I", _read_except(fp, 4))[0] 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))) @@ -904,7 +904,7 @@ def _unpack_map(code, fp, options): elif code == b'\xdf': length = struct.unpack(">I", _read_except(fp, 4))[0] 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') else collections.OrderedDict() for _ in xrange(length): @@ -916,10 +916,10 @@ def _unpack_map(code, fp, options): k = _deep_list_to_tuple(k) elif not isinstance(k, Hashable): 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: 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 v = _unpack(fp, options) @@ -928,7 +928,7 @@ def _unpack_map(code, fp, options): d[k] = v except TypeError: raise UnhashableKeyException( - "encountered unhashable key: %s" % str(k)) + "encountered unhashable key: \"{:s}\"".format(str(k))) return d From 36aede91aa9ead95bc84d0838e649da3ac866f84 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:33:22 -0500 Subject: [PATCH 14/41] change to .format() strings in unit test reporting --- test_umsgpack.py | 58 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test_umsgpack.py b/test_umsgpack.py index f6bedd2..ba9d771 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -404,24 +404,24 @@ class TestUmsgpack(unittest.TestCase): def test_pack_single(self): for (name, obj, data) in single_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) self.assertEqual(umsgpack.packb(obj), data) def test_pack_composite(self): for (name, obj, data) in composite_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) self.assertEqual(umsgpack.packb(obj), data) def test_pack_exceptions(self): for (name, obj, exception) in pack_exception_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) with self.assertRaises(exception): umsgpack.packb(obj) @@ -429,8 +429,8 @@ def test_pack_exceptions(self): def test_unpack_single(self): for (name, obj, data) in single_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) unpacked = umsgpack.unpackb(data) @@ -452,14 +452,14 @@ def test_unpack_single(self): def test_unpack_composite(self): for (name, obj, data) in composite_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) self.assertEqual(umsgpack.unpackb(data), obj) def test_unpack_exceptions(self): for (name, data, exception) in unpack_exception_test_vectors: - print("\tTesting %s" % name) + print("\tTesting {:s}".format(name)) with self.assertRaises(exception): umsgpack.unpackb(data) @@ -469,8 +469,8 @@ def test_pack_compatibility(self): for (name, obj, data) in compatibility_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) self.assertEqual(umsgpack.packb(obj), data) @@ -481,8 +481,8 @@ def test_unpack_compatibility(self): for (name, obj, data) in compatibility_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) unpacked = umsgpack.unpackb(data) @@ -556,8 +556,8 @@ def test_ext_exceptions(self): def test_pack_ext_handler(self): for (name, obj, data) in ext_handlers_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) packed = umsgpack.packb(obj, ext_handlers=ext_handlers) self.assertEqual(packed, data) @@ -565,8 +565,8 @@ def test_pack_ext_handler(self): def test_unpack_ext_handler(self): for (name, obj, data) in ext_handlers_test_vectors: obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) unpacked = umsgpack.unpackb(data, ext_handlers=ext_handlers) self.assertEqual(unpacked, obj) @@ -574,8 +574,8 @@ def test_unpack_ext_handler(self): def test_pack_force_float_precision(self): for ((name, obj, data), precision) in zip(float_precision_test_vectors, ["single", "double"]): obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) packed = umsgpack.packb(obj, force_float_precision=precision) self.assertEqual(packed, data) @@ -583,8 +583,8 @@ def test_pack_force_float_precision(self): def test_pack_naive_timestamp(self): for (name, obj, data, _) in naive_timestamp_test_vectors: obj_repr = repr(obj) - print("\t Testing %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) packed = umsgpack.packb(obj) self.assertEqual(packed, data) @@ -592,8 +592,8 @@ def test_pack_naive_timestamp(self): def test_unpack_naive_timestamp(self): for (name, _, data, obj) in naive_timestamp_test_vectors: obj_repr = repr(obj) - print("\t Testing %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) unpacked = umsgpack.unpackb(data) self.assertEqual(unpacked, obj) @@ -602,8 +602,8 @@ def test_pack_ext_override(self): # Test overridden packing of datetime.datetime (name, obj, data) = override_ext_handlers_test_vectors[0] obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) packed = umsgpack.packb(obj, ext_handlers=override_ext_handlers) self.assertEqual(packed, data) @@ -612,8 +612,8 @@ def test_unpack_ext_override(self): # Test overridden unpacking of Ext type -1 (name, obj, data) = override_ext_handlers_test_vectors[1] obj_repr = repr(obj) - print("\tTesting %s: object %s" % - (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) + print("\tTesting {:s}: object {:s}".format( + name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) unpacked = umsgpack.unpackb(data, ext_handlers=override_ext_handlers) self.assertEqual(unpacked, obj) From a827029734f688899ab1515cc9d0762a319611c6 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:34:46 -0500 Subject: [PATCH 15/41] simplify filter expressions in unit tests --- test_umsgpack.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test_umsgpack.py b/test_umsgpack.py index ba9d771..1652d60 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -766,11 +766,10 @@ def test_streaming_reader(self): def test_namespacing(self): # Get a list of global variables from umsgpack module - exported_vars = list(filter(lambda x: not x.startswith("_"), - dir(umsgpack))) + exported_vars = list([x for x in dir(umsgpack) if not x.startswith("_")]) # Ignore imports - exported_vars = list(filter(lambda x: x != "struct" and x != "collections" and x != "datetime" and x != - "sys" and x != "io" and x != "xrange" and x != "Hashable", exported_vars)) + exported_vars = list([x for x in exported_vars if x != "struct" and x != "collections" and x != "datetime" and x != + "sys" and x != "io" and x != "xrange" and x != "Hashable"]) self.assertTrue(len(exported_vars) == len(exported_vars_test_vector)) for var in exported_vars_test_vector: From e0c4000224d3d8e232fb9e94b5a07ed2c17ca802 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 24 Oct 2020 22:42:26 -0500 Subject: [PATCH 16/41] update version and changelog to v2.7.1 --- CHANGELOG.md | 4 ++++ setup.py | 2 +- umsgpack.py | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 355e351..39d2bd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +* Version 2.7.1 - 10/24/2020 + * Add Ext type value validation to Ext class and `ext_serializable()` decorator. + * Change string formatting from `%` to `.format()` throughout codebase. + * Version 2.7.0 - 08/01/2020 * Add support for packing subclasses of `ext_serializable()` application classes. * Contributors diff --git a/setup.py b/setup.py index 7fdafa4..7f040eb 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='u-msgpack-python', - version='2.7.0', + version='2.7.1', description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.', author='vsergeev', author_email='v@sergeev.io', diff --git a/umsgpack.py b/umsgpack.py index 4d63a1e..c6fa1e7 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.7.0 - v at sergeev.io +# u-msgpack-python v2.7.1 - v at sergeev.io # https://github.com/vsergeev/u-msgpack-python # # u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.7.0 - v at sergeev.io +u-msgpack-python v2.7.1 - v at sergeev.io https://github.com/vsergeev/u-msgpack-python u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -54,10 +54,10 @@ else: from collections import Hashable -__version__ = "2.7.0" +__version__ = "2.7.1" "Module version string" -version = (2, 7, 0) +version = (2, 7, 1) "Module version tuple" From d2e5f36a18460ecad767623163dac78f8bbad433 Mon Sep 17 00:00:00 2001 From: Matt Smith Date: Fri, 24 Sep 2021 09:44:49 +0100 Subject: [PATCH 17/41] replace dash-separated options in setup.cfg Recent versions of setuptools report that options with names separated by a dash (e.g. 'home-page') are deprecated and support will be removed in later versions. Signed-off-by: Vanya A. Sergeev --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 768d2bc..689fa54 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -description-file = README.md +description_file = README.md [bdist_wheel] universal = True From 6d38e3f50428641689c0d8c87dc97346d058af0a Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 2 Apr 2022 15:34:52 -0500 Subject: [PATCH 18/41] update dist to focal in travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0b6e534..26f74e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ sudo: false -dist: xenial +dist: focal language: python install: pip install tox script: tox From 14a7e976287adc4b8ed8ed06cb2d96fd64229bfa Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sat, 2 Apr 2022 15:42:07 -0500 Subject: [PATCH 19/41] update python versions in travis config --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26f74e6..ba6728f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,11 @@ matrix: env: TOXENV=py37 - python: 3.8 env: TOXENV=py38 - - python: pypy2.7-6.0 + - python: 3.9 + env: TOXENV=py39 + - python: 3.10 + env: TOXENV=py310 + - python: pypy2.7-7.3.1 env: TOXENV=pypy - - python: pypy3.5-6.0 + - python: pypy3.6-7.3.1 env: TOXENV=pypy3 From 309b1a91f4c60c52ecb041db0ccfc525ea27f634 Mon Sep 17 00:00:00 2001 From: Stefan Ring Date: Wed, 19 Oct 2022 11:52:35 +0200 Subject: [PATCH 20/41] fix hex formatting of data bytes in Ext string representation Signed-off-by: Vanya A. Sergeev --- umsgpack.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/umsgpack.py b/umsgpack.py index c6fa1e7..43ea874 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -128,7 +128,7 @@ def __str__(self): String representation of this Ext object. """ s = "Ext Object (Type: {:d}, Data: ".format(self.type) - s += " ".join(["0x{:02}".format(ord(self.data[i:i + 1])) + s += " ".join(["0x{:02x}".format(ord(self.data[i:i + 1])) for i in xrange(min(len(self.data), 8))]) if len(self.data) > 8: s += " ..." From 8794887e2a71258ddacb7c435ca6f43304c2a21b Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Tue, 8 Nov 2022 00:01:11 -0600 Subject: [PATCH 21/41] update copyright years in license --- LICENSE | 2 +- umsgpack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index ba6591c..8bf1bb8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2013-2020 vsergeev / Ivan (Vanya) A. Sergeev + Copyright (c) 2013-2022 vsergeev / Ivan (Vanya) A. Sergeev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/umsgpack.py b/umsgpack.py index 43ea874..592a573 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -10,7 +10,7 @@ # # MIT License # -# Copyright (c) 2013-2020 vsergeev / Ivan (Vanya) A. Sergeev +# Copyright (c) 2013-2022 vsergeev / Ivan (Vanya) A. Sergeev # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From e5d4eb62e299cac1409e80bfaf5cc566d432c91e Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Tue, 8 Nov 2022 00:03:38 -0600 Subject: [PATCH 22/41] update travis-ci link in build status badge in readme and msgpack.org.md --- README.md | 2 +- msgpack.org.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bb04cbb..24ebb63 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# u-msgpack-python [![Build Status](https://travis-ci.org/vsergeev/u-msgpack-python.svg?branch=master)](https://travis-ci.org/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) +# u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with both Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. diff --git a/msgpack.org.md b/msgpack.org.md index 3117777..ae4c100 100644 --- a/msgpack.org.md +++ b/msgpack.org.md @@ -1,4 +1,4 @@ -# u-msgpack-python [![Build Status](https://travis-ci.org/vsergeev/u-msgpack-python.svg?branch=master)](https://travis-ci.org/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) +# u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with both Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). From e8eed4031a6b0ad77c5896d8c884a1d7272f4912 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Tue, 8 Nov 2022 00:07:45 -0600 Subject: [PATCH 23/41] update version and changelog to v2.7.2 --- CHANGELOG.md | 5 +++++ setup.py | 2 +- umsgpack.py | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39d2bd8..ad1d433 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +* Version 2.7.2 - 11/07/2022 + * Fix hex formatting of data bytes in Ext string representation. + * Contributors + * Stefan Ring, @Ringdingcoder - 309b1a9 + * Version 2.7.1 - 10/24/2020 * Add Ext type value validation to Ext class and `ext_serializable()` decorator. * Change string formatting from `%` to `.format()` throughout codebase. diff --git a/setup.py b/setup.py index 7f040eb..ced0b12 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='u-msgpack-python', - version='2.7.1', + version='2.7.2', description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.', author='vsergeev', author_email='v@sergeev.io', diff --git a/umsgpack.py b/umsgpack.py index 592a573..3d80c55 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.7.1 - v at sergeev.io +# u-msgpack-python v2.7.2 - v at sergeev.io # https://github.com/vsergeev/u-msgpack-python # # u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.7.1 - v at sergeev.io +u-msgpack-python v2.7.2 - v at sergeev.io https://github.com/vsergeev/u-msgpack-python u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -54,10 +54,10 @@ else: from collections import Hashable -__version__ = "2.7.1" +__version__ = "2.7.2" "Module version string" -version = (2, 7, 1) +version = (2, 7, 2) "Module version tuple" From d205f24755225334430d3fc72a5fc4697fe7d9d3 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sun, 9 Apr 2023 17:12:19 -0500 Subject: [PATCH 24/41] add py39, py310, py311 environments to tox config --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 73d8b30..5499181 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py35, py36, py37, py38, pypy, pypy3 +envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy, pypy3 skip_missing_interpreters=true [testenv] deps = pytest From 12ed425b4fb0622f18c72cae385162e7fc7bc251 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Sun, 9 Apr 2023 17:12:42 -0500 Subject: [PATCH 25/41] enable py311 tox environment in travis config --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index ba6728f..e295bf7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,8 @@ matrix: env: TOXENV=py39 - python: 3.10 env: TOXENV=py310 + - python: 3.11 + env: TOXENV=py311 - python: pypy2.7-7.3.1 env: TOXENV=pypy - python: pypy3.6-7.3.1 From 931960b89372d1a94aa9734b549cbe87361222de Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 3 May 2023 01:09:56 -0500 Subject: [PATCH 26/41] update copyright years in license --- LICENSE | 2 +- umsgpack.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 8bf1bb8..79056ac 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2013-2022 vsergeev / Ivan (Vanya) A. Sergeev + Copyright (c) 2013-2023 vsergeev / Ivan (Vanya) A. Sergeev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/umsgpack.py b/umsgpack.py index 3d80c55..dcf1918 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -10,7 +10,7 @@ # # MIT License # -# Copyright (c) 2013-2022 vsergeev / Ivan (Vanya) A. Sergeev +# Copyright (c) 2013-2023 vsergeev / Ivan (Vanya) A. Sergeev # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From 07eb1540c76efcfffa82605e0755c180e2b2aab7 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 3 May 2023 01:10:14 -0500 Subject: [PATCH 27/41] add flake8 configuration --- .flake8 | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..a6578a3 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +extend-ignore = E501 From 479cfcc3b54bb3d7f821c1250f937affa10e775e Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 3 May 2023 01:11:07 -0500 Subject: [PATCH 28/41] add flake8 noqa ignores for python2 types --- umsgpack.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index dcf1918..af596e9 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -498,15 +498,15 @@ def _pack2(obj, fp, **options): raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) - elif isinstance(obj, (int, long)): + elif isinstance(obj, (int, long)): # noqa: F821 _pack_integer(obj, fp, options) elif isinstance(obj, float): _pack_float(obj, fp, options) - elif compatibility and isinstance(obj, unicode): + elif compatibility and isinstance(obj, unicode): # noqa: F821 _pack_oldspec_raw(bytes(obj), fp, options) elif compatibility and isinstance(obj, bytes): _pack_oldspec_raw(obj, fp, options) - elif isinstance(obj, unicode): + elif isinstance(obj, unicode): # noqa: F821 _pack_string(obj, fp, options) elif isinstance(obj, str): _pack_binary(obj, fp, options) From aa532297e7fdb56b52a99ef56beacddacb8c115f Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 10 May 2023 02:18:52 -0500 Subject: [PATCH 29/41] fix UnsupportedTypeException name in pack docstrings --- umsgpack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index af596e9..e756c27 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -475,7 +475,7 @@ def _pack2(obj, fp, **options): None. Raises: - UnsupportedType(PackException): + UnsupportedTypeException(PackException): Object type not supported for packing. Example: @@ -562,7 +562,7 @@ def _pack3(obj, fp, **options): None. Raises: - UnsupportedType(PackException): + UnsupportedTypeException(PackException): Object type not supported for packing. Example: @@ -648,7 +648,7 @@ def _packb2(obj, **options): A 'str' containing serialized MessagePack bytes. Raises: - UnsupportedType(PackException): + UnsupportedTypeException(PackException): Object type not supported for packing. Example: @@ -681,7 +681,7 @@ def _packb3(obj, **options): A 'bytes' containing serialized MessagePack bytes. Raises: - UnsupportedType(PackException): + UnsupportedTypeException(PackException): Object type not supported for packing. Example: From a4c5ad6de64d2d14a8e7715435fbc5d711082426 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 10 May 2023 02:33:21 -0500 Subject: [PATCH 30/41] improve types and formatting in docstrings --- umsgpack.py | 164 ++++++++++++++++++++++++---------------------------- 1 file changed, 77 insertions(+), 87 deletions(-) diff --git a/umsgpack.py b/umsgpack.py index e756c27..03f39a1 100644 --- a/umsgpack.py +++ b/umsgpack.py @@ -77,24 +77,24 @@ def __init__(self, type, data): Construct a new Ext object. Args: - type: application-defined type integer - data: application-defined data byte array + type (int): application-defined type integer + data (bytes): 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). + Raises: + 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: - >>> foo = umsgpack.Ext(5, b"\x01\x02\x03") - >>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) - '\x82\xa7awesome\xc3\xadspecial stuff\xc7\x03\x05\x01\x02\x03' - >>> bar = umsgpack.unpackb(_) - >>> print(bar["special stuff"]) - Ext Object (Type: 5, Data: 01 02 03) - >>> + >>> foo = umsgpack.Ext(5, b"\\x01\\x02\\x03") + >>> umsgpack.packb({u"special stuff": foo, u"awesome": True}) + '\\x82\\xa7awesome\\xc3\\xadspecial stuff\\xc7\\x03\\x05\\x01\\x02\\x03' + >>> bar = umsgpack.unpackb(_) + >>> print(bar["special stuff"]) + Ext Object (Type: 5, Data: 01 02 03) """ # Check type is type int and in range if not isinstance(type, int): @@ -163,7 +163,7 @@ def ext_serializable(ext_type): instance of the application class. Args: - ext_type: application-defined Ext type code + ext_type (int): application-defined Ext type code Raises: TypeError: @@ -266,13 +266,11 @@ class DuplicateKeyException(UnpackException): old MessagePack specification. Example: ->>> umsgpack.compatibility = True ->>> ->>> umsgpack.packb([u"some string", b"some bytes"]) -b'\x92\xabsome string\xaasome bytes' ->>> umsgpack.unpackb(_) -[b'some string', b'some bytes'] ->>> + >>> umsgpack.compatibility = True + >>> umsgpack.packb([u"some string", b"some bytes"]) + b'\\x92\\xabsome string\\xaasome bytes' + >>> umsgpack.unpackb(_) + [b'some string', b'some bytes'] """ ############################################################################## @@ -462,26 +460,25 @@ def _pack2(obj, fp, **options): obj: a Python object fp: a .write()-supporting file-like object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object force_float_precision (str): "single" to force packing floats as IEEE-754 single-precision floats, "double" to force packing floats as - IEEE-754 double-precision floats. + IEEE-754 double-precision floats Returns: - None. + None Raises: UnsupportedTypeException(PackException): Object type not supported for packing. Example: - >>> f = open('test.bin', 'wb') - >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) - >>> + >>> f = open('test.bin', 'wb') + >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) """ global compatibility @@ -549,26 +546,25 @@ def _pack3(obj, fp, **options): obj: a Python object fp: a .write()-supporting file-like object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object force_float_precision (str): "single" to force packing floats as IEEE-754 single-precision floats, "double" to force packing floats as - IEEE-754 double-precision floats. + IEEE-754 double-precision floats Returns: - None. + None Raises: UnsupportedTypeException(PackException): Object type not supported for packing. Example: - >>> f = open('test.bin', 'wb') - >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) - >>> + >>> f = open('test.bin', 'wb') + >>> umsgpack.pack({u"compact": True, u"schema": 0}, f) """ global compatibility @@ -635,26 +631,25 @@ def _packb2(obj, **options): Args: obj: a Python object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object force_float_precision (str): "single" to force packing floats as IEEE-754 single-precision floats, "double" to force packing floats as - IEEE-754 double-precision floats. + IEEE-754 double-precision floats Returns: - A 'str' containing serialized MessagePack bytes. + str: Serialized MessagePack bytes Raises: UnsupportedTypeException(PackException): Object type not supported for packing. Example: - >>> umsgpack.packb({u"compact": True, u"schema": 0}) - '\x82\xa7compact\xc3\xa6schema\x00' - >>> + >>> umsgpack.packb({u"compact": True, u"schema": 0}) + '\\x82\\xa7compact\\xc3\\xa6schema\\x00' """ fp = io.BytesIO() _pack2(obj, fp, **options) @@ -668,26 +663,25 @@ def _packb3(obj, **options): Args: obj: a Python object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping a custom type to a callable that packs an instance of the type into an Ext object force_float_precision (str): "single" to force packing floats as IEEE-754 single-precision floats, "double" to force packing floats as - IEEE-754 double-precision floats. + IEEE-754 double-precision floats Returns: - A 'bytes' containing serialized MessagePack bytes. + bytes: Serialized MessagePack bytes Raises: UnsupportedTypeException(PackException): Object type not supported for packing. Example: - >>> umsgpack.packb({u"compact": True, u"schema": 0}) - b'\x82\xa7compact\xc3\xa6schema\x00' - >>> + >>> umsgpack.packb({u"compact": True, u"schema": 0}) + b'\\x82\\xa7compact\\xc3\\xa6schema\\x00' """ fp = io.BytesIO() _pack3(obj, fp, **options) @@ -946,20 +940,20 @@ def _unpack2(fp, **options): Args: fp: a .read()-supporting file-like object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext type to a callable that unpacks an instance of Ext into an object - use_ordered_dict (bool): unpack maps into OrderedDict, instead of - unordered dict (default False) + use_ordered_dict (bool): unpack maps into OrderedDict, instead of 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 - InvalidString, for access to the bytes - (default False) + :class:`InvalidString`, for access to the + bytes (default False) Returns: - A Python object. + Python object Raises: InsufficientDataException(UnpackException): @@ -977,10 +971,9 @@ def _unpack2(fp, **options): Duplicate key encountered during map unpacking. Example: - >>> f = open('test.bin', 'rb') - >>> umsgpack.unpackb(f) - {u'compact': True, u'schema': 0} - >>> + >>> f = open('test.bin', 'rb') + >>> umsgpack.unpackb(f) + {u'compact': True, u'schema': 0} """ return _unpack(fp, options) @@ -992,20 +985,20 @@ def _unpack3(fp, **options): Args: fp: a .read()-supporting file-like object - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext type to a callable that unpacks an instance of Ext into an object - use_ordered_dict (bool): unpack maps into OrderedDict, instead of - unordered dict (default False) + use_ordered_dict (bool): unpack maps into OrderedDict, instead of 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 - InvalidString, for access to the bytes - (default False) + :class:`InvalidString`, for access to the + bytes (default False) Returns: - A Python object. + Python object Raises: InsufficientDataException(UnpackException): @@ -1023,10 +1016,9 @@ def _unpack3(fp, **options): Duplicate key encountered during map unpacking. Example: - >>> f = open('test.bin', 'rb') - >>> umsgpack.unpackb(f) - {'compact': True, 'schema': 0} - >>> + >>> f = open('test.bin', 'rb') + >>> umsgpack.unpackb(f) + {'compact': True, 'schema': 0} """ return _unpack(fp, options) @@ -1037,22 +1029,22 @@ def _unpackb2(s, **options): Deserialize MessagePack bytes into a Python object. Args: - s: a 'str' or 'bytearray' containing serialized MessagePack bytes + s (str, bytearray): serialized MessagePack bytes - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext type to a callable that unpacks an instance of Ext into an object - use_ordered_dict (bool): unpack maps into OrderedDict, instead of - unordered dict (default False) + use_ordered_dict (bool): unpack maps into OrderedDict, instead of 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 - InvalidString, for access to the bytes - (default False) + :class:`InvalidString`, for access to the + bytes (default False) Returns: - A Python object. + Python object Raises: TypeError: @@ -1072,9 +1064,8 @@ def _unpackb2(s, **options): Duplicate key encountered during map unpacking. Example: - >>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') - {u'compact': True, u'schema': 0} - >>> + >>> umsgpack.unpackb(b'\\x82\\xa7compact\\xc3\\xa6schema\\x00') + {u'compact': True, u'schema': 0} """ if not isinstance(s, (str, bytearray)): raise TypeError("packed data must be type 'str' or 'bytearray'") @@ -1087,22 +1078,22 @@ def _unpackb3(s, **options): Deserialize MessagePack bytes into a Python object. Args: - s: a 'bytes' or 'bytearray' containing serialized MessagePack bytes + s (bytes, bytearray): serialized MessagePack bytes - Kwargs: + Keyword Args: ext_handlers (dict): dictionary of Ext handlers, mapping integer Ext type to a callable that unpacks an instance of Ext into an object - use_ordered_dict (bool): unpack maps into OrderedDict, instead of - unordered dict (default False) + use_ordered_dict (bool): unpack maps into OrderedDict, instead of 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 - InvalidString, for access to the bytes - (default False) + :class:`InvalidString`, for access to the + bytes (default False) Returns: - A Python object. + Python object Raises: TypeError: @@ -1122,9 +1113,8 @@ def _unpackb3(s, **options): Duplicate key encountered during map unpacking. Example: - >>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') - {'compact': True, 'schema': 0} - >>> + >>> umsgpack.unpackb(b'\\x82\\xa7compact\\xc3\\xa6schema\\x00') + {'compact': True, 'schema': 0} """ if not isinstance(s, (bytes, bytearray)): raise TypeError("packed data must be type 'bytes' or 'bytearray'") From 18717a19f81c294d331942afed8ebcaaa3837653 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 10 May 2023 02:33:49 -0500 Subject: [PATCH 31/41] migrate documentation to sphinx --- .gitignore | 1 + README.md | 369 +---------------------------------------- docs/Makefile | 20 +++ docs/api.md | 105 ++++++++++++ docs/behavior-notes.md | 33 ++++ docs/conf.py | 47 ++++++ docs/examples.md | 117 +++++++++++++ docs/extension.md | 111 +++++++++++++ docs/index.md | 28 ++++ docs/installation.md | 19 +++ docs/license.md | 3 + docs/make.bat | 35 ++++ docs/packing.md | 103 ++++++++++++ docs/streaming.md | 54 ++++++ docs/unpacking.md | 225 +++++++++++++++++++++++++ msgpack.org.md | 2 +- 16 files changed, 910 insertions(+), 362 deletions(-) create mode 100644 docs/Makefile create mode 100644 docs/api.md create mode 100644 docs/behavior-notes.md create mode 100644 docs/conf.py create mode 100644 docs/examples.md create mode 100644 docs/extension.md create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/license.md create mode 100644 docs/make.bat create mode 100644 docs/packing.md create mode 100644 docs/streaming.md create mode 100644 docs/unpacking.md diff --git a/.gitignore b/.gitignore index 2b225f4..200d697 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist/ .tox/ .cache/ .pytest_cache/ +docs/_build diff --git a/README.md b/README.md index 24ebb63..82f7201 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) -u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with both Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. +u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. u-msgpack-python is currently distributed on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py). @@ -130,373 +130,20 @@ b'\x82\xa7compact\xc3\xa6schema\x00' >>> ``` -## Ext Serializable +## Documentation -The `ext_serializable()` decorator registers application classes for automatic -packing and unpacking with the specified Ext type. The decorator accepts the -Ext type code as an argument. 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. +Documentation is hosted at [https://u-msgpack-python.readthedocs.io](https://u-msgpack-python.readthedocs.io). -Example for registering, packing, and unpacking a custom class with Ext type -code 0x10: +To build documentation locally with Sphinx, run: -``` python -@umsgpack.ext_serializable(0x10) -class Point(object): - def __init__(self, x, y, z): - self.x = x - self.y = y - self.z = z - - def __str__(self): - return "Point({}, {}, {})".format(self.x, self.y, self.z) - - def packb(self): - return struct.pack(">iii", self.x, self.y, self.z) - - @staticmethod - def unpackb(data): - return Point(*struct.unpack(">iii", data)) - -# Pack -obj = Point(1,2,3) -data = umsgpack.packb(obj) - -# Unpack -obj = umsgpack.unpackb(data) -print(obj) # -> Point(1, 2, 3) -``` - -## Ext Handlers - -The packing functions accept an optional `ext_handlers` dictionary that maps -custom types to callables that pack the type into an Ext object. The callable -should accept the custom type object as an argument and return a packed -`umsgpack.Ext` object. - -Example for packing `set`, `complex`, and `decimal.Decimal` types into Ext -objects with type codes 0x20, 0x30, and 0x40, respectively: - -``` python ->>> umsgpack.packb([1, True, {"foo", 2}, complex(3, 4), decimal.Decimal("0.31")], -... ext_handlers = { -... set: lambda obj: umsgpack.Ext(0x20, umsgpack.packb(list(obj))), -... complex: lambda obj: umsgpack.Ext(0x30, struct.pack("ff", obj.real, obj.imag)), -... decimal.Decimal: lambda obj: umsgpack.Ext(0x40, str(obj).encode()), -... }) -b'\x95\x01\xc3\xc7\x06 \x92\xa3foo\x02\xd70\x00\x00@@\x00\x00\x80@\xd6@0.31' ->>> -``` -Similarly, the unpacking functions accept an optional `ext_handlers` dictionary -that maps Ext type codes to callables that unpack the Ext into a custom object. -The callable should accept a `umsgpack.Ext` object as an argument and return an -unpacked custom type object. - -Example for unpacking Ext objects with type codes 0x20, 0x30, and 0x40, into -`set`, `complex`, and `decimal.Decimal` typed objects, respectively: - -``` python ->>> umsgpack.unpackb(b'\x95\x01\xc3\xc7\x06 \x92\xa3foo\x02\xd70\x00\x00@@\x00\x00\x80@\xd6@0.31', -... ext_handlers = { -... 0x20: lambda ext: set(umsgpack.unpackb(ext.data)), -... 0x30: lambda ext: complex(*struct.unpack("ff", ext.data)), -... 0x40: lambda ext: decimal.Decimal(ext.data.decode()), -... }) -[1, True, {'foo', 2}, (3+4j), Decimal('0.31')] ->>> -``` - -Example for packing and unpacking a custom class: - -``` python -class Point(object): - def __init__(self, x, y, z): - self.x = x - self.y = y - self.z = z - - def __str__(self): - return "Point({}, {}, {})".format(self.x, self.y, self.z) - - def pack(self): - return struct.pack(">iii", self.x, self.y, self.z) - - @staticmethod - def unpack(data): - return Point(*struct.unpack(">iii", data)) - -# Pack -obj = Point(1,2,3) -data = umsgpack.packb(obj, ext_handlers = {Point: lambda obj: umsgpack.Ext(0x10, obj.pack())}) - -# Unpack -obj = umsgpack.unpackb(data, ext_handlers = {0x10: lambda ext: Point.unpack(ext.data)}) -print(obj) # -> Point(1, 2, 3) -``` - -## Streaming Serialization and Deserialization - -The streaming `pack()`/`dump()` and `unpack()`/`load()` functions allow packing and unpacking objects directly to and from a stream, respectively. Streaming may be necessary when unpacking serialized bytes whose size is unknown in advance, or it may be more convenient and efficient when working directly with stream objects (e.g. files or stream sockets). - -`pack(obj, fp)` / `dump(obj, fp)` serialize Python object `obj` to a `.write()` supporting file-like object `fp`. - -``` python ->>> class Foo: -... def write(self, data): -... # write 'data' bytes -... pass -... ->>> f = Foo() ->>> umsgpack.pack({u"compact": True, u"schema": 0}, f) ->>> -``` - -`unpack(fp)` / `load(fp)` deserialize a Python object from a `.read()` supporting file-like object `fp`. - -``` python ->>> class Bar: -... def read(self, n): -... # read and return 'n' number of bytes -... return b"\x01"*n -... ->>> f = Bar() ->>> umsgpack.unpack(f) -1 ->>> -``` - -## Options - -### Ordered Dictionaries - -The unpacking functions provide a `use_ordered_dict` option to unpack MessagePack maps into the `collections.OrderedDict` type, rather than the unordered `dict` type, to preserve the order of deserialized MessagePack maps. - -``` python ->>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') -{'compact': True, 'schema': 0} ->>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00', use_ordered_dict=True) -OrderedDict([('compact', True), ('schema', 0)]) ->>> ``` - -## Tuples - -The unpacking functions provide a `use_tuple` option to unpack MessagePack arrays into tuples, rather than lists. - -``` python ->>> umsgpack.unpackb(b'\x93\xa1a\xc3\x92\x01\x92\x02\x03') -['a', True, [1, [2, 3]]] ->>> umsgpack.unpackb(b'\x93\xa1a\xc3\x92\x01\x92\x02\x03', use_tuple=True) -('a', True, (1, (2, 3))) ->>> +cd docs +make html ``` -### Invalid UTF-8 Strings - -The unpacking functions provide an `allow_invalid_utf8` option to unpack MessagePack strings with invalid UTF-8 into the `umsgpack.InvalidString` type, instead of throwing an exception. The `umsgpack.InvalidString` type is a subclass of `bytes`, and can be used like any other `bytes` object. - -``` python ->>> # Attempt to unpack invalid UTF-8 string -... umsgpack.unpackb(b'\xa4\x80\x01\x02\x03') -... -umsgpack.InvalidStringException: unpacked string is invalid utf-8 ->>> umsgpack.unpackb(b'\xa4\x80\x01\x02\x03', allow_invalid_utf8=True) -b'\x80\x01\x02\x03' ->>> -``` - -### Float Precision - -The packing functions provide a `force_float_precision` option to force packing of floats into the specified precision: `"single"` for IEEE-754 single-precision floats, or `"double"` for IEEE-754 double-precision floats. - -``` python ->>> # Force float packing to single-precision floats -... umsgpack.packb(2.5, force_float_precision="single") -b'\xca@ \x00\x00' ->>> # Force float packing to double-precision floats -... umsgpack.packb(2.5, force_float_precision="double") -b'\xcb@\x04\x00\x00\x00\x00\x00\x00' ->>> -``` - -### Old Specification Compatibility Mode - -The compatibility mode supports the "raw" bytes MessagePack type from the [old specification](https://github.com/msgpack/msgpack/blob/master/spec-old.md). When the module-wide `compatibility` option is enabled, both unicode strings and bytes will be serialized into the "raw" MessagePack type, and the "raw" MessagePack type will be deserialized into bytes. - -``` python ->>> umsgpack.compatibility = True ->>> ->>> umsgpack.packb([u"some string", b"some bytes"]) -b'\x92\xabsome string\xaasome bytes' ->>> umsgpack.unpackb(_) -[b'some string', b'some bytes'] ->>> -``` - -## Exceptions - -### Packing Exceptions - -If an error occurs during packing, umsgpack will raise an exception derived from `umsgpack.PackException`. All possible packing exceptions are described below. - -* `UnsupportedTypeException`: Object type not supported for packing. - - ``` python - >>> # Attempt to pack set type - ... umsgpack.packb(set([1,2,3])) - ... - umsgpack.UnsupportedTypeException: unsupported type: - >>> - - >>> # Attempt to pack > 64-bit unsigned int - ... umsgpack.packb(2**64) - ... - umsgpack.UnsupportedTypeException: huge unsigned int - >>> - ``` - -* `NotImplementedError`: Ext serializable class is missing implementation of `packb()`. - - ``` python - >>> @umsgpack.ext_serializable(0x50) - ... class Point(collections.namedtuple('Point', ['x', 'y'])): - ... pass - ... - >>> umsgpack.packb(Point(1, 2)) - ... - NotImplementedError: Ext serializable class is missing implementation of packb() - >>> - ``` - -### Unpacking Exceptions - -If a non-byte-string argument is passed to `umsgpack.unpackb()`, it will raise a `TypeError` exception. If an error occurs during unpacking, umsgpack will raise an exception derived from `umsgpack.UnpackException`. All possible unpacking exceptions are described below. - -* `TypeError`: Packed data is not type `str` (Python 2), or not type `bytes` (Python 3). - - ``` python - # Attempt to unpack non-str type data in Python 2 - >>> umsgpack.unpackb(u"no good") - ... - TypeError: expected packed data as type 'str' - >>> - - # Attempt to unpack non-bytes type data in Python 3 - >>> umsgpack.unpackb("no good") - ... - TypeError: expected packed data as type 'bytes' - >>> - ``` - -* `InsufficientDataException`: Insufficient data to unpack the serialized object. - - ``` python - # Attempt to unpack a cut-off serialized 32-bit unsigned int - >>> umsgpack.unpackb(b"\xce\xff\xff\xff") - ... - umsgpack.InsufficientDataException - >>> - - # Attempt to unpack an array of length 2 missing the second item - >>> umsgpack.unpackb(b"\x92\xc2") - ... - umsgpack.InsufficientDataException - >>> - - ``` -* `InvalidStringException`: Invalid UTF-8 string encountered during unpacking. - - String bytes are strictly decoded with UTF-8. This exception is thrown if - UTF-8 decoding of string bytes fails. Use the `allow_invalid_utf8` option - to unpack invalid MessagePack strings into byte strings. - - ``` python - # Attempt to unpack invalid UTF-8 string - >>> umsgpack.unpackb(b"\xa2\x80\x81") - ... - umsgpack.InvalidStringException: unpacked string is invalid utf-8 - >>> - ``` - -* `UnsupportedTimestampException`: Unsupported timestamp encountered during unpacking. - - The official timestamp extension type supports 32-bit, 64-bit and 96-bit - formats. This exception is thrown if a timestamp extension type with an - unsupported format is encountered. - - ``` python - # Attempt to unpack invalid timestamp - >>> umsgpack.unpackb(b"\xd5\xff\x01\x02") - ... - umsgpack.UnsupportedTimestampException: unsupported timestamp with data length 2 - >>> - ``` - -* `ReservedCodeException`: Reserved code encountered during unpacking. - - ``` python - # Attempt to unpack reserved code 0xc1 - >>> umsgpack.unpackb(b"\xc1") - ... - umsgpack.ReservedCodeException: reserved code encountered: 0xc1 - >>> - ``` - -* `UnhashableKeyException`: Unhashable key encountered during map unpacking. The packed map cannot be unpacked into a Python dictionary. - - Python dictionaries only support keys that are instances of `collections.Hashable`, so while the map `{ { u'abc': True } : 5 }` has a MessagePack serialization, it cannot be unpacked into a valid Python dictionary. - - ``` python - # Attempt to unpack { {} : False } - >>> umsgpack.unpackb(b"\x82\x80\xc2") - ... - umsgpack.UnhashableKeyException: encountered unhashable key type: {}, - >>> - ``` - -* `DuplicateKeyException`: Duplicate key encountered during map unpacking. - - Python dictionaries do not support duplicate keys, but MessagePack maps may be serialized with duplicate keys. - - ``` python - # Attempt to unpack { 1: True, 1: False } - >>> umsgpack.unpackb(b"\x82\x01\xc3\x01\xc2") - ... - umsgpack.DuplicateKeyException: encountered duplicate key: 1, - >>> - ``` - -* `NotImplementedError`: Ext serializable class is missing implementation of `unpackb()`. - - ``` python - >>> @umsgpack.ext_serializable(0x50) - ... class Point(collections.namedtuple('Point', ['x', 'y'])): - ... pass - ... - >>> umsgpack.unpackb(b'\xd7\x50\x00\x00\x00\x01\x00\x00\x00\x02') - ... - NotImplementedError: Ext serializable class is missing implementation of unpackb() - >>> - ``` - -## Behavior Notes +Sphinx will produce the HTML documentation in `docs/_build/html/`. -* Python 2 - * `unicode` type objects are packed into, and unpacked from, the msgpack `string` format - * `str` type objects are packed into, and unpacked from, the msgpack `binary` format -* Python 3 - * `str` type objects are packed into, and unpacked from, the msgpack `string` format - * `bytes` type objects are packed into, and unpacked from, the msgpack `binary` format -* The msgpack string format is strictly decoded with UTF-8 — an exception is thrown if the string bytes cannot be decoded into a valid UTF-8 string, unless the `allow_invalid_utf8` option is enabled -* The msgpack array format is unpacked into a Python list, unless it is the key of a map, in which case it is unpacked into a Python tuple -* Python tuples and lists are both packed into the msgpack array format -* Python float types are packed into the msgpack float32 or float64 format depending on the system's `sys.float_info` -* The Python `datetime.datetime` type is packed into, and unpacked from, the msgpack `timestamp` format - * Note that this Python type only supports microsecond resolution, while the msgpack `timestamp` format supports nanosecond resolution. Timestamps with finer than microsecond resolution will lose precision during unpacking. Users may override the packing and unpacking of the msgpack `timestamp` format with a custom type for alternate behavior. - * Both naive and aware timestamp are supported. Naive timestamps are packed as if they are in the UTC timezone. Timestamps are always unpacked as aware `datetime.datetime` objects in the UTC timezone. -* Ext type handlers specified in the optional `ext_handlers` dictionary will override `ext_serializable()` classes during packing and unpacking +Run `make help` to see other output targets (LaTeX, man, text, etc.). ## Testing diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..d53f6d4 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,105 @@ +# API + +## Packing + +```{eval-rst} +.. autofunction:: umsgpack.packb + +Also available under the ``umsgpack.dumps()`` alias. +``` + +```{eval-rst} +.. autofunction:: umsgpack.pack + +Also available under the ``umsgpack.dump()`` alias. +``` + +## Unpacking + +```{eval-rst} +.. autofunction:: umsgpack.unpackb + +Also available under the ``umsgpack.loads()`` alias. +``` + +```{eval-rst} +.. autofunction:: umsgpack.unpack + +Also available under the ``umsgpack.load()`` alias. +``` + +## Packing Exceptions + +```{eval-rst} +.. autoexception:: umsgpack.PackException +``` + +```{eval-rst} +.. autoexception:: umsgpack.UnsupportedTypeException +``` + +## Unpacking Exceptions + +```{eval-rst} +.. autoexception:: umsgpack.UnpackException +``` + +```{eval-rst} +.. autoexception:: umsgpack.InsufficientDataException +``` + +```{eval-rst} +.. autoexception:: umsgpack.InvalidStringException +``` + +```{eval-rst} +.. autoexception:: umsgpack.UnsupportedTimestampException +``` + +```{eval-rst} +.. autoexception:: umsgpack.ReservedCodeException +``` + +```{eval-rst} +.. autoexception:: umsgpack.UnhashableKeyException +``` + +```{eval-rst} +.. autoexception:: umsgpack.DuplicateKeyException +``` + +## Ext Class + +```{eval-rst} +.. autoclass:: umsgpack.Ext + :member-order: bysource + :special-members: __init__, __eq__, __ne__, __str__, __hash__ +``` + +## Ext Serializable Decorator + +```{eval-rst} +.. autodecorator:: umsgpack.ext_serializable +``` + +## Invalid String Class + +```{eval-rst} +.. autoclass:: umsgpack.InvalidString +``` + +## Attributes + +```{eval-rst} +.. autodata:: umsgpack.compatibility +``` + +## Constants + +```{eval-rst} +.. autodata:: umsgpack.version +``` + +```{eval-rst} +.. autodata:: umsgpack.__version__ +``` diff --git a/docs/behavior-notes.md b/docs/behavior-notes.md new file mode 100644 index 0000000..25474ee --- /dev/null +++ b/docs/behavior-notes.md @@ -0,0 +1,33 @@ +# Behavior Notes + +* Python 2 + * `unicode` type objects are packed into, and unpacked from, the MessagePack + `string` format + * `str` type objects are packed into, and unpacked from, the MessagePack + `binary` format +* Python 3 + * `str` type objects are packed into, and unpacked from, the MessagePack + `string` format + * `bytes` type objects are packed into, and unpacked from, the MessagePack + `binary` format +* The MessagePack string format is strictly decoded with UTF-8 — an exception + is thrown if the string bytes cannot be decoded into a valid UTF-8 string, + unless the `allow_invalid_utf8` option is enabled +* The MessagePack array format is unpacked into a Python list, unless it is the + key of a map, in which case it is unpacked into a Python tuple +* Python tuples and lists are both packed into the MessagePack array format +* Python float types are packed into the MessagePack float32 or float64 format + depending on the system's `sys.float_info` +* The Python `datetime.datetime` type is packed into, and unpacked from, the + MessagePack `timestamp` format + * Note that the Python `datetime.datetime` type only supports microsecond + resolution, while the MessagePack `timestamp` format supports nanosecond + resolution. Timestamps with finer than microsecond resolution will lose + precision during unpacking. Users may override the packing and unpacking + of the MessagePack `timestamp` format with a custom type for alternate + behavior. + * Both naive and aware timestamp are supported. Naive timestamps are packed + as if they are in the UTC timezone. Timestamps are always unpacked as + aware `datetime.datetime` objects in the UTC timezone. +* Ext type handlers specified in the optional `ext_handlers` dictionary will + override `ext_serializable()` classes during packing and unpacking diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..6856870 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,47 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. + +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + +import umsgpack + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'u-msgpack-python' +copyright = '2013-2023, Vanya A. Sergeev' +author = 'Vanya A. Sergeev' +release = '2.7.2' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'sphinx.ext.viewcode', + 'sphinx_rtd_theme', + 'myst_parser', +] + +templates_path = ['_templates'] +exclude_patterns = ['_build'] + +pygments_style = 'sphinx' + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] +html_show_sourcelink = False diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..6d63f8c --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,117 @@ +# Examples + +Basic Example: + +``` python +>>> import umsgpack +>>> umsgpack.packb({u"compact": True, u"schema": 0}) +b'\x82\xa7compact\xc3\xa6schema\x00' +>>> umsgpack.unpackb(_) +{u'compact': True, u'schema': 0} +>>> +``` + +A more complicated example: + +``` python +>>> umsgpack.packb([1, True, False, 0xffffffff, {u"foo": b"\x80\x01\x02", \ +... u"bar": [1,2,3, {u"a": [1,2,3,{}]}]}, -1, 2.12345]) +b'\x97\x01\xc3\xc2\xce\xff\xff\xff\xff\x82\xa3foo\xc4\x03\x80\x01\ +\x02\xa3bar\x94\x01\x02\x03\x81\xa1a\x94\x01\x02\x03\x80\xff\xcb\ +@\x00\xfc\xd3Z\x85\x87\x94' +>>> umsgpack.unpackb(_) +[1, True, False, 4294967295, {u'foo': b'\x80\x01\x02', \ + u'bar': [1, 2, 3, {u'a': [1, 2, 3, {}]}]}, -1, 2.12345] +>>> +``` + +Streaming serialization with file-like objects: + +``` python +>>> f = open('test.bin', 'wb') +>>> umsgpack.pack({u"compact": True, u"schema": 0}, f) +>>> umsgpack.pack([1,2,3], f) +>>> f.close() +>>> +>>> f = open('test.bin', 'rb') +>>> umsgpack.unpack(f) +{u'compact': True, u'schema': 0} +>>> umsgpack.unpack(f) +[1, 2, 3] +>>> f.close() +>>> +``` + +Serializing and deserializing a raw Ext type: + +``` python +>>> # Create an Ext object with type 5 and data b"\x01\x02\x03" +... foo = umsgpack.Ext(5, b"\x01\x02\x03") +>>> umsgpack.packb({u"stuff": foo, u"awesome": True}) +b'\x82\xa5stuff\xc7\x03\x05\x01\x02\x03\xa7awesome\xc3' +>>> +>>> bar = umsgpack.unpackb(_) +>>> print(bar['stuff']) +Ext Object (Type: 5, Data: 0x01 0x02 0x03) +>>> bar['stuff'].type +5 +>>> bar['stuff'].data +b'\x01\x02\x03' +>>> +``` + +Serializing and deserializing application-defined types with + +`ext_serializable()`: +``` python +>>> @umsgpack.ext_serializable(0x50) +... class Point(collections.namedtuple('Point', ['x', 'y'])): +... def packb(self): +... return struct.pack(">ii", self.x, self.y) +... @staticmethod +... def unpackb(data): +... return Point(*struct.unpack(">ii", data)) +... +>>> umsgpack.packb(Point(1, 2)) +b'\xd7P\x00\x00\x00\x01\x00\x00\x00\x02' +>>> umsgpack.unpackb(_) +Point(x=1, y=2) +>>> +``` + +Serializing and deserializing application-defined types with Ext handlers: + +``` python +>>> umsgpack.packb([complex(1,2), decimal.Decimal("0.31")], +... ext_handlers = { +... complex: lambda obj: umsgpack.Ext(0x30, struct.pack("ff", obj.real, obj.imag)), +... decimal.Decimal: lambda obj: umsgpack.Ext(0x40, str(obj).encode()), +... }) +b'\x92\xd70\x00\x00\x80?\x00\x00\x00@\xd6@0.31' +>>> umsgpack.unpackb(_, +... ext_handlers = { +... 0x30: lambda ext: complex(*struct.unpack("ff", ext.data)), +... 0x40: lambda ext: decimal.Decimal(ext.data.decode()), +... }) +[(1+2j), Decimal('0.31')] +>>> +``` + +Python standard library style names `dump`, `dumps`, `load`, `loads` are also +available: + +``` python +>>> umsgpack.dumps({u"compact": True, u"schema": 0}) +b'\x82\xa7compact\xc3\xa6schema\x00' +>>> umsgpack.loads(_) +{u'compact': True, u'schema': 0} +>>> +>>> f = open('test.bin', 'wb') +>>> umsgpack.dump({u"compact": True, u"schema": 0}, f) +>>> f.close() +>>> +>>> f = open('test.bin', 'rb') +>>> umsgpack.load(f) +{u'compact': True, u'schema': 0} +>>> +``` diff --git a/docs/extension.md b/docs/extension.md new file mode 100644 index 0000000..c967809 --- /dev/null +++ b/docs/extension.md @@ -0,0 +1,111 @@ +# Extension Types + +u-msgpack-python supports two mechanisms for packing and unpacking MessagePack +Ext types: the `ext_handlers` keyword option, and the `ext_serializable()` +decorator. + +## Ext Handlers + +The packing functions accept an optional `ext_handlers` dictionary that maps +custom types to callables that pack the type into an Ext object. The callable +should accept the custom type object as an argument and return a packed +`umsgpack.Ext` object. + +Example for packing `set`, `complex`, and `decimal.Decimal` types into Ext +objects with type codes 0x20, 0x30, and 0x40, respectively: + +``` python +>>> umsgpack.packb([1, True, {"foo", 2}, complex(3, 4), decimal.Decimal("0.31")], +... ext_handlers = { +... set: lambda obj: umsgpack.Ext(0x20, umsgpack.packb(list(obj))), +... complex: lambda obj: umsgpack.Ext(0x30, struct.pack("ff", obj.real, obj.imag)), +... decimal.Decimal: lambda obj: umsgpack.Ext(0x40, str(obj).encode()), +... }) +b'\x95\x01\xc3\xc7\x06 \x92\xa3foo\x02\xd70\x00\x00@@\x00\x00\x80@\xd6@0.31' +>>> +``` +Similarly, the unpacking functions accept an optional `ext_handlers` dictionary +that maps Ext type codes to callables that unpack the Ext into a custom object. +The callable should accept a `umsgpack.Ext` object as an argument and return an +unpacked custom type object. + +Example for unpacking Ext objects with type codes 0x20, 0x30, and 0x40, into +`set`, `complex`, and `decimal.Decimal` typed objects, respectively: + +``` python +>>> umsgpack.unpackb(b'\x95\x01\xc3\xc7\x06 \x92\xa3foo\x02\xd70\x00\x00@@\x00\x00\x80@\xd6@0.31', +... ext_handlers = { +... 0x20: lambda ext: set(umsgpack.unpackb(ext.data)), +... 0x30: lambda ext: complex(*struct.unpack("ff", ext.data)), +... 0x40: lambda ext: decimal.Decimal(ext.data.decode()), +... }) +[1, True, {'foo', 2}, (3+4j), Decimal('0.31')] +>>> +``` + +Example for packing and unpacking a custom class: + +``` python +class Point(object): + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def __str__(self): + return "Point({}, {}, {})".format(self.x, self.y, self.z) + + def pack(self): + return struct.pack(">iii", self.x, self.y, self.z) + + @staticmethod + def unpack(data): + return Point(*struct.unpack(">iii", data)) + +# Pack +obj = Point(1,2,3) +data = umsgpack.packb(obj, ext_handlers = {Point: lambda obj: umsgpack.Ext(0x10, obj.pack())}) + +# Unpack +obj = umsgpack.unpackb(data, ext_handlers = {0x10: lambda ext: Point.unpack(ext.data)}) +print(obj) # -> Point(1, 2, 3) +``` + +## Ext Serializable + +The `ext_serializable()` decorator registers application classes for automatic +packing and unpacking with the specified Ext type. The decorator accepts the +Ext type code as an argument. 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. + +Example for registering, packing, and unpacking a custom class with Ext type +code 0x10: + +``` python +@umsgpack.ext_serializable(0x10) +class Point(object): + def __init__(self, x, y, z): + self.x = x + self.y = y + self.z = z + + def __str__(self): + return "Point({}, {}, {})".format(self.x, self.y, self.z) + + def packb(self): + return struct.pack(">iii", self.x, self.y, self.z) + + @staticmethod + def unpackb(data): + return Point(*struct.unpack(">iii", data)) + +# Pack +obj = Point(1,2,3) +data = umsgpack.packb(obj) + +# Unpack +obj = umsgpack.unpackb(data) +print(obj) # -> Point(1, 2, 3) +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..0c39881 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,28 @@ +# Welcome to u-msgpack-python's documentation! + +[u-msgpack-python](https://github.com/vsergeev/u-msgpack-python) is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. + +u-msgpack-python is currently distributed on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py). + +## Contents + + +```{toctree} +:maxdepth: 1 + +Home +installation.md +examples.md +packing.md +unpacking.md +streaming.md +extension.md +api.md +behavior-notes.md +license.md +``` + +## Indices and tables + +* [](genindex) +* [](search) diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..3da50a3 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,19 @@ +# Installation + +Install with pip: + +``` text +$ pip install u-msgpack-python +``` + +Install with easy_install: + +``` text +$ easy_install u-msgpack-python +``` + +or simply drop [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) into your project! + +``` text +$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py +``` diff --git a/docs/license.md b/docs/license.md new file mode 100644 index 0000000..9594f3e --- /dev/null +++ b/docs/license.md @@ -0,0 +1,3 @@ +# License + +u-msgpack-python is MIT licensed. See the included [`LICENSE`](https://raw.github.com/vsergeev/u-msgpack-python/master/LICENSE) file for more details. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/packing.md b/docs/packing.md new file mode 100644 index 0000000..b5293db --- /dev/null +++ b/docs/packing.md @@ -0,0 +1,103 @@ +# Packing + +## Example + +``` python +>>> umsgpack.packb([1, True, False, 0xffffffff, {'foo': b'\x80\x01\x02', \ +... 'bar': [1,2,3, {'a': [1,2,3,{}]}]}, -1, 2.12345]) +b'\x97\x01\xc3\xc2\xce\xff\xff\xff\xff\x82\xa3foo\xc4\x03\x80\x01\ +\x02\xa3bar\x94\x01\x02\x03\x81\xa1a\x94\x01\x02\x03\x80\xff\xcb\ +@\x00\xfc\xd3Z\x85\x87\x94' +>>> +``` + +## API + +```{eval-rst} +.. autofunction:: umsgpack.packb + :noindex: + +Also available under the ``umsgpack.dumps()`` alias. +``` + +## Options + +### Ext Handlers + +See the [Extension Types](extension.md) section. + +### Float Precision + +The packing functions provide a `force_float_precision` option to force packing +of floats into the specified precision: `"single"` for IEEE-754 +single-precision floats, or `"double"` for IEEE-754 double-precision floats. + +``` python +>>> # Force float packing to single-precision floats +... umsgpack.packb(2.5, force_float_precision="single") +b'\xca@ \x00\x00' +>>> # Force float packing to double-precision floats +... umsgpack.packb(2.5, force_float_precision="double") +b'\xcb@\x04\x00\x00\x00\x00\x00\x00' +>>> +``` + +### Old Specification Compatibility Mode + +The compatibility mode supports the "raw" bytes MessagePack type from the [old +specification](https://github.com/msgpack/msgpack/blob/master/spec-old.md). +When the module-wide `compatibility` attribute is enabled, both unicode strings +and bytes will be serialized into the "raw" MessagePack type, and the "raw" +MessagePack type will be deserialized into bytes. + +``` python +>>> umsgpack.compatibility = True +>>> +>>> umsgpack.packb([u"some string", b"some bytes"]) +b'\x92\xabsome string\xaasome bytes' +>>> umsgpack.unpackb(_) +[b'some string', b'some bytes'] +>>> +``` + +## Exceptions + +If an error occurs during packing, u-msgpack-python will raise an exception +derived from `umsgpack.PackException`. Possible packing exceptions are +described below. + +### UnsupportedTypeException + +```{eval-rst} +.. autoexception:: umsgpack.UnsupportedTypeException + :noindex: +``` + +``` python +>>> # Attempt to pack set type +... umsgpack.packb(set([1,2,3])) +... +umsgpack.UnsupportedTypeException: unsupported type: +>>> + +>>> # Attempt to pack > 64-bit unsigned int +... umsgpack.packb(2**64) +... +umsgpack.UnsupportedTypeException: huge unsigned int +>>> +``` + +### NotImplementedError + +Ext serializable class is missing implementation of `packb()`. + +``` python +>>> @umsgpack.ext_serializable(0x50) +... class Point(collections.namedtuple('Point', ['x', 'y'])): +... pass +... +>>> umsgpack.packb(Point(1, 2)) +... +NotImplementedError: Ext serializable class is missing implementation of packb() +>>> +``` diff --git a/docs/streaming.md b/docs/streaming.md new file mode 100644 index 0000000..2e30fdc --- /dev/null +++ b/docs/streaming.md @@ -0,0 +1,54 @@ +# Streaming + +The streaming `pack()` and `unpack()` functions allow packing and unpacking +objects directly to and from a stream, respectively. Streaming may be necessary +when unpacking serialized bytes whose size is unknown in advance, or it may be +more convenient and efficient when working directly with stream objects (e.g. +files or stream sockets). + +## Packing + +`pack(obj, fp)` serializes Python object `obj` to a `.write()` supporting +file-like object `fp`. + +``` python +>>> class Foo: +... def write(self, data): +... # write 'data' bytes +... pass +... +>>> f = Foo() +>>> umsgpack.pack({u"compact": True, u"schema": 0}, f) +>>> +``` + +```{eval-rst} +.. autofunction:: umsgpack.pack + :noindex: + +Also available under the ``umsgpack.dump()`` alias. +``` + +## Unpacking + +`unpack(fp)` deserializes a Python object from a `.read()` supporting file-like +object `fp`. + +``` python +>>> class Bar: +... def read(self, n): +... # read and return 'n' number of bytes +... return b"\x01"*n +... +>>> f = Bar() +>>> umsgpack.unpack(f) +1 +>>> +``` + +```{eval-rst} +.. autofunction:: umsgpack.unpack + :noindex: + +Also available under the ``umsgpack.load()`` alias. +``` diff --git a/docs/unpacking.md b/docs/unpacking.md new file mode 100644 index 0000000..43ccf94 --- /dev/null +++ b/docs/unpacking.md @@ -0,0 +1,225 @@ +# Unpacking + +## Example + +``` python +>>> umsgpack.unpackb(b'\x97\x01\xc3\xc2\xce\xff\xff\xff\xff\x82\xa3\ +foo\xc4\x03\x80\x01\x02\xa3bar\x94\x01\x02\x03\x81\xa1a\x94\x01\x02\ +\x03\x80\xff\xcb@\x00\xfc\xd3Z\x85\x87\x94') +[1, True, False, 4294967295, {'foo': b'\x80\x01\x02', \ + 'bar': [1, 2, 3, {u'a': [1, 2, 3, {}]}]}, -1, 2.12345] +>>> +``` + +## API + +```{eval-rst} +.. autofunction:: umsgpack.unpackb + :noindex: + +Also available under the ``umsgpack.loads()`` alias. +``` + +## Options + +### Ext Handlers + +See the [Extension Types](extension.md) section. + +### Ordered Dictionaries + +The unpacking functions provide a `use_ordered_dict` option to unpack +MessagePack maps into the `collections.OrderedDict` type, rather than the +unordered `dict` type, to preserve the order of deserialized MessagePack maps. +Note that as of Python 3.6, dictionaries are insertion ordered by default. + +``` python +>>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00') +{'compact': True, 'schema': 0} +>>> umsgpack.unpackb(b'\x82\xa7compact\xc3\xa6schema\x00', use_ordered_dict=True) +OrderedDict([('compact', True), ('schema', 0)]) +>>> +``` + +### Tuples + +The unpacking functions provide a `use_tuple` option to unpack MessagePack +arrays into tuples, rather than lists. + +``` python +>>> umsgpack.unpackb(b'\x93\xa1a\xc3\x92\x01\x92\x02\x03') +['a', True, [1, [2, 3]]] +>>> umsgpack.unpackb(b'\x93\xa1a\xc3\x92\x01\x92\x02\x03', use_tuple=True) +('a', True, (1, (2, 3))) +>>> +``` + +### Invalid UTF-8 Strings + +The unpacking functions provide an `allow_invalid_utf8` option to unpack +MessagePack strings with invalid UTF-8 into the `umsgpack.InvalidString` type, +instead of throwing an exception. The `umsgpack.InvalidString` type is a +subclass of `bytes`, and can be used like any other `bytes` object. + +``` python +>>> # Attempt to unpack invalid UTF-8 string +... umsgpack.unpackb(b'\xa4\x80\x01\x02\x03') +... +umsgpack.InvalidStringException: unpacked string is invalid utf-8 +>>> umsgpack.unpackb(b'\xa4\x80\x01\x02\x03', allow_invalid_utf8=True) +b'\x80\x01\x02\x03' +>>> +``` + +## Exceptions + +If a non-byte-string argument is passed to `umsgpack.unpackb()`, it will raise +a `TypeError` exception. If an error occurs during unpacking, u-msgpack-python +will raise an exception derived from `umsgpack.UnpackException`. Possible +unpacking exceptions are described below. + + +### TypeError + +Packed data is not type `str` (Python 2), or not type `bytes` (Python 3). + +``` python +# Attempt to unpack non-str type data in Python 2 +>>> umsgpack.unpackb(u"no good") +... +TypeError: expected packed data as type 'str' +>>> + +# Attempt to unpack non-bytes type data in Python 3 +>>> umsgpack.unpackb("no good") +... +TypeError: expected packed data as type 'bytes' +>>> +``` + +### InsufficientDataException + +```{eval-rst} +.. autoexception:: umsgpack.InsufficientDataException + :noindex: +``` + +``` python +# Attempt to unpack a cut-off serialized 32-bit unsigned int +>>> umsgpack.unpackb(b"\xce\xff\xff\xff") +... +umsgpack.InsufficientDataException +>>> + +# Attempt to unpack an array of length 2 missing the second item +>>> umsgpack.unpackb(b"\x92\xc2") +... +umsgpack.InsufficientDataException +>>> +``` + +### InvalidStringException + +```{eval-rst} +.. autoexception:: umsgpack.InvalidStringException + :noindex: +``` + +String bytes are strictly decoded with UTF-8. This exception is thrown if UTF-8 +decoding of string bytes fails. Use the `allow_invalid_utf8` option to unpack +invalid MessagePack strings into byte strings. + +``` python +# Attempt to unpack invalid UTF-8 string +>>> umsgpack.unpackb(b"\xa2\x80\x81") +... +umsgpack.InvalidStringException: unpacked string is invalid utf-8 +>>> +``` + +### UnsupportedTimestampException + +```{eval-rst} +.. autoexception:: umsgpack.UnsupportedTimestampException + :noindex: +``` + +The official timestamp extension type supports 32-bit, 64-bit and 96-bit +formats. This exception is thrown if a timestamp extension type with an +unsupported format is encountered. + +``` python +# Attempt to unpack invalid timestamp +>>> umsgpack.unpackb(b"\xd5\xff\x01\x02") +... +umsgpack.UnsupportedTimestampException: unsupported timestamp with data length 2 +>>> +``` + +### ReservedCodeException + +```{eval-rst} +.. autoexception:: umsgpack.ReservedCodeException + :noindex: +``` + +``` python +# Attempt to unpack reserved code 0xc1 +>>> umsgpack.unpackb(b"\xc1") +... +umsgpack.ReservedCodeException: reserved code encountered: 0xc1 +>>> +``` + +### UnhashableKeyException + +```{eval-rst} +.. autoexception:: umsgpack.UnhashableKeyException + :noindex: +``` + +Python dictionaries only support keys that are instances of +`collections.Hashable`, so while the map `{ { u'abc': True } : 5 }` has a +MessagePack serialization, it cannot be unpacked into a valid Python +dictionary. + +``` python +# Attempt to unpack { {} : False } +>>> umsgpack.unpackb(b"\x82\x80\xc2") +... +umsgpack.UnhashableKeyException: encountered unhashable key type: {}, +>>> +``` + +### DuplicateKeyException + +```{eval-rst} +.. autoexception:: umsgpack.DuplicateKeyException + :noindex: +``` + +Python dictionaries do not support duplicate keys, but MessagePack maps may be +serialized with duplicate keys. + +``` python +# Attempt to unpack { 1: True, 1: False } +>>> umsgpack.unpackb(b"\x82\x01\xc3\x01\xc2") +... +umsgpack.DuplicateKeyException: encountered duplicate key: 1, +>>> +``` + +### NotImplementedError + +Ext serializable class is missing implementation of `unpackb()`. + +``` python +>>> @umsgpack.ext_serializable(0x50) +... class Point(collections.namedtuple('Point', ['x', 'y'])): +... pass +... +>>> umsgpack.unpackb(b'\xd7\x50\x00\x00\x00\x01\x00\x00\x00\x02') +... +NotImplementedError: Ext serializable class is missing implementation of unpackb() +>>> +``` diff --git a/msgpack.org.md b/msgpack.org.md index ae4c100..a832e26 100644 --- a/msgpack.org.md +++ b/msgpack.org.md @@ -1,6 +1,6 @@ # u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) -u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with both Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). +u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). u-msgpack-python is currently distributed on PyPI: https://pypi.python.org/pypi/u-msgpack-python and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) From 47e19a2f1bd5573fff76bf6ea816e0dd709cbda5 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 12 May 2023 01:16:51 -0500 Subject: [PATCH 32/41] add readthedocs configuration --- .readthedocs.yaml | 13 +++++++++++++ docs/requirements.txt | 2 ++ 2 files changed, 15 insertions(+) create mode 100644 .readthedocs.yaml create mode 100644 docs/requirements.txt diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..f334b70 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,13 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..7c98d98 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +sphinx-rtd-theme>=1.2.0 +myst-parser From 9d67b976a085a03cfdd8306222fb569a96683956 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Wed, 10 May 2023 02:35:08 -0500 Subject: [PATCH 33/41] add docs badge to readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 82f7201..d6cdda7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) +# u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![Docs Status](https://readthedocs.org/projects/u-msgpack-python/badge/)](https://u-msgpack-python.readthedocs.io/en/latest/) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. From 98ad62fda6819fbb4c23251395576b471f32d4d2 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Fri, 12 May 2023 00:49:49 -0500 Subject: [PATCH 34/41] add type stubs --- umsgpack.pyi | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 umsgpack.pyi diff --git a/umsgpack.pyi b/umsgpack.pyi new file mode 100644 index 0000000..61a8413 --- /dev/null +++ b/umsgpack.pyi @@ -0,0 +1,41 @@ +from typing import Any + +__version__: str + +version: tuple[int, int, int] + +def pack(obj, fp, **options) -> None: ... +def packb(obj, **options) -> bytes: ... +def dump(obj, fp, **options) -> None: ... +def dumps(obj, **options) -> bytes: ... + +def unpackb(s: bytes | bytearray, **options) -> Any: ... +def unpack(fp, **options) -> Any: ... +def loads(s: bytes | bytearray, **options) -> Any: ... +def load(fp, **options) -> Any: ... + +class Ext: + type: int + data: bytes + def __init__(self, type: int, data: bytes) -> None: ... + def __eq__(self, other) -> bool: ... + def __ne__(self, other) -> bool: ... + def __hash__(self) -> int: ... + +class InvalidString(bytes): ... + +def ext_serializable(ext_type: int): ... + +class PackException(Exception): ... +class UnpackException(Exception): ... +class UnsupportedTypeException(PackException): ... +class InsufficientDataException(UnpackException): ... +class InvalidStringException(UnpackException): ... +class UnsupportedTimestampException(UnpackException): ... +class ReservedCodeException(UnpackException): ... +class UnhashableKeyException(UnpackException): ... +class DuplicateKeyException(UnpackException): ... +KeyNotPrimitiveException = UnhashableKeyException +KeyDuplicateException = DuplicateKeyException + +compatibility: bool From 2ea8b78d63e39603acaf1aad8683e9690efb6711 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Tue, 16 May 2023 02:03:16 -0500 Subject: [PATCH 35/41] migrate from module to package --- README.md | 6 +++--- docs/index.md | 2 +- docs/installation.md | 4 ++-- msgpack.org.md | 6 +++--- setup.py | 3 ++- umsgpack.py => umsgpack/__init__.py | 0 umsgpack.pyi => umsgpack/__init__.pyi | 0 7 files changed, 11 insertions(+), 10 deletions(-) rename umsgpack.py => umsgpack/__init__.py (100%) rename umsgpack.pyi => umsgpack/__init__.pyi (100%) diff --git a/README.md b/README.md index d6cdda7..4d40466 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. -u-msgpack-python is currently distributed on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py). +u-msgpack-python is currently distributed as a package on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file module. ## Installation @@ -16,9 +16,9 @@ With easy_install: $ easy_install u-msgpack-python ``` -or simply drop [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) into your project! +or simply drop `umsgpack.py` into your project! ``` text -$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py +$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack/__init__.py -O umsgpack.py ``` ## Examples diff --git a/docs/index.md b/docs/index.md index 0c39881..f18f8ff 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,7 +2,7 @@ [u-msgpack-python](https://github.com/vsergeev/u-msgpack-python) is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. -u-msgpack-python is currently distributed on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py). +u-msgpack-python is currently distributed as a package on [PyPI](https://pypi.python.org/pypi/u-msgpack-python) and as a single file module. ## Contents diff --git a/docs/installation.md b/docs/installation.md index 3da50a3..8651fd6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -12,8 +12,8 @@ Install with easy_install: $ easy_install u-msgpack-python ``` -or simply drop [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) into your project! +or simply drop `umsgpack.py` into your project! ``` text -$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py +$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack/__init__.py -O umsgpack.py ``` diff --git a/msgpack.org.md b/msgpack.org.md index a832e26..d508bf0 100644 --- a/msgpack.org.md +++ b/msgpack.org.md @@ -2,7 +2,7 @@ u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). -u-msgpack-python is currently distributed on PyPI: https://pypi.python.org/pypi/u-msgpack-python and as a single file: [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) +u-msgpack-python is currently distributed as a package on PyPI: https://pypi.python.org/pypi/u-msgpack-python and as a single file module. ## Installation @@ -16,9 +16,9 @@ With easy_install: $ easy_install u-msgpack-python ``` -or simply drop [umsgpack.py](https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py) into your project! +or simply drop `umsgpack.py` into your project! ``` text -$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack.py +$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack/__init__.py -O umsgpack.py ``` ## Examples diff --git a/setup.py b/setup.py index ced0b12..5d5ecc3 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,8 @@ author='vsergeev', author_email='v@sergeev.io', url='https://github.com/vsergeev/u-msgpack-python', - py_modules=['umsgpack'], + packages=['umsgpack'], + package_data={'umsgpack': ['*.pyi', 'py.typed']}, long_description="""u-msgpack-python is a lightweight `MessagePack `_ serializer and deserializer module written in pure Python, compatible with both Python 2 and Python 3, as well as CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest `MessagePack specification `_. In particular, it supports the new binary, UTF-8 string, and application-defined ext types. See https://github.com/vsergeev/u-msgpack-python for more information.""", classifiers=[ "Development Status :: 5 - Production/Stable", diff --git a/umsgpack.py b/umsgpack/__init__.py similarity index 100% rename from umsgpack.py rename to umsgpack/__init__.py diff --git a/umsgpack.pyi b/umsgpack/__init__.pyi similarity index 100% rename from umsgpack.pyi rename to umsgpack/__init__.pyi From e5c9b9b16ae9b5a482aa11223c43574927fc163d Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Thu, 18 May 2023 04:18:42 -0500 Subject: [PATCH 36/41] use pypy2 environment in tox config instead of pypy --- .travis.yml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index e295bf7..5c0a602 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,6 @@ matrix: - python: 3.11 env: TOXENV=py311 - python: pypy2.7-7.3.1 - env: TOXENV=pypy + env: TOXENV=pypy2 - python: pypy3.6-7.3.1 env: TOXENV=pypy3 diff --git a/tox.ini b/tox.ini index 5499181..7f0edc0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy, pypy3 +envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy2, pypy3 skip_missing_interpreters=true [testenv] deps = pytest From 5ba07ec23457839e27020ff3441fdfd2c6957d87 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Thu, 18 May 2023 03:54:45 -0500 Subject: [PATCH 37/41] add mypy environment to tox config --- tox.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7f0edc0..6b6086b 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,11 @@ [tox] -envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy2, pypy3 +envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy2, pypy3, mypy skip_missing_interpreters=true + [testenv] deps = pytest commands = pytest + +[testenv:mypy] +deps = mypy +commands = mypy umsgpack From ff5ca534043a6b2d8f5d29b9f3ab9a66a4fd85c4 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Thu, 18 May 2023 03:55:23 -0500 Subject: [PATCH 38/41] add github actions workflow --- .github/workflows/tests.yml | 27 +++++++++++++++++++++++++++ tox.ini | 13 +++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..8e0c942 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,27 @@ +name: Tests + +on: + - push + - pull_request + +jobs: + build: + + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + matrix: + python-version: ["2.7", "3.5", "3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "pypy2.7", "pypy3.9"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install tox tox-gh-actions + - name: Test with tox + run: tox diff --git a/tox.ini b/tox.ini index 6b6086b..7047fd8 100644 --- a/tox.ini +++ b/tox.ini @@ -2,6 +2,19 @@ envlist = py27, py35, py36, py37, py38, py39, py310, py311, pypy2, pypy3, mypy skip_missing_interpreters=true +[gh-actions] +python = + 2.7: py27 + 3.5: py35 + 3.6: py36, mypy + 3.7: py37, mypy + 3.8: py38, mypy + 3.9: py39, mypy + 3.10: py310, mypy + 3.11: py311, mypy + pypy-2: pypy2 + pypy-3: pypy3 + [testenv] deps = pytest commands = pytest From 9af0cf7503e97517090d6d2dc38a73600bc6717f Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Thu, 18 May 2023 04:22:58 -0500 Subject: [PATCH 39/41] remove travis config --- .travis.yml | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5c0a602..0000000 --- a/.travis.yml +++ /dev/null @@ -1,28 +0,0 @@ -sudo: false -dist: focal -language: python -install: pip install tox -script: tox - -matrix: - include: - - python: 2.7 - env: TOXENV=py27 - - python: 3.5 - env: TOXENV=py35 - - python: 3.6 - env: TOXENV=py36 - - python: 3.7 - env: TOXENV=py37 - - python: 3.8 - env: TOXENV=py38 - - python: 3.9 - env: TOXENV=py39 - - python: 3.10 - env: TOXENV=py310 - - python: 3.11 - env: TOXENV=py311 - - python: pypy2.7-7.3.1 - env: TOXENV=pypy2 - - python: pypy3.6-7.3.1 - env: TOXENV=pypy3 From 6a2b4a3e4c6f173ab6ed29a679d726f2c2355419 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Thu, 18 May 2023 03:58:04 -0500 Subject: [PATCH 40/41] update tests status badge in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4d40466..485a60b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# u-msgpack-python [![Build Status](https://app.travis-ci.com/vsergeev/u-msgpack-python.svg?branch=master)](https://app.travis-ci.com/github/vsergeev/u-msgpack-python) [![Docs Status](https://readthedocs.org/projects/u-msgpack-python/badge/)](https://u-msgpack-python.readthedocs.io/en/latest/) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) +# u-msgpack-python [![Tests Status](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml/badge.svg)](https://github.com/vsergeev/u-msgpack-python/actions/workflows/tests.yml) [![Docs Status](https://readthedocs.org/projects/u-msgpack-python/badge/)](https://u-msgpack-python.readthedocs.io/en/latest/) [![GitHub release](https://img.shields.io/github/release/vsergeev/u-msgpack-python.svg?maxAge=7200)](https://github.com/vsergeev/u-msgpack-python) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vsergeev/u-msgpack-python/blob/master/LICENSE) u-msgpack-python is a lightweight [MessagePack](http://msgpack.org/) serializer and deserializer module written in pure Python, compatible with Python 2 and 3, as well CPython and PyPy implementations of Python. u-msgpack-python is fully compliant with the latest [MessagePack specification](https://github.com/msgpack/msgpack/blob/master/spec.md). In particular, it supports the new binary, UTF-8 string, application-defined ext, and timestamp types. From 6a9aa1cbbbc828120ed041fd8f467f1efb8dee31 Mon Sep 17 00:00:00 2001 From: "Vanya A. Sergeev" Date: Tue, 16 May 2023 02:09:07 -0500 Subject: [PATCH 41/41] update version and changelog to v2.8.0 --- CHANGELOG.md | 6 ++++++ docs/conf.py | 2 +- setup.py | 2 +- umsgpack/__init__.py | 8 ++++---- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad1d433..3cb131b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +* Version 2.8.0 - 05/18/2023 + * Migrate module to package. + * Migrate documentation to Sphinx. + * Improve types and formatting in docstrings. + * Add type stubs. + * Version 2.7.2 - 11/07/2022 * Fix hex formatting of data bytes in Ext string representation. * Contributors diff --git a/docs/conf.py b/docs/conf.py index 6856870..6958620 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -21,7 +21,7 @@ project = 'u-msgpack-python' copyright = '2013-2023, Vanya A. Sergeev' author = 'Vanya A. Sergeev' -release = '2.7.2' +release = '2.8.0' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/setup.py b/setup.py index 5d5ecc3..1cf6809 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='u-msgpack-python', - version='2.7.2', + version='2.8.0', description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.', author='vsergeev', author_email='v@sergeev.io', diff --git a/umsgpack/__init__.py b/umsgpack/__init__.py index 03f39a1..e7e194f 100644 --- a/umsgpack/__init__.py +++ b/umsgpack/__init__.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.7.2 - v at sergeev.io +# u-msgpack-python v2.8.0 - v at sergeev.io # https://github.com/vsergeev/u-msgpack-python # # u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.7.2 - v at sergeev.io +u-msgpack-python v2.8.0 - v at sergeev.io https://github.com/vsergeev/u-msgpack-python u-msgpack-python is a lightweight MessagePack serializer and deserializer @@ -54,10 +54,10 @@ else: from collections import Hashable -__version__ = "2.7.2" +__version__ = "2.8.0" "Module version string" -version = (2, 7, 2) +version = (2, 8, 0) "Module version tuple"