diff options
Diffstat (limited to 'third_party/python/cbor2')
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/DESCRIPTION.rst | 26 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/METADATA | 50 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/RECORD | 11 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/WHEEL | 6 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/metadata.json | 1 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2-4.0.1.dist-info/top_level.txt | 1 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2/__init__.py | 3 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2/compat.py | 49 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2/decoder.py | 411 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2/encoder.py | 362 | ||||
-rw-r--r-- | third_party/python/cbor2/cbor2/types.py | 55 |
11 files changed, 975 insertions, 0 deletions
diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/DESCRIPTION.rst b/third_party/python/cbor2/cbor2-4.0.1.dist-info/DESCRIPTION.rst new file mode 100644 index 0000000000..734481b638 --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/DESCRIPTION.rst @@ -0,0 +1,26 @@ +.. image:: https://travis-ci.org/agronholm/cbor2.svg?branch=master + :target: https://travis-ci.org/agronholm/cbor2 + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/cbor2/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/cbor2?branch=master + :alt: Code Coverage + +This library provides encoding and decoding for the Concise Binary Object Representation (CBOR) +(`RFC 7049`_) serialization format. + +There exists another Python CBOR implementation (cbor) which is faster on CPython due to its C +extensions. On PyPy, cbor2 and cbor are almost identical in performance. The other implementation +also lacks documentation and a comprehensive test suite, does not support most standard extension +tags and is known to crash (segfault) when passed a cyclic structure (say, a list containing +itself). + +.. _RFC 7049: https://tools.ietf.org/html/rfc7049 + +Project links +------------- + +* `Documentation <http://cbor2.readthedocs.org/>`_ +* `Source code <https://github.com/agronholm/cbor2>`_ +* `Issue tracker <https://github.com/agronholm/cbor2/issues>`_ + + diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/METADATA b/third_party/python/cbor2/cbor2-4.0.1.dist-info/METADATA new file mode 100644 index 0000000000..c7f42ac60f --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/METADATA @@ -0,0 +1,50 @@ +Metadata-Version: 2.0 +Name: cbor2 +Version: 4.0.1 +Summary: Pure Python CBOR (de)serializer with extensive tag support +Home-page: https://github.com/agronholm/cbor2 +Author: Alex Grönholm +Author-email: alex.gronholm@nextday.fi +License: MIT +Keywords: serialization cbor +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 +Classifier: Programming Language :: Python :: 3.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Provides-Extra: testing +Requires-Dist: pytest; extra == 'testing' +Requires-Dist: pytest-cov; extra == 'testing' + +.. image:: https://travis-ci.org/agronholm/cbor2.svg?branch=master + :target: https://travis-ci.org/agronholm/cbor2 + :alt: Build Status +.. image:: https://coveralls.io/repos/github/agronholm/cbor2/badge.svg?branch=master + :target: https://coveralls.io/github/agronholm/cbor2?branch=master + :alt: Code Coverage + +This library provides encoding and decoding for the Concise Binary Object Representation (CBOR) +(`RFC 7049`_) serialization format. + +There exists another Python CBOR implementation (cbor) which is faster on CPython due to its C +extensions. On PyPy, cbor2 and cbor are almost identical in performance. The other implementation +also lacks documentation and a comprehensive test suite, does not support most standard extension +tags and is known to crash (segfault) when passed a cyclic structure (say, a list containing +itself). + +.. _RFC 7049: https://tools.ietf.org/html/rfc7049 + +Project links +------------- + +* `Documentation <http://cbor2.readthedocs.org/>`_ +* `Source code <https://github.com/agronholm/cbor2>`_ +* `Issue tracker <https://github.com/agronholm/cbor2/issues>`_ + + diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/RECORD b/third_party/python/cbor2/cbor2-4.0.1.dist-info/RECORD new file mode 100644 index 0000000000..e29279b8df --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/RECORD @@ -0,0 +1,11 @@ +cbor2/__init__.py,sha256=Si4l50bD5McrzpgQ6bEmhla2w2U910scs0lCqHzwxOo,239
+cbor2/compat.py,sha256=aBzyMrGwl061zdmlFPQrk4U1rqZQcVNl5ojRsQdG5d0,1033
+cbor2/decoder.py,sha256=6bJMq6fC8RRe5uJFrvKy9T-J3VLYKIkSF9UUmmlYj2A,11936
+cbor2/encoder.py,sha256=OimwLht642jK61Vl2X5FeIv3rHL0hd5yjQ7ajoO2hko,11496
+cbor2/types.py,sha256=I2lpvqktj8Nm8MJtUwdhOYXAUJw-UctYTQlKg0qZ9pc,1302
+cbor2-4.0.1.dist-info/DESCRIPTION.rst,sha256=1Lg57ktrF2XHHyDuGfWtKY5VZd4ydp3-7Ptr27cbWrE,1091
+cbor2-4.0.1.dist-info/METADATA,sha256=h1mC4t8mFZcyJc3cHWJFUf5wUWYVPAqh4Q4DRe0ajQg,1981
+cbor2-4.0.1.dist-info/RECORD,,
+cbor2-4.0.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+cbor2-4.0.1.dist-info/metadata.json,sha256=lHkH6x7w_MNrQqe5ZNu9kihQi3Gg-XOQpYTTElRtKe8,1006
+cbor2-4.0.1.dist-info/top_level.txt,sha256=4Z7JYs5_QM6eqOa2Ew1n_2-uKm2SYl76j2NWTtfCChs,6
diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/WHEEL b/third_party/python/cbor2/cbor2-4.0.1.dist-info/WHEEL new file mode 100644 index 0000000000..8b6dd1b5a8 --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.29.0) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/metadata.json b/third_party/python/cbor2/cbor2-4.0.1.dist-info/metadata.json new file mode 100644 index 0000000000..85d36a4496 --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/metadata.json @@ -0,0 +1 @@ +{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6"], "extensions": {"python.details": {"contacts": [{"email": "alex.gronholm@nextday.fi", "name": "Alex Gr\u00f6nholm", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/agronholm/cbor2"}}}, "extras": ["testing"], "generator": "bdist_wheel (0.29.0)", "keywords": ["serialization", "cbor"], "license": "MIT", "metadata_version": "2.0", "name": "cbor2", "run_requires": [{"extra": "testing", "requires": ["pytest-cov", "pytest"]}], "summary": "Pure Python CBOR (de)serializer with extensive tag support", "version": "4.0.1"}
\ No newline at end of file diff --git a/third_party/python/cbor2/cbor2-4.0.1.dist-info/top_level.txt b/third_party/python/cbor2/cbor2-4.0.1.dist-info/top_level.txt new file mode 100644 index 0000000000..615ca8aeba --- /dev/null +++ b/third_party/python/cbor2/cbor2-4.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +cbor2 diff --git a/third_party/python/cbor2/cbor2/__init__.py b/third_party/python/cbor2/cbor2/__init__.py new file mode 100644 index 0000000000..474841ace4 --- /dev/null +++ b/third_party/python/cbor2/cbor2/__init__.py @@ -0,0 +1,3 @@ +from cbor2.decoder import load, loads, CBORDecoder, CBORDecodeError # noqa +from cbor2.encoder import dump, dumps, CBOREncoder, CBOREncodeError, shareable_encoder # noqa +from cbor2.types import CBORTag, CBORSimpleValue, undefined # noqa diff --git a/third_party/python/cbor2/cbor2/compat.py b/third_party/python/cbor2/cbor2/compat.py new file mode 100644 index 0000000000..983efda59b --- /dev/null +++ b/third_party/python/cbor2/cbor2/compat.py @@ -0,0 +1,49 @@ +import sys + + +if sys.version_info.major < 3: + from datetime import tzinfo, timedelta + + class timezone(tzinfo): + def __init__(self, offset): + self.offset = offset + + def utcoffset(self, dt): + return self.offset + + def dst(self, dt): + return timedelta(0) + + def tzname(self, dt): + return 'UTC+00:00' + + def as_unicode(string): + return string.decode('utf-8') + + def iteritems(self): + return self.iteritems() + + def bytes_from_list(values): + return bytes(bytearray(values)) + + byte_as_integer = ord + timezone.utc = timezone(timedelta(0)) + xrange = xrange # noqa + long = long # noqa + unicode = unicode # noqa +else: + from datetime import timezone + + def byte_as_integer(bytestr): + return bytestr[0] + + def as_unicode(string): + return string + + def iteritems(self): + return self.items() + + xrange = range # noqa + long = int # noqa + unicode = str # noqa + bytes_from_list = bytes diff --git a/third_party/python/cbor2/cbor2/decoder.py b/third_party/python/cbor2/cbor2/decoder.py new file mode 100644 index 0000000000..5833d9e9f4 --- /dev/null +++ b/third_party/python/cbor2/cbor2/decoder.py @@ -0,0 +1,411 @@ +import re +import struct +from datetime import datetime, timedelta +from io import BytesIO + +from cbor2.compat import timezone, xrange, byte_as_integer +from cbor2.types import CBORTag, undefined, break_marker, CBORSimpleValue + +timestamp_re = re.compile(r'^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)' + r'(?:\.(\d+))?(?:Z|([+-]\d\d):(\d\d))$') + + +class CBORDecodeError(Exception): + """Raised when an error occurs deserializing a CBOR datastream.""" + + +def decode_uint(decoder, subtype, shareable_index=None, allow_infinite=False): + # Major tag 0 + if subtype < 24: + return subtype + elif subtype == 24: + return struct.unpack('>B', decoder.read(1))[0] + elif subtype == 25: + return struct.unpack('>H', decoder.read(2))[0] + elif subtype == 26: + return struct.unpack('>L', decoder.read(4))[0] + elif subtype == 27: + return struct.unpack('>Q', decoder.read(8))[0] + elif subtype == 31 and allow_infinite: + return None + else: + raise CBORDecodeError('unknown unsigned integer subtype 0x%x' % subtype) + + +def decode_negint(decoder, subtype, shareable_index=None): + # Major tag 1 + uint = decode_uint(decoder, subtype) + return -uint - 1 + + +def decode_bytestring(decoder, subtype, shareable_index=None): + # Major tag 2 + length = decode_uint(decoder, subtype, allow_infinite=True) + if length is None: + # Indefinite length + buf = bytearray() + while True: + initial_byte = byte_as_integer(decoder.read(1)) + if initial_byte == 255: + return buf + else: + length = decode_uint(decoder, initial_byte & 31) + value = decoder.read(length) + buf.extend(value) + else: + return decoder.read(length) + + +def decode_string(decoder, subtype, shareable_index=None): + # Major tag 3 + return decode_bytestring(decoder, subtype).decode('utf-8') + + +def decode_array(decoder, subtype, shareable_index=None): + # Major tag 4 + items = [] + decoder.set_shareable(shareable_index, items) + length = decode_uint(decoder, subtype, allow_infinite=True) + if length is None: + # Indefinite length + while True: + value = decoder.decode() + if value is break_marker: + break + else: + items.append(value) + else: + for _ in xrange(length): + item = decoder.decode() + items.append(item) + + return items + + +def decode_map(decoder, subtype, shareable_index=None): + # Major tag 5 + dictionary = {} + decoder.set_shareable(shareable_index, dictionary) + length = decode_uint(decoder, subtype, allow_infinite=True) + if length is None: + # Indefinite length + while True: + key = decoder.decode() + if key is break_marker: + break + else: + value = decoder.decode() + dictionary[key] = value + else: + for _ in xrange(length): + key = decoder.decode() + value = decoder.decode() + dictionary[key] = value + + if decoder.object_hook: + return decoder.object_hook(decoder, dictionary) + else: + return dictionary + + +def decode_semantic(decoder, subtype, shareable_index=None): + # Major tag 6 + tagnum = decode_uint(decoder, subtype) + + # Special handling for the "shareable" tag + if tagnum == 28: + shareable_index = decoder._allocate_shareable() + return decoder.decode(shareable_index) + + value = decoder.decode() + semantic_decoder = semantic_decoders.get(tagnum) + if semantic_decoder: + return semantic_decoder(decoder, value, shareable_index) + + tag = CBORTag(tagnum, value) + if decoder.tag_hook: + return decoder.tag_hook(decoder, tag, shareable_index) + else: + return tag + + +def decode_special(decoder, subtype, shareable_index=None): + # Simple value + if subtype < 20: + return CBORSimpleValue(subtype) + + # Major tag 7 + return special_decoders[subtype](decoder) + + +# +# Semantic decoders (major tag 6) +# + +def decode_datetime_string(decoder, value, shareable_index=None): + # Semantic tag 0 + match = timestamp_re.match(value) + if match: + year, month, day, hour, minute, second, micro, offset_h, offset_m = match.groups() + if offset_h: + tz = timezone(timedelta(hours=int(offset_h), minutes=int(offset_m))) + else: + tz = timezone.utc + + return datetime(int(year), int(month), int(day), int(hour), int(minute), int(second), + int(micro or 0), tz) + else: + raise CBORDecodeError('invalid datetime string: {}'.format(value)) + + +def decode_epoch_datetime(decoder, value, shareable_index=None): + # Semantic tag 1 + return datetime.fromtimestamp(value, timezone.utc) + + +def decode_positive_bignum(decoder, value, shareable_index=None): + # Semantic tag 2 + from binascii import hexlify + return int(hexlify(value), 16) + + +def decode_negative_bignum(decoder, value, shareable_index=None): + # Semantic tag 3 + return -decode_positive_bignum(decoder, value) - 1 + + +def decode_fraction(decoder, value, shareable_index=None): + # Semantic tag 4 + from decimal import Decimal + exp = Decimal(value[0]) + mantissa = Decimal(value[1]) + return mantissa * (10 ** exp) + + +def decode_bigfloat(decoder, value, shareable_index=None): + # Semantic tag 5 + from decimal import Decimal + exp = Decimal(value[0]) + mantissa = Decimal(value[1]) + return mantissa * (2 ** exp) + + +def decode_sharedref(decoder, value, shareable_index=None): + # Semantic tag 29 + try: + shared = decoder._shareables[value] + except IndexError: + raise CBORDecodeError('shared reference %d not found' % value) + + if shared is None: + raise CBORDecodeError('shared value %d has not been initialized' % value) + else: + return shared + + +def decode_rational(decoder, value, shareable_index=None): + # Semantic tag 30 + from fractions import Fraction + return Fraction(*value) + + +def decode_regexp(decoder, value, shareable_index=None): + # Semantic tag 35 + return re.compile(value) + + +def decode_mime(decoder, value, shareable_index=None): + # Semantic tag 36 + from email.parser import Parser + return Parser().parsestr(value) + + +def decode_uuid(decoder, value, shareable_index=None): + # Semantic tag 37 + from uuid import UUID + return UUID(bytes=value) + + +# +# Special decoders (major tag 7) +# + +def decode_simple_value(decoder, shareable_index=None): + return CBORSimpleValue(struct.unpack('>B', decoder.read(1))[0]) + + +def decode_float16(decoder, shareable_index=None): + # Code adapted from RFC 7049, appendix D + from math import ldexp + + def decode_single(single): + return struct.unpack("!f", struct.pack("!I", single))[0] + + payload = struct.unpack('>H', decoder.read(2))[0] + value = (payload & 0x7fff) << 13 | (payload & 0x8000) << 16 + if payload & 0x7c00 != 0x7c00: + return ldexp(decode_single(value), 112) + + return decode_single(value | 0x7f800000) + + +def decode_float32(decoder, shareable_index=None): + return struct.unpack('>f', decoder.read(4))[0] + + +def decode_float64(decoder, shareable_index=None): + return struct.unpack('>d', decoder.read(8))[0] + + +major_decoders = { + 0: decode_uint, + 1: decode_negint, + 2: decode_bytestring, + 3: decode_string, + 4: decode_array, + 5: decode_map, + 6: decode_semantic, + 7: decode_special +} + +special_decoders = { + 20: lambda self: False, + 21: lambda self: True, + 22: lambda self: None, + 23: lambda self: undefined, + 24: decode_simple_value, + 25: decode_float16, + 26: decode_float32, + 27: decode_float64, + 31: lambda self: break_marker +} + +semantic_decoders = { + 0: decode_datetime_string, + 1: decode_epoch_datetime, + 2: decode_positive_bignum, + 3: decode_negative_bignum, + 4: decode_fraction, + 5: decode_bigfloat, + 29: decode_sharedref, + 30: decode_rational, + 35: decode_regexp, + 36: decode_mime, + 37: decode_uuid +} + + +class CBORDecoder(object): + """ + Deserializes a CBOR encoded byte stream. + + :param tag_hook: Callable that takes 3 arguments: the decoder instance, the + :class:`~cbor2.types.CBORTag` and the shareable index for the resulting object, if any. + This callback is called for any tags for which there is no built-in decoder. + The return value is substituted for the CBORTag object in the deserialized output. + :param object_hook: Callable that takes 2 arguments: the decoder instance and the dictionary. + This callback is called for each deserialized :class:`dict` object. + The return value is substituted for the dict in the deserialized output. + """ + + __slots__ = ('fp', 'tag_hook', 'object_hook', '_shareables') + + def __init__(self, fp, tag_hook=None, object_hook=None): + self.fp = fp + self.tag_hook = tag_hook + self.object_hook = object_hook + self._shareables = [] + + def _allocate_shareable(self): + self._shareables.append(None) + return len(self._shareables) - 1 + + def set_shareable(self, index, value): + """ + Set the shareable value for the last encountered shared value marker, if any. + + If the given index is ``None``, nothing is done. + + :param index: the value of the ``shared_index`` argument to the decoder + :param value: the shared value + + """ + if index is not None: + self._shareables[index] = value + + def read(self, amount): + """ + Read bytes from the data stream. + + :param int amount: the number of bytes to read + + """ + data = self.fp.read(amount) + if len(data) < amount: + raise CBORDecodeError('premature end of stream (expected to read {} bytes, got {} ' + 'instead)'.format(amount, len(data))) + + return data + + def decode(self, shareable_index=None): + """ + Decode the next value from the stream. + + :raises CBORDecodeError: if there is any problem decoding the stream + + """ + try: + initial_byte = byte_as_integer(self.fp.read(1)) + major_type = initial_byte >> 5 + subtype = initial_byte & 31 + except Exception as e: + raise CBORDecodeError('error reading major type at index {}: {}' + .format(self.fp.tell(), e)) + + decoder = major_decoders[major_type] + try: + return decoder(self, subtype, shareable_index) + except CBORDecodeError: + raise + except Exception as e: + raise CBORDecodeError('error decoding value at index {}: {}'.format(self.fp.tell(), e)) + + def decode_from_bytes(self, buf): + """ + Wrap the given bytestring as a file and call :meth:`decode` with it as the argument. + + This method was intended to be used from the ``tag_hook`` hook when an object needs to be + decoded separately from the rest but while still taking advantage of the shared value + registry. + + """ + old_fp = self.fp + self.fp = BytesIO(buf) + retval = self.decode() + self.fp = old_fp + return retval + + +def loads(payload, **kwargs): + """ + Deserialize an object from a bytestring. + + :param bytes payload: the bytestring to serialize + :param kwargs: keyword arguments passed to :class:`~.CBORDecoder` + :return: the deserialized object + + """ + fp = BytesIO(payload) + return CBORDecoder(fp, **kwargs).decode() + + +def load(fp, **kwargs): + """ + Deserialize an object from an open file. + + :param fp: the input file (any file-like object) + :param kwargs: keyword arguments passed to :class:`~.CBORDecoder` + :return: the deserialized object + + """ + return CBORDecoder(fp, **kwargs).decode() diff --git a/third_party/python/cbor2/cbor2/encoder.py b/third_party/python/cbor2/cbor2/encoder.py new file mode 100644 index 0000000000..adcb2722e5 --- /dev/null +++ b/third_party/python/cbor2/cbor2/encoder.py @@ -0,0 +1,362 @@ +import re +import struct +from collections import OrderedDict, defaultdict +from contextlib import contextmanager +from functools import wraps +from datetime import datetime, date, time +from io import BytesIO + +from cbor2.compat import iteritems, timezone, long, unicode, as_unicode, bytes_from_list +from cbor2.types import CBORTag, undefined, CBORSimpleValue + + +class CBOREncodeError(Exception): + """Raised when an error occurs while serializing an object into a CBOR datastream.""" + + +def shareable_encoder(func): + """ + Wrap the given encoder function to gracefully handle cyclic data structures. + + If value sharing is enabled, this marks the given value shared in the datastream on the + first call. If the value has already been passed to this method, a reference marker is + instead written to the data stream and the wrapped function is not called. + + If value sharing is disabled, only infinite recursion protection is done. + + """ + @wraps(func) + def wrapper(encoder, value, *args, **kwargs): + value_id = id(value) + container, container_index = encoder._shared_containers.get(value_id, (None, None)) + if encoder.value_sharing: + if container is value: + # Generate a reference to the previous index instead of encoding this again + encoder.write(encode_length(0xd8, 0x1d)) + encode_int(encoder, container_index) + else: + # Mark the container as shareable + encoder._shared_containers[value_id] = (value, len(encoder._shared_containers)) + encoder.write(encode_length(0xd8, 0x1c)) + func(encoder, value, *args, **kwargs) + else: + if container is value: + raise CBOREncodeError('cyclic data structure detected but value sharing is ' + 'disabled') + else: + encoder._shared_containers[value_id] = (value, None) + func(encoder, value, *args, **kwargs) + del encoder._shared_containers[value_id] + + return wrapper + + +def encode_length(major_tag, length): + if length < 24: + return struct.pack('>B', major_tag | length) + elif length < 256: + return struct.pack('>BB', major_tag | 24, length) + elif length < 65536: + return struct.pack('>BH', major_tag | 25, length) + elif length < 4294967296: + return struct.pack('>BL', major_tag | 26, length) + else: + return struct.pack('>BQ', major_tag | 27, length) + + +def encode_int(encoder, value): + # Big integers (2 ** 64 and over) + if value >= 18446744073709551616 or value < -18446744073709551616: + if value >= 0: + major_type = 0x02 + else: + major_type = 0x03 + value = -value - 1 + + values = [] + while value > 0: + value, remainder = divmod(value, 256) + values.insert(0, remainder) + + payload = bytes_from_list(values) + encode_semantic(encoder, CBORTag(major_type, payload)) + elif value >= 0: + encoder.write(encode_length(0, value)) + else: + encoder.write(encode_length(0x20, abs(value) - 1)) + + +def encode_bytestring(encoder, value): + encoder.write(encode_length(0x40, len(value)) + value) + + +def encode_bytearray(encoder, value): + encode_bytestring(encoder, bytes(value)) + + +def encode_string(encoder, value): + encoded = value.encode('utf-8') + encoder.write(encode_length(0x60, len(encoded)) + encoded) + + +@shareable_encoder +def encode_array(encoder, value): + encoder.write(encode_length(0x80, len(value))) + for item in value: + encoder.encode(item) + + +@shareable_encoder +def encode_map(encoder, value): + encoder.write(encode_length(0xa0, len(value))) + for key, val in iteritems(value): + encoder.encode(key) + encoder.encode(val) + + +def encode_semantic(encoder, value): + encoder.write(encode_length(0xc0, value.tag)) + encoder.encode(value.value) + + +# +# Semantic decoders (major tag 6) +# + +def encode_datetime(encoder, value): + # Semantic tag 0 + if not value.tzinfo: + if encoder.timezone: + value = value.replace(tzinfo=encoder.timezone) + else: + raise CBOREncodeError( + 'naive datetime encountered and no default timezone has been set') + + if encoder.datetime_as_timestamp: + from calendar import timegm + timestamp = timegm(value.utctimetuple()) + value.microsecond // 1000000 + encode_semantic(encoder, CBORTag(1, timestamp)) + else: + datestring = as_unicode(value.isoformat().replace('+00:00', 'Z')) + encode_semantic(encoder, CBORTag(0, datestring)) + + +def encode_date(encoder, value): + value = datetime.combine(value, time()).replace(tzinfo=timezone.utc) + encode_datetime(encoder, value) + + +def encode_decimal(encoder, value): + # Semantic tag 4 + if value.is_nan(): + encoder.write(b'\xf9\x7e\x00') + elif value.is_infinite(): + encoder.write(b'\xf9\x7c\x00' if value > 0 else b'\xf9\xfc\x00') + else: + dt = value.as_tuple() + mantissa = sum(d * 10 ** i for i, d in enumerate(reversed(dt.digits))) + with encoder.disable_value_sharing(): + encode_semantic(encoder, CBORTag(4, [dt.exponent, mantissa])) + + +def encode_rational(encoder, value): + # Semantic tag 30 + with encoder.disable_value_sharing(): + encode_semantic(encoder, CBORTag(30, [value.numerator, value.denominator])) + + +def encode_regexp(encoder, value): + # Semantic tag 35 + encode_semantic(encoder, CBORTag(35, as_unicode(value.pattern))) + + +def encode_mime(encoder, value): + # Semantic tag 36 + encode_semantic(encoder, CBORTag(36, as_unicode(value.as_string()))) + + +def encode_uuid(encoder, value): + # Semantic tag 37 + encode_semantic(encoder, CBORTag(37, value.bytes)) + + +# +# Special encoders (major tag 7) +# + +def encode_simple_value(encoder, value): + if value.value < 20: + encoder.write(struct.pack('>B', 0xe0 | value.value)) + else: + encoder.write(struct.pack('>BB', 0xf8, value.value)) + + +def encode_float(encoder, value): + # Handle special values efficiently + import math + if math.isnan(value): + encoder.write(b'\xf9\x7e\x00') + elif math.isinf(value): + encoder.write(b'\xf9\x7c\x00' if value > 0 else b'\xf9\xfc\x00') + else: + encoder.write(struct.pack('>Bd', 0xfb, value)) + + +def encode_boolean(encoder, value): + encoder.write(b'\xf5' if value else b'\xf4') + + +def encode_none(encoder, value): + encoder.write(b'\xf6') + + +def encode_undefined(encoder, value): + encoder.write(b'\xf7') + + +default_encoders = OrderedDict([ + (bytes, encode_bytestring), + (bytearray, encode_bytearray), + (unicode, encode_string), + (int, encode_int), + (long, encode_int), + (float, encode_float), + (('decimal', 'Decimal'), encode_decimal), + (bool, encode_boolean), + (type(None), encode_none), + (tuple, encode_array), + (list, encode_array), + (dict, encode_map), + (defaultdict, encode_map), + (OrderedDict, encode_map), + (type(undefined), encode_undefined), + (datetime, encode_datetime), + (date, encode_date), + (type(re.compile('')), encode_regexp), + (('fractions', 'Fraction'), encode_rational), + (('email.message', 'Message'), encode_mime), + (('uuid', 'UUID'), encode_uuid), + (CBORSimpleValue, encode_simple_value), + (CBORTag, encode_semantic) +]) + + +class CBOREncoder(object): + """ + Serializes objects to a byte stream using Concise Binary Object Representation. + + :param datetime_as_timestamp: set to ``True`` to serialize datetimes as UNIX timestamps + (this makes datetimes more concise on the wire but loses the time zone information) + :param datetime.tzinfo timezone: the default timezone to use for serializing naive datetimes + :param value_sharing: if ``True``, allows more efficient serializing of repeated values and, + more importantly, cyclic data structures, at the cost of extra line overhead + :param default: a callable that is called by the encoder with three arguments + (encoder, value, file object) when no suitable encoder has been found, and should use the + methods on the encoder to encode any objects it wants to add to the data stream + """ + + __slots__ = ('fp', 'datetime_as_timestamp', 'timezone', 'default', 'value_sharing', + 'json_compatible', '_shared_containers', '_encoders') + + def __init__(self, fp, datetime_as_timestamp=False, timezone=None, value_sharing=False, + default=None): + self.fp = fp + self.datetime_as_timestamp = datetime_as_timestamp + self.timezone = timezone + self.value_sharing = value_sharing + self.default = default + self._shared_containers = {} # indexes used for value sharing + self._encoders = default_encoders.copy() + + def _find_encoder(self, obj_type): + from sys import modules + + for type_, enc in list(iteritems(self._encoders)): + if type(type_) is tuple: + modname, typename = type_ + imported_type = getattr(modules.get(modname), typename, None) + if imported_type is not None: + del self._encoders[type_] + self._encoders[imported_type] = enc + type_ = imported_type + else: # pragma: nocover + continue + + if issubclass(obj_type, type_): + self._encoders[obj_type] = enc + return enc + + return None + + @contextmanager + def disable_value_sharing(self): + """Disable value sharing in the encoder for the duration of the context block.""" + old_value_sharing = self.value_sharing + self.value_sharing = False + yield + self.value_sharing = old_value_sharing + + def write(self, data): + """ + Write bytes to the data stream. + + :param data: the bytes to write + + """ + self.fp.write(data) + + def encode(self, obj): + """ + Encode the given object using CBOR. + + :param obj: the object to encode + + """ + obj_type = obj.__class__ + encoder = self._encoders.get(obj_type) or self._find_encoder(obj_type) or self.default + if not encoder: + raise CBOREncodeError('cannot serialize type %s' % obj_type.__name__) + + encoder(self, obj) + + def encode_to_bytes(self, obj): + """ + Encode the given object to a byte buffer and return its value as bytes. + + This method was intended to be used from the ``default`` hook when an object needs to be + encoded separately from the rest but while still taking advantage of the shared value + registry. + + """ + old_fp = self.fp + self.fp = fp = BytesIO() + self.encode(obj) + self.fp = old_fp + return fp.getvalue() + + +def dumps(obj, **kwargs): + """ + Serialize an object to a bytestring. + + :param obj: the object to serialize + :param kwargs: keyword arguments passed to :class:`~.CBOREncoder` + :return: the serialized output + :rtype: bytes + + """ + fp = BytesIO() + dump(obj, fp, **kwargs) + return fp.getvalue() + + +def dump(obj, fp, **kwargs): + """ + Serialize an object to a file. + + :param obj: the object to serialize + :param fp: a file-like object + :param kwargs: keyword arguments passed to :class:`~.CBOREncoder` + + """ + CBOREncoder(fp, **kwargs).encode(obj) diff --git a/third_party/python/cbor2/cbor2/types.py b/third_party/python/cbor2/cbor2/types.py new file mode 100644 index 0000000000..1d3afb0601 --- /dev/null +++ b/third_party/python/cbor2/cbor2/types.py @@ -0,0 +1,55 @@ +class CBORTag(object): + """ + Represents a CBOR semantic tag. + + :param int tag: tag number + :param value: encapsulated value (any object) + """ + + __slots__ = 'tag', 'value' + + def __init__(self, tag, value): + self.tag = tag + self.value = value + + def __eq__(self, other): + if isinstance(other, CBORTag): + return self.tag == other.tag and self.value == other.value + return NotImplemented + + def __repr__(self): + return 'CBORTag({self.tag}, {self.value!r})'.format(self=self) + + +class CBORSimpleValue(object): + """ + Represents a CBOR "simple value". + + :param int value: the value (0-255) + """ + + __slots__ = 'value' + + def __init__(self, value): + if value < 0 or value > 255: + raise TypeError('simple value too big') + self.value = value + + def __eq__(self, other): + if isinstance(other, CBORSimpleValue): + return self.value == other.value + elif isinstance(other, int): + return self.value == other + return NotImplemented + + def __repr__(self): + return 'CBORSimpleValue({self.value})'.format(self=self) + + +class UndefinedType(object): + __slots__ = () + + +#: Represents the "undefined" value. +undefined = UndefinedType() +break_marker = object() |