X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=t%2Fmoe%2Fstatus.py;h=144bf486f66afffaf6e272d79a74ebc7b727e43a;hb=420a49640eb8a89b578c390a939bc0ba818f9a7e;hp=86ed37edc5014b81296e64a4f2c935c0ddf629c6;hpb=0fda8189b956eb8d024625d7b2d596e9aeabb073;p=moe.git diff --git a/t/moe/status.py b/t/moe/status.py index 86ed37e..144bf48 100644 --- a/t/moe/status.py +++ b/t/moe/status.py @@ -2,14 +2,20 @@ import sys import types import re -key_pattern = re.compile("\A[A-Za-z0-9_-]+\Z") +key_pattern_str = "\A[A-Za-z0-9_-]+\Z" +key_pattern = re.compile(key_pattern_str) -class MoeInvalidStatusFile(Exception): +class InvalidStatusFile(StandardError): pass -class Status: +class Status(object): """ - (One subtree of) Moe status file. + (One subtree of) a status file. + + Each Status is a dictionary with string keys matching the specs. and + values either strings or nested Status subtrees. + + The class defines `__getitem__`, `__setitem__`, `__eq__` and `keys`. """ def __init__(self): @@ -21,6 +27,9 @@ class Status: def __setitem__(self, k, v): self.d[k] = v + def __eq__(self, s): + return self.d == s.d + def keys(self): return self.d.keys() @@ -54,7 +63,7 @@ class Status: for k,v in self.d.items(): if isinstance(v, Status): l.append(prefix + k + " (") - l.extend(self.dump(prefix+" ")) + l.extend(v.dump(prefix+" ")) l.append(prefix + ")") else: d = str(v).split('\n') @@ -78,59 +87,75 @@ class Status: for l in self.dump(): f.write(l+"\n") - def read(self, f=None, name=None): + def read(self, f=None, name=None, lines=None): """ - Parse Status from File ``f`` or from file ``name`` or from ``stdin`` (otherwise) - Deletes all contents of the Status. + Parse Status file + * from File ``f`` + * or from file ``name`` opened for reading 8-bit ASCII + * or from ``lines`` (a list/iterator of lines) + + Deletes all previous contents of the Status. """ self.d = {} - if not f and name is not None: + if f is not None: + return self.do_read(f.readlines()) + if name is not None: with open(name, 'r') as f: - self.do_read(f) - else: - if not f: - f = sys.stdin - self.do_read(f) + return self.do_read(f.readlines()) + if lines is not None: + return self.do_read(lines) + raise ValueError('Provide at least one parameter to Status.read()') def read_val(self, k, v): """ - Internal: Safely add a new value + Internal: Safely add a new value to Status """ if not key_pattern.match(k): - raise MoeInvalidStatusFile("Parse error: invalid key %r"%k) + raise InvalidStatusFile("Parse error: invalid key %r"%k) if k in self.d: - raise MoeInvalidStatusFile("Multiple occurences of key %r"%k) + raise InvalidStatusFile("Multiple occurences of key %r"%k) self.d[k]=v - def do_read(self, f): + def do_read(self, lines): """ - Internal: Parse an open file + Internal: Parse a status file given as list/iterator of lines """ - stk = [] - this = self - for x in f.readlines(): + stk = [] # stack + this = self # currently read nested Status + lastk = None # for multiline appending + for x in lines: x = x.rstrip("\n").lstrip(" \t") if x=="" or x.startswith("#"): - pass + lastk = None else: sep = x.find(":") - if sep >= 0: + if sep > 0: # key:value k = x[:sep].rstrip(" \t") v = x[sep+1:] this.read_val(k, v) - elif x.endswith("("): + lastk = k + elif sep == 0: # continuation of multiline :value + if not lastk: + raise InvalidStatusFile("Parse error: key expected before ':'") + v = x[sep+1:] + this[lastk] += '\n' + v + elif x.endswith("("): # close subtree k = x[:-1].rstrip(" \t") new = Status() this.read_val(k, new) stk.append(this) this = new + lastk = None elif x == ")": + lastk = None if len(stk) == 0: - raise MoeInvalidStatusFile("Parse error: incorrect nesting") + raise InvalidStatusFile("Parse error: incorrect nesting") else: this = stk.pop() else: - raise MoeInvalidStatusFile("Parse error: malformed line") + raise InvalidStatusFile("Parse error: malformed line") + if stk: + raise InvalidStatusFile("Parse error: not all subtrees closed")