From: David Kerkeslager Date: Tue, 11 Apr 2017 19:21:06 +0000 (-0400) Subject: Major reorganization X-Git-Url: https://code.kerkeslager.com/?p=ton;a=commitdiff_plain;h=d19bdaf03aeb26d4812600347273a27d2c9b9caa Major reorganization --- diff --git a/don/__init__.py b/don/__init__.py index 6f8bf47..dd93327 100644 --- a/don/__init__.py +++ b/don/__init__.py @@ -1,329 +1,10 @@ -import binascii import collections import struct -VOID = 0x00 -TRUE = 0x01 -FALSE = 0x02 -BOOL = (TRUE, FALSE) -INT8 = 0x10 -INT16 = 0x11 -INT32 = 0x12 -INT64 = 0x13 -FLOAT = 0x20 -DOUBLE = 0x21 -BINARY = 0x30 -UTF8 = 0x31 -UTF16 = 0x32 -UTF32 = 0x33 -LIST = 0x40 -DICTIONARY = 0x41 - -DEFAULT_INTEGER_ENCODING = INT32 -DEFAULT_DECIMAL_ENCODING = DOUBLE -DEFAULT_STRING_ENCODING = UTF8 - -TaggedObject = collections.namedtuple('TaggedObject', ['tag', 'value']) - -_TYPES_TO_TAGS = { - int: DEFAULT_INTEGER_ENCODING, - float: DEFAULT_DECIMAL_ENCODING, - bytes: BINARY, - str: DEFAULT_STRING_ENCODING, - list: LIST, - dict: DICTIONARY, - collections.OrderedDict: DICTIONARY, -} - -def _tag(o): - if isinstance(o, TaggedObject): - return o - - if o is None: - return TaggedObject(tag = VOID, value = o) - - if o is True: - return TaggedObject(tag = TRUE, value = o) - - if o is False: - return TaggedObject(tag = FALSE, value = o) - - return TaggedObject(tag = _TYPES_TO_TAGS[type(o)], value = o) - -def _binary_serialize_tag_only_type(o): - return b'' - -def _pack_format_string_to_binary_serializer(pfs): - def serializer(i): - return struct.pack(pfs, i) - return serializer - -def _encoder_to_binary_serializer(e): - def serializer(s): - encoded = e(s) - return struct.pack('!I', len(encoded)) + encoded - return serializer - -def _binary_serialize_list(items): - # TODO Enforce that items are all the same type - items = [_tag(i) for i in items] - - if len(items) == 0: - item_tag = VOID - else: - item_tag = items[0].tag - - item_serializer = _BINARY_SERIALIZERS[item_tag] - items = [item_serializer(i.value) for i in items] - item_length = len(items) - items = b''.join(items) - byte_length = len(items) - return struct.pack('!BII', item_tag, byte_length, item_length) + items - -def _binary_serialize_dict(d): - item_length = 0 - serialized = b'' - - key_serializer = _BINARY_SERIALIZERS[UTF8] - - for key, value in d.items(): - assert isinstance(key, str) - item_length += 1 - serialized += key_serializer(key) + _binary_serialize(value) - - byte_length = len(serialized) - return struct.pack('!II', byte_length, item_length) + serialized - -_BINARY_SERIALIZERS = { - VOID: _binary_serialize_tag_only_type, - TRUE: _binary_serialize_tag_only_type, - FALSE: _binary_serialize_tag_only_type, - INT8: _pack_format_string_to_binary_serializer('!b'), - INT16: _pack_format_string_to_binary_serializer('!h'), - INT32: _pack_format_string_to_binary_serializer('!i'), - FLOAT: _pack_format_string_to_binary_serializer('!f'), - DOUBLE: _pack_format_string_to_binary_serializer('!d'), - BINARY: _encoder_to_binary_serializer(lambda b: b), - UTF8: _encoder_to_binary_serializer(lambda s: s.encode('utf-8')), - UTF16: _encoder_to_binary_serializer(lambda s: s.encode('utf-16')), - UTF32: _encoder_to_binary_serializer(lambda s: s.encode('utf-32')), - LIST: _binary_serialize_list, - DICTIONARY: _binary_serialize_dict, -} - -def _binary_serialize(o): - o = _tag(o) - return struct.pack('!B', o.tag) + _BINARY_SERIALIZERS[o.tag](o.value) - -ParseResult = collections.namedtuple( - 'ParseResult', - [ - 'success', - 'value', - 'remaining', - ], -) - -_FAILED_PARSE_RESULT = ParseResult(success = False, value = None, remaining = None) - -_BYTE_SIZES_TO_UNPACK_FORMATS = { - 1: '!b', - 2: '!h', - 4: '!i', - 8: '!q', -} - -def make_integer_parser(size_in_bytes): - unpack_format = _BYTE_SIZES_TO_UNPACK_FORMATS[size_in_bytes] - - def integer_parser(source): - value = struct.unpack(unpack_format, source[:size_in_bytes])[0] - remaining = source[size_in_bytes:] - - return ParseResult(success = True, value = value, remaining = remaining) - - return integer_parser - -def binary64_parser(source): - return ParseResult( - success = True, - value = struct.unpack('!d', source[:8])[0], - remaining = source[8:], - ) - -def make_string_parser(decoder): - def string_parser(source): - length = struct.unpack('!I', source[:4])[0] - source = source[4:] - return ParseResult( - success = True, - value = decoder(source[:length]), - remaining = source[length:], - ) - - return string_parser - -def _list_parser(source): - tag = source[0] - parser = _TAGS_TO_PARSERS[tag] - - source = source[1:] - byte_length, items_length = struct.unpack('!II', source[:8]) - source = source[8:] - - remaining = source[byte_length:] - source = source[:byte_length] - - def item_iterator(source): - count = 0 - - while len(source) > 0: - parse_result = parser(source) - - if parse_result.success: - count += 1 - yield parse_result.value - source = parse_result.remaining - - assert count == items_length - - return ParseResult( - success = True, - value = item_iterator(source), - remaining = remaining, - ) - -def dictionary_parser(source): - key_parser = _TAGS_TO_PARSERS[UTF8] - - byte_length, item_length = struct.unpack('!II', source[:8]) - source = source[8:] - - remaining = source[byte_length:] - source = source[:byte_length] - - def kvp_iterator(source): - count = 0 - - while len(source) > 0: - count += 1 - key_parse_result = key_parser(source) - key, source = key_parse_result.value, key_parse_result.remaining - value_parse_result = _object_parser(source) - value, source = value_parse_result.value, value_parse_result.remaining - - yield key, value - - assert count == item_length - - return ParseResult( - success = True, - value = collections.OrderedDict(kvp_iterator(source)), - remaining = remaining, - ) - - -_TAGS_TO_PARSERS = { - VOID: lambda r: ParseResult(True, None, r), - TRUE: lambda r: ParseResult(True, True, r), - FALSE: lambda r: ParseResult(True, False, r), - INT8: make_integer_parser(1), - INT16: make_integer_parser(2), - INT32: make_integer_parser(4), - INT64: make_integer_parser(8), - DOUBLE: binary64_parser, - BINARY: make_string_parser(lambda b : b), - UTF8: make_string_parser(lambda b : b.decode('utf-8')), - UTF16: make_string_parser(lambda b : b.decode('utf-16')), - UTF32: make_string_parser(lambda b : b.decode('utf-32')), - LIST: _list_parser, - DICTIONARY: dictionary_parser, -} - -def _object_parser(source): - return _TAGS_TO_PARSERS[source[0]](source[1:]) - -def _parse(parser, source, consume_all = True): - result = parser(source) - - if result.success and result.remaining == b'': - return result.value - - raise Exception('Unparsed trailing bytes: {}'.format(result.remaining)) - -def _binary_deserialize(b): - return _parse(_object_parser, b) - -def _integer_size_to_string_serializer(integer_size): - minimum = -(2 ** (integer_size - 1)) - maximum = 2 ** (integer_size - 1) - 1 - - def serializer(integer): - assert minimum <= integer and integer <= maximum - return '{}i{}'.format(integer, integer_size) - - return serializer - -def _serialize_float(f): - return '{}f'.format(f) - -def _serialize_double(d): - return '{}d'.format(d) - -def _serialize_binary(b): - return '"{}"b'.format(binascii.hexlify(b).decode('ascii')) - -def _utf_encoding_to_serializer(utf_encoding): - def serializer(s): - return '"{}"{}'.format(s, utf_encoding) - - return serializer - -def _string_serialize_list(l): - return '[{}]'.format(', '.join(map(_string_serialize, l))) - -def _string_serialize_dictionary(d): - def serialize_kvp(kvp): - return _string_serialize(kvp[0]) + ': ' + _string_serialize(kvp[1]) - return '{ ' + ', '.join(map(serialize_kvp, d.items())) + ' }' - -_STRING_SERIALIZERS = { - VOID: lambda o: 'null', - TRUE: lambda o: 'true', - FALSE: lambda o: 'false', - INT8: _integer_size_to_string_serializer(8), - INT16: _integer_size_to_string_serializer(16), - INT32: _integer_size_to_string_serializer(32), - INT64: _integer_size_to_string_serializer(64), - FLOAT: _serialize_float, - DOUBLE: _serialize_double, - BINARY: _serialize_binary, - UTF8: _utf_encoding_to_serializer('utf8'), - UTF16: _utf_encoding_to_serializer('utf16'), - UTF32: _utf_encoding_to_serializer('utf32'), - LIST: _string_serialize_list, - DICTIONARY: _string_serialize_dictionary, -} - -def _string_serialize(o): - o = _tag(o) - - return _STRING_SERIALIZERS[o.tag](o.value) - -def _string_deserialize(o): - pass - -Serializer = collections.namedtuple('Serializer', ['serialize', 'deserialize']) - -binary = Serializer( - serialize = _binary_serialize, - deserialize = _binary_deserialize, -) - -string = Serializer( - serialize = _string_serialize, - deserialize = _string_deserialize, -) +from don import binary +from don import string +from don.constants import * +from don._shared import TaggedObject def binary_to_string(b): return string.serialize(binary.deserialize(b)) diff --git a/don/_shared.py b/don/_shared.py new file mode 100644 index 0000000..ca4c839 --- /dev/null +++ b/don/_shared.py @@ -0,0 +1,41 @@ +import collections + +from don import constants + +_TYPES_TO_TAGS = { + int: constants.DEFAULT_INTEGER_ENCODING, + float: constants.DEFAULT_DECIMAL_ENCODING, + bytes: constants.BINARY, + str: constants.DEFAULT_STRING_ENCODING, + list: constants.LIST, + dict: constants.DICTIONARY, + collections.OrderedDict: constants.DICTIONARY, +} + +TaggedObject = collections.namedtuple('TaggedObject', ['tag', 'value']) + +def _tag(o): + if isinstance(o, TaggedObject): + return o + + if o is None: + return TaggedObject(tag = constants.VOID, value = o) + + if o is True: + return TaggedObject(tag = constants.TRUE, value = o) + + if o is False: + return TaggedObject(tag = constants.FALSE, value = o) + + return TaggedObject(tag = _TYPES_TO_TAGS[type(o)], value = o) + +ParseResult = collections.namedtuple( + 'ParseResult', + [ + 'success', + 'value', + 'remaining', + ], +) + +_FAILED_PARSE_RESULT = ParseResult(success = False, value = None, remaining = None) diff --git a/don/binary.py b/don/binary.py new file mode 100644 index 0000000..d29116c --- /dev/null +++ b/don/binary.py @@ -0,0 +1,197 @@ +import collections +import struct + +from don import constants, _shared + +def _binary_serialize_tag_only_type(o): + return b'' + +def _pack_format_string_to_binary_serializer(pfs): + def serializer(i): + return struct.pack(pfs, i) + return serializer + +def _encoder_to_binary_serializer(e): + def serializer(s): + encoded = e(s) + return struct.pack('!I', len(encoded)) + encoded + return serializer + +def _binary_serialize_list(items): + # TODO Enforce that items are all the same type + items = [_shared._tag(i) for i in items] + + if len(items) == 0: + item_tag = constants.VOID + else: + item_tag = items[0].tag + + item_serializer = _BINARY_SERIALIZERS[item_tag] + items = [item_serializer(i.value) for i in items] + item_length = len(items) + items = b''.join(items) + byte_length = len(items) + return struct.pack('!BII', item_tag, byte_length, item_length) + items + +def _binary_serialize_dict(d): + item_length = 0 + serialized = b'' + + key_serializer = _BINARY_SERIALIZERS[constants.UTF8] + + for key, value in d.items(): + assert isinstance(key, str) + item_length += 1 + serialized += key_serializer(key) + serialize(value) + + byte_length = len(serialized) + return struct.pack('!II', byte_length, item_length) + serialized + +_BINARY_SERIALIZERS = { + constants.VOID: _binary_serialize_tag_only_type, + constants.TRUE: _binary_serialize_tag_only_type, + constants.FALSE: _binary_serialize_tag_only_type, + constants.INT8: _pack_format_string_to_binary_serializer('!b'), + constants.INT16: _pack_format_string_to_binary_serializer('!h'), + constants.INT32: _pack_format_string_to_binary_serializer('!i'), + constants.FLOAT: _pack_format_string_to_binary_serializer('!f'), + constants.DOUBLE: _pack_format_string_to_binary_serializer('!d'), + constants.BINARY: _encoder_to_binary_serializer(lambda b: b), + constants.UTF8: _encoder_to_binary_serializer(lambda s: s.encode('utf-8')), + constants.UTF16: _encoder_to_binary_serializer(lambda s: s.encode('utf-16')), + constants.UTF32: _encoder_to_binary_serializer(lambda s: s.encode('utf-32')), + constants.LIST: _binary_serialize_list, + constants.DICTIONARY: _binary_serialize_dict, +} + +def serialize(o): + o = _shared._tag(o) + return struct.pack('!B', o.tag) + _BINARY_SERIALIZERS[o.tag](o.value) + +_BYTE_SIZES_TO_UNPACK_FORMATS = { + 1: '!b', + 2: '!h', + 4: '!i', + 8: '!q', +} + +def make_integer_parser(size_in_bytes): + unpack_format = _BYTE_SIZES_TO_UNPACK_FORMATS[size_in_bytes] + + def integer_parser(source): + value = struct.unpack(unpack_format, source[:size_in_bytes])[0] + remaining = source[size_in_bytes:] + + return _shared.ParseResult(success = True, value = value, remaining = remaining) + + return integer_parser + +def binary64_parser(source): + return _shared.ParseResult( + success = True, + value = struct.unpack('!d', source[:8])[0], + remaining = source[8:], + ) + +def make_string_parser(decoder): + def string_parser(source): + length = struct.unpack('!I', source[:4])[0] + source = source[4:] + return _shared.ParseResult( + success = True, + value = decoder(source[:length]), + remaining = source[length:], + ) + + return string_parser + +def _list_parser(source): + tag = source[0] + parser = _TAGS_TO_PARSERS[tag] + + source = source[1:] + byte_length, items_length = struct.unpack('!II', source[:8]) + source = source[8:] + + remaining = source[byte_length:] + source = source[:byte_length] + + def item_iterator(source): + count = 0 + + while len(source) > 0: + parse_result = parser(source) + + if parse_result.success: + count += 1 + yield parse_result.value + source = parse_result.remaining + + assert count == items_length + + return _shared.ParseResult( + success = True, + value = item_iterator(source), + remaining = remaining, + ) + +def dictionary_parser(source): + key_parser = _TAGS_TO_PARSERS[constants.UTF8] + + byte_length, item_length = struct.unpack('!II', source[:8]) + source = source[8:] + + remaining = source[byte_length:] + source = source[:byte_length] + + def kvp_iterator(source): + count = 0 + + while len(source) > 0: + count += 1 + key_parse_result = key_parser(source) + key, source = key_parse_result.value, key_parse_result.remaining + value_parse_result = _object_parser(source) + value, source = value_parse_result.value, value_parse_result.remaining + + yield key, value + + assert count == item_length + + return _shared.ParseResult( + success = True, + value = collections.OrderedDict(kvp_iterator(source)), + remaining = remaining, + ) + + +_TAGS_TO_PARSERS = { + constants.VOID: lambda r: _shared.ParseResult(True, None, r), + constants.TRUE: lambda r: _shared.ParseResult(True, True, r), + constants.FALSE: lambda r: _shared.ParseResult(True, False, r), + constants.INT8: make_integer_parser(1), + constants.INT16: make_integer_parser(2), + constants.INT32: make_integer_parser(4), + constants.INT64: make_integer_parser(8), + constants.DOUBLE: binary64_parser, + constants.BINARY: make_string_parser(lambda b : b), + constants.UTF8: make_string_parser(lambda b : b.decode('utf-8')), + constants.UTF16: make_string_parser(lambda b : b.decode('utf-16')), + constants.UTF32: make_string_parser(lambda b : b.decode('utf-32')), + constants.LIST: _list_parser, + constants.DICTIONARY: dictionary_parser, +} + +def _object_parser(source): + return _TAGS_TO_PARSERS[source[0]](source[1:]) + +def _parse(parser, source, consume_all = True): + result = parser(source) + + if result.success and result.remaining == b'': + return result.value + + raise Exception('Unparsed trailing bytes: {}'.format(result.remaining)) + +def deserialize(b): + return _parse(_object_parser, b) diff --git a/don/constants.py b/don/constants.py new file mode 100644 index 0000000..d90254a --- /dev/null +++ b/don/constants.py @@ -0,0 +1,20 @@ +VOID = 0x00 +TRUE = 0x01 +FALSE = 0x02 +BOOL = (TRUE, FALSE) +INT8 = 0x10 +INT16 = 0x11 +INT32 = 0x12 +INT64 = 0x13 +FLOAT = 0x20 +DOUBLE = 0x21 +BINARY = 0x30 +UTF8 = 0x31 +UTF16 = 0x32 +UTF32 = 0x33 +LIST = 0x40 +DICTIONARY = 0x41 + +DEFAULT_INTEGER_ENCODING = INT32 +DEFAULT_DECIMAL_ENCODING = DOUBLE +DEFAULT_STRING_ENCODING = UTF8 diff --git a/don/string.py b/don/string.py index a005098..a7974a4 100644 --- a/don/string.py +++ b/don/string.py @@ -1,5 +1,62 @@ +import binascii + +from don import constants, _shared + +def _integer_size_to_string_serializer(integer_size): + minimum = -(2 ** (integer_size - 1)) + maximum = 2 ** (integer_size - 1) - 1 + + def serializer(integer): + assert minimum <= integer and integer <= maximum + return '{}i{}'.format(integer, integer_size) + + return serializer + +def _serialize_float(f): + return '{}f'.format(f) + +def _serialize_double(d): + return '{}d'.format(d) + +def _serialize_binary(b): + return '"{}"b'.format(binascii.hexlify(b).decode('ascii')) + +def _utf_encoding_to_serializer(utf_encoding): + def serializer(s): + return '"{}"{}'.format(s, utf_encoding) + + return serializer + +def _string_serialize_list(l): + return '[{}]'.format(', '.join(map(serialize, l))) + +def _string_serialize_dictionary(d): + def serialize_kvp(kvp): + return serialize(kvp[0]) + ': ' + serialize(kvp[1]) + return '{ ' + ', '.join(map(serialize_kvp, d.items())) + ' }' + +_STRING_SERIALIZERS = { + constants.VOID: lambda o: 'null', + constants.TRUE: lambda o: 'true', + constants.FALSE: lambda o: 'false', + constants.INT8: _integer_size_to_string_serializer(8), + constants.INT16: _integer_size_to_string_serializer(16), + constants.INT32: _integer_size_to_string_serializer(32), + constants.INT64: _integer_size_to_string_serializer(64), + constants.FLOAT: _serialize_float, + constants.DOUBLE: _serialize_double, + constants.BINARY: _serialize_binary, + constants.UTF8: _utf_encoding_to_serializer('utf8'), + constants.UTF16: _utf_encoding_to_serializer('utf16'), + constants.UTF32: _utf_encoding_to_serializer('utf32'), + constants.LIST: _string_serialize_list, + constants.DICTIONARY: _string_serialize_dictionary, +} + def serialize(o): - pass + o = _shared._tag(o) + + return _STRING_SERIALIZERS[o.tag](o.value) def deserialize(s): pass diff --git a/test_don.py b/test_don.py index 6d5ed81..5ca30e1 100644 --- a/test_don.py +++ b/test_don.py @@ -1,7 +1,8 @@ import collections import unittest -from don import * +import don +from don import binary, string class TestBinarySerialize(unittest.TestCase): def test_serializes_null(self): @@ -124,56 +125,72 @@ class TestStringSerialize(unittest.TestCase): self.assertEqual(string.serialize(False), 'false') def test_serializes_int8(self): - self.assertEqual(string.serialize(TaggedObject(INT8, 1)), '1i8') - self.assertEqual(string.serialize(TaggedObject(INT8, -1)), '-1i8') - self.assertEqual(string.serialize(TaggedObject(INT8, 42)), '42i8') + self.assertEqual(string.serialize(don.TaggedObject(don.INT8, 1)), '1i8') + self.assertEqual(string.serialize(don.TaggedObject(don.INT8, -1)), '-1i8') + self.assertEqual(string.serialize(don.TaggedObject(don.INT8, 42)), '42i8') def test_serializes_int16(self): - self.assertEqual(string.serialize(TaggedObject(INT16, 1)), '1i16') - self.assertEqual(string.serialize(TaggedObject(INT16, -1)), '-1i16') - self.assertEqual(string.serialize(TaggedObject(INT16, 42)), '42i16') + self.assertEqual(string.serialize(don.TaggedObject(don.INT16, 1)), '1i16') + self.assertEqual(string.serialize(don.TaggedObject(don.INT16, -1)), '-1i16') + self.assertEqual(string.serialize(don.TaggedObject(don.INT16, 42)), '42i16') def test_serializes_int32(self): - self.assertEqual(string.serialize(TaggedObject(INT32, 1)), '1i32') - self.assertEqual(string.serialize(TaggedObject(INT32, -1)), '-1i32') - self.assertEqual(string.serialize(TaggedObject(INT32, 42)), '42i32') + self.assertEqual(string.serialize(don.TaggedObject(don.INT32, 1)), '1i32') + self.assertEqual(string.serialize(don.TaggedObject(don.INT32, -1)), '-1i32') + self.assertEqual(string.serialize(don.TaggedObject(don.INT32, 42)), '42i32') def test_serializes_int64(self): - self.assertEqual(string.serialize(TaggedObject(INT64, 1)), '1i64') - self.assertEqual(string.serialize(TaggedObject(INT64, -1)), '-1i64') - self.assertEqual(string.serialize(TaggedObject(INT64, 42)), '42i64') + self.assertEqual(string.serialize(don.TaggedObject(don.INT64, 1)), '1i64') + self.assertEqual(string.serialize(don.TaggedObject(don.INT64, -1)), '-1i64') + self.assertEqual(string.serialize(don.TaggedObject(don.INT64, 42)), '42i64') def test_serializes_float(self): - self.assertEqual(string.serialize(TaggedObject(FLOAT, 1.0)), '1.0f') + self.assertEqual(string.serialize(don.TaggedObject(don.FLOAT, 1.0)), '1.0f') def test_serializes_double(self): - self.assertEqual(string.serialize(TaggedObject(DOUBLE, 1.0)), '1.0d') + self.assertEqual(string.serialize(don.TaggedObject(don.DOUBLE, 1.0)), '1.0d') def test_serializes_binary(self): - self.assertEqual(string.serialize(TaggedObject(BINARY, b'\xde\xad\xbe\xef')), '"deadbeef"b') + self.assertEqual(string.serialize(don.TaggedObject(don.BINARY, b'\xde\xad\xbe\xef')), '"deadbeef"b') def test_serializes_utf8(self): - self.assertEqual(string.serialize(TaggedObject(UTF8, 'Hello, world')), '"Hello, world"utf8') + self.assertEqual(string.serialize(don.TaggedObject(don.UTF8, 'Hello, world')), '"Hello, world"utf8') def test_serializes_utf16(self): - self.assertEqual(string.serialize(TaggedObject(UTF16, 'Hello, world')), '"Hello, world"utf16') + self.assertEqual(string.serialize(don.TaggedObject(don.UTF16, 'Hello, world')), '"Hello, world"utf16') def test_serializes_utf32(self): - self.assertEqual(string.serialize(TaggedObject(UTF32, 'Hello, world')), '"Hello, world"utf32') + self.assertEqual(string.serialize(don.TaggedObject(don.UTF32, 'Hello, world')), '"Hello, world"utf32') def test_serializes_list(self): - self.assertEqual(string.serialize(TaggedObject(LIST, [1,2,3])), '[1i32, 2i32, 3i32]') + self.assertEqual(string.serialize(don.TaggedObject(don.LIST, [1,2,3])), '[1i32, 2i32, 3i32]') def test_serializes_dictionary(self): self.assertEqual( - string.serialize(TaggedObject(DICTIONARY, collections.OrderedDict([ + string.serialize(don.TaggedObject(don.DICTIONARY, collections.OrderedDict([ ('foo', 1), ('bar', 'baz'), ]))), '{ "foo"utf8: 1i32, "bar"utf8: "baz"utf8 }' ) -class TestStringDeserialize(unittest.TestCase): - pass +# class TestStringDeserialize(unittest.TestCase): +# def test_deserializes_null(self): +# self.assertEqual( +# None, +# string.deserialize('null'), +# ) +# +# def test_deserializes_null(self): +# self.assertEqual( +# True, +# string.deserialize('true'), +# ) +# +# def test_deserializes_null(self): +# self.assertEqual( +# False, +# string.deserialize('false'), +# ) unittest.main()