1 | '''
|
---|
2 | Serializer and dumper for a simple, YAMLish format (actually a YAML subset).
|
---|
3 | The top level object is a dict.
|
---|
4 | lists and dicts can contain lists, dicts, and strings.
|
---|
5 | '''
|
---|
6 |
|
---|
7 | import re
|
---|
8 |
|
---|
9 |
|
---|
10 | def dump(data, file_object):
|
---|
11 | file_object.write('\n'.join(_dump(data, 0)))
|
---|
12 |
|
---|
13 |
|
---|
14 | def _dump(obj, indent):
|
---|
15 | if isinstance(obj, list):
|
---|
16 | for item in obj:
|
---|
17 | yield '%s- %s' % (' ' * indent,
|
---|
18 | '\n'.join(_dump(item, indent + 2)).strip())
|
---|
19 | elif isinstance(obj, dict):
|
---|
20 | for k, v in obj.iteritems():
|
---|
21 | if isinstance(v, basestring):
|
---|
22 | yield '%s%s: %s' % (' ' * indent, k, v)
|
---|
23 | else:
|
---|
24 | extra = 0 if isinstance(v, list) else 2
|
---|
25 | yield '%s%s:\n%s' % (' ' * indent, k,
|
---|
26 | '\n'.join(_dump(v, indent + extra)))
|
---|
27 | elif isinstance(obj, basestring):
|
---|
28 | yield '%s%s' % (' ' * indent, obj)
|
---|
29 |
|
---|
30 |
|
---|
31 | spaces_re = re.compile(r'^(\s*)(.*)')
|
---|
32 | list_re = re.compile(r'^(-\s+)(.*)')
|
---|
33 | dict_re = re.compile(r'^([^-:]+):\s?(.*)')
|
---|
34 |
|
---|
35 |
|
---|
36 | def load(file_object):
|
---|
37 | # Stack of (indent level, container object)
|
---|
38 | stack = [(0, {})]
|
---|
39 | indent = lambda: stack[-1][0]
|
---|
40 | obj = lambda: stack[-1][1]
|
---|
41 |
|
---|
42 | # When a dict's value is a nested block, remember the key
|
---|
43 | parent_key = None
|
---|
44 |
|
---|
45 | for line in file_object:
|
---|
46 | spaces, line = spaces_re.match(line).groups()
|
---|
47 |
|
---|
48 | while len(spaces) < indent():
|
---|
49 | stack.pop()
|
---|
50 |
|
---|
51 | lm = list_re.match(line)
|
---|
52 | dm = dict_re.match(line)
|
---|
53 | if len(spaces) == indent():
|
---|
54 | if lm:
|
---|
55 | assert isinstance(obj(), list) or parent_key
|
---|
56 | if isinstance(obj(), dict) and parent_key:
|
---|
57 | # Starting a list in a dict
|
---|
58 | stack.append((indent(), []))
|
---|
59 | stack[-2][1][parent_key] = obj()
|
---|
60 | parent_key = None
|
---|
61 | elif dm and isinstance(obj(), list):
|
---|
62 | # Left an embedded list
|
---|
63 | stack.pop()
|
---|
64 |
|
---|
65 | if len(spaces) > indent():
|
---|
66 | assert parent_key
|
---|
67 | if dm:
|
---|
68 | # Nested dict
|
---|
69 | stack.append((len(spaces), {}))
|
---|
70 | elif lm:
|
---|
71 | # Over-indented list in a dict
|
---|
72 | stack.append((len(spaces), []))
|
---|
73 | stack[-2][1][parent_key] = obj()
|
---|
74 | parent_key = None
|
---|
75 |
|
---|
76 | while lm and lm.group(2).startswith('- '):
|
---|
77 | # Nested lists
|
---|
78 | prefix, line = lm.groups()
|
---|
79 | stack.append((indent() + len(prefix), []))
|
---|
80 | stack[-2][1].append(obj())
|
---|
81 | lm = list_re.match(line)
|
---|
82 |
|
---|
83 | if lm:
|
---|
84 | prefix, line = lm.groups()
|
---|
85 | dm = dict_re.match(line)
|
---|
86 | if dm:
|
---|
87 | stack.append((indent() + len(prefix), {}))
|
---|
88 | stack[-2][1].append(obj())
|
---|
89 | else:
|
---|
90 | obj().append(line)
|
---|
91 |
|
---|
92 | if dm:
|
---|
93 | key, value = dm.groups()
|
---|
94 | if value:
|
---|
95 | obj()[key] = value
|
---|
96 | else:
|
---|
97 | parent_key = key
|
---|
98 |
|
---|
99 | return stack[0][1]
|
---|