Changeset 111:16d9f3a0dc29


Ignore:
Timestamp:
Sep 2, 2013, 12:45:18 PM (7 years ago)
Author:
Stefano Rivera <stefano@…>
Branch:
default
amend_source:
8a314fe69efa27db6994429ce49b0090335e05e9
Message:

Refactored as classes for readability and to make it easier to support more types

File:
1 edited

Legend:

Unmodified
Added
Removed
  • nagslang/yamlish.py

    r103 r111  
    99
    1010def dump(data, file_object):
    11     file_object.write('\n'.join(_dump(data, 0)))
     11    file_object.write(dump_s(data))
    1212
    1313
    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?(.*)')
     14def dump_s(data):
     15    return Dumper().dump(data)
    3416
    3517
    3618def 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]
     19    yaml = file_object.read()
     20    return load_s(yaml)
    4121
    42     # When a dict's value is a nested block, remember the key
    43     parent_key = None
    4422
    45     for line in file_object:
    46         spaces, line = spaces_re.match(line).groups()
     23def load_s(yaml):
     24    return Parser().parse(yaml)
    4725
    48         while len(spaces) < indent():
    49             stack.pop()
    5026
    51         lm = list_re.match(line)
    52         dm = dict_re.match(line)
    53         if len(spaces) == indent():
     27
     28class Dumper(object):
     29    def dump(self, data):
     30        return '\n'.join(self._dump(data))
     31
     32    def _dump(self, data, indent=0):
     33        for type_ in (list, dict, basestring):
     34            if isinstance(data, type_):
     35                f = getattr(self, '_dump_%s' % type_.__name__)
     36                return f(data, indent)
     37        raise NotImplementedError()
     38
     39    def _dump_list(self, data, indent):
     40        output = []
     41        for item in data:
     42            dumped = self._dump(item, indent + 2)
     43            dumped[0] = '%s- %s' % (' ' * indent, dumped[0][indent + 2:])
     44            output += dumped
     45        return output
     46
     47    def _dump_dict(self, data, indent):
     48        output = []
     49        for k, v in data.iteritems():
     50            output.append('%s%s:' % (' ' * indent, k))
     51            if isinstance(v, dict):
     52                output += self._dump(v, indent + 2)
     53            elif isinstance(v, list):
     54                output += self._dump(v, indent)
     55            else:
     56                value = self._dump(v)
     57                assert len(value) == 1
     58                output[-1] += ' %s' % value[0]
     59        return output
     60
     61    def _dump_basestring(self, data, indent):
     62        return [' ' * indent + data]
     63
     64
     65class Parser(object):
     66    _spaces_re = re.compile(r'^(\s*)(.*)')
     67    _list_re = re.compile(r'^(-\s+)(.*)')
     68    _dict_re = re.compile(r'^([^-:]+):\s?(.*)')
     69
     70    def __init__(self):
     71        # Stack of (indent level, container object)
     72        self._stack = [(0, {})]
     73        # When a dict's value is a nested block, remember the key
     74        self._parent_key = None
     75
     76    @property
     77    def _indent(self):
     78        return self._stack[-1][0]
     79
     80    @property
     81    def _container(self):
     82        return self._stack[-1][1]
     83
     84    @property
     85    def _in_list(self):
     86        return isinstance(self._container, list)
     87
     88    @property
     89    def _in_dict(self):
     90        return isinstance(self._container, dict)
     91
     92    def _push(self, container, indent=None):
     93        in_list = self._in_list
     94        assert in_list or self._parent_key
     95
     96        if indent is None:
     97            indent = self._indent
     98        self._stack.append((indent, container()))
     99        if in_list:
     100            self._stack[-2][1].append(self._container)
     101        else:
     102            self._stack[-2][1][self._parent_key] = self._container
     103            self._parent_key = None
     104
     105    def parse(self, yaml):
     106        for line in yaml.splitlines():
     107            spaces, line = self._spaces_re.match(line).groups()
     108
     109            while len(spaces) < self._indent:
     110                self._stack.pop()
     111
     112            lm = self._list_re.match(line)
     113            dm = self._dict_re.match(line)
     114            if len(spaces) == self._indent:
     115                if lm and self._in_dict:
     116                    # Starting a list in a dict
     117                    self._push(list)
     118                elif dm and self._in_list:
     119                    # Left an embedded list
     120                    self._stack.pop()
     121
     122            if len(spaces) > self._indent:
     123                assert self._parent_key
     124                if dm:
     125                    # Nested dict
     126                    self._push(dict, len(spaces))
     127                elif lm:
     128                    # Over-indented list in a dict
     129                    self._push(list, len(spaces))
     130
     131            indent = self._indent
     132            while lm and lm.group(2).startswith('- '):
     133                # Nested lists
     134                prefix, line = lm.groups()
     135                indent += len(prefix)
     136                self._push(list, indent)
     137                lm = self._list_re.match(line)
     138            del indent
     139
    54140            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()
     141                prefix, line = lm.groups()
     142                dm = self._dict_re.match(line)
     143                if dm:
     144                    self._push(dict, self._indent + len(prefix))
     145                else:
     146                    assert self._in_list
     147                    self._container.append(line)
    64148
    65         if len(spaces) > indent():
    66             assert parent_key
    67149            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
     150                key, value = dm.groups()
     151                if value:
     152                    assert self._in_dict
     153                    self._container[key] = value
     154                else:
     155                    self._parent_key = key
    75156
    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]
     157        return self._stack[0][1]
Note: See TracChangeset for help on using the changeset viewer.