5 key_pattern_str = "\A[A-Za-z0-9_-]+\Z"
6 key_pattern = re.compile(key_pattern_str)
8 class InvalidStatusFile(StandardError):
13 (One subtree of) a status file.
15 Each Status is a dictionary with string keys matching the specs. and
16 values either strings or nested Status subtrees.
18 The class defines `__getitem__`, `__setitem__`, `__eq__` and `keys`.
24 def __getitem__(self, k):
27 def __setitem__(self, k, v):
36 def update(self, stat2):
38 Updates values of `self` with values of `stat2`, recursively.
40 Directly references objects (values and subtrees) of `stat2`, so making a deep copy of `stat2`
41 may be necessary if you intend to modify `stat2` afterwards.
44 for k,v2 in stat2.d.items():
49 if isinstance(v, Status) != isinstance(v2, Status):
50 raise TypeError("Mixing Status and value while updating key %r"%k)
51 if isinstance(v, Status):
56 def dump(self, prefix=""):
58 Dump Status in status file format.
59 Returns a list of lines, ``prefix`` is indentation prefix.
63 for k,v in self.d.items():
64 if isinstance(v, Status):
65 l.append(prefix + k + " (")
66 l.extend(v.dump(prefix+" "))
67 l.append(prefix + ")")
69 d = str(v).split('\n')
70 l.append(prefix + k + ":" + d[0])
72 l.append(prefix + ' '*len(k) + ':' + i)
75 def write(self, f=None, name=None):
77 Write Status to File ``f`` or overwrite file ``name`` or write to ``stdout`` (otherwise).
80 if not f and name is not None:
81 with open(name, "w") as f:
90 def read(self, f=None, name=None, lines=None):
94 * or from file ``name`` opened for reading 8-bit ASCII
95 * or from ``lines`` (a list/iterator of lines)
97 Deletes all previous contents of the Status.
102 return self.do_read(f.readlines())
104 with open(name, 'r') as f:
105 return self.do_read(f.readlines())
106 if lines is not None:
107 return self.do_read(lines)
108 raise ValueError('Provide at least one parameter to Status.read()')
110 def read_val(self, k, v):
112 Internal: Safely add a new value to Status
115 if not key_pattern.match(k):
116 raise InvalidStatusFile("Parse error: invalid key %r"%k)
118 raise InvalidStatusFile("Multiple occurences of key %r"%k)
121 def do_read(self, lines):
123 Internal: Parse a status file given as list/iterator of lines
127 this = self # currently read nested Status
128 lastk = None # for multiline appending
130 x = x.rstrip("\n").lstrip(" \t")
131 if x=="" or x.startswith("#"):
135 if sep > 0: # key:value
136 k = x[:sep].rstrip(" \t")
140 elif sep == 0: # continuation of multiline :value
142 raise InvalidStatusFile("Parse error: key expected before ':'")
144 this[lastk] += '\n' + v
145 elif x.endswith("("): # close subtree
146 k = x[:-1].rstrip(" \t")
148 this.read_val(k, new)
155 raise InvalidStatusFile("Parse error: incorrect nesting")
159 raise InvalidStatusFile("Parse error: malformed line")
161 raise InvalidStatusFile("Parse error: not all subtrees closed")