7 key_pattern = re.compile("^[A-Za-z0-9_-]+$")
8 ref_pattern = re.compile("^[A-Za-z0-9_-]+")
10 class MoeConfigInvalid(moe.MoeError):
13 class MoeConfigEvalError(moe.MoeError):
17 """Moe configuration file. Should be immutable once a part of a stack."""
19 def __init__(self, file=None, name=None, type="<unnamed>"):
24 elif name is not None:
27 file = open(name, "r")
29 raise MoeConfigInvalid, "Cannot open configuration file %s: %s" % (name, err.strerror)
34 self.vars[k] = [("s", v)]
36 def parse_line(self, x):
37 x = x.rstrip("\n").lstrip(" \t")
38 if x=="" or x.startswith("#"):
47 if not self.vars.has_key(k):
48 self.vars[k] = [("a","")];
51 if not key_pattern.match(k):
52 raise MoeConfigInvalid, "Malformed name of configuration variable"
55 if not v.endswith("'"):
56 raise MoeConfigInvalid, "Misquoted string"
57 self.vars[k].append(("s", v[:-1]))
58 elif v.startswith('"'):
60 if not v.endswith('"'):
61 raise MoeConfigInvalid, "Misquoted string"
62 self.parse_interpolated(self.vars[k], v[:-1])
64 self.parse_interpolated(self.vars[k], v)
66 raise MoeConfigInvalid, "Parse error"
70 for x in file.readlines():
74 except MoeConfigInvalid, x:
75 msg = x.message + " at line " + str(lino)
76 if hasattr(self, "name"):
77 msg += " of " + self.name
78 raise MoeConfigInvalid, msg
80 def parse_interpolated(self, list, s):
87 raise MoeConfigInvalid, "Unbalanced braces"
88 k, s = s[1:p], s[p+1:]
89 if not key_pattern.match(k):
90 raise MoeConfigInvalid, "Invalid variable name"
92 m = ref_pattern.match(s)
94 k, s = s[:m.end()], s[m.end():]
96 raise MoeConfigInvalid, "Invalid variable reference"
102 list.append(("s", s[:p]))
105 def dump(self, file=sys.stdout, prefix=""):
106 for k,v in self.vars.items():
109 if len(v) > 0 and v[0][0] == "a":
115 file.write("'" + w + "'")
117 file.write('"$' + w + '"')
120 class MoeConfigStack:
121 """Stack of configuration files."""
123 def __init__(self, base=None):
124 ## FIXME: Do we need to duplicate the config files themselves?
126 self.stk = base.stk[:]
129 self.in_progress = {}
132 def reset_cache(self):
139 def __getitem__(self, k):
140 if self.cache.has_key(k):
142 if self.in_progress.has_key(k):
143 raise MoeConfigEvalError, "Definition of $%s is recursive" % k;
144 self.in_progress[k] = 1;
145 v = self.do_get(k, len(self.stk)-1)
146 del self.in_progress[k]
147 ## FIXME: This is disabled, because the immutability invariant is broken!
151 def do_get(self, k, pos):
154 if cfg.vars.has_key(k):
156 if len(new) > 0 and new[0][0] == "a":
157 v = self.do_get(k, pos-1)
172 for k in cfg.vars.keys():
176 def dump(self, file=sys.stdout, prefix=""):
177 for k in sorted(self.keys()):
179 file.write("%s%s=%s\n" % (prefix,k,v))
181 def dump_defs(self, file=sys.stdout, prefix=""):
185 file.write("%s(level %d: %s)\n" % (prefix,level,cfg.type))
186 cfg.dump(file, prefix + "\t")
187 file.write("%s(end)\n" % prefix)
189 def apply_overrides(self, prefix):
192 over = MoeConfig(type = cfg.type + '-overrides')
194 for k in cfg.vars.keys():
195 if k.startswith(prefix):
196 over.vars[k[len(prefix):]] = cfg.vars[k]
199 clean = MoeConfig(type = cfg.type)
200 for k in cfg.vars.keys():
201 if not k.startswith(prefix):
202 clean.vars[k] = cfg.vars[k]
210 def parse_overrides(argv):
213 while len(argv) > 0 and argv[0].find("=") >= 0:
215 cfg = MoeConfig(type='cmdline')
216 cfg.parse_line(argv.pop(0))
217 argv.insert(0, argv0)