"""
-config.py
----------
-
Lazy conditional string evaluation module for Moe configuration variables.
-
* Each variable has ordered list of operations (definitions), each defining operation either
assigns (SET) or appends (APPEND) value of an expression to the variable. Each operation may be guarded by condition(s).
class CyclicConfigError(ConfigError):
pass
+class ParseProxy(list):
+ """Proxy helper class around values returned by `parse` and `parse_file`,
+ useful in "with" constructs."""
+ def __init__(self, config, parsed_ops):
+ super(ParseProxy, self).__init__(parsed_ops)
+ self.config = config
+ def __enter__(self):
+ pass
+ def __exit__(self, etype, value, traceback):
+ self.config.remove(list(self))
+
class ConfigTree(object):
"""
self.variables[k].dump(prefix) for k in sorted(self.variables.keys())
])
- def parse(self, s, source=None, level=0):
- """Parse `s` (stream/string) into the tree, see `moe.confparser.ConfigParser` for details."""
- import moe.confparser
- p = moe.confparser.ConfigParser(text, self, source=source, level=level)
- p.parse()
-
- def parse_file(self, filename, desc=None, level=0):
- """Parse an utf-8 file into the tree, see `moe.confparser.ConfigParser` for details.
+ def fix(self, keys):
+ "Fix value of variable or list of variables. Fixing undefined variable raises `UndefinedError`."
+ if isinstance(keys, types.StringTypes):
+ keys = [keys]
+ for key in keys:
+ self.lookup(key, create=False).fix()
+
+ def unfix(self, keys):
+ "Unfix value of variable or list of variables. Unfixing undefined variable raises `UndefinedError`."
+ if isinstance(keys, types.StringTypes):
+ keys = [keys]
+ for key in keys:
+ self.lookup(key, create=False).unfix()
+
+ def remove(self, parsed):
+ """Given a list [(varname, `Operation`)] as returned by `parse` or `parse_file`,
+ removes the operations from the respective variables config tree.
+ Variables/operations not present int the tree raise ValueError.
+ """
+ for vname, o in parsed:
+ v = self.lookup(vname, create = True)
+ v.remove_operation(o)
+
+ def parse(self, s, source=None, level=0, proxy=True):
+ """Parse `s` (stream/string) into the tree, see `moe.config_parser.ConfigParser` for details.
+ Returns list of parset operations: [(varname, `Operation`)].
+ By default returns a proxy list-like object that can be used in "with" constructs:
+
+ with config.parse("TEST='1'"):
+ print config['TEST']
+ raise StupidError
+ """
+ import moe.config_parser
+ p = moe.config_parser.ConfigParser(s, self, source=source, level=level)
+ l = p.parse()
+ if not proxy:
+ return l
+ return ParseProxy(self, l)
+
+ def parse_file(self, filename, desc=None, level=0, proxy=True):
+ """Parse an utf-8 file into the tree using func:`parse`.
Names the source "`filename` <`desc`>". """
- f = open(filename, 'rt')
- if desc:
- filename += " <" + desc + ">"
- self.parse(f, source=filename, level=level)
+ with open(filename, 'rt') as f:
+ if desc:
+ filename += " <" + desc + ">"
+ return self.parse(f, source=filename, level=level, proxy=proxy)
class ConfigElem(object):
def variables(self):
"Return a set of variables used in the expressions"
- return set(sum([ list(op.expression.variables()) for op in self.operations ], []))
+ if not self.operations:
+ return set([])
+ return set.union(*[ op.expression.variables() for op in self.operations ])
def fix(self):
"""
self.fixed = True
def unfix(self):
- "Set the variable to be modifiable again."
+ "Make the variable modifiable again."
self.fixed = False
def value(self, depth=0):
"Handle the case when fixed, raise exc. on different evaluation"
val = super(ConfigVar,self).value(depth)
if self.fixed and self.fixed_val != val:
- raise VariableFixedError("value of var %s was fixed to %r but evaluated to %r", self.name, self.fixed_val, val)
+ raise VariableFixedError("value of var %r was fixed to %r but evaluated to %r", self.name, self.fixed_val, val)
return val
def add_operation(self, operation):
self.invalidate()
# Remove the operation
self.operations.remove(operation)
- # Remove dependencies on variables unused in other operations
+ # Remove dependencies on variables unused in other defining operations
vs = self.variables()
for v in operation.expression.variables():
if v not in vs:
self.exprlist[i] = unicode(e, 'ascii')
def variables(self):
- "Return an iterator of variables user in the expression"
- return itertools.ifilter(lambda e: isinstance(e, ConfigVar), self.exprlist)
+ "Return a set of variables used in the expression"
+ return set([e for e in self.exprlist if isinstance(e, ConfigVar)])
def __str__(self):
return self.original