]> mj.ucw.cz Git - moe.git/blobdiff - t/moe/status.py
Refactor
[moe.git] / t / moe / status.py
index 86ed37edc5014b81296e64a4f2c935c0ddf629c6..144bf486f66afffaf6e272d79a74ebc7b727e43a 100644 (file)
@@ -2,14 +2,20 @@ import sys
 import types
 import re
 
 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
 
     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):
     """
 
     def __init__(self):
@@ -21,6 +27,9 @@ class Status:
     def __setitem__(self, k, v):
        self.d[k] = v
 
     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()
 
     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 + " (")
         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')
                l.append(prefix + ")")
            else:
                d = str(v).split('\n')
@@ -78,59 +87,75 @@ class Status:
            for l in self.dump():
                f.write(l+"\n")
 
            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 = {}
        """
 
        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:
            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):
        """
 
     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):
        """
 
        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:
        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
 
        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("#"):
            x = x.rstrip("\n").lstrip(" \t")
            if x=="" or x.startswith("#"):
-               pass
+               lastk = None
            else:
                sep = x.find(":")
            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)
                    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
                    k = x[:-1].rstrip(" \t")
                    new = Status()
                    this.read_val(k, new)
                    stk.append(this)
                    this = new
+                   lastk = None
                elif x == ")":
                elif x == ")":
+                   lastk = None
                    if len(stk) == 0:
                    if len(stk) == 0:
-                       raise MoeInvalidStatusFile("Parse error: incorrect nesting")
+                       raise InvalidStatusFile("Parse error: incorrect nesting")
                    else:
                        this = stk.pop()
                else:
                    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")