# HG changeset patch # User Stefano Rivera # Date 1378133879 -7200 # Node ID 423266f0b9e457729b6ad58d93097fceca152bd4 # Parent fbb073720bace8c708ca1a7f499cf1eb15d67d7a Support inline lists, dicts, etc. And alternative top level objects diff -r fbb073720bac -r 423266f0b9e4 nagslang/tests/test_yamlish.py --- a/nagslang/tests/test_yamlish.py Mon Sep 02 17:23:13 2013 +0200 +++ b/nagslang/tests/test_yamlish.py Mon Sep 02 16:57:59 2013 +0200 @@ -48,6 +48,9 @@ def test_simple_dict(self): self.roundtrip({'foo': 'bar'}) + def test_simple_list(self): + self.roundtrip(['foo', 'bar']) + def test_dict_of_dicts(self): self.roundtrip({'foo': {'bar': 'baz'}}) @@ -61,11 +64,20 @@ } }) + def test_list_of_lists(self): + self.roundtrip(['foo', ['bar', 'baz'], 'qux']) + def test_dict_list(self): self.roundtrip({ 'foo': ['bar', 'baz'], }) + def test_list_dict(self): + self.roundtrip([ + {'foo': 'bar'}, + {'baz': 'qux', 'quux': 'corge'}, + ]) + def test_nested_lists(self): self.roundtrip({ 'foo': [['bar', 'baz', 'qux'], 'quux'], @@ -97,6 +109,13 @@ return yaml.dump(data, default_flow_style=False) +class TestFromPyYAMLInlineLists(TestRoundTrip): + def dump_s(self, data): + if yaml is None: + raise SkipTest('yaml module unavailable') + return yaml.dump(data) + + class TestToPyYAML(TestRoundTrip): def load_s(self, text): if yaml is None: diff -r fbb073720bac -r 423266f0b9e4 nagslang/yamlish.py --- a/nagslang/yamlish.py Mon Sep 02 17:23:13 2013 +0200 +++ b/nagslang/yamlish.py Mon Sep 02 16:57:59 2013 +0200 @@ -1,6 +1,6 @@ ''' Serializer and dumper for a simple, YAMLish format (actually a YAML subset). -The top level object is a dict. +The top level object is a dict or list. lists and dicts can contain: * lists, dicts, * single line strings, @@ -86,11 +86,12 @@ class Parser(object): _spaces_re = re.compile(r'^(\s*)(.*)') _list_re = re.compile(r'^(-\s+)(.*)') - _dict_re = re.compile(r'^([^-:]+):\s?(.*)') + _dict_re = re.compile(r'^((?![{[])[^-:]+):\s?(.*)') + _inline_list_re = re.compile(r"^([^',]+|''|'.+?[^'](?:'')*')(?:, (.*))?$") def __init__(self): # Stack of (indent level, container object) - self._stack = [(0, {})] + self._stack = [] # When a dict's value is a nested block, remember the key self._parent_key = None @@ -124,6 +125,14 @@ self._parent_key = None def parse(self, yaml): + if yaml.startswith(('[', '{')): + return self._parse_value(yaml) + + if yaml.startswith('-'): + self._stack.append((0, [])) + else: + self._stack.append((0, {})) + for line in yaml.splitlines(): spaces, line = self._spaces_re.match(line).groups() @@ -180,6 +189,27 @@ def _parse_value(self, value): if value.startswith("'") and value.endswith("'"): return value[1:-1] + if value.startswith('[') and value.endswith(']'): + value = value[1:-1] + output = [] + while value: + m = self._inline_list_re.match(value) + assert m + output.append(self._parse_value(m.group(1))) + value = m.group(2) + return output + if value.startswith('{') and value.endswith('}'): + value = value[1:-1] + output = {} + while value: + key, value = value.split(': ', 1) + m = self._inline_list_re.match(value) + assert m + output[key] = self._parse_value(m.group(1)) + value = m.group(2) + return output + if value.startswith('!!'): + raise NotImplemented() if value == 'true': return True if value == 'false':