6 key_pattern = re.compile("^[A-Za-z0-9_-]+$")
7 ref_pattern = re.compile("^[A-Za-z0-9_-]+")
9 class MoeConfigInvalid(Exception):
12 class MoeConfigEvalErr(Exception):
16 """Moe configuration file. Should be immutable once a part of a stack."""
18 def __init__(self, file=None, name=None, type="<unnamed>"):
23 elif name is not None:
26 file = open(name, "r")
28 raise MoeConfigInvalid, "Cannot open configuration file %s: %s" % (name, err.strerror)
33 self.vars[k] = [("s", v)]
35 def parse_line(self, x):
36 x = x.rstrip("\n").lstrip(" \t")
37 if x=="" or x.startswith("#"):
46 if not self.vars.has_key(k):
47 self.vars[k] = [("a","")];
50 if not key_pattern.match(k):
51 raise MoeConfigInvalid, "Malformed name of configuration variable"
54 if not v.endswith("'"):
55 raise MoeConfigInvalid, "Misquoted string"
56 self.vars[k].append(("s", v[:-1]))
57 elif v.startswith('"'):
59 if not v.endswith('"'):
60 raise MoeConfigInvalid, "Misquoted string"
61 self.parse_interpolated(self.vars[k], v[:-1])
63 self.parse_interpolated(self.vars[k], v)
65 raise MoeConfigInvalid, "Parse error"
69 for x in file.readlines():
73 except MoeConfigInvalid, x:
74 msg = x.message + " at line " + str(lino)
75 if hasattr(self, "name"):
76 msg += " of " + self.name
77 raise MoeConfigInvalid, msg
79 def parse_interpolated(self, list, s):
86 raise MoeConfigInvalid, "Unbalanced braces"
87 k, s = s[1:p], s[p+1:]
88 if not key_pattern.match(k):
89 raise MoeConfigInvalid, "Invalid variable name"
91 m = ref_pattern.match(s)
93 k, s = s[:m.end()], s[m.end():]
95 raise MoeConfigInvalid, "Invalid variable reference"
101 list.append(("s", s[:p]))
104 def dump(self, file=sys.stdout, prefix=""):
105 for k,v in self.vars.items():
108 if len(v) > 0 and v[0][0] == "a":
114 file.write("'" + w + "'")
116 file.write('"$' + w + '"')
119 class MoeConfigStack:
120 """Stack of configuration files."""
122 def __init__(self, base=None):
123 ## FIXME: Do we need to duplicate the config files themselves?
125 self.stk = base.stk[:]
128 self.in_progress = {}
131 def reset_cache(self):
138 def __getitem__(self, k):
139 if self.cache.has_key(k):
141 if self.in_progress.has_key(k):
142 raise MoeConfigEvalErr, "Definition of $%s is recursive" % k;
143 self.in_progress[k] = 1;
144 v = self.do_get(k, len(self.stk)-1)
145 del self.in_progress[k]
146 ## FIXME: This is disabled, because the immutability invariant is broken!
150 def do_get(self, k, pos):
153 if cfg.vars.has_key(k):
156 v = self.do_get(k, pos-1)
171 for k in cfg.vars.keys():
175 def dump(self, file=sys.stdout, prefix=""):
176 for k in sorted(self.keys()):
178 file.write("%s%s=%s\n" % (prefix,k,v))
180 def dump_defs(self, file=sys.stdout, prefix=""):
184 file.write("%s(level %d: %s)\n" % (prefix,level,cfg.type))
185 cfg.dump(file, prefix + "\t")
186 file.write("%s(end)\n" % prefix)
188 def apply_overrides(self, prefix):
191 over = MoeConfig(type = cfg.type + '-overrides')
193 for k in cfg.vars.keys():
194 if k.startswith(prefix):
195 over.vars[k[len(prefix):]] = cfg.vars[k]
198 clean = MoeConfig(type = cfg.type)
199 for k in cfg.vars.keys():
200 if not k.startswith(prefix):
201 clean.vars[k] = cfg.vars[k]
209 def parse_overrides(argv):
212 while len(argv) > 0 and argv[0].find("=") >= 0:
214 cfg = MoeConfig(type='cmdline')
215 cfg.parse_line(argv.pop(0))
216 argv.insert(0, argv0)