]> mj.ucw.cz Git - eval.git/blobdiff - t/moe/config.py
New ConfigError subclasses, move re_VARNAME
[eval.git] / t / moe / config.py
index f2ca64136bc19c7c3e52fd6aaedf68735a4f15cb..a96fdb6207e2dea03870934573b26f39fcff001e 100644 (file)
@@ -2,40 +2,52 @@
 
 import re
 import sys
+import moe
 
-key_pattern = re.compile('^[A-Za-z0-9_-]+$')
-ref_pattern = re.compile('^[A-Za-z0-9_-]+')
+key_pattern = re.compile("^[A-Za-z0-9_-]+$")
+ref_pattern = re.compile("^[A-Za-z0-9_-]+")
 
-class MoeConfigInvalid(Exception):
+class MoeConfigInvalid(moe.MoeError):
     pass
 
-class MoeConfigEvalErr(Exception):
+class MoeConfigEvalError(moe.MoeError):
     pass
 
 class MoeConfig:
-    """Moe configuration file."""
+    """Moe configuration file. Should be immutable once a part of a stack."""
 
-    def __init__(self, file=None, name=None):
+    def __init__(self, file=None, name=None, type="<unnamed>"):
         self.vars = {}
+       self.type = type
        if file is not None:
            self.load(file)
        elif name is not None:
            self.name = name
-           self.load(open(name, 'r'))
+           try:
+               file = open(name, "r")
+           except IOError, err:
+               raise MoeConfigInvalid, "Cannot open configuration file %s: %s" % (name, err.strerror)
+           else:
+               self.load(file)
+
+    def set(self, k, v):
+       self.vars[k] = [("s", v)]
 
     def parse_line(self, x):
        x = x.rstrip("\n").lstrip(" \t")
-       if x=='' or x.startswith('#'):
+       if x=="" or x.startswith("#"):
            pass
        else:
-           sep = x.find('=')
+           sep = x.find("=")
            if sep >= 0:
                k = x[:sep]
                v = x[sep+1:]
                if k.endswith("+"):
                    k = k[:-1]
                    if not self.vars.has_key(k):
-                       self.vars[k] = [('a','')];
+                       self.vars[k] = [("a","")];
+                   else:
+                       self.vars[k] += [("s"," ")]
                else:
                    self.vars[k] = []
                if not key_pattern.match(k):
@@ -44,7 +56,7 @@ class MoeConfig:
                    v=v[1:]
                    if not v.endswith("'"):
                        raise MoeConfigInvalid, "Misquoted string"
-                   self.vars[k].append(('s', v[:-1]))
+                   self.vars[k].append(("s", v[:-1]))
                elif v.startswith('"'):
                    v=v[1:]
                    if not v.endswith('"'):
@@ -62,17 +74,17 @@ class MoeConfig:
            try:
                self.parse_line(x)
            except MoeConfigInvalid, x:
-               msg = x.message + ' at line ' + str(lino)
-               if hasattr(self, 'name'):
-                   msg += ' of ' + self.name
+               msg = x.message + " at line " + str(lino)
+               if hasattr(self, "name"):
+                   msg += " of " + self.name
                raise MoeConfigInvalid, msg
 
     def parse_interpolated(self, list, s):
-        while s<>'':
-           if s.startswith('$'):
+        while s<>"":
+           if s.startswith("$"):
                s = s[1:]
-               if s.startswith('{'):
-                   p = s.find('}')
+               if s.startswith("{"):
+                   p = s.find("}")
                    if not p:
                        raise MoeConfigInvalid, "Unbalanced braces"
                    k, s = s[1:p], s[p+1:]
@@ -84,25 +96,26 @@ class MoeConfig:
                        k, s = s[:m.end()], s[m.end():]
                    else:
                        raise MoeConfigInvalid, "Invalid variable reference"
-               list.append(('i', k))
+               list.append(("i", k))
            else:
-               p = s.find('$')
+               p = s.find("$")
                if p < 0:
                    p = len(s)
-               list.append(('s', s[:p]))
+               list.append(("s", s[:p]))
                s = s[p:]
 
-    def dump(self, file=sys.stdout):
+    def dump(self, file=sys.stdout, prefix=""):
         for k,v in self.vars.items():
+           file.write(prefix)
            file.write(k)
-           if len(v) > 0 and v[0][0] == 'a':
-               file.write('+')
+           if len(v) > 0 and v[0][0] == "a":
+               file.write("+")
                v = v[1:]
-           file.write('=')
+           file.write("=")
            for t,w in v:
-               if t == 's':
+               if t == "s":
                    file.write("'" + w + "'")
-               elif t == 'i':
+               elif t == "i":
                    file.write('"$' + w + '"')
            file.write("\n")
 
@@ -110,7 +123,6 @@ class MoeConfigStack:
     """Stack of configuration files."""
 
     def __init__(self, base=None):
-       ## FIXME: Do we need to duplicate the config files themselves?
         if base:
            self.stk = base.stk[:]
        else:
@@ -122,7 +134,7 @@ class MoeConfigStack:
 
     def __getitem__(self, k):
         if self.in_progress.has_key(k):
-           raise MoeConfigEvalErr, "Definition of $%s is recursive" % k;
+           raise MoeConfigEvalError, "Definition of $%s is recursive" % k;
        self.in_progress[k] = 1;
         v = self.do_get(k, len(self.stk)-1)
        del self.in_progress[k]
@@ -133,18 +145,20 @@ class MoeConfigStack:
            cfg = self.stk[pos]
            if cfg.vars.has_key(k):
                new = cfg.vars[k]
-               if new[0][0] == 'a':
+               if len(new) > 0 and new[0][0] == "a":
                    v = self.do_get(k, pos-1)
+                   if v != "" and not v.endswith(" "):
+                       v += " "
                else:
-                   v = ''
+                   v = ""
                for op,arg in new:
-                   if op == 's':
+                   if op == "s":
                        v = v + arg
-                   elif op == 'i':
+                   elif op == "i":
                        v = v + self[arg]
                return v
            pos -= 1
-       return ''
+       return ""
 
     def keys(self):
         seen = {}
@@ -153,31 +167,30 @@ class MoeConfigStack:
                seen[k] = None
        return seen.keys()
 
-    def dump(self, file=sys.stdout):
-       for k in self.keys():
+    def dump(self, file=sys.stdout, prefix=""):
+       for k in sorted(self.keys()):
            v = self[k]
-           file.write("%s=%s\n" % (k,v))
+           file.write("%s%s=%s\n" % (prefix,k,v))
 
-    def dump_defs(self, file=sys.stdout):
-       file.write("Configuration stack:\n")
+    def dump_defs(self, file=sys.stdout, prefix=""):
        level = 0
        for cfg in self.stk:
            level += 1
-           file.write("(level %d)\n" % level)
-           cfg.dump(file)
-       file.write("(end)\n")
+           file.write("%s(level %d: %s)\n" % (prefix,level,cfg.type))
+           cfg.dump(file, prefix + "\t")
+       file.write("%s(end)\n" % prefix)
 
     def apply_overrides(self, prefix):
         newstk = []
        for cfg in self.stk:
-           over = MoeConfig()
+           over = MoeConfig(type = cfg.type + '-overrides')
            changed = False
            for k in cfg.vars.keys():
                if k.startswith(prefix):
                    over.vars[k[len(prefix):]] = cfg.vars[k]
                    changed = True
            if changed:
-               clean = MoeConfig()
+               clean = MoeConfig(type = cfg.type)
                for k in cfg.vars.keys():
                    if not k.startswith(prefix):
                        clean.vars[k] = cfg.vars[k]
@@ -186,3 +199,13 @@ class MoeConfigStack:
            else:
                newstk.append(cfg)
        self.stk = newstk
+
+def parse_overrides(argv):
+    cfg = None
+    argv0 = argv.pop(0)
+    while len(argv) > 0 and argv[0].find("=") >= 0:
+       if cfg is None:
+           cfg = MoeConfig(type='cmdline')
+       cfg.parse_line(argv.pop(0))
+    argv.insert(0, argv0)
+    return cfg