TODO: Test conditions and unicode
"""
-import types, itertools, re
+import types, itertools, re, bisect
import logging as log
from confparser import re_VARNAME
def __str__(self):
return self.str(parents=False)
+class Operation(object):
+ "Helper class for operation data. Must not be present in more variables or present multiple times."
+ 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.level = level
+ self.source = source
+ def __str__(self):
+ return "%s <%d, %s> [%s] %r" % ( {'SET':'=', 'APPEND':'+'}[self.operation], self.level, self.source,
+ (self.condition and self.condition.str(parents=True)) or '', unicode(self.expression))
+
class ConfigVar(ConfigElem):
def __init__(self, name):
super(ConfigVar, self).__init__(name)
- # Ordered list of operations
- # (operation, condition, expression)
- # operation is currently 'SET' and 'APPEND'
+ # Ordered list of `Operations` (ascending by `level`)
self.operations = []
- # Fixed to value (may be None) # TODO
+ # Fixed to value (may be None)
+ # TODO: fixing
self.fixed = False
self.fixed_val = None
def variables(self):
"Return a set of variables used in the expressions"
- return set(sum([ list(e[2].variables()) for e in self.operations ], []))
- def add_operation(self, operation, condition, expression, index=None):
+ return set(sum([ list(op.expression.variables()) for op in self.operations ], []))
+ def add_operation(self, operation):
"""
- Inserts a new operation to position `index` (`None` appends).
+ Inserts an operation. The operations are sorted by `level` (ascending), new operation goes last among
+ these with the same level.
Adds the variable as a dependant of the conditions and variables used in the expressions.
"""
# Invalidate cached value
self.invalidate()
# Add the operation
- expr = (operation, condition, expression)
- if index:
- self.operations.insert(index, expr)
- else:
- self.operations.append(expr)
+ pos = bisect.bisect_right([o.level for o in self.operations], operation.level)
+ self.operations.insert(pos, operation)
# Create dependencies
- for v in expression.variables():
+ for v in operation.expression.variables():
v.dependants.add(self)
- if condition:
- condition.dependants.add(self)
- def remove_operation(self, index):
+ if operation.condition:
+ operation.condition.dependants.add(self)
+ def remove_operation(self, operation):
"""
- Remove the operation at given index.
+ Remove the Operation.
Also removes the variable as dependant from all conditions and variables used in this
- operation that are no longer used.
+ operation that are no longer used.
"""
# Invalidate cached value
self.invalidate()
# Remove the operation
- operation, condition, expression = self.operations[index]
- self.operations.pop(index)
+ self.operations.remove(operation)
# Remove dependencies on variables unused in other operations
vs = self.variables()
- for v in expression.variables():
+ for v in operation.expression.variables():
if v not in vs:
v.dependants.remove(self)
# Remove the dependency on the conditions (if not used in another operation)
- if condition and condition not in [e[1] for e in self.operations]:
+ if operation.condition and operation.condition not in [op.condition for op in self.operations]:
condition.dependants.remove(self)
def evaluate(self, depth=0):
"""
val = []
# Scan for last applicable expression - try each starting from the end, concatenate extensions
for i in range(len(self.operations)-1, -1, -1):
- operation, condition, expr = self.operations[i]
+ op = self.operations[i]
# Check the guarding condition
- if (not condition) or condition.value(depth+1):
- val.insert(0, expr.evaluate(depth+1))
- if operation == 'SET':
+ if (not op.condition) or op.condition.value(depth+1):
+ val.insert(0, op.expression.evaluate(depth+1))
+ if op.operation == 'SET':
return u''.join(val)
return None
def dump(self, prefix=''):
except ConfigError:
pass
yield prefix+u'%s = %r' % (self.name, v)
- for operation, condition, expr in self.operations:
- yield prefix+u' %s [%s] %s' % (operation, condition and condition.str(parents=True), expr)
+ for op in self.operations:
+ #yield prefix+u' %s [%s] %s' % (op.operation, op.condition and op.condition.str(parents=True), op.expression)
+ yield prefix + u' ' + unicode(op)
class ConfigExpression(object):
"""
-import conf
+import conf, confparser
import logging as log
-log.getLogger().setLevel(log.DEBUG)
+import unittest
+
+#log.getLogger().setLevel(log.DEBUG)
vcnt = 3
def cs(s):
return conf.ConfigExpression([s], s)
+# WIP
+class Test(unittest.TestCase):
+ def setUp(self):
+ self.t = conf.ConfigTree()
+ def parse(self, s, level=0, fname='test'):
+ c=confparser.ConfigParser(s, self.t, fname, level)
+ c.parse()
+ c.p_WS()
+ assert c.eof()
+ s1 = r"""a="1";b='2';c.d='\n';e="{a}{b}";e+='{c.d}';a+="\"\n\{\}";f+='Z{a.b}'#comment"""
+ def test_parser_nows(self):
+ self.parse(s1)
+
root = conf.ConfigTree()
for i in range(vcnt):
- root.lookup('a.v%d'%i).add_operation('SET', None, cs('A%d'%i))
+ root.lookup('a.v%d'%i).add_operation(conf.Operation('SET', None, cs('A%d'%i)))
b = root.lookup('b.v%d'%i)
- b.add_operation('APPEND', None, cs(' <FOO>'))
- b.add_operation('SET', None, conf.ConfigExpression([root.lookup('a.v%d'%i)], '{a.v%d}'%i))
- b.add_operation('APPEND', None, cs(' <BAR>'))
+ b.add_operation(conf.Operation('APPEND', None, cs(' <FOO>')))
+ b.add_operation(conf.Operation('SET', None, conf.ConfigExpression([root.lookup('a.v%d'%i)], '{a.v%d}'%i)))
+ b.add_operation(conf.Operation('APPEND', None, cs(' <BAR>')))
if i<vcnt-1:
- b.add_operation('APPEND', [], conf.ConfigExpression([' [', root.lookup('b.v%d'%(i+1)), ']'], ' [{b.v%d}]'%(i+1)))
+ b.add_operation(conf.Operation('APPEND', None, conf.ConfigExpression([' [', root.lookup('b.v%d'%(i+1)), ']'], ' [{b.v%d}]'%(i+1))))
print '\n'.join(root.dump())
b0 = root.lookup('b.v0')
-b0.remove_operation(1)
-b0.add_operation('SET', None, cs('NEW-B0'))
-root.lookup('b.v2').add_operation('APPEND', None, cs(' <NEW-B3>'))
-root.lookup('a.v1').add_operation('APPEND', None, cs(' <NEW-A1>'))
+b0.remove_operation(b0.operations[1])
+b0.add_operation(conf.Operation('SET', None, cs('NEW-B0')))
+root.lookup('b.v2').add_operation(conf.Operation('APPEND', None, cs(' <NEW-B3>')))
+root.lookup('a.v1').add_operation(conf.Operation('APPEND', None, cs(' <NEW-A1>')))
print '\n'.join(root.dump())
-root.lookup('a.v0').add_operation('SET', [], cs('<OVERRIDE-A0>'))
+root.lookup('a.v0').add_operation(conf.Operation('SET', None, cs('<OVERRIDE-A0>')))
print '\n'.join(root.dump())
print 'maxdepth: %d'%conf.debug_maxdepth