From 96862790bcf36293287aba0d2211c7aa5344578c Mon Sep 17 00:00:00 2001 From: David Kerkeslager Date: Sun, 9 Oct 2016 19:21:17 -0400 Subject: [PATCH] Started implementing object serialization --- serial/serial/binary.py | 8 +++++ serial/serial/tags.py | 1 + serial/serial/text.py | 68 +++++++++++++++++++++++++++++++++++++++-- serial/test.py | 32 +++++++++++++++++++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/serial/serial/binary.py b/serial/serial/binary.py index d19a221..dd1b990 100644 --- a/serial/serial/binary.py +++ b/serial/serial/binary.py @@ -61,6 +61,9 @@ def _serialize_list(to): return struct.pack(fmt, tags.LIST, list_tag, len(payload)) + payload +def _serialize_object(to): + raise Exception('Not implemented') + _TAGS_TO_SERIALIZERS = { tags.NULL: _make_tag_only_serializer(tags.NULL, None), tags.TRUE: _make_tag_only_serializer(tags.TRUE, True), @@ -79,6 +82,7 @@ _TAGS_TO_SERIALIZERS = { tags.UTF32: _make_string_serializer(lambda s: s.encode('utf-32')), tags.TUPLE: _serialize_tuple, tags.LIST: _serialize_list, + tags.OBJECT: _serialize_object, } def serialize(to): @@ -160,6 +164,9 @@ def _deserialize_list(b): # TODO Return tags = (tags.LIST, list_tag) to function like a generic type return bytes_read, tags.TaggedObject(tag = tags.LIST, instance = instance) +def _deserialize_object(b): + raise Exception('Not implemented') + _TAGS_TO_PARSERS = { tags.NULL: _make_tag_only_parser(tags.NULL, None), tags.TRUE: _make_tag_only_parser(tags.TRUE, True), @@ -178,6 +185,7 @@ _TAGS_TO_PARSERS = { tags.UTF32: _make_string_deserializer(tags.UTF32, lambda b: b.decode('utf-32')), tags.TUPLE: _deserialize_tuple, tags.LIST: _deserialize_list, + tags.OBJECT: _deserialize_object, } def _deserialize_partial(b): diff --git a/serial/serial/tags.py b/serial/serial/tags.py index b1edfb7..4136c77 100644 --- a/serial/serial/tags.py +++ b/serial/serial/tags.py @@ -17,6 +17,7 @@ UTF16 = 0x22 UTF32 = 0x23 TUPLE = 0x30 LIST = 0x31 +OBJECT = 0x32 TaggedObject = collections.namedtuple( 'TaggedObject', diff --git a/serial/serial/text.py b/serial/serial/text.py index 2d957d6..2746ba1 100644 --- a/serial/serial/text.py +++ b/serial/serial/text.py @@ -54,6 +54,20 @@ def _serialize_list(to): return '[\n' + _indent(',\n'.join(serialize(i) for i in to.instance)) + '\n]' +def _serialize_key_value_pair(kvp): + assert isinstance(kvp, tuple) + key, value = kvp + + assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32] + + return '{}: {}'.format(serialize(key), serialize(value)) + +def _serialize_object(to): + assert isinstance(to.instance, list) + + return '{\n' + _indent(',\n'.join(_serialize_key_value_pair(kvp) for kvp in to.instance)) + '\n}' + raise Exception('Not implemented') + _SERIALIZERS = { tags.NULL: _make_literal_serializer(None, 'null'), tags.TRUE: _make_literal_serializer(True, 'true'), @@ -71,6 +85,7 @@ _SERIALIZERS = { tags.UTF16: _make_string_serializer('utf16'), tags.UTF32: _make_string_serializer('utf32'), tags.LIST: _serialize_list, + tags.OBJECT: _serialize_object, } def serialize(to): @@ -181,8 +196,6 @@ def _make_string_deserializer(tag, prefix): return deserializer def _deserialize_list(s): - s = s.lstrip() - if not s.startswith('['): return False, None, None @@ -213,6 +226,54 @@ def _deserialize_list(s): assert s.startswith(']') return True, tags.TaggedObject(tag = tags.LIST, instance = instance), s[1:] +def _deserialize_object(s): + if not s.startswith('{'): + return False, None, None + + instance = [] + + s = s[1:].lstrip() + + # TODO Handle empty objects + + succeeded, key, s = _deserialize_one(s) + + assert succeeded + assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32] + + s = s.lstrip() + assert s.startswith(':') + + s = s[1:].lstrip() + + succeeded, value, s = _deserialize_one(s) + + assert succeeded + instance.append((key, value)) + + s = s.lstrip() + + while s.startswith(','): + succeeded, key, s = _deserialize_one(s) + + assert succeeded + assert key.tag in [tags.UTF8, tags.UTF16, tags.UTF32] + + s = s.lstrip() + assert s.startswith(':') + + s = s[1:].lstrip() + + succeeded, value, s = _deserialize_one(s) + + assert succeeded + instance.append((key, value)) + + s = s.lstrip() + + assert s.startswith('}') + return True, tags.TaggedObject(tag = tags.LIST, instance = instance), s[1:] + _DESERIALIZERS = [ _make_literal_deserializer(tags.NULL, None, 'null'), _make_literal_deserializer(tags.TRUE, True, 'true'), @@ -230,9 +291,12 @@ _DESERIALIZERS = [ _make_string_deserializer(tags.UTF16, 'utf16'), _make_string_deserializer(tags.UTF32, 'utf32'), _deserialize_list, + _deserialize_object, ] def _deserialize_one(s): + s = s.lstrip() + for deserializer in _DESERIALIZERS: succeeded, result, remaining = deserializer(s) diff --git a/serial/test.py b/serial/test.py index 239c694..72c7b35 100644 --- a/serial/test.py +++ b/serial/test.py @@ -53,6 +53,22 @@ EXAMPLE_BINARY_REPRESENTATIONS = [ ), b'\x31\x03\x00\x00\x00\x03\x09\x16\x24', ), + ( + tags.TaggedObject( + tag = tags.OBJECT, + instance = [ + ( + tags.TaggedObject(tag = tags.UTF8, instance = 'foo'), + tags.TaggedObject(tag = tags.UTF8, instance = 'bar'), + ), + ( + tags.TaggedObject(tag = tags.UTF8, instance = 'baz'), + tags.TaggedObject(tag = tags.UINT8, instance = 42), + ), + ], + ), + b'\x32\x21\x00\x00\x00\x03foo\x21\x00\x00\x00\x03bar\x00\x00\x00\x03baz\x03\x2a', + ), ] class BinarySerializeTests(unittest.TestCase): @@ -98,6 +114,22 @@ EXAMPLE_TEXT_REPRESENTATIONS = [ ), '[\n 9u8,\n 22u8,\n 36u8\n]' ), + ( + tags.TaggedObject( + tag = tags.OBJECT, + instance = [ + ( + tags.TaggedObject(tag = tags.UTF8, instance = 'foo'), + tags.TaggedObject(tag = tags.UTF8, instance = 'bar'), + ), + ( + tags.TaggedObject(tag = tags.UTF8, instance = 'baz'), + tags.TaggedObject(tag = tags.UINT8, instance = 42), + ), + ], + ), + '{\n utf8"foo": utf8"bar",\n utf8"baz": 42u8\n}', + ), ] class TextSerializeTests(unittest.TestCase): -- 2.20.1