]> mj.ucw.cz Git - moe.git/blobdiff - t/moe/config.py
Added function config_escape
[moe.git] / t / moe / config.py
index 04e363f1887ef53446392b202a34df97faa00630..ec057e1cc3bb9edeb347a0ca9fe348fe684e3cfe 100644 (file)
@@ -1,15 +1,6 @@
 """
 """
-Lazy conditional string evaluation module for Moe configuration variables.
+Module for managing and evaluation of 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). 
-
-* Each condition is a formula (tree consisting of 'AND', 'OR', 'NOT' and '==', '!=' between two expressions.
-
-* Expression is a list of strings and variables to be expanded.
-
-.. note:: If no 'SET' applies, a variable is still undefined even if some 'APPEND' applies. This might change.
-.. note:: All expanded data should be (or is converted to) unicode 
 .. todo:: (OPT) Cleanup of unused undefined variables.
 .. todo:: (OPT) Better variable name checking (no name '.'-structural prefix of another)
 .. todo:: (OPT) Implemet "subtree" listing.
 .. todo:: (OPT) Cleanup of unused undefined variables.
 .. todo:: (OPT) Better variable name checking (no name '.'-structural prefix of another)
 .. todo:: (OPT) Implemet "subtree" listing.
@@ -41,26 +32,41 @@ def check_depth(depth):
 
 
 class ConfigError(MoeError):
 
 
 class ConfigError(MoeError):
+  "Base class for moe.config errors"
   pass
 
 class UndefinedError(ConfigError):
   pass
 
 class UndefinedError(ConfigError):
+  "Raised when no **SET** operation applies to evaluated variable."
   pass
 
 class VariableNameError(ConfigError):
   pass
 
 class VariableNameError(ConfigError):
+  "Raised on invalid config variable name."
   pass
 
 class VariableFixedError(ConfigError):
   pass
 
 class VariableFixedError(ConfigError):
+  "Raised when modifying a fixed variable"
   pass
 
 class CyclicConfigError(ConfigError):
   pass
 
 class CyclicConfigError(ConfigError):
+  "Raised when evaluation recursion is too deep"
   pass
 
 
   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):
   """
 class ConfigTree(object):
   """
-  Configuration tree containing all the variables.
-
-  The variables in `self.variables` are referenced directly by the full name.
+  Configuration environment containing the variables.
   """
 
   def __init__(self):
   """
 
   def __init__(self):
@@ -99,7 +105,14 @@ class ConfigTree(object):
     if isinstance(keys, types.StringTypes):
       keys = [keys]
     for key in keys:
     if isinstance(keys, types.StringTypes):
       keys = [keys]
     for key in keys:
-      self.lookup(key, create=True).fix()
+      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`, 
 
   def remove(self, parsed):
     """Given a list [(varname, `Operation`)] as returned by `parse` or `parse_file`, 
@@ -110,25 +123,34 @@ class ConfigTree(object):
       v = self.lookup(vname, create = True)
       v.remove_operation(o)
 
       v = self.lookup(vname, create = True)
       v.remove_operation(o)
 
-  def parse(self, s, source=None, level=0):
-    """Parse `s` (stream/string) into the tree, see `moe.confparser.ConfigParser` for details.
-    Returns list of parset operations: [(varname, `Operation`)]"""
+  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)
     import moe.config_parser
     p = moe.config_parser.ConfigParser(s, self, source=source, level=level)
-    return p.parse()
+    l = p.parse()
+    if not proxy:
+      return l
+    return ParseProxy(self, l)
 
 
-  def parse_file(self, filename, desc=None, level=0):
-    """Parse an utf-8 file into the tree, see `moe.confparser.ConfigParser` for details
+  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`>". """
     with open(filename, 'rt') as f:
       if desc: 
        filename += " <" + desc + ">" 
     Names the source "`filename` <`desc`>". """
     with open(filename, 'rt') as f:
       if desc: 
        filename += " <" + desc + ">" 
-      return self.parse(f, source=filename, level=level)
+      return self.parse(f, source=filename, level=level, proxy=proxy)
 
 
 class ConfigElem(object):
   """
 
 
 class ConfigElem(object):
   """
-  Base class for cahed config elements - variables and conditions
+  Base class for cached config elements - variables and conditions
   """
 
   def __init__(self, name):
   """
 
   def __init__(self, name):
@@ -169,11 +191,11 @@ class ConfigElem(object):
 class ConfigCondition(ConfigElem):
   """
   Condition using equality and logic operators.
 class ConfigCondition(ConfigElem):
   """
   Condition using equality and logic operators.
-  Clause is a tuple-tree in the following recursive form::
+  Formula is a tuple-tree in the following recursive form::
     
     ('AND', c1, c1), ('OR', c1, c2), ('NOT', c1), ('==', e1, e2), ('!=', e1, e2) 
     
     
     ('AND', c1, c1), ('OR', c1, c2), ('NOT', c1), ('==', e1, e2), ('!=', e1, e2) 
     
-  where e1, e2 are `ConfigExpression`, c1, c2, `ConfigCondition`.
+  where ``e1``, ``e2`` are :class:`ConfigExpression`, ``c1``, ``c2``, :class:`ConfigCondition`.
   """
 
   def __init__(self, formula, text=None, parent=None):
   """
 
   def __init__(self, formula, text=None, parent=None):
@@ -251,10 +273,15 @@ class ConfigCondition(ConfigElem):
 
 
 class Operation(object):
 
 
 class Operation(object):
-  "Helper class for operation data. Must not be present in more variables or present multiple times."
+  """
+  Helper class for operation data. Must be present at most once in at most one variable.
+
+  ``operation`` is either ``"SET"`` or ``"APPEND"``, ``condition`` is a :class:`ConfigCondition` instance or ``None``,
+  ``expression`` is a :class:`ConfigExpression` instance, ``level`` is the priority of the operation and ``source``
+  is an informative string describing the operation origin.  
+  """
 
   def __init__(self, operation, condition, expression, level=0, source='?'):
 
   def __init__(self, operation, condition, expression, level=0, source='?'):
-    # operation is currently 'SET' and 'APPEND'
     self.operation = operation
     self.condition = condition
     self.expression = expression
     self.operation = operation
     self.condition = condition
     self.expression = expression
@@ -267,6 +294,7 @@ class Operation(object):
 
 
 class ConfigVar(ConfigElem):
 
 
 class ConfigVar(ConfigElem):
+  "Class representing a single configuration variable"
 
   def __init__(self, name):
     super(ConfigVar, self).__init__(name)
 
   def __init__(self, name):
     super(ConfigVar, self).__init__(name)
@@ -277,7 +305,7 @@ class ConfigVar(ConfigElem):
     self.fixed_val = None
 
   def variables(self):
     self.fixed_val = None
 
   def variables(self):
-    "Return a set of variables used in the expressions"
+    "Return a set of variables used in the expressions of the operations"
     if not self.operations:
       return set([])
     return set.union(*[ 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 ])