##// END OF EJS Templates
thirdparty: vendor cbor2 python library...
Pulkit Goyal -
r37144:4bd73a95 default
parent child Browse files
Show More
@@ -0,0 +1,58 b''
1 language: python
2 sudo: false
3
4 stages:
5 - name: test
6 - name: deploy to pypi
7 if: type = push AND tag =~ ^\d+\.\d+\.\d+
8
9 jobs:
10 fast_finish: true
11 include:
12 - env: TOXENV=flake8
13
14 - env: TOXENV=pypy
15 python: pypy3
16
17 - env: TOXENV=pypy3
18 python: pypy3
19
20 - env: TOXENV=py27
21 python: "2.7"
22 after_success: &after_success
23 - pip install coveralls
24 - coveralls
25
26 - env: TOXENV=py33
27 python: "3.3"
28 after_success: *after_success
29
30 - env: TOXENV=py34
31 python: "3.4"
32 after_success: *after_success
33
34 - env: TOXENV=py35
35 python: "3.5"
36 after_success: *after_success
37
38 - env: TOXENV=py36
39 python: "3.6"
40 after_success: *after_success
41
42 - stage: deploy to pypi
43 install: pip install "setuptools >= 36.2.7"
44 script: skip
45 deploy:
46 provider: pypi
47 user: agronholm
48 password:
49 secure: QZ5qoxsrzns/b27adWNzh/OAJp86yRuxTyAFhvas/pbkiALdlT/+PGyhJBnpe+7WBTWnkIXl+YU//voJ0btf6DJcWwgRavMsy22LJJPkvvK+2DHiZ//DbpLbqKWc74y4moce29BCajFTm9JkVwcL2dgN9WuZt+Tay0efcP4sESLxo5lIGdlaQbu+9zVs61Z4Ov+yyEMO/j3LeKshNmUq+84CveQWMiXndXBfJX5TWwjahmUNDp5fMctJxr4fqgL4HCTVQhU79dPc00yDEGS45QkpP8JDrF1DQvU5Ht4COz/Lvzt11pwsAvws2ddclqBUCQsGaWvEWH5rxZTYx/MaMVdTctaUVNoT0wnFUsXXZkomQV0x8vb5RtRLDrKwXosXlSEqnRyiKhdgHGoswHvB7XF5BtQ5RmydRX77pwEGmFd3lqRif2bos0MEeOJA8Xds0TGOKO4PyokBnj/a0tjT2LEVxObmTT6grz5QPXi386AWgxbNl0Lp7cnkSpCqC1hEHVqrDlbtu7uvfGwwe/sYlEcQ07PNCvFoR2GXJawbeHmJRfz+KXjffrt2yCzc671FL1goUysHKdBCppvUInI8FCMQpVWEh5MmQJKB4IpDrhqfo0VS+NNZgZ8lFStq27Pmwqf1HUTGlaDi9VQ0Vo7tW5j4JbD/JvOQSb3j9DjUFps=
50 distributions: sdist bdist_wheel
51 on:
52 tags: true
53
54 install:
55 - pip install "setuptools >= 36.2.7"
56 - pip install tox
57
58 script: tox
@@ -0,0 +1,19 b''
1 This is the MIT license: http://www.opensource.org/licenses/mit-license.php
2
3 Copyright (c) Alex GrΓΆnholm
4
5 Permission is hereby granted, free of charge, to any person obtaining a copy of this
6 software and associated documentation files (the "Software"), to deal in the Software
7 without restriction, including without limitation the rights to use, copy, modify, merge,
8 publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
9 to whom the Software is furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all copies or
12 substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15 INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16 PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17 FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,24 b''
1 .. image:: https://travis-ci.org/agronholm/cbor2.svg?branch=master
2 :target: https://travis-ci.org/agronholm/cbor2
3 :alt: Build Status
4 .. image:: https://coveralls.io/repos/github/agronholm/cbor2/badge.svg?branch=master
5 :target: https://coveralls.io/github/agronholm/cbor2?branch=master
6 :alt: Code Coverage
7
8 This library provides encoding and decoding for the Concise Binary Object Representation (CBOR)
9 (`RFC 7049`_) serialization format.
10
11 There exists another Python CBOR implementation (cbor) which is faster on CPython due to its C
12 extensions. On PyPy, cbor2 and cbor are almost identical in performance. The other implementation
13 also lacks documentation and a comprehensive test suite, does not support most standard extension
14 tags and is known to crash (segfault) when passed a cyclic structure (say, a list containing
15 itself).
16
17 .. _RFC 7049: https://tools.ietf.org/html/rfc7049
18
19 Project links
20 -------------
21
22 * `Documentation <http://cbor2.readthedocs.org/>`_
23 * `Source code <https://github.com/agronholm/cbor2>`_
24 * `Issue tracker <https://github.com/agronholm/cbor2/issues>`_
@@ -0,0 +1,3 b''
1 from .decoder import load, loads, CBORDecoder, CBORDecodeError # noqa
2 from .encoder import dump, dumps, CBOREncoder, CBOREncodeError, shareable_encoder # noqa
3 from .types import CBORTag, CBORSimpleValue, undefined # noqa
@@ -0,0 +1,101 b''
1 from math import ldexp
2 import struct
3 import sys
4
5
6 if sys.version_info.major < 3:
7 from datetime import tzinfo, timedelta
8
9 class timezone(tzinfo):
10 def __init__(self, offset):
11 self.offset = offset
12
13 def utcoffset(self, dt):
14 return self.offset
15
16 def dst(self, dt):
17 return timedelta(0)
18
19 def tzname(self, dt):
20 return 'UTC+00:00'
21
22 def as_unicode(string):
23 return string.decode('utf-8')
24
25 def iteritems(self):
26 return self.iteritems()
27
28 def bytes_from_list(values):
29 return bytes(bytearray(values))
30
31 byte_as_integer = ord
32 timezone.utc = timezone(timedelta(0))
33 xrange = xrange # noqa: F821
34 long = long # noqa: F821
35 unicode = unicode # noqa: F821
36 else:
37 from datetime import timezone
38
39 def byte_as_integer(bytestr):
40 return bytestr[0]
41
42 def as_unicode(string):
43 return string
44
45 def iteritems(self):
46 return self.items()
47
48 xrange = range
49 long = int
50 unicode = str
51 bytes_from_list = bytes
52
53
54 if sys.version_info.major >= 3 and sys.version_info.minor >= 6:
55 # Python 3.6 added 16 bit floating point to struct
56
57 def pack_float16(value):
58 try:
59 return struct.pack('>Be', 0xf9, value)
60 except OverflowError:
61 return False
62
63 def unpack_float16(payload):
64 return struct.unpack('>e', payload)[0]
65 else:
66 def pack_float16(value):
67 # Based on node-cbor by hildjj
68 # which was based in turn on Carsten Borman's cn-cbor
69 u32 = struct.pack('>f', value)
70 u = struct.unpack('>I', u32)[0]
71
72 if u & 0x1FFF != 0:
73 return False
74
75 s16 = (u >> 16) & 0x8000
76 exponent = (u >> 23) & 0xff
77 mantissa = u & 0x7fffff
78
79 if 113 <= exponent <= 142:
80 s16 += ((exponent - 112) << 10) + (mantissa >> 13)
81 elif 103 <= exponent < 113:
82 if mantissa & ((1 << (126 - exponent)) - 1):
83 return False
84
85 s16 += ((mantissa + 0x800000) >> (126 - exponent))
86 else:
87 return False
88
89 return struct.pack('>BH', 0xf9, s16)
90
91 def unpack_float16(payload):
92 # Code adapted from RFC 7049, appendix D
93 def decode_single(single):
94 return struct.unpack("!f", struct.pack("!I", single))[0]
95
96 payload = struct.unpack('>H', payload)[0]
97 value = (payload & 0x7fff) << 13 | (payload & 0x8000) << 16
98 if payload & 0x7c00 != 0x7c00:
99 return ldexp(decode_single(value), 112)
100
101 return decode_single(value | 0x7f800000)
@@ -0,0 +1,407 b''
1 import re
2 import struct
3 from datetime import datetime, timedelta
4 from io import BytesIO
5
6 from .compat import timezone, xrange, byte_as_integer, unpack_float16
7 from .types import CBORTag, undefined, break_marker, CBORSimpleValue
8
9 timestamp_re = re.compile(r'^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)'
10 r'(?:\.(\d+))?(?:Z|([+-]\d\d):(\d\d))$')
11
12
13 class CBORDecodeError(Exception):
14 """Raised when an error occurs deserializing a CBOR datastream."""
15
16
17 def decode_uint(decoder, subtype, shareable_index=None, allow_indefinite=False):
18 # Major tag 0
19 if subtype < 24:
20 return subtype
21 elif subtype == 24:
22 return struct.unpack('>B', decoder.read(1))[0]
23 elif subtype == 25:
24 return struct.unpack('>H', decoder.read(2))[0]
25 elif subtype == 26:
26 return struct.unpack('>L', decoder.read(4))[0]
27 elif subtype == 27:
28 return struct.unpack('>Q', decoder.read(8))[0]
29 elif subtype == 31 and allow_indefinite:
30 return None
31 else:
32 raise CBORDecodeError('unknown unsigned integer subtype 0x%x' % subtype)
33
34
35 def decode_negint(decoder, subtype, shareable_index=None):
36 # Major tag 1
37 uint = decode_uint(decoder, subtype)
38 return -uint - 1
39
40
41 def decode_bytestring(decoder, subtype, shareable_index=None):
42 # Major tag 2
43 length = decode_uint(decoder, subtype, allow_indefinite=True)
44 if length is None:
45 # Indefinite length
46 buf = bytearray()
47 while True:
48 initial_byte = byte_as_integer(decoder.read(1))
49 if initial_byte == 255:
50 return buf
51 else:
52 length = decode_uint(decoder, initial_byte & 31)
53 value = decoder.read(length)
54 buf.extend(value)
55 else:
56 return decoder.read(length)
57
58
59 def decode_string(decoder, subtype, shareable_index=None):
60 # Major tag 3
61 return decode_bytestring(decoder, subtype).decode('utf-8')
62
63
64 def decode_array(decoder, subtype, shareable_index=None):
65 # Major tag 4
66 items = []
67 decoder.set_shareable(shareable_index, items)
68 length = decode_uint(decoder, subtype, allow_indefinite=True)
69 if length is None:
70 # Indefinite length
71 while True:
72 value = decoder.decode()
73 if value is break_marker:
74 break
75 else:
76 items.append(value)
77 else:
78 for _ in xrange(length):
79 item = decoder.decode()
80 items.append(item)
81
82 return items
83
84
85 def decode_map(decoder, subtype, shareable_index=None):
86 # Major tag 5
87 dictionary = {}
88 decoder.set_shareable(shareable_index, dictionary)
89 length = decode_uint(decoder, subtype, allow_indefinite=True)
90 if length is None:
91 # Indefinite length
92 while True:
93 key = decoder.decode()
94 if key is break_marker:
95 break
96 else:
97 value = decoder.decode()
98 dictionary[key] = value
99 else:
100 for _ in xrange(length):
101 key = decoder.decode()
102 value = decoder.decode()
103 dictionary[key] = value
104
105 if decoder.object_hook:
106 return decoder.object_hook(decoder, dictionary)
107 else:
108 return dictionary
109
110
111 def decode_semantic(decoder, subtype, shareable_index=None):
112 # Major tag 6
113 tagnum = decode_uint(decoder, subtype)
114
115 # Special handling for the "shareable" tag
116 if tagnum == 28:
117 shareable_index = decoder._allocate_shareable()
118 return decoder.decode(shareable_index)
119
120 value = decoder.decode()
121 semantic_decoder = semantic_decoders.get(tagnum)
122 if semantic_decoder:
123 return semantic_decoder(decoder, value, shareable_index)
124
125 tag = CBORTag(tagnum, value)
126 if decoder.tag_hook:
127 return decoder.tag_hook(decoder, tag, shareable_index)
128 else:
129 return tag
130
131
132 def decode_special(decoder, subtype, shareable_index=None):
133 # Simple value
134 if subtype < 20:
135 return CBORSimpleValue(subtype)
136
137 # Major tag 7
138 return special_decoders[subtype](decoder)
139
140
141 #
142 # Semantic decoders (major tag 6)
143 #
144
145 def decode_datetime_string(decoder, value, shareable_index=None):
146 # Semantic tag 0
147 match = timestamp_re.match(value)
148 if match:
149 year, month, day, hour, minute, second, micro, offset_h, offset_m = match.groups()
150 if offset_h:
151 tz = timezone(timedelta(hours=int(offset_h), minutes=int(offset_m)))
152 else:
153 tz = timezone.utc
154
155 return datetime(int(year), int(month), int(day), int(hour), int(minute), int(second),
156 int(micro or 0), tz)
157 else:
158 raise CBORDecodeError('invalid datetime string: {}'.format(value))
159
160
161 def decode_epoch_datetime(decoder, value, shareable_index=None):
162 # Semantic tag 1
163 return datetime.fromtimestamp(value, timezone.utc)
164
165
166 def decode_positive_bignum(decoder, value, shareable_index=None):
167 # Semantic tag 2
168 from binascii import hexlify
169 return int(hexlify(value), 16)
170
171
172 def decode_negative_bignum(decoder, value, shareable_index=None):
173 # Semantic tag 3
174 return -decode_positive_bignum(decoder, value) - 1
175
176
177 def decode_fraction(decoder, value, shareable_index=None):
178 # Semantic tag 4
179 from decimal import Decimal
180 exp = Decimal(value[0])
181 mantissa = Decimal(value[1])
182 return mantissa * (10 ** exp)
183
184
185 def decode_bigfloat(decoder, value, shareable_index=None):
186 # Semantic tag 5
187 from decimal import Decimal
188 exp = Decimal(value[0])
189 mantissa = Decimal(value[1])
190 return mantissa * (2 ** exp)
191
192
193 def decode_sharedref(decoder, value, shareable_index=None):
194 # Semantic tag 29
195 try:
196 shared = decoder._shareables[value]
197 except IndexError:
198 raise CBORDecodeError('shared reference %d not found' % value)
199
200 if shared is None:
201 raise CBORDecodeError('shared value %d has not been initialized' % value)
202 else:
203 return shared
204
205
206 def decode_rational(decoder, value, shareable_index=None):
207 # Semantic tag 30
208 from fractions import Fraction
209 return Fraction(*value)
210
211
212 def decode_regexp(decoder, value, shareable_index=None):
213 # Semantic tag 35
214 return re.compile(value)
215
216
217 def decode_mime(decoder, value, shareable_index=None):
218 # Semantic tag 36
219 from email.parser import Parser
220 return Parser().parsestr(value)
221
222
223 def decode_uuid(decoder, value, shareable_index=None):
224 # Semantic tag 37
225 from uuid import UUID
226 return UUID(bytes=value)
227
228
229 def decode_set(decoder, value, shareable_index=None):
230 # Semantic tag 258
231 return set(value)
232
233
234 #
235 # Special decoders (major tag 7)
236 #
237
238 def decode_simple_value(decoder, shareable_index=None):
239 return CBORSimpleValue(struct.unpack('>B', decoder.read(1))[0])
240
241
242 def decode_float16(decoder, shareable_index=None):
243 payload = decoder.read(2)
244 return unpack_float16(payload)
245
246
247 def decode_float32(decoder, shareable_index=None):
248 return struct.unpack('>f', decoder.read(4))[0]
249
250
251 def decode_float64(decoder, shareable_index=None):
252 return struct.unpack('>d', decoder.read(8))[0]
253
254
255 major_decoders = {
256 0: decode_uint,
257 1: decode_negint,
258 2: decode_bytestring,
259 3: decode_string,
260 4: decode_array,
261 5: decode_map,
262 6: decode_semantic,
263 7: decode_special
264 }
265
266 special_decoders = {
267 20: lambda self: False,
268 21: lambda self: True,
269 22: lambda self: None,
270 23: lambda self: undefined,
271 24: decode_simple_value,
272 25: decode_float16,
273 26: decode_float32,
274 27: decode_float64,
275 31: lambda self: break_marker
276 }
277
278 semantic_decoders = {
279 0: decode_datetime_string,
280 1: decode_epoch_datetime,
281 2: decode_positive_bignum,
282 3: decode_negative_bignum,
283 4: decode_fraction,
284 5: decode_bigfloat,
285 29: decode_sharedref,
286 30: decode_rational,
287 35: decode_regexp,
288 36: decode_mime,
289 37: decode_uuid,
290 258: decode_set
291 }
292
293
294 class CBORDecoder(object):
295 """
296 Deserializes a CBOR encoded byte stream.
297
298 :param tag_hook: Callable that takes 3 arguments: the decoder instance, the
299 :class:`~cbor2.types.CBORTag` and the shareable index for the resulting object, if any.
300 This callback is called for any tags for which there is no built-in decoder.
301 The return value is substituted for the CBORTag object in the deserialized output.
302 :param object_hook: Callable that takes 2 arguments: the decoder instance and the dictionary.
303 This callback is called for each deserialized :class:`dict` object.
304 The return value is substituted for the dict in the deserialized output.
305 """
306
307 __slots__ = ('fp', 'tag_hook', 'object_hook', '_shareables')
308
309 def __init__(self, fp, tag_hook=None, object_hook=None):
310 self.fp = fp
311 self.tag_hook = tag_hook
312 self.object_hook = object_hook
313 self._shareables = []
314
315 def _allocate_shareable(self):
316 self._shareables.append(None)
317 return len(self._shareables) - 1
318
319 def set_shareable(self, index, value):
320 """
321 Set the shareable value for the last encountered shared value marker, if any.
322
323 If the given index is ``None``, nothing is done.
324
325 :param index: the value of the ``shared_index`` argument to the decoder
326 :param value: the shared value
327
328 """
329 if index is not None:
330 self._shareables[index] = value
331
332 def read(self, amount):
333 """
334 Read bytes from the data stream.
335
336 :param int amount: the number of bytes to read
337
338 """
339 data = self.fp.read(amount)
340 if len(data) < amount:
341 raise CBORDecodeError('premature end of stream (expected to read {} bytes, got {} '
342 'instead)'.format(amount, len(data)))
343
344 return data
345
346 def decode(self, shareable_index=None):
347 """
348 Decode the next value from the stream.
349
350 :raises CBORDecodeError: if there is any problem decoding the stream
351
352 """
353 try:
354 initial_byte = byte_as_integer(self.fp.read(1))
355 major_type = initial_byte >> 5
356 subtype = initial_byte & 31
357 except Exception as e:
358 raise CBORDecodeError('error reading major type at index {}: {}'
359 .format(self.fp.tell(), e))
360
361 decoder = major_decoders[major_type]
362 try:
363 return decoder(self, subtype, shareable_index)
364 except CBORDecodeError:
365 raise
366 except Exception as e:
367 raise CBORDecodeError('error decoding value at index {}: {}'.format(self.fp.tell(), e))
368
369 def decode_from_bytes(self, buf):
370 """
371 Wrap the given bytestring as a file and call :meth:`decode` with it as the argument.
372
373 This method was intended to be used from the ``tag_hook`` hook when an object needs to be
374 decoded separately from the rest but while still taking advantage of the shared value
375 registry.
376
377 """
378 old_fp = self.fp
379 self.fp = BytesIO(buf)
380 retval = self.decode()
381 self.fp = old_fp
382 return retval
383
384
385 def loads(payload, **kwargs):
386 """
387 Deserialize an object from a bytestring.
388
389 :param bytes payload: the bytestring to serialize
390 :param kwargs: keyword arguments passed to :class:`~.CBORDecoder`
391 :return: the deserialized object
392
393 """
394 fp = BytesIO(payload)
395 return CBORDecoder(fp, **kwargs).decode()
396
397
398 def load(fp, **kwargs):
399 """
400 Deserialize an object from an open file.
401
402 :param fp: the input file (any file-like object)
403 :param kwargs: keyword arguments passed to :class:`~.CBORDecoder`
404 :return: the deserialized object
405
406 """
407 return CBORDecoder(fp, **kwargs).decode()
@@ -0,0 +1,425 b''
1 import re
2 import struct
3 from collections import OrderedDict, defaultdict
4 from contextlib import contextmanager
5 from functools import wraps
6 from datetime import datetime, date, time
7 from io import BytesIO
8
9 from .compat import (
10 iteritems, timezone, long, unicode, as_unicode, bytes_from_list, pack_float16, unpack_float16)
11 from .types import CBORTag, undefined, CBORSimpleValue
12
13
14 class CBOREncodeError(Exception):
15 """Raised when an error occurs while serializing an object into a CBOR datastream."""
16
17
18 def shareable_encoder(func):
19 """
20 Wrap the given encoder function to gracefully handle cyclic data structures.
21
22 If value sharing is enabled, this marks the given value shared in the datastream on the
23 first call. If the value has already been passed to this method, a reference marker is
24 instead written to the data stream and the wrapped function is not called.
25
26 If value sharing is disabled, only infinite recursion protection is done.
27
28 """
29 @wraps(func)
30 def wrapper(encoder, value, *args, **kwargs):
31 value_id = id(value)
32 container, container_index = encoder._shared_containers.get(value_id, (None, None))
33 if encoder.value_sharing:
34 if container is value:
35 # Generate a reference to the previous index instead of encoding this again
36 encoder.write(encode_length(0xd8, 0x1d))
37 encode_int(encoder, container_index)
38 else:
39 # Mark the container as shareable
40 encoder._shared_containers[value_id] = (value, len(encoder._shared_containers))
41 encoder.write(encode_length(0xd8, 0x1c))
42 func(encoder, value, *args, **kwargs)
43 else:
44 if container is value:
45 raise CBOREncodeError('cyclic data structure detected but value sharing is '
46 'disabled')
47 else:
48 encoder._shared_containers[value_id] = (value, None)
49 func(encoder, value, *args, **kwargs)
50 del encoder._shared_containers[value_id]
51
52 return wrapper
53
54
55 def encode_length(major_tag, length):
56 if length < 24:
57 return struct.pack('>B', major_tag | length)
58 elif length < 256:
59 return struct.pack('>BB', major_tag | 24, length)
60 elif length < 65536:
61 return struct.pack('>BH', major_tag | 25, length)
62 elif length < 4294967296:
63 return struct.pack('>BL', major_tag | 26, length)
64 else:
65 return struct.pack('>BQ', major_tag | 27, length)
66
67
68 def encode_int(encoder, value):
69 # Big integers (2 ** 64 and over)
70 if value >= 18446744073709551616 or value < -18446744073709551616:
71 if value >= 0:
72 major_type = 0x02
73 else:
74 major_type = 0x03
75 value = -value - 1
76
77 values = []
78 while value > 0:
79 value, remainder = divmod(value, 256)
80 values.insert(0, remainder)
81
82 payload = bytes_from_list(values)
83 encode_semantic(encoder, CBORTag(major_type, payload))
84 elif value >= 0:
85 encoder.write(encode_length(0, value))
86 else:
87 encoder.write(encode_length(0x20, abs(value) - 1))
88
89
90 def encode_bytestring(encoder, value):
91 encoder.write(encode_length(0x40, len(value)) + value)
92
93
94 def encode_bytearray(encoder, value):
95 encode_bytestring(encoder, bytes(value))
96
97
98 def encode_string(encoder, value):
99 encoded = value.encode('utf-8')
100 encoder.write(encode_length(0x60, len(encoded)) + encoded)
101
102
103 @shareable_encoder
104 def encode_array(encoder, value):
105 encoder.write(encode_length(0x80, len(value)))
106 for item in value:
107 encoder.encode(item)
108
109
110 @shareable_encoder
111 def encode_map(encoder, value):
112 encoder.write(encode_length(0xa0, len(value)))
113 for key, val in iteritems(value):
114 encoder.encode(key)
115 encoder.encode(val)
116
117
118 def encode_sortable_key(encoder, value):
119 """Takes a key and calculates the length of its optimal byte representation"""
120 encoded = encoder.encode_to_bytes(value)
121 return len(encoded), encoded
122
123
124 @shareable_encoder
125 def encode_canonical_map(encoder, value):
126 """Reorder keys according to Canonical CBOR specification"""
127 keyed_keys = ((encode_sortable_key(encoder, key), key) for key in value.keys())
128 encoder.write(encode_length(0xa0, len(value)))
129 for sortkey, realkey in sorted(keyed_keys):
130 encoder.write(sortkey[1])
131 encoder.encode(value[realkey])
132
133
134 def encode_semantic(encoder, value):
135 encoder.write(encode_length(0xc0, value.tag))
136 encoder.encode(value.value)
137
138
139 #
140 # Semantic decoders (major tag 6)
141 #
142
143 def encode_datetime(encoder, value):
144 # Semantic tag 0
145 if not value.tzinfo:
146 if encoder.timezone:
147 value = value.replace(tzinfo=encoder.timezone)
148 else:
149 raise CBOREncodeError(
150 'naive datetime encountered and no default timezone has been set')
151
152 if encoder.datetime_as_timestamp:
153 from calendar import timegm
154 timestamp = timegm(value.utctimetuple()) + value.microsecond // 1000000
155 encode_semantic(encoder, CBORTag(1, timestamp))
156 else:
157 datestring = as_unicode(value.isoformat().replace('+00:00', 'Z'))
158 encode_semantic(encoder, CBORTag(0, datestring))
159
160
161 def encode_date(encoder, value):
162 value = datetime.combine(value, time()).replace(tzinfo=timezone.utc)
163 encode_datetime(encoder, value)
164
165
166 def encode_decimal(encoder, value):
167 # Semantic tag 4
168 if value.is_nan():
169 encoder.write(b'\xf9\x7e\x00')
170 elif value.is_infinite():
171 encoder.write(b'\xf9\x7c\x00' if value > 0 else b'\xf9\xfc\x00')
172 else:
173 dt = value.as_tuple()
174 mantissa = sum(d * 10 ** i for i, d in enumerate(reversed(dt.digits)))
175 with encoder.disable_value_sharing():
176 encode_semantic(encoder, CBORTag(4, [dt.exponent, mantissa]))
177
178
179 def encode_rational(encoder, value):
180 # Semantic tag 30
181 with encoder.disable_value_sharing():
182 encode_semantic(encoder, CBORTag(30, [value.numerator, value.denominator]))
183
184
185 def encode_regexp(encoder, value):
186 # Semantic tag 35
187 encode_semantic(encoder, CBORTag(35, as_unicode(value.pattern)))
188
189
190 def encode_mime(encoder, value):
191 # Semantic tag 36
192 encode_semantic(encoder, CBORTag(36, as_unicode(value.as_string())))
193
194
195 def encode_uuid(encoder, value):
196 # Semantic tag 37
197 encode_semantic(encoder, CBORTag(37, value.bytes))
198
199
200 def encode_set(encoder, value):
201 # Semantic tag 258
202 encode_semantic(encoder, CBORTag(258, tuple(value)))
203
204
205 def encode_canonical_set(encoder, value):
206 # Semantic tag 258
207 values = sorted([(encode_sortable_key(encoder, key), key) for key in value])
208 encode_semantic(encoder, CBORTag(258, [key[1] for key in values]))
209
210
211 #
212 # Special encoders (major tag 7)
213 #
214
215 def encode_simple_value(encoder, value):
216 if value.value < 20:
217 encoder.write(struct.pack('>B', 0xe0 | value.value))
218 else:
219 encoder.write(struct.pack('>BB', 0xf8, value.value))
220
221
222 def encode_float(encoder, value):
223 # Handle special values efficiently
224 import math
225 if math.isnan(value):
226 encoder.write(b'\xf9\x7e\x00')
227 elif math.isinf(value):
228 encoder.write(b'\xf9\x7c\x00' if value > 0 else b'\xf9\xfc\x00')
229 else:
230 encoder.write(struct.pack('>Bd', 0xfb, value))
231
232
233 def encode_minimal_float(encoder, value):
234 # Handle special values efficiently
235 import math
236 if math.isnan(value):
237 encoder.write(b'\xf9\x7e\x00')
238 elif math.isinf(value):
239 encoder.write(b'\xf9\x7c\x00' if value > 0 else b'\xf9\xfc\x00')
240 else:
241 encoded = struct.pack('>Bf', 0xfa, value)
242 if struct.unpack('>Bf', encoded)[1] != value:
243 encoded = struct.pack('>Bd', 0xfb, value)
244 encoder.write(encoded)
245 else:
246 f16 = pack_float16(value)
247 if f16 and unpack_float16(f16[1:]) == value:
248 encoder.write(f16)
249 else:
250 encoder.write(encoded)
251
252
253 def encode_boolean(encoder, value):
254 encoder.write(b'\xf5' if value else b'\xf4')
255
256
257 def encode_none(encoder, value):
258 encoder.write(b'\xf6')
259
260
261 def encode_undefined(encoder, value):
262 encoder.write(b'\xf7')
263
264
265 default_encoders = OrderedDict([
266 (bytes, encode_bytestring),
267 (bytearray, encode_bytearray),
268 (unicode, encode_string),
269 (int, encode_int),
270 (long, encode_int),
271 (float, encode_float),
272 (('decimal', 'Decimal'), encode_decimal),
273 (bool, encode_boolean),
274 (type(None), encode_none),
275 (tuple, encode_array),
276 (list, encode_array),
277 (dict, encode_map),
278 (defaultdict, encode_map),
279 (OrderedDict, encode_map),
280 (type(undefined), encode_undefined),
281 (datetime, encode_datetime),
282 (date, encode_date),
283 (type(re.compile('')), encode_regexp),
284 (('fractions', 'Fraction'), encode_rational),
285 (('email.message', 'Message'), encode_mime),
286 (('uuid', 'UUID'), encode_uuid),
287 (CBORSimpleValue, encode_simple_value),
288 (CBORTag, encode_semantic),
289 (set, encode_set),
290 (frozenset, encode_set)
291 ])
292
293 canonical_encoders = OrderedDict([
294 (float, encode_minimal_float),
295 (dict, encode_canonical_map),
296 (defaultdict, encode_canonical_map),
297 (OrderedDict, encode_canonical_map),
298 (set, encode_canonical_set),
299 (frozenset, encode_canonical_set)
300 ])
301
302
303 class CBOREncoder(object):
304 """
305 Serializes objects to a byte stream using Concise Binary Object Representation.
306
307 :param datetime_as_timestamp: set to ``True`` to serialize datetimes as UNIX timestamps
308 (this makes datetimes more concise on the wire but loses the time zone information)
309 :param datetime.tzinfo timezone: the default timezone to use for serializing naive datetimes
310 :param value_sharing: if ``True``, allows more efficient serializing of repeated values and,
311 more importantly, cyclic data structures, at the cost of extra line overhead
312 :param default: a callable that is called by the encoder with three arguments
313 (encoder, value, file object) when no suitable encoder has been found, and should use the
314 methods on the encoder to encode any objects it wants to add to the data stream
315 :param canonical: Forces mapping types to be output in a stable order to guarantee that the
316 output will always produce the same hash given the same input.
317 """
318
319 __slots__ = ('fp', 'datetime_as_timestamp', 'timezone', 'default', 'value_sharing',
320 'json_compatible', '_shared_containers', '_encoders')
321
322 def __init__(self, fp, datetime_as_timestamp=False, timezone=None, value_sharing=False,
323 default=None, canonical=False):
324 self.fp = fp
325 self.datetime_as_timestamp = datetime_as_timestamp
326 self.timezone = timezone
327 self.value_sharing = value_sharing
328 self.default = default
329 self._shared_containers = {} # indexes used for value sharing
330 self._encoders = default_encoders.copy()
331 if canonical:
332 self._encoders.update(canonical_encoders)
333
334 def _find_encoder(self, obj_type):
335 from sys import modules
336
337 for type_, enc in list(iteritems(self._encoders)):
338 if type(type_) is tuple:
339 modname, typename = type_
340 imported_type = getattr(modules.get(modname), typename, None)
341 if imported_type is not None:
342 del self._encoders[type_]
343 self._encoders[imported_type] = enc
344 type_ = imported_type
345 else: # pragma: nocover
346 continue
347
348 if issubclass(obj_type, type_):
349 self._encoders[obj_type] = enc
350 return enc
351
352 return None
353
354 @contextmanager
355 def disable_value_sharing(self):
356 """Disable value sharing in the encoder for the duration of the context block."""
357 old_value_sharing = self.value_sharing
358 self.value_sharing = False
359 yield
360 self.value_sharing = old_value_sharing
361
362 def write(self, data):
363 """
364 Write bytes to the data stream.
365
366 :param data: the bytes to write
367
368 """
369 self.fp.write(data)
370
371 def encode(self, obj):
372 """
373 Encode the given object using CBOR.
374
375 :param obj: the object to encode
376
377 """
378 obj_type = obj.__class__
379 encoder = self._encoders.get(obj_type) or self._find_encoder(obj_type) or self.default
380 if not encoder:
381 raise CBOREncodeError('cannot serialize type %s' % obj_type.__name__)
382
383 encoder(self, obj)
384
385 def encode_to_bytes(self, obj):
386 """
387 Encode the given object to a byte buffer and return its value as bytes.
388
389 This method was intended to be used from the ``default`` hook when an object needs to be
390 encoded separately from the rest but while still taking advantage of the shared value
391 registry.
392
393 """
394 old_fp = self.fp
395 self.fp = fp = BytesIO()
396 self.encode(obj)
397 self.fp = old_fp
398 return fp.getvalue()
399
400
401 def dumps(obj, **kwargs):
402 """
403 Serialize an object to a bytestring.
404
405 :param obj: the object to serialize
406 :param kwargs: keyword arguments passed to :class:`~.CBOREncoder`
407 :return: the serialized output
408 :rtype: bytes
409
410 """
411 fp = BytesIO()
412 dump(obj, fp, **kwargs)
413 return fp.getvalue()
414
415
416 def dump(obj, fp, **kwargs):
417 """
418 Serialize an object to a file.
419
420 :param obj: the object to serialize
421 :param fp: a file-like object
422 :param kwargs: keyword arguments passed to :class:`~.CBOREncoder`
423
424 """
425 CBOREncoder(fp, **kwargs).encode(obj)
@@ -0,0 +1,55 b''
1 class CBORTag(object):
2 """
3 Represents a CBOR semantic tag.
4
5 :param int tag: tag number
6 :param value: encapsulated value (any object)
7 """
8
9 __slots__ = 'tag', 'value'
10
11 def __init__(self, tag, value):
12 self.tag = tag
13 self.value = value
14
15 def __eq__(self, other):
16 if isinstance(other, CBORTag):
17 return self.tag == other.tag and self.value == other.value
18 return NotImplemented
19
20 def __repr__(self):
21 return 'CBORTag({self.tag}, {self.value!r})'.format(self=self)
22
23
24 class CBORSimpleValue(object):
25 """
26 Represents a CBOR "simple value".
27
28 :param int value: the value (0-255)
29 """
30
31 __slots__ = 'value'
32
33 def __init__(self, value):
34 if value < 0 or value > 255:
35 raise TypeError('simple value too big')
36 self.value = value
37
38 def __eq__(self, other):
39 if isinstance(other, CBORSimpleValue):
40 return self.value == other.value
41 elif isinstance(other, int):
42 return self.value == other
43 return NotImplemented
44
45 def __repr__(self):
46 return 'CBORSimpleValue({self.value})'.format(self=self)
47
48
49 class UndefinedType(object):
50 __slots__ = ()
51
52
53 #: Represents the "undefined" value.
54 undefined = UndefinedType()
55 break_marker = object()
General Comments 0
You need to be logged in to leave comments. Login now