X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=t%2Fmoe%2Fconf.py;h=994214124aeb3a1add5afea3e229ec63f913a6bd;hb=5558e9844b447b1f00549bc2997475ffd25b5a5d;hp=e5c3b385bc4c43c86d3a926d8c6ff832e3df0544;hpb=16cdbe07d6da578e69dcbc35115abe90ab4b476e;p=moe.git diff --git a/t/moe/conf.py b/t/moe/conf.py index e5c3b38..9942141 100644 --- a/t/moe/conf.py +++ b/t/moe/conf.py @@ -1,75 +1,37 @@ -import types, itertools, re -import logging as log - """ -Lazy conditional string evaluation module for configuration variables. +conf.py +------- +Lazy conditional string evaluation module for Moe configuration variables. -* Each variable has ordered list of operations (definitions), each SETs or APPENDs an expression -to the value. Each operation may be guarded by condition. -NOTE: Variable is undefined even if some 'APPEND' apply but no 'SET' applies. This might change. +* 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. +NOTE: If no 'SET' applies, a variable is still undefined even if some 'APPEND' applies. This might change. + +* 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: All expanded data should be (or is converted to) unicode -TODO: Fixing variables. + +TODO: Fixing value of variables. TODO: Cleanup of unused undefined variables. TODO: Better variable name checking (no name '.'-structural prefix of another) TODO: Implemet "subtree" listing. TODO: Test conditions and unicode """ -""" -The configuration syntax is the following (TODO: add whitespaces WSP) -TODO: decide '()' around formulas -TODO: check escaping in expressions -TODO: should whitespace (incl. '\\n') be allowed (almost) everywhere? - can comment be anywhere whitespace can? - -FILE = BLOCK -BLOCK + '\\n' = () | STATEMENT ( STATEMENT-SEP STATEMENT )* - -STATEMENT-SEP = ( '\\n' | ';' ) -WSP = ( ' ' | '\\t' | '\\n' | COMMENT )* - -COMMENT = re'#[^\\n]*\\n' - -STATEMENT = CONDITION | OPERATION | SUBTREE - -OPERATION = VARNAME ( '=' | '+=' ) EXPRESSION -SUBTREE = VARNAME '{' BLOCK '}' -CONDITION = 'if' FORMULA '{' BLOCK '}' - -FORMULA = ( EXPRESSION ( '!=' | '==' ) EXPRESSION ) | '(' AND ')' | '(' OR ')' | NOT -AND = FORMULA 'and' FORMULA -OR = FORMULA 'or' FORMULA -NOT = 'not' FORMULA - -EXPRESSION = '"' ( ECHAR | '{' VARNAME '}' )* '"' | re"'[^'\\n]*'" -ECHAR = re'([^\\{}]|\\\\|\\{|\\}|\\n)*' -""" - - - - -c_tree_sep = u'.' -c_comment = u'#' -c_open = u'{' -c_close = u'}' -c_if = u'if' - -"Variable name regexp, dots (separators) must be separated from edges and each other." -re_key = re.compile(r'\A([A-Za-z0-9_-]+\.)*[A-Za-z0-9_-]+\Z') +import types, itertools, re +import logging as log +from confparser import VARNAME_re -"Allowed depth of recursion -- includes ALL recursive calls, so should quite high." +"Allowed depth of recursion - includes ALL recursive calls, so should quite high." c_maxdepth = 256 -"Maximum attained depth of recursion" +"Maximum attained depth of recursion - for debug/testing" debug_maxdepth = 0 def check_depth(depth): @@ -101,7 +63,7 @@ class ConfigTree(object): if not key in self.variables: if not create: raise ConfigError('Config variable %r undefined.', key) - if not re_key.match(key): + if not VARNAME_re.match(key): raise ConfigError('Invalid variable identifier %r in config', key) self.variables[key] = ConfigVar(key) return self.variables[key] @@ -156,13 +118,15 @@ class ConfigCondition(ConfigElem): ('AND', c1, c1), ('OR', c1, c2), ('NOT', c1), ('==', e1, e2), ('!=', e1, e2) where e1, e2 are `ConfigExpression`s. """ - def __init__(self, text, clause, parent=None): + def __init__(self, formula, text=None, parent=None): """ - Condition defined by `text` (informative), `clause` as in class definition, + Condition defined by `text` (informative), `formula` as in class definition, `parent` is the parent condition (if any). """ + if not text: + text = self.formula_string(formula) super(ConfigVar, self).__init__(text) - self.clause = clause + self.formula = formula self.parent = parent # Setup dependencies on used variables (not on the parent condition) for v in self.variables(): @@ -170,9 +134,9 @@ class ConfigCondition(ConfigElem): if self.parent: self.parent.dependants.add(self) def variables(self, cl=None): - "Return an iterator of variables used in clause `cl`" + "Return an iterator of variables used in formula `cl`" if not cl: - cl = self.clause + cl = self.formula if cl[0] in ['==','!=']: return itertools.chain(cl[1].variables(), cl[2].variables()) if cl[0] in ['AND','OR']: @@ -185,11 +149,11 @@ class ConfigCondition(ConfigElem): if self.parent: self.parent.dependants.discard(self) def evaluate(self, cl=None, depth=0): - """Evaluate clause `cl` (or the entire condition). + """Evaluate formula `cl` (or the entire condition). Partial evaluation for AND and OR. Tests the parent condition first.""" check_depth(depth) if not cl: - cl = self.clause + cl = self.formula if self.parent and not self.parent.value(): return False if cl[0] in ['==','!=']: @@ -202,6 +166,17 @@ class ConfigCondition(ConfigElem): if cl[0] == 'OR' and v1: return True if cl[0] == 'AND' and not v1: return False return self.evaluate(cl[2], depth+1) + def formula_string(self, formula): + "Create a string representation of a formula." + if formula[0] == 'AND': + return itertools.chain(['('], self.formula_string(formula[1]), [' and '], self.formula_string(formula[2]),[')']) + elif formula[0] == 'OR': + return itertools.chain(['('], self.formula_string(formula[1]), [' or '], self.formula_string(formula[2]),[')']) + elif formula[0] == 'NOT': + return itertools.chain(['(not '], self.formula_string(formula[1]),[')']) + elif formula[0] in ['==', '!=']: + return itertools.chain(formula[1], formula[0], formula[2]) + return iter(['']) def str(self, parents=False): "Retur the defining expression, if `parents` set, then prefixed with parent conditions." if parents and self.parent: