diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 224e7f0..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.pc/ diff --git a/LICENSE b/LICENSE index 7e330d5..79056ac 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ - Copyright (c) 2013-2016 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/PKG-INFO b/PKG-INFO index 1dfe610..b09dbe5 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,14 +1,12 @@ -Metadata-Version: 1.1 +Metadata-Version: 2.1 Name: u-msgpack-python -Version: 2.3.0 +Version: 2.8.0 Summary: A portable, lightweight MessagePack serializer and deserializer written in pure Python. Home-page: https://github.com/vsergeev/u-msgpack-python Author: vsergeev Author-email: v@sergeev.io License: MIT -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. Keywords: msgpack serialization deserialization -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent @@ -17,3 +15,6 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +License-File: LICENSE + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..485a60b --- /dev/null +++ b/README.md @@ -0,0 +1,168 @@ +# 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. + +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 + +With pip: +``` text +$ pip install u-msgpack-python +``` + +With easy_install: +``` text +$ easy_install u-msgpack-python +``` + +or simply drop `umsgpack.py` into your project! +``` text +$ wget https://raw.github.com/vsergeev/u-msgpack-python/master/umsgpack/__init__.py -O umsgpack.py +``` + +## 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} +>>> +``` + +## Documentation + +Documentation is hosted at [https://u-msgpack-python.readthedocs.io](https://u-msgpack-python.readthedocs.io). + +To build documentation locally with Sphinx, run: + +``` +cd docs +make html +``` + +Sphinx will produce the HTML documentation in `docs/_build/html/`. + +Run `make help` to see other output targets (LaTeX, man, text, etc.). + +## Testing + +The included unit tests may be run with `test_umsgpack.py`, under your favorite interpreter. + +``` text +$ python2 test_umsgpack.py +$ python3 test_umsgpack.py +$ pypy test_umsgpack.py +$ pypy3 test_umsgpack.py +``` + +Alternatively, you can use `tox` or `detox` to test multiple Python versions at once. + +``` text +$ pip install tox +$ tox +``` + +## License + +u-msgpack-python is MIT licensed. See the included `LICENSE` file for more details. diff --git a/debian/changelog b/debian/changelog index c1d4080..9578f77 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,37 @@ +u-msgpack-python (2.8.0-2) unstable; urgency=medium + + * Team upload. + * Add dependency on python3-setuptools (Closes: #1080823) + + -- Alexandre Detiste Sun, 20 Oct 2024 23:48:26 +0200 + +u-msgpack-python (2.8.0-1) unstable; urgency=medium + + * Team Upload + * New upstream version 2.8.0 + * Use new dh-sequence-python3 + * Set Rules-Requires-Root: no + + -- Alexandre Detiste Tue, 20 Aug 2024 13:14:34 +0200 + +u-msgpack-python (2.3.0-3) unstable; urgency=medium + + [ Debian Janitor ] + * Bump debhelper from deprecated 9 to 12. + * Set upstream metadata fields: Bug-Database, Bug-Submit, Repository, + Repository-Browse. + + [ Ondřej Nový ] + * d/control: Update Maintainer field with new Debian Python Team + contact address. + * d/control: Update Vcs-* fields with new Debian Python Team Salsa + layout. + + [ Debian Janitor ] + * Bump debhelper from old 12 to 13. + + -- Sandro Tosi Sat, 04 Jun 2022 21:52:58 -0400 + u-msgpack-python (2.3.0-2) unstable; urgency=medium [ Ondřej Nový ] diff --git a/debian/control b/debian/control index b98e944..c54529f 100644 --- a/debian/control +++ b/debian/control @@ -1,17 +1,19 @@ Source: u-msgpack-python Section: python Priority: optional -Maintainer: Debian Python Modules Team +Maintainer: Debian Python Team Uploaders: Orestis Ioannou , Build-Depends: - debhelper-compat (= 9), - dh-python, + debhelper-compat (= 13), + dh-sequence-python3, python3-all, + python3-setuptools, +Rules-Requires-Root: no Standards-Version: 3.9.8 Homepage: https://github.com/vsergeev/u-msgpack-python -Vcs-Git: https://salsa.debian.org/python-team/modules/u-msgpack-python.git -Vcs-Browser: https://salsa.debian.org/python-team/modules/u-msgpack-python +Vcs-Git: https://salsa.debian.org/python-team/packages/u-msgpack-python.git +Vcs-Browser: https://salsa.debian.org/python-team/packages/u-msgpack-python Package: python3-u-msgpack Architecture: all @@ -24,5 +26,3 @@ Description: Python3 MessagePack serializer and deserializer 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. - . - This is the Python 3 package. diff --git a/debian/rules b/debian/rules index b529ce9..9248755 100755 --- a/debian/rules +++ b/debian/rules @@ -1,5 +1,5 @@ #!/usr/bin/make -f -export PYBUILD_NAME=u-msgpack +export PYBUILD_NAME=umsgpack %: - dh $@ --with python3 --buildsystem=pybuild + dh $@ --buildsystem=pybuild diff --git a/debian/upstream/metadata b/debian/upstream/metadata new file mode 100644 index 0000000..ee8675d --- /dev/null +++ b/debian/upstream/metadata @@ -0,0 +1,4 @@ +Bug-Database: https://github.com/vsergeev/u-msgpack-python/issues +Bug-Submit: https://github.com/vsergeev/u-msgpack-python/issues/new +Repository: https://github.com/vsergeev/u-msgpack-python.git +Repository-Browse: https://github.com/vsergeev/u-msgpack-python diff --git a/setup.cfg b/setup.cfg index 73051f9..4c519dd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [metadata] -description-file = README.md +description_file = README.md [bdist_wheel] universal = True @@ -7,5 +7,4 @@ universal = True [egg_info] tag_build = tag_date = 0 -tag_svn_revision = 0 diff --git a/setup.py b/setup.py index b326345..1cf6809 100644 --- a/setup.py +++ b/setup.py @@ -5,12 +5,13 @@ setup( name='u-msgpack-python', - version='2.3.0', + version='2.8.0', description='A portable, lightweight MessagePack serializer and deserializer written in pure Python.', 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/test_umsgpack.py b/test_umsgpack.py index 3754e47..1652d60 100644 --- a/test_umsgpack.py +++ b/test_umsgpack.py @@ -11,6 +11,7 @@ import sys import struct import unittest +import datetime import io from collections import OrderedDict, namedtuple @@ -18,221 +19,353 @@ single_test_vectors = [ # None - [ "nil", None, b"\xc0" ], + ["nil", None, b"\xc0"], # Booleans - [ "bool false", False, b"\xc2" ], - [ "bool true", True, b"\xc3" ], + ["bool false", False, b"\xc2"], + ["bool true", True, b"\xc3"], # + 7-bit uint - [ "7-bit uint", 0x00, b"\x00" ], - [ "7-bit uint", 0x10, b"\x10" ], - [ "7-bit uint", 0x7f, b"\x7f" ], + ["7-bit uint", 0x00, b"\x00"], + ["7-bit uint", 0x10, b"\x10"], + ["7-bit uint", 0x7f, b"\x7f"], # - 5-bit int - [ "5-bit sint", -1, b"\xff" ], - [ "5-bit sint", -16, b"\xf0" ], - [ "5-bit sint", -32, b"\xe0" ], + ["5-bit sint", -1, b"\xff"], + ["5-bit sint", -16, b"\xf0"], + ["5-bit sint", -32, b"\xe0"], # 8-bit uint - [ "8-bit uint", 0x80, b"\xcc\x80" ], - [ "8-bit uint", 0xf0, b"\xcc\xf0" ], - [ "8-bit uint", 0xff, b"\xcc\xff" ], + ["8-bit uint", 0x80, b"\xcc\x80"], + ["8-bit uint", 0xf0, b"\xcc\xf0"], + ["8-bit uint", 0xff, b"\xcc\xff"], # 16-bit uint - [ "16-bit uint", 0x100, b"\xcd\x01\x00" ], - [ "16-bit uint", 0x2000, b"\xcd\x20\x00" ], - [ "16-bit uint", 0xffff, b"\xcd\xff\xff" ], + ["16-bit uint", 0x100, b"\xcd\x01\x00"], + ["16-bit uint", 0x2000, b"\xcd\x20\x00"], + ["16-bit uint", 0xffff, b"\xcd\xff\xff"], # 32-bit uint - [ "32-bit uint", 0x10000, b"\xce\x00\x01\x00\x00" ], - [ "32-bit uint", 0x200000, b"\xce\x00\x20\x00\x00" ], - [ "32-bit uint", 0xffffffff, b"\xce\xff\xff\xff\xff" ], + ["32-bit uint", 0x10000, b"\xce\x00\x01\x00\x00"], + ["32-bit uint", 0x200000, b"\xce\x00\x20\x00\x00"], + ["32-bit uint", 0xffffffff, b"\xce\xff\xff\xff\xff"], # 64-bit uint - [ "64-bit uint", 0x100000000, b"\xcf\x00\x00\x00\x01\x00\x00\x00\x00" ], - [ "64-bit uint", 0x200000000000, b"\xcf\x00\x00\x20\x00\x00\x00\x00\x00" ], - [ "64-bit uint", 0xffffffffffffffff, b"\xcf\xff\xff\xff\xff\xff\xff\xff\xff" ], + ["64-bit uint", 0x100000000, b"\xcf\x00\x00\x00\x01\x00\x00\x00\x00"], + ["64-bit uint", 0x200000000000, b"\xcf\x00\x00\x20\x00\x00\x00\x00\x00"], + ["64-bit uint", 0xffffffffffffffff, b"\xcf\xff\xff\xff\xff\xff\xff\xff\xff"], # 8-bit int - [ "8-bit int", -33, b"\xd0\xdf" ], - [ "8-bit int", -100, b"\xd0\x9c" ], - [ "8-bit int", -128, b"\xd0\x80" ], + ["8-bit int", -33, b"\xd0\xdf"], + ["8-bit int", -100, b"\xd0\x9c"], + ["8-bit int", -128, b"\xd0\x80"], # 16-bit int - [ "16-bit int", -129, b"\xd1\xff\x7f" ], - [ "16-bit int", -2000, b"\xd1\xf8\x30" ], - [ "16-bit int", -32768, b"\xd1\x80\x00" ], + ["16-bit int", -129, b"\xd1\xff\x7f"], + ["16-bit int", -2000, b"\xd1\xf8\x30"], + ["16-bit int", -32768, b"\xd1\x80\x00"], # 32-bit int - [ "32-bit int", -32769, b"\xd2\xff\xff\x7f\xff" ], - [ "32-bit int", -1000000000, b"\xd2\xc4\x65\x36\x00" ], - [ "32-bit int", -2147483648, b"\xd2\x80\x00\x00\x00" ], + ["32-bit int", -32769, b"\xd2\xff\xff\x7f\xff"], + ["32-bit int", -1000000000, b"\xd2\xc4\x65\x36\x00"], + ["32-bit int", -2147483648, b"\xd2\x80\x00\x00\x00"], # 64-bit int - [ "64-bit int", -2147483649, b"\xd3\xff\xff\xff\xff\x7f\xff\xff\xff" ], - [ "64-bit int", -1000000000000000002, b"\xd3\xf2\x1f\x49\x4c\x58\x9b\xff\xfe" ], - [ "64-bit int", -9223372036854775808, b"\xd3\x80\x00\x00\x00\x00\x00\x00\x00" ], + ["64-bit int", -2147483649, b"\xd3\xff\xff\xff\xff\x7f\xff\xff\xff"], + ["64-bit int", -1000000000000000002, b"\xd3\xf2\x1f\x49\x4c\x58\x9b\xff\xfe"], + ["64-bit int", -9223372036854775808, b"\xd3\x80\x00\x00\x00\x00\x00\x00\x00"], # 64-bit float - [ "64-bit float", 0.0, b"\xcb\x00\x00\x00\x00\x00\x00\x00\x00" ], - [ "64-bit float", 2.5, b"\xcb\x40\x04\x00\x00\x00\x00\x00\x00" ], - [ "64-bit float", float(10**35), b"\xcb\x47\x33\x42\x61\x72\xc7\x4d\x82" ], + ["64-bit float", 0.0, b"\xcb\x00\x00\x00\x00\x00\x00\x00\x00"], + ["64-bit float", 2.5, b"\xcb\x40\x04\x00\x00\x00\x00\x00\x00"], + ["64-bit float", float(10**35), b"\xcb\x47\x33\x42\x61\x72\xc7\x4d\x82"], # Fixstr String - [ "fix string", u"", b"\xa0" ], - [ "fix string", u"a", b"\xa1\x61" ], - [ "fix string", u"abc", b"\xa3\x61\x62\x63" ], - [ "fix string", u"a"*31, b"\xbf" + b"\x61"*31 ], + ["fix string", u"", b"\xa0"], + ["fix string", u"a", b"\xa1\x61"], + ["fix string", u"abc", b"\xa3\x61\x62\x63"], + ["fix string", u"a" * 31, b"\xbf" + b"\x61" * 31], # 8-bit String - [ "8-bit string", u"b"*32, b"\xd9\x20" + b"b"*32 ], - [ "8-bit string", u"c"*100, b"\xd9\x64" + b"c"*100 ], - [ "8-bit string", u"d"*255, b"\xd9\xff" + b"d"*255 ], + ["8-bit string", u"b" * 32, b"\xd9\x20" + b"b" * 32], + ["8-bit string", u"c" * 100, b"\xd9\x64" + b"c" * 100], + ["8-bit string", u"d" * 255, b"\xd9\xff" + b"d" * 255], # 16-bit String - [ "16-bit string", u"b"*256, b"\xda\x01\x00" + b"b"*256 ], - [ "16-bit string", u"c"*65535, b"\xda\xff\xff" + b"c"*65535 ], + ["16-bit string", u"b" * 256, b"\xda\x01\x00" + b"b" * 256], + ["16-bit string", u"c" * 65535, b"\xda\xff\xff" + b"c" * 65535], # 32-bit String - [ "32-bit string", u"b"*65536, b"\xdb\x00\x01\x00\x00" + b"b"*65536 ], + ["32-bit string", u"b" * 65536, b"\xdb\x00\x01\x00\x00" + b"b" * 65536], # Wide character String - [ "wide char string", u"Allagbé", b"\xa8Allagb\xc3\xa9" ], - [ "wide char string", u"По оживлённым берегам", b"\xd9\x28\xd0\x9f\xd0\xbe\x20\xd0\xbe\xd0\xb6\xd0\xb8\xd0\xb2\xd0\xbb\xd1\x91\xd0\xbd\xd0\xbd\xd1\x8b\xd0\xbc\x20\xd0\xb1\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb3\xd0\xb0\xd0\xbc" ], + ["wide char string", u"Allagbé", b"\xa8Allagb\xc3\xa9"], + ["wide char string", u"По оживлённым берегам", + b"\xd9\x28\xd0\x9f\xd0\xbe\x20\xd0\xbe\xd0\xb6\xd0\xb8\xd0\xb2\xd0\xbb\xd1\x91\xd0\xbd\xd0\xbd\xd1\x8b\xd0\xbc\x20\xd0\xb1\xd0\xb5\xd1\x80\xd0\xb5\xd0\xb3\xd0\xb0\xd0\xbc"], # 8-bit Binary - [ "8-bit binary", b"\x80"*1, b"\xc4\x01" + b"\x80"*1 ], - [ "8-bit binary", b"\x80"*32, b"\xc4\x20" + b"\x80"*32 ], - [ "8-bit binary", b"\x80"*255, b"\xc4\xff" + b"\x80"*255 ], + ["8-bit binary", b"\x80" * 1, b"\xc4\x01" + b"\x80" * 1], + ["8-bit binary", b"\x80" * 32, b"\xc4\x20" + b"\x80" * 32], + ["8-bit binary", b"\x80" * 255, b"\xc4\xff" + b"\x80" * 255], # 16-bit Binary - [ "16-bit binary", b"\x80"*256, b"\xc5\x01\x00" + b"\x80"*256 ], + ["16-bit binary", b"\x80" * 256, b"\xc5\x01\x00" + b"\x80" * 256], # 32-bit Binary - [ "32-bit binary", b"\x80"*65536, b"\xc6\x00\x01\x00\x00" + b"\x80"*65536 ], + ["32-bit binary", b"\x80" * 65536, b"\xc6\x00\x01\x00\x00" + b"\x80" * 65536], # Fixext 1 - [ "fixext 1", umsgpack.Ext(0x05, b"\x80"*1), b"\xd4\x05" + b"\x80"*1 ], + ["fixext 1", umsgpack.Ext(0x05, b"\x80" * 1), b"\xd4\x05" + b"\x80" * 1], # Fixext 2 - [ "fixext 2", umsgpack.Ext(0x05, b"\x80"*2), b"\xd5\x05" + b"\x80"*2 ], + ["fixext 2", umsgpack.Ext(0x05, b"\x80" * 2), b"\xd5\x05" + b"\x80" * 2], # Fixext 4 - [ "fixext 4", umsgpack.Ext(0x05, b"\x80"*4), b"\xd6\x05" + b"\x80"*4 ], + ["fixext 4", umsgpack.Ext(0x05, b"\x80" * 4), b"\xd6\x05" + b"\x80" * 4], # Fixext 8 - [ "fixext 8", umsgpack.Ext(0x05, b"\x80"*8), b"\xd7\x05" + b"\x80"*8 ], + ["fixext 8", umsgpack.Ext(0x05, b"\x80" * 8), b"\xd7\x05" + b"\x80" * 8], # Fixext 16 - [ "fixext 16", umsgpack.Ext(0x05, b"\x80"*16), b"\xd8\x05" + b"\x80"*16 ], + ["fixext 16", umsgpack.Ext(0x05, b"\x80" * 16), + b"\xd8\x05" + b"\x80" * 16], # 8-bit Ext - [ "8-bit ext", umsgpack.Ext(0x05, b"\x80"*255), b"\xc7\xff\x05" + b"\x80"*255 ], + ["8-bit ext", umsgpack.Ext(0x05, b"\x80" * 255), + b"\xc7\xff\x05" + b"\x80" * 255], # 16-bit Ext - [ "16-bit ext", umsgpack.Ext(0x05, b"\x80"*256), b"\xc8\x01\x00\x05" + b"\x80"*256 ], + ["16-bit ext", umsgpack.Ext(0x05, b"\x80" * 256), + b"\xc8\x01\x00\x05" + b"\x80" * 256], # 32-bit Ext - [ "32-bit ext", umsgpack.Ext(0x05, b"\x80"*65536), b"\xc9\x00\x01\x00\x00\x05" + b"\x80"*65536 ], + ["32-bit ext", umsgpack.Ext(0x05, b"\x80" * 65536), + b"\xc9\x00\x01\x00\x00\x05" + b"\x80" * 65536], # Empty Array - [ "empty array", [], b"\x90" ], + ["empty array", [], b"\x90"], # Empty Map - [ "empty map", {}, b"\x80" ], + ["empty map", {}, b"\x80"], + # 32-bit Timestamp + ["32-bit timestamp", datetime.datetime(1970, 1, 1, 0, 0, 0, 0, umsgpack._utc_tzinfo), + b"\xd6\xff\x00\x00\x00\x00"], + ["32-bit timestamp", datetime.datetime(2000, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo), + b"\xd6\xff\x38\x6d\xd1\x4e"], + # 64-bit Timestamp + ["64-bit timestamp", datetime.datetime(2000, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xd7\xff\x00\x4b\x51\x40\x38\x6d\xd1\x4e"], + ["64-bit timestamp", datetime.datetime(2200, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo), + b"\xd7\xff\x00\x00\x00\x01\xb0\x9e\xa6\xce"], + ["64-bit timestamp", datetime.datetime(2200, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xd7\xff\x00\x4b\x51\x41\xb0\x9e\xa6\xce"], + # 96-bit Timestamp + ["96-bit timestamp", datetime.datetime(1900, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo), + b"\xc7\x0c\xff\x00\x00\x00\x00\xff\xff\xff\xff\x7c\x56\x0f\x4e"], + ["96-bit timestamp", datetime.datetime(1900, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xc7\x0c\xff\x00\x12\xd4\x50\xff\xff\xff\xff\x7c\x56\x0f\x4e"], + ["96-bit timestamp", datetime.datetime(3000, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo), + b"\xc7\x0c\xff\x00\x00\x00\x00\x00\x00\x00\x07\x91\x5f\x59\xce"], + ["96-bit timestamp", datetime.datetime(3000, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xc7\x0c\xff\x00\x12\xd4\x50\x00\x00\x00\x07\x91\x5f\x59\xce"], ] composite_test_vectors = [ # Fix Array - [ "fix array", [ 5, u"abc", True ], b"\x93\x05\xa3\x61\x62\x63\xc3" ], + ["fix array", [5, u"abc", True], + b"\x93\x05\xa3\x61\x62\x63\xc3"], # 16-bit Array - [ "16-bit array", [ 0x05 ]*16, b"\xdc\x00\x10" + b"\x05"*16 ], - [ "16-bit array", [ 0x05 ]*65535, b"\xdc\xff\xff" + b"\x05"*65535 ], + ["16-bit array", [0x05] * 16, + b"\xdc\x00\x10" + b"\x05" * 16], + ["16-bit array", [0x05] * 65535, + b"\xdc\xff\xff" + b"\x05" * 65535], # 32-bit Array - [ "32-bit array", [ 0x05 ]*65536, b"\xdd\x00\x01\x00\x00" + b"\x05"*65536 ], + ["32-bit array", [0x05] * 65536, + b"\xdd\x00\x01\x00\x00" + b"\x05" * 65536], # Fix Map - [ "fix map", OrderedDict([(1, True), (2, u"abc"), (3, b"\x80")]), b"\x83\x01\xc3\x02\xa3\x61\x62\x63\x03\xc4\x01\x80" ], - [ "fix map", { u"abc" : 5 }, b"\x81\xa3\x61\x62\x63\x05" ], - [ "fix map", { b"\x80" : 0xffff }, b"\x81\xc4\x01\x80\xcd\xff\xff" ], - [ "fix map", { True : None }, b"\x81\xc3\xc0" ], + ["fix map", OrderedDict([(1, True), (2, u"abc"), (3, b"\x80")]), + b"\x83\x01\xc3\x02\xa3\x61\x62\x63\x03\xc4\x01\x80"], + ["fix map", {u"abc": 5}, + b"\x81\xa3\x61\x62\x63\x05"], + ["fix map", {b"\x80": 0xffff}, + b"\x81\xc4\x01\x80\xcd\xff\xff"], + ["fix map", {True: None}, + b"\x81\xc3\xc0"], # 16-bit Map - [ "16-bit map", OrderedDict([(k, 0x05) for k in range(16)]), b"\xde\x00\x10" + b"".join([struct.pack("B", i) + b"\x05" for i in range(16)])], - [ "16-bit map", OrderedDict([(k, 0x05) for k in range(6000)]), b"\xde\x17\x70" + b"".join([struct.pack("B", i) + b"\x05" for i in range(128)]) + b"".join([b"\xcc" + struct.pack("B", i) + b"\x05" for i in range(128, 256)]) + b"".join([b"\xcd" + struct.pack(">H", i) + b"\x05" for i in range(256, 6000)]) ], + ["16-bit map", OrderedDict([(k, 0x05) for k in range(16)]), + b"\xde\x00\x10" + b"".join([struct.pack("B", i) + b"\x05" for i in range(16)])], + ["16-bit map", OrderedDict([(k, 0x05) for k in range(6000)]), + b"\xde\x17\x70" + b"".join([struct.pack("B", i) + b"\x05" for i in range(128)]) + + b"".join([b"\xcc" + struct.pack("B", i) + b"\x05" for i in range(128, 256)]) + + b"".join([b"\xcd" + struct.pack(">H", i) + b"\x05" for i in range(256, 6000)])], # Complex Array - [ "complex array", [ True, 0x01, umsgpack.Ext(0x03, b"foo"), 0xff, OrderedDict([(1, False), (2, u"abc")]), b"\x80", [1, 2, 3], u"abc" ], b"\x98\xc3\x01\xc7\x03\x03\x66\x6f\x6f\xcc\xff\x82\x01\xc2\x02\xa3\x61\x62\x63\xc4\x01\x80\x93\x01\x02\x03\xa3\x61\x62\x63" ], + ["complex array", [True, 0x01, umsgpack.Ext(0x03, b"foo"), 0xff, + OrderedDict([(1, False), (2, u"abc")]), b"\x80", + [1, 2, 3], u"abc"], + b"\x98\xc3\x01\xc7\x03\x03\x66\x6f\x6f\xcc\xff\x82\x01\xc2\x02\xa3\x61\x62\x63\xc4\x01\x80\x93\x01\x02\x03\xa3\x61\x62\x63"], # Complex Map - [ "complex map", OrderedDict([(1, [OrderedDict([(1, 2), (3, 4)]), {}]), (2, 1), (3, [False, u"def"]), (4, OrderedDict([(0x100000000, u"a"), (0xffffffff, u"b")]))]), b"\x84\x01\x92\x82\x01\x02\x03\x04\x80\x02\x01\x03\x92\xc2\xa3\x64\x65\x66\x04\x82\xcf\x00\x00\x00\x01\x00\x00\x00\x00\xa1\x61\xce\xff\xff\xff\xff\xa1\x62" ], + ["complex map", OrderedDict([(1, [OrderedDict([(1, 2), (3, 4)]), {}]), + (2, 1), (3, [False, u"def"]), + (4, OrderedDict([(0x100000000, u"a"), + (0xffffffff, u"b")]))]), + b"\x84\x01\x92\x82\x01\x02\x03\x04\x80\x02\x01\x03\x92\xc2\xa3\x64\x65\x66\x04\x82\xcf\x00\x00\x00\x01\x00\x00\x00\x00\xa1\x61\xce\xff\xff\xff\xff\xa1\x62"], # Map with Tuple Keys - [ "map with tuple keys", OrderedDict([((u"foo", False, 3), True), ((3e6, -5), u"def")]), b"\x82\x93\xa3\x66\x6f\x6f\xc2\x03\xc3\x92\xcb\x41\x46\xe3\x60\x00\x00\x00\x00\xfb\xa3\x64\x65\x66" ], + ["map with tuple keys", OrderedDict([((u"foo", False, 3), True), + ((3e6, -5), u"def")]), + b"\x82\x93\xa3\x66\x6f\x6f\xc2\x03\xc3\x92\xcb\x41\x46\xe3\x60\x00\x00\x00\x00\xfb\xa3\x64\x65\x66"], # Map with Complex Tuple Keys - [ "map with complex tuple keys", {(u"foo", (1,2,3), 3) : -5}, b"\x81\x93\xa3\x66\x6f\x6f\x93\x01\x02\x03\x03\xfb" ] + ["map with complex tuple keys", {(u"foo", (1, 2, 3), 3): -5}, + b"\x81\x93\xa3\x66\x6f\x6f\x93\x01\x02\x03\x03\xfb"] ] pack_exception_test_vectors = [ # Unsupported type exception - [ "unsupported type", set([1,2,3]), umsgpack.UnsupportedTypeException ], - [ "unsupported type", -2**(64-1)-1, umsgpack.UnsupportedTypeException ], - [ "unsupported type", 2**64, umsgpack.UnsupportedTypeException ], + ["unsupported type", set([1, 2, 3]), umsgpack.UnsupportedTypeException], + ["unsupported type", -2**(64 - 1) - 1, umsgpack.UnsupportedTypeException], + ["unsupported type", 2**64, umsgpack.UnsupportedTypeException], ] unpack_exception_test_vectors = [ # Type errors - [ "type error unpack unicode string", u"\x01", TypeError ], - [ "type error unpack boolean", True, TypeError ], + ["type error unpack unicode string", u"\x01", TypeError], + ["type error unpack boolean", True, TypeError], # Insufficient data to unpack object - [ "insufficient data 8-bit uint", b"\xcc", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit uint", b"\xcd\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit uint", b"\xce\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 64-bit uint", b"\xcf\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 8-bit int", b"\xd0", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit int", b"\xd1\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit int", b"\xd2\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 64-bit int", b"\xd3\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit float", b"\xca\xff", umsgpack.InsufficientDataException ], - [ "insufficient data 64-bit float", b"\xcb\xff", umsgpack.InsufficientDataException ], - [ "insufficient data fixstr", b"\xa1", umsgpack.InsufficientDataException ], - [ "insufficient data 8-bit string", b"\xd9", umsgpack.InsufficientDataException ], - [ "insufficient data 8-bit string", b"\xd9\x01", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit string", b"\xda\x01\x00", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit string", b"\xdb\x00\x01\x00\x00", umsgpack.InsufficientDataException ], - [ "insufficient data 8-bit binary", b"\xc4", umsgpack.InsufficientDataException ], - [ "insufficient data 8-bit binary", b"\xc4\x01", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit binary", b"\xc5\x01\x00", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit binary", b"\xc6\x00\x01\x00\x00", umsgpack.InsufficientDataException ], - [ "insufficient data fixarray", b"\x91", umsgpack.InsufficientDataException ], - [ "insufficient data fixarray", b"\x92\xc2", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit array", b"\xdc\x00\xf0\xc2\xc3", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit array", b"\xdd\x00\x01\x00\x00\xc2\xc3", umsgpack.InsufficientDataException ], - [ "insufficient data fixmap", b"\x81", umsgpack.InsufficientDataException ], - [ "insufficient data fixmap", b"\x82\xc2\xc3", umsgpack.InsufficientDataException ], - [ "insufficient data 16-bit map", b"\xde\x00\xf0\xc2\xc3", umsgpack.InsufficientDataException ], - [ "insufficient data 32-bit map", b"\xdf\x00\x01\x00\x00\xc2\xc3", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 1", b"\xd4", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 1", b"\xd4\x05", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 2", b"\xd5\x05\x01", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 4", b"\xd6\x05\x01\x02\x03", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 8", b"\xd7\x05\x01\x02\x03", umsgpack.InsufficientDataException ], - [ "insufficient data fixext 16", b"\xd8\x05\x01\x02\x03", umsgpack.InsufficientDataException ], - [ "insufficient data ext 8-bit", b"\xc7\x05\x05\x01\x02\x03", umsgpack.InsufficientDataException ], - [ "insufficient data ext 16-bit", b"\xc8\x01\x00\x05\x01\x02\x03", umsgpack.InsufficientDataException ], - [ "insufficient data ext 32-bit", b"\xc9\x00\x01\x00\x00\x05\x01\x02\x03", umsgpack.InsufficientDataException ], + ["insufficient data 8-bit uint", b"\xcc", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit uint", b"\xcd\xff", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit uint", b"\xce\xff", + umsgpack.InsufficientDataException], + ["insufficient data 64-bit uint", b"\xcf\xff", + umsgpack.InsufficientDataException], + ["insufficient data 8-bit int", b"\xd0", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit int", b"\xd1\xff", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit int", b"\xd2\xff", + umsgpack.InsufficientDataException], + ["insufficient data 64-bit int", b"\xd3\xff", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit float", b"\xca\xff", + umsgpack.InsufficientDataException], + ["insufficient data 64-bit float", b"\xcb\xff", + umsgpack.InsufficientDataException], + ["insufficient data fixstr", b"\xa1", + umsgpack.InsufficientDataException], + ["insufficient data 8-bit string", b"\xd9", + umsgpack.InsufficientDataException], + ["insufficient data 8-bit string", b"\xd9\x01", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit string", b"\xda\x01\x00", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit string", b"\xdb\x00\x01\x00\x00", + umsgpack.InsufficientDataException], + ["insufficient data 8-bit binary", b"\xc4", + umsgpack.InsufficientDataException], + ["insufficient data 8-bit binary", b"\xc4\x01", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit binary", b"\xc5\x01\x00", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit binary", b"\xc6\x00\x01\x00\x00", + umsgpack.InsufficientDataException], + ["insufficient data fixarray", b"\x91", + umsgpack.InsufficientDataException], + ["insufficient data fixarray", b"\x92\xc2", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit array", b"\xdc\x00\xf0\xc2\xc3", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit array", b"\xdd\x00\x01\x00\x00\xc2\xc3", + umsgpack.InsufficientDataException], + ["insufficient data fixmap", b"\x81", + umsgpack.InsufficientDataException], + ["insufficient data fixmap", b"\x82\xc2\xc3", + umsgpack.InsufficientDataException], + ["insufficient data 16-bit map", b"\xde\x00\xf0\xc2\xc3", + umsgpack.InsufficientDataException], + ["insufficient data 32-bit map", b"\xdf\x00\x01\x00\x00\xc2\xc3", + umsgpack.InsufficientDataException], + ["insufficient data fixext 1", b"\xd4", + umsgpack.InsufficientDataException], + ["insufficient data fixext 1", b"\xd4\x05", + umsgpack.InsufficientDataException], + ["insufficient data fixext 2", b"\xd5\x05\x01", + umsgpack.InsufficientDataException], + ["insufficient data fixext 4", b"\xd6\x05\x01\x02\x03", + umsgpack.InsufficientDataException], + ["insufficient data fixext 8", b"\xd7\x05\x01\x02\x03", + umsgpack.InsufficientDataException], + ["insufficient data fixext 16", b"\xd8\x05\x01\x02\x03", + umsgpack.InsufficientDataException], + ["insufficient data ext 8-bit", b"\xc7\x05\x05\x01\x02\x03", + umsgpack.InsufficientDataException], + ["insufficient data ext 16-bit", b"\xc8\x01\x00\x05\x01\x02\x03", + umsgpack.InsufficientDataException], + ["insufficient data ext 32-bit", b"\xc9\x00\x01\x00\x00\x05\x01\x02\x03", + umsgpack.InsufficientDataException], # Unhashable key { 1 : True, { 1 : 1 } : False } - [ "unhashable key", b"\x82\x01\xc3\x81\x01\x01\xc2", umsgpack.UnhashableKeyException ], + ["unhashable key", b"\x82\x01\xc3\x81\x01\x01\xc2", + umsgpack.UnhashableKeyException], # Unhashable key { [ 1, 2, {} ] : True } - [ "unhashable key", b"\x81\x93\x01\x02\x80\xc3", umsgpack.UnhashableKeyException ], + ["unhashable key", b"\x81\x93\x01\x02\x80\xc3", + umsgpack.UnhashableKeyException], # Key duplicate { 1 : True, 1 : False } - [ "duplicate key", b"\x82\x01\xc3\x01\xc2", umsgpack.DuplicateKeyException ], + ["duplicate key", b"\x82\x01\xc3\x01\xc2", + umsgpack.DuplicateKeyException], # Reserved code (0xc1) - [ "reserved code", b"\xc1", umsgpack.ReservedCodeException ], + ["reserved code", b"\xc1", + umsgpack.ReservedCodeException], + # Unsupported timestamp (unsupported data length) + ["unsupported timestamp", b"\xc7\x02\xff\xaa\xbb", + umsgpack.UnsupportedTimestampException], # Invalid string (non utf-8) - [ "invalid string", b"\xa1\x80", umsgpack.InvalidStringException ], + ["invalid string", b"\xa1\x80", + umsgpack.InvalidStringException], ] compatibility_test_vectors = [ # Fix Raw - [ "fix raw", b"", b"\xa0" ], - [ "fix raw", u"", b"\xa0" ], - [ "fix raw", b"a", b"\xa1\x61" ], - [ "fix raw", u"a", b"\xa1\x61" ], - [ "fix raw", b"abc", b"\xa3\x61\x62\x63" ], - [ "fix raw", u"abc", b"\xa3\x61\x62\x63" ], - [ "fix raw", b"a"*31, b"\xbf" + b"\x61"*31 ], - [ "fix raw", u"a"*31, b"\xbf" + b"\x61"*31 ], + ["fix raw", b"", b"\xa0"], + ["fix raw", u"", b"\xa0"], + ["fix raw", b"a", b"\xa1\x61"], + ["fix raw", u"a", b"\xa1\x61"], + ["fix raw", b"abc", b"\xa3\x61\x62\x63"], + ["fix raw", u"abc", b"\xa3\x61\x62\x63"], + ["fix raw", b"a" * 31, b"\xbf" + b"\x61" * 31], + ["fix raw", u"a" * 31, b"\xbf" + b"\x61" * 31], # 16-bit Raw - [ "16-bit raw", u"b"*32, b"\xda\x00\x20" + b"b"*32 ], - [ "16-bit raw", b"b"*32, b"\xda\x00\x20" + b"b"*32 ], - [ "16-bit raw", u"b"*256, b"\xda\x01\x00" + b"b"*256 ], - [ "16-bit raw", b"b"*256, b"\xda\x01\x00" + b"b"*256 ], - [ "16-bit raw", u"c"*65535, b"\xda\xff\xff" + b"c"*65535 ], - [ "16-bit raw", b"c"*65535, b"\xda\xff\xff" + b"c"*65535 ], + ["16-bit raw", u"b" * 32, b"\xda\x00\x20" + b"b" * 32], + ["16-bit raw", b"b" * 32, b"\xda\x00\x20" + b"b" * 32], + ["16-bit raw", u"b" * 256, b"\xda\x01\x00" + b"b" * 256], + ["16-bit raw", b"b" * 256, b"\xda\x01\x00" + b"b" * 256], + ["16-bit raw", u"c" * 65535, b"\xda\xff\xff" + b"c" * 65535], + ["16-bit raw", b"c" * 65535, b"\xda\xff\xff" + b"c" * 65535], # 32-bit Raw - [ "32-bit raw", u"b"*65536, b"\xdb\x00\x01\x00\x00" + b"b"*65536 ], - [ "32-bit raw", b"b"*65536, b"\xdb\x00\x01\x00\x00" + b"b"*65536 ], + ["32-bit raw", u"b" * 65536, b"\xdb\x00\x01\x00\x00" + b"b" * 65536], + ["32-bit raw", b"b" * 65536, b"\xdb\x00\x01\x00\x00" + b"b" * 65536], +] + +float_precision_test_vectors = [ + ["float precision single", 2.5, b"\xca\x40\x20\x00\x00"], + ["float precision double", 2.5, b"\xcb\x40\x04\x00\x00\x00\x00\x00\x00"], +] + +tuple_test_vectors = [ + ["nested array", [0x01, [b"\x80", [[u"a", u"b", u"c"], True]]], + b"\x92\x01\x92\xc4\x01\x80\x92\x93\xa1a\xa1b\xa1c\xc3", + (0x01, (b"\x80", ((u"a", u"b", u"c"), True)))], +] + +naive_timestamp_test_vectors = [ + ["32-bit timestamp (naive)", datetime.datetime(2000, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo), + b"\xd6\xff\x38\x6d\xd1\x4e", + datetime.datetime(2000, 1, 1, 10, 5, 2, 0, umsgpack._utc_tzinfo)], + ["64-bit timestamp (naive)", datetime.datetime(2200, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xd7\xff\x00\x4b\x51\x41\xb0\x9e\xa6\xce", + datetime.datetime(2200, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo)], + ["96-bit timestamp (naive)", datetime.datetime(3000, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo), + b"\xc7\x0c\xff\x00\x12\xd4\x50\x00\x00\x00\x07\x91\x5f\x59\xce", + datetime.datetime(3000, 1, 1, 10, 5, 2, 1234, umsgpack._utc_tzinfo)], ] CustomType = namedtuple('CustomType', ['x', 'y', 'z']) ext_handlers = { - complex: lambda obj: umsgpack.Ext(0x20, struct.pack("ff", obj.real, obj.imag)), + complex: lambda obj: umsgpack.Ext(0x20, struct.pack(" unpackb -> {int,long} if isinstance(obj, int) or isinstance(obj, long): - self.assertTrue(isinstance(unpacked, int) or isinstance(unpacked, long)) + self.assertTrue(isinstance(unpacked, int) or + isinstance(unpacked, long)) else: self.assertTrue(isinstance(unpacked, type(obj))) # In Python3, we only have the int integer type @@ -306,12 +452,15 @@ 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) @@ -320,7 +469,9 @@ 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) umsgpack.compatibility = False @@ -330,7 +481,9 @@ 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) # Encoded raw should always unpack to bytes in compatibility mode, @@ -372,27 +525,231 @@ def test_unpack_ordered_dict(self): self.assertTrue(isinstance(unpacked, OrderedDict)) self.assertEqual(unpacked, obj) - def test_ext_exceptions(self): - with self.assertRaises(TypeError): - _ = umsgpack.Ext(-1, b"") + def test_unpack_tuple(self): + # Use tuple test vector + (_, obj, data, obj_tuple) = tuple_test_vectors[0] + + # Unpack with default options (list) + self.assertEqual(umsgpack.unpackb(data), obj) + + # Unpack with use_tuple=False (list) + self.assertEqual(umsgpack.unpackb(data, use_tuple=False), obj) + # Unpack with use_tuple=True (tuple) + 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(128, b"") + _ = 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) - print("\tTesting %s: object %s" % (name, obj_repr if len(obj_repr) < 24 else obj_repr[0:24] + "...")) - self.assertEqual(umsgpack.packb(obj, ext_handlers=ext_handlers), data) + 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) 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] + "...")) - self.assertEqual(umsgpack.unpackb(data, ext_handlers=ext_handlers), obj) + 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) + + 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}".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) + + def test_pack_naive_timestamp(self): + for (name, obj, data, _) in naive_timestamp_test_vectors: + obj_repr = repr(obj) + 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) + + def test_unpack_naive_timestamp(self): + for (name, _, data, obj) in naive_timestamp_test_vectors: + obj_repr = repr(obj) + 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) + + 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}".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) + + 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}".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) + + 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) + class CustomComplex: + def __init__(self, real, imag): + self.real = real + self.imag = imag + + def __eq__(self, other): + return self.real == other.real and self.imag == other.imag + + def packb(self): + return struct.pack("`_ 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. Keywords: msgpack serialization deserialization -Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent @@ -17,3 +15,6 @@ Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy +License-File: LICENSE + +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. diff --git a/u_msgpack_python.egg-info/SOURCES.txt b/u_msgpack_python.egg-info/SOURCES.txt index 08e0ca2..a71d4ae 100644 --- a/u_msgpack_python.egg-info/SOURCES.txt +++ b/u_msgpack_python.egg-info/SOURCES.txt @@ -1,10 +1,12 @@ LICENSE MANIFEST.in +README.md setup.cfg setup.py test_umsgpack.py -umsgpack.py u_msgpack_python.egg-info/PKG-INFO u_msgpack_python.egg-info/SOURCES.txt u_msgpack_python.egg-info/dependency_links.txt -u_msgpack_python.egg-info/top_level.txt \ No newline at end of file +u_msgpack_python.egg-info/top_level.txt +umsgpack/__init__.py +umsgpack/__init__.pyi \ No newline at end of file diff --git a/umsgpack.py b/umsgpack/__init__.py similarity index 57% rename from umsgpack.py rename to umsgpack/__init__.py index c0647e0..e7e194f 100644 --- a/umsgpack.py +++ b/umsgpack/__init__.py @@ -1,4 +1,4 @@ -# u-msgpack-python v2.3.0 - 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 @@ -10,7 +10,7 @@ # # MIT License # -# Copyright (c) 2013-2016 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 @@ -31,7 +31,7 @@ # THE SOFTWARE. # """ -u-msgpack-python v2.3.0 - 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 @@ -43,24 +43,30 @@ License: MIT """ +import struct +import collections +import datetime +import sys +import io -__version__ = "2.3.0" +if sys.version_info[0:2] >= (3, 3): + from collections.abc import Hashable +else: + from collections import Hashable + +__version__ = "2.8.0" "Module version string" -version = (2,3,0) +version = (2, 8, 0) "Module version tuple" -import struct -import collections -import sys -import io -################################################################################ -### Ext Class -################################################################################ +############################################################################## +# Ext Class +############################################################################## # Extension type for application-defined types and data -class Ext: +class Ext(object): """ The Ext class facilitates creating a serializable extension object to store an application-defined type and data byte array. @@ -71,30 +77,36 @@ def __init__(self, type, data): Construct a new Ext object. Args: - type: application-defined type integer from 0 to 127 - data: application-defined data byte array + type (int): application-defined type integer + data (bytes): application-defined data byte array Raises: TypeError: - Specified ext type is outside of 0 to 127 range. + 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(0x05, 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) - >>> + >>> 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) """ - # Application ext type should be 0 <= type <= 127 - if not isinstance(type, int) or not (type >= 0 and type <= 127): - raise TypeError("ext type out of range") - # Check data is type bytes + # Check type is type int and in range + if not isinstance(type, int): + raise TypeError("ext type is not type integer") + 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 @@ -102,9 +114,8 @@ def __eq__(self, other): """ Compare this Ext object with another for equality. """ - return (isinstance(other, self.__class__) and - self.type == other.type and - self.data == other.data) + return isinstance(other, self.__class__) \ + and self.type == other.type and self.data == other.data def __ne__(self, other): """ @@ -116,61 +127,124 @@ def __str__(self): """ String representation of this Ext object. """ - s = "Ext Object (Type: 0x%02x, Data: " % self.type - s += " ".join(["0x%02x" % ord(self.data[i:i+1]) for i in xrange(min(len(self.data), 8))]) + s = "Ext Object (Type: {:d}, Data: ".format(self.type) + 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 += " ..." s += ")" return s + def __hash__(self): + """ + Provide a hash of this Ext object. + """ + return hash((self.type, self.data)) + + class InvalidString(bytes): """Subclass of bytes to hold invalid UTF-8 strings.""" - pass -################################################################################ -### Exceptions -################################################################################ + +############################################################################## +# Ext Serializable Decorator +############################################################################## + +_ext_class_to_type = {} +_ext_type_to_class = {} + + +def ext_serializable(ext_type): + """ + Return a decorator to register a class for automatic packing and unpacking + with the specified Ext type code. The application class should implement a + `packb()` method that returns serialized bytes, and an `unpackb()` class + method or static method that accepts serialized bytes and returns an + instance of the application class. + + Args: + ext_type (int): application-defined Ext type code + + Raises: + TypeError: + Ext type is not an integer. + ValueError: + Ext type is out of range of -128 to 127. + ValueError: + Ext type or class already registered. + """ + def wrapper(cls): + if not isinstance(ext_type, int): + raise TypeError("Ext type is not type integer") + elif not (-2**7 <= ext_type <= 2**7 - 1): + raise ValueError("Ext type value {:d} is out of range of -128 to 127".format(ext_type)) + elif ext_type in _ext_type_to_class: + raise ValueError("Ext type {:d} already registered with class {:s}".format(ext_type, repr(_ext_type_to_class[ext_type]))) + elif cls in _ext_class_to_type: + raise ValueError("Class {:s} already registered with Ext type {:d}".format(repr(cls), ext_type)) + + _ext_type_to_class[ext_type] = cls + _ext_class_to_type[cls] = ext_type + + return cls + + return wrapper + + +############################################################################## +# Exceptions +############################################################################## + # Base Exception classes class PackException(Exception): "Base class for exceptions encountered during packing." - pass + + class UnpackException(Exception): "Base class for exceptions encountered during unpacking." - pass + # Packing error class UnsupportedTypeException(PackException): "Object type not supported for packing." - pass + # Unpacking error class InsufficientDataException(UnpackException): "Insufficient data to unpack the serialized object." - pass + + class InvalidStringException(UnpackException): "Invalid UTF-8 string encountered during unpacking." - pass + + +class UnsupportedTimestampException(UnpackException): + "Unsupported timestamp format encountered during unpacking." + + class ReservedCodeException(UnpackException): "Reserved code encountered during unpacking." - pass + + class UnhashableKeyException(UnpackException): """ Unhashable key encountered during map unpacking. The serialized map cannot be deserialized into a Python dictionary. """ - pass + + class DuplicateKeyException(UnpackException): "Duplicate key encountered during map unpacking." - pass + # Backwards compatibility KeyNotPrimitiveException = UnhashableKeyException KeyDuplicateException = DuplicateKeyException -################################################################################ -### Exported Functions and Globals -################################################################################ +############################################################################# +# Exported Functions and Glob +############################################################################# # Exported functions and variables, set up in __init() pack = None @@ -192,146 +266,191 @@ 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'] """ -################################################################################ -### Packing -################################################################################ +############################################################################## +# Packing +############################################################################## # You may notice struct.pack("B", obj) instead of the simpler chr(obj) in the # code below. This is to allow for seamless Python 2 and 3 compatibility, as # chr(obj) has a str return type instead of bytes in Python 3, and # struct.pack(...) has the right return type in both versions. + def _pack_integer(obj, fp, options): if obj < 0: if obj >= -32: fp.write(struct.pack("b", obj)) - elif obj >= -2**(8-1): + elif obj >= -2**(8 - 1): fp.write(b"\xd0" + struct.pack("b", obj)) - elif obj >= -2**(16-1): + elif obj >= -2**(16 - 1): fp.write(b"\xd1" + struct.pack(">h", obj)) - elif obj >= -2**(32-1): + elif obj >= -2**(32 - 1): fp.write(b"\xd2" + struct.pack(">i", obj)) - elif obj >= -2**(64-1): + elif obj >= -2**(64 - 1): fp.write(b"\xd3" + struct.pack(">q", obj)) else: raise UnsupportedTypeException("huge signed int") else: - if obj <= 127: + if obj < 128: fp.write(struct.pack("B", obj)) - elif obj <= 2**8-1: + elif obj < 2**8: fp.write(b"\xcc" + struct.pack("B", obj)) - elif obj <= 2**16-1: + elif obj < 2**16: fp.write(b"\xcd" + struct.pack(">H", obj)) - elif obj <= 2**32-1: + elif obj < 2**32: fp.write(b"\xce" + struct.pack(">I", obj)) - elif obj <= 2**64-1: + elif obj < 2**64: fp.write(b"\xcf" + struct.pack(">Q", obj)) else: raise UnsupportedTypeException("huge unsigned int") + def _pack_nil(obj, fp, options): fp.write(b"\xc0") + def _pack_boolean(obj, fp, options): fp.write(b"\xc3" if obj else b"\xc2") + def _pack_float(obj, fp, options): - if _float_size == 64: + float_precision = options.get('force_float_precision', _float_precision) + + if float_precision == "double": fp.write(b"\xcb" + struct.pack(">d", obj)) - else: + elif float_precision == "single": fp.write(b"\xca" + struct.pack(">f", obj)) + else: + raise ValueError("invalid float precision") + def _pack_string(obj, fp, options): obj = obj.encode('utf-8') - if len(obj) <= 31: - fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) - elif len(obj) <= 2**8-1: - fp.write(b"\xd9" + struct.pack("B", len(obj)) + obj) - elif len(obj) <= 2**16-1: - fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) - elif len(obj) <= 2**32-1: - fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) + obj_len = len(obj) + if obj_len < 32: + fp.write(struct.pack("B", 0xa0 | obj_len) + obj) + elif obj_len < 2**8: + fp.write(b"\xd9" + struct.pack("B", obj_len) + obj) + elif obj_len < 2**16: + fp.write(b"\xda" + struct.pack(">H", obj_len) + obj) + elif obj_len < 2**32: + fp.write(b"\xdb" + struct.pack(">I", obj_len) + obj) else: raise UnsupportedTypeException("huge string") + def _pack_binary(obj, fp, options): - if len(obj) <= 2**8-1: - fp.write(b"\xc4" + struct.pack("B", len(obj)) + obj) - elif len(obj) <= 2**16-1: - fp.write(b"\xc5" + struct.pack(">H", len(obj)) + obj) - elif len(obj) <= 2**32-1: - fp.write(b"\xc6" + struct.pack(">I", len(obj)) + obj) + obj_len = len(obj) + if obj_len < 2**8: + fp.write(b"\xc4" + struct.pack("B", obj_len) + obj) + elif obj_len < 2**16: + fp.write(b"\xc5" + struct.pack(">H", obj_len) + obj) + elif obj_len < 2**32: + fp.write(b"\xc6" + struct.pack(">I", obj_len) + obj) else: raise UnsupportedTypeException("huge binary string") + def _pack_oldspec_raw(obj, fp, options): - if len(obj) <= 31: - fp.write(struct.pack("B", 0xa0 | len(obj)) + obj) - elif len(obj) <= 2**16-1: - fp.write(b"\xda" + struct.pack(">H", len(obj)) + obj) - elif len(obj) <= 2**32-1: - fp.write(b"\xdb" + struct.pack(">I", len(obj)) + obj) + obj_len = len(obj) + if obj_len < 32: + fp.write(struct.pack("B", 0xa0 | obj_len) + obj) + elif obj_len < 2**16: + fp.write(b"\xda" + struct.pack(">H", obj_len) + obj) + elif obj_len < 2**32: + fp.write(b"\xdb" + struct.pack(">I", obj_len) + obj) else: raise UnsupportedTypeException("huge raw string") + def _pack_ext(obj, fp, options): - if len(obj.data) == 1: + obj_len = len(obj.data) + if obj_len == 1: fp.write(b"\xd4" + struct.pack("B", obj.type & 0xff) + obj.data) - elif len(obj.data) == 2: + elif obj_len == 2: fp.write(b"\xd5" + struct.pack("B", obj.type & 0xff) + obj.data) - elif len(obj.data) == 4: + elif obj_len == 4: fp.write(b"\xd6" + struct.pack("B", obj.type & 0xff) + obj.data) - elif len(obj.data) == 8: + elif obj_len == 8: fp.write(b"\xd7" + struct.pack("B", obj.type & 0xff) + obj.data) - elif len(obj.data) == 16: + elif obj_len == 16: fp.write(b"\xd8" + struct.pack("B", obj.type & 0xff) + obj.data) - elif len(obj.data) <= 2**8-1: - fp.write(b"\xc7" + struct.pack("BB", len(obj.data), obj.type & 0xff) + obj.data) - elif len(obj.data) <= 2**16-1: - fp.write(b"\xc8" + struct.pack(">HB", len(obj.data), obj.type & 0xff) + obj.data) - elif len(obj.data) <= 2**32-1: - fp.write(b"\xc9" + struct.pack(">IB", len(obj.data), obj.type & 0xff) + obj.data) + elif obj_len < 2**8: + fp.write(b"\xc7" + struct.pack("BB", obj_len, obj.type & 0xff) + obj.data) + elif obj_len < 2**16: + fp.write(b"\xc8" + struct.pack(">HB", obj_len, obj.type & 0xff) + obj.data) + elif obj_len < 2**32: + fp.write(b"\xc9" + struct.pack(">IB", obj_len, obj.type & 0xff) + obj.data) else: raise UnsupportedTypeException("huge ext data") + +def _pack_ext_timestamp(obj, fp, options): + if not obj.tzinfo: + # Object is naive datetime, convert to aware date time, + # assuming UTC timezone + delta = obj.replace(tzinfo=_utc_tzinfo) - _epoch + else: + # Object is aware datetime + delta = obj - _epoch + + seconds = delta.seconds + delta.days * 86400 + microseconds = delta.microseconds + + if microseconds == 0 and 0 <= seconds <= 2**32 - 1: + # 32-bit timestamp + fp.write(b"\xd6\xff" + struct.pack(">I", seconds)) + elif 0 <= seconds <= 2**34 - 1: + # 64-bit timestamp + value = ((microseconds * 1000) << 34) | seconds + fp.write(b"\xd7\xff" + struct.pack(">Q", value)) + elif -2**63 <= abs(seconds) <= 2**63 - 1: + # 96-bit timestamp + fp.write(b"\xc7\x0c\xff" + struct.pack(">Iq", microseconds * 1000, seconds)) + else: + raise UnsupportedTypeException("huge timestamp") + + def _pack_array(obj, fp, options): - if len(obj) <= 15: - fp.write(struct.pack("B", 0x90 | len(obj))) - elif len(obj) <= 2**16-1: - fp.write(b"\xdc" + struct.pack(">H", len(obj))) - elif len(obj) <= 2**32-1: - fp.write(b"\xdd" + struct.pack(">I", len(obj))) + obj_len = len(obj) + if obj_len < 16: + fp.write(struct.pack("B", 0x90 | obj_len)) + elif obj_len < 2**16: + fp.write(b"\xdc" + struct.pack(">H", obj_len)) + elif obj_len < 2**32: + fp.write(b"\xdd" + struct.pack(">I", obj_len)) else: raise UnsupportedTypeException("huge array") for e in obj: pack(e, fp, **options) + def _pack_map(obj, fp, options): - if len(obj) <= 15: - fp.write(struct.pack("B", 0x80 | len(obj))) - elif len(obj) <= 2**16-1: - fp.write(b"\xde" + struct.pack(">H", len(obj))) - elif len(obj) <= 2**32-1: - fp.write(b"\xdf" + struct.pack(">I", len(obj))) + obj_len = len(obj) + if obj_len < 16: + fp.write(struct.pack("B", 0x80 | obj_len)) + elif obj_len < 2**16: + fp.write(b"\xde" + struct.pack(">H", obj_len)) + elif obj_len < 2**32: + fp.write(b"\xdf" + struct.pack(">I", obj_len)) else: raise UnsupportedTypeException("huge array") - for k,v in obj.items(): + for k, v in obj.items(): pack(k, fp, **options) pack(v, fp, **options) ######################################## + # Pack for Python 2, with 'unicode' type, 'str' type, and 'long' type def _pack2(obj, fp, **options): """ @@ -341,22 +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 Returns: - None. + None Raises: - UnsupportedType(PackException): + 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 @@ -366,24 +488,31 @@ 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_class_to_type: + try: + _pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options) + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) - elif isinstance(obj, int) or isinstance(obj, 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) - elif isinstance(obj, list) or isinstance(obj, tuple): + elif isinstance(obj, (list, tuple)): _pack_array(obj, fp, options) elif isinstance(obj, dict): _pack_map(obj, fp, options) + elif isinstance(obj, datetime.datetime): + _pack_ext_timestamp(obj, fp, options) elif isinstance(obj, Ext): _pack_ext(obj, fp, options) elif ext_handlers: @@ -392,9 +521,21 @@ def _pack2(obj, fp, **options): if t: _pack_ext(ext_handlers[t](obj), fp, options) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException( + "unsupported type: {:s}".format(str(type(obj)))) + elif _ext_class_to_type: + # Linear search for superclass + t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None) + if t: + try: + _pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options) + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) + else: + raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) else: - 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 def _pack3(obj, fp, **options): @@ -405,22 +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 Returns: - None. + None Raises: - UnsupportedType(PackException): + 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 @@ -430,6 +574,11 @@ 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_class_to_type: + try: + _pack_ext(Ext(_ext_class_to_type[obj.__class__], obj.packb()), fp, options) + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(obj.__class__))) elif isinstance(obj, bool): _pack_boolean(obj, fp, options) elif isinstance(obj, int): @@ -444,10 +593,12 @@ def _pack3(obj, fp, **options): _pack_string(obj, fp, options) elif isinstance(obj, bytes): _pack_binary(obj, fp, options) - elif isinstance(obj, list) or isinstance(obj, tuple): + elif isinstance(obj, (list, tuple)): _pack_array(obj, fp, options) elif isinstance(obj, dict): _pack_map(obj, fp, options) + elif isinstance(obj, datetime.datetime): + _pack_ext_timestamp(obj, fp, options) elif isinstance(obj, Ext): _pack_ext(obj, fp, options) elif ext_handlers: @@ -456,9 +607,22 @@ def _pack3(obj, fp, **options): if t: _pack_ext(ext_handlers[t](obj), fp, options) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException( + "unsupported type: {:s}".format(str(type(obj)))) + elif _ext_class_to_type: + # Linear search for superclass + t = next((t for t in _ext_class_to_type if isinstance(obj, t)), None) + if t: + try: + _pack_ext(Ext(_ext_class_to_type[t], obj.packb()), fp, options) + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of packb()".format(repr(t))) + else: + raise UnsupportedTypeException("unsupported type: {:s}".format(str(type(obj)))) else: - raise UnsupportedTypeException("unsupported type: %s" % str(type(obj))) + raise UnsupportedTypeException( + "unsupported type: {:s}".format(str(type(obj)))) + def _packb2(obj, **options): """ @@ -467,27 +631,31 @@ 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 Returns: - A 'str' containing serialized MessagePack bytes. + str: Serialized MessagePack bytes Raises: - UnsupportedType(PackException): + 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) return fp.getvalue() + def _packb3(obj, **options): """ Serialize a Python object into MessagePack bytes. @@ -495,37 +663,53 @@ 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 Returns: - A 'bytes' containing serialized MessagePack bytes. + bytes: Serialized MessagePack bytes Raises: - UnsupportedType(PackException): + 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) return fp.getvalue() -################################################################################ -### Unpacking -################################################################################ +############################################################################# +# Unpacking +############################################################################# + def _read_except(fp, n): + if n == 0: + return b"" + data = fp.read(n) - if len(data) < n: + if len(data) == 0: raise InsufficientDataException() + + while len(data) < n: + chunk = fp.read(n - len(data)) + if len(chunk) == 0: + raise InsufficientDataException() + + data += chunk + return data + def _unpack_integer(code, fp, options): if (ord(code) & 0xe0) == 0xe0: return struct.unpack("b", code)[0] @@ -547,31 +731,38 @@ 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)) - raise Exception("logic error, not reserved code: 0x%02x" % ord(code)) + raise ReservedCodeException( + "encountered reserved code: 0x{:02x}".format(ord(code))) + raise Exception( + "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): if code == b'\xc2': 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): if code == b'\xca': 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): if (ord(code) & 0xe0) == 0xa0: @@ -583,7 +774,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 @@ -598,6 +789,7 @@ def _unpack_string(code, fp, options): return InvalidString(data) raise InvalidStringException("unpacked string is invalid utf-8") + def _unpack_binary(code, fp, options): if code == b'\xc4': length = struct.unpack("B", _read_except(fp, 1))[0] @@ -606,10 +798,11 @@ 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) + def _unpack_ext(code, fp, options): if code == b'\xd4': length = 1 @@ -628,16 +821,52 @@ 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 = Ext(ord(_read_except(fp, 1)), _read_except(fp, length)) + ext_type = struct.unpack("b", _read_except(fp, 1))[0] + ext_data = _read_except(fp, length) # Unpack with ext handler, if we have one ext_handlers = options.get("ext_handlers") - if ext_handlers and ext.type in ext_handlers: - ext = ext_handlers[ext.type](ext) + if ext_handlers and ext_type in ext_handlers: + return ext_handlers[ext_type](Ext(ext_type, ext_data)) + + # Unpack with ext classes, if type is registered + if ext_type in _ext_type_to_class: + try: + return _ext_type_to_class[ext_type].unpackb(ext_data) + except AttributeError: + raise NotImplementedError("Ext serializable class {:s} is missing implementation of unpackb()".format(repr(_ext_type_to_class[ext_type]))) + + # Timestamp extension + if ext_type == -1: + return _unpack_ext_timestamp(ext_data, options) + + return Ext(ext_type, ext_data) + + +def _unpack_ext_timestamp(ext_data, options): + obj_len = len(ext_data) + if obj_len == 4: + # 32-bit timestamp + seconds = struct.unpack(">I", ext_data)[0] + microseconds = 0 + elif obj_len == 8: + # 64-bit timestamp + value = struct.unpack(">Q", ext_data)[0] + seconds = value & 0x3ffffffff + microseconds = (value >> 34) // 1000 + elif obj_len == 12: + # 96-bit timestamp + seconds = struct.unpack(">q", ext_data[4:12])[0] + microseconds = struct.unpack(">I", ext_data[0:4])[0] // 1000 + else: + raise UnsupportedTimestampException( + "unsupported timestamp with data length {:d}".format(len(ext_data))) + + return _epoch + datetime.timedelta(seconds=seconds, + microseconds=microseconds) - return ext def _unpack_array(code, fp, options): if (ord(code) & 0xf0) == 0x90: @@ -647,15 +876,20 @@ 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))) return [_unpack(fp, options) for i in xrange(length)] + def _deep_list_to_tuple(obj): if isinstance(obj, list): return tuple([_deep_list_to_tuple(e) for e in obj]) return obj + def _unpack_map(code, fp, options): if (ord(code) & 0xf0) == 0x80: length = (ord(code) & ~0xf0) @@ -664,7 +898,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): @@ -674,10 +908,12 @@ def _unpack_map(code, fp, options): if isinstance(k, list): # Attempt to convert list into a hashable tuple k = _deep_list_to_tuple(k) - elif not isinstance(k, collections.Hashable): - raise UnhashableKeyException("encountered unhashable key: %s, %s" % (str(k), str(type(k)))) + elif not isinstance(k, Hashable): + raise UnhashableKeyException( + "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)))) + raise DuplicateKeyException( + "encountered duplicate key: \"{:s}\" ({:s})".format(str(k), str(type(k)))) # Unpack value v = _unpack(fp, options) @@ -685,15 +921,18 @@ def _unpack_map(code, fp, options): try: d[k] = v except TypeError: - raise UnhashableKeyException("encountered unhashable key: %s" % str(k)) + raise UnhashableKeyException( + "encountered unhashable key: \"{:s}\"".format(str(k))) return d + def _unpack(fp, options): code = _read_except(fp, 1) return _unpack_dispatch_table[code](code, fp, options) ######################################## + def _unpack2(fp, **options): """ Deserialize MessagePack bytes into a Python object. @@ -701,24 +940,28 @@ 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): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -728,13 +971,13 @@ 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) + def _unpack3(fp, **options): """ Deserialize MessagePack bytes into a Python object. @@ -742,24 +985,28 @@ 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): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -769,33 +1016,35 @@ 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) + # For Python 2, expects a str object 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: @@ -804,6 +1053,8 @@ def _unpackb2(s, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -813,34 +1064,36 @@ 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'") return _unpack(io.BytesIO(s), options) + # For Python 3, expects a bytes object 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: @@ -849,6 +1102,8 @@ def _unpackb3(s, **options): Insufficient data to unpack the serialized object. InvalidStringException(UnpackException): Invalid UTF-8 string encountered during unpacking. + UnsupportedTimestampException(UnpackException): + Unsupported timestamp format encountered during unpacking. ReservedCodeException(UnpackException): Reserved code encountered during unpacking. UnhashableKeyException(UnpackException): @@ -858,17 +1113,17 @@ 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'") return _unpack(io.BytesIO(s), options) -################################################################################ -### Module Initialization -################################################################################ +############################################################################# +# Module Initialization +############################################################################# + def __init(): global pack @@ -880,18 +1135,40 @@ def __init(): global load global loads global compatibility - global _float_size + global _epoch + global _utc_tzinfo + global _float_precision global _unpack_dispatch_table global xrange # Compatibility mode for handling strings/bytes with the old specification compatibility = False + if sys.version_info[0] == 3: + _utc_tzinfo = datetime.timezone.utc + else: + class UTC(datetime.tzinfo): + ZERO = datetime.timedelta(0) + + def utcoffset(self, dt): + return UTC.ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return UTC.ZERO + + _utc_tzinfo = UTC() + + # Calculate an aware epoch datetime + _epoch = datetime.datetime(1970, 1, 1, tzinfo=_utc_tzinfo) + # Auto-detect system float precision if sys.float_info.mant_dig == 53: - _float_size = 64 + _float_precision = "double" else: - _float_size = 32 + _float_precision = "single" # Map packb and unpackb to the appropriate version if sys.version_info[0] == 3: @@ -918,16 +1195,16 @@ def __init(): _unpack_dispatch_table = {} # Fix uint - for code in range(0, 0x7f+1): + for code in range(0, 0x7f + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer # Fix map - for code in range(0x80, 0x8f+1): + for code in range(0x80, 0x8f + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_map # Fix array - for code in range(0x90, 0x9f+1): + for code in range(0x90, 0x9f + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_array # Fix str - for code in range(0xa0, 0xbf+1): + for code in range(0xa0, 0xbf + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_string # Nil _unpack_dispatch_table[b'\xc0'] = _unpack_nil @@ -937,25 +1214,25 @@ def __init(): _unpack_dispatch_table[b'\xc2'] = _unpack_boolean _unpack_dispatch_table[b'\xc3'] = _unpack_boolean # Bin - for code in range(0xc4, 0xc6+1): + for code in range(0xc4, 0xc6 + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_binary # Ext - for code in range(0xc7, 0xc9+1): + for code in range(0xc7, 0xc9 + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_ext # Float _unpack_dispatch_table[b'\xca'] = _unpack_float _unpack_dispatch_table[b'\xcb'] = _unpack_float # Uint - for code in range(0xcc, 0xcf+1): + for code in range(0xcc, 0xcf + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer # Int - for code in range(0xd0, 0xd3+1): + for code in range(0xd0, 0xd3 + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer # Fixext - for code in range(0xd4, 0xd8+1): + for code in range(0xd4, 0xd8 + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_ext # String - for code in range(0xd9, 0xdb+1): + for code in range(0xd9, 0xdb + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_string # Array _unpack_dispatch_table[b'\xdc'] = _unpack_array @@ -964,7 +1241,8 @@ def __init(): _unpack_dispatch_table[b'\xde'] = _unpack_map _unpack_dispatch_table[b'\xdf'] = _unpack_map # Negative fixint - for code in range(0xe0, 0xff+1): + for code in range(0xe0, 0xff + 1): _unpack_dispatch_table[struct.pack("B", code)] = _unpack_integer + __init() diff --git a/umsgpack/__init__.pyi b/umsgpack/__init__.pyi new file mode 100644 index 0000000..61a8413 --- /dev/null +++ b/umsgpack/__init__.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