[pod] Added the possibility to define several variables, in 'with' statements, separated by ';'.

This commit is contained in:
Gaetan Delannay 2013-03-19 17:07:11 +01:00
parent da1f2699cd
commit d5296ba321
4 changed files with 89 additions and 53 deletions

View file

@ -53,28 +53,38 @@ class BufferAction:
# We store the result of evaluation of expr and fromExpr # We store the result of evaluation of expr and fromExpr
self.exprResult = None self.exprResult = None
self.fromExprResult = None self.fromExprResult = None
def writeError(self, errorMessage, dumpTb=True): def writeError(self, errorMessage, dumpTb=True):
# Empty the buffer # Empty the buffer
self.buffer.__init__(self.buffer.env, self.buffer.parent) self.buffer.__init__(self.buffer.env, self.buffer.parent)
PodError.dump(self.buffer, errorMessage, withinElement=self.elem, PodError.dump(self.buffer, errorMessage, withinElement=self.elem,
dumpTb=dumpTb) dumpTb=dumpTb)
self.buffer.evaluate() self.buffer.evaluate()
def evaluateExpression(self, expr):
'''Evaluates expression p_expr with the current context. Returns a tuple
(result, errorOccurred).'''
try:
res = eval(expr, self.buffer.env.context)
error = False
except:
res = None
self.writeError(EVAL_ERROR % expr)
error = True
return res, error
def execute(self): def execute(self):
# Check that if minus is set, we have an element which can accept it # Check that if minus is set, we have an element which can accept it
if self.minus and isinstance(self.elem, Table) and \ if self.minus and isinstance(self.elem, Table) and \
(not self.elem.tableInfo.isOneCell()): (not self.elem.tableInfo.isOneCell()):
self.writeError(TABLE_NOT_ONE_CELL % self.expr) self.writeError(TABLE_NOT_ONE_CELL % self.expr)
else: else:
errorOccurred = False error = False
if self.expr: if self.expr:
try: self.exprResult, error = self.evaluateExpression(self.expr)
self.exprResult = eval(self.expr, self.buffer.env.context) if not error:
except:
self.exprResult = None
self.writeError(EVAL_ERROR % self.expr)
errorOccurred = True
if not errorOccurred:
self.do() self.do()
def evaluateBuffer(self): def evaluateBuffer(self):
if self.source == 'buffer': if self.source == 'buffer':
self.buffer.evaluate(removeMainElems = self.minus) self.buffer.evaluate(removeMainElems = self.minus)
@ -243,27 +253,48 @@ class NullAction(BufferAction):
def do(self): def do(self):
self.evaluateBuffer() self.evaluateBuffer()
class VariableAction(BufferAction): class VariablesAction(BufferAction):
'''Action that allows to define a variable somewhere in the template.''' '''Action that allows to define a set of variables somewhere in the
def __init__(self, name, buffer, expr, elem, minus, varName, source, template.'''
fromExpr): def __init__(self, name, buffer, elem, minus, variables, source, fromExpr):
BufferAction.__init__(self, name, buffer, expr, elem, minus, source, # We do not use the default Buffer.expr attribute for storing the Python
# expression, because here we will have several expressions, one for
# every defined variable.
BufferAction.__init__(self, name, buffer, None, elem, minus, source,
fromExpr) fromExpr)
self.varName = varName # Name of the variable # Definitions of variables: ~{s_name: s_expr}~
self.variables = variables
# Results of executing the variables: ~{s_name: exprResult}~
self.results = {}
def do(self): def do(self):
context = self.buffer.env.context context = self.buffer.env.context
# Remember the variable hidden by our variable definition, if any # Evaluate the variables' expressions: because there are several
hasHiddenVariable = False # expressions, we did not use the standard, single-expression-minded
if context.has_key(self.varName): # BufferAction code for evaluating the expression.
hiddenVariable = context[self.varName] # Also: remember the names and values of the variables that we will hide
hasHiddenVariable = True # in the context: after execution of this buffer we will restore those
# Add the variable to the context # values in the context.
context[self.varName] = self.exprResult hidden = None
for name, expr in self.variables.iteritems():
# Evaluate the expression
result, error = self.evaluateExpression(expr)
if error: return
self.results[name] = result
# Remember the variable previous value if already in the context
if name in context:
if not hidden:
hidden = {name: context[name]}
else:
hidden[name] = context[name]
# Add our variables to the context
context.update(self.results)
# Evaluate the buffer # Evaluate the buffer
self.evaluateBuffer() self.evaluateBuffer()
# Restore hidden variable if any # Restore hidden variables if any
if hasHiddenVariable: if hidden: context.update(hidden)
context[self.varName] = hiddenVariable # Delete not-hidden variables
else: for name in self.variables.iterkeys():
del context[self.varName] if hidden and (name in hidden): continue
del context[name]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -23,7 +23,7 @@ from xml.sax.saxutils import quoteattr
from appy.shared.xml_parser import xmlPrologue, escapeXml from appy.shared.xml_parser import xmlPrologue, escapeXml
from appy.pod import PodError from appy.pod import PodError
from appy.pod.elements import * from appy.pod.elements import *
from appy.pod.actions import IfAction, ElseAction, ForAction, VariableAction, \ from appy.pod.actions import IfAction, ElseAction, ForAction, VariablesAction, \
NullAction NullAction
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -359,6 +359,18 @@ class MemoryBuffer(Buffer):
self.content += u' ' self.content += u' '
return attrs return attrs
def _getVariables(self, expr):
'''Returns variable definitions in p_expr as a dict
~{s_varName: s_expr}~.'''
exprs = expr.strip().split(';')
res = {}
for sub in exprs:
varRes = MemoryBuffer.varRex.match(sub)
if not varRes:
raise ParsingError(BAD_VAR_EXPRESSION % sub)
res[varRes.group(1)] = varRes.group(2)
return res
def createAction(self, statementGroup): def createAction(self, statementGroup):
'''Tries to create an action based on p_statementGroup. If the statement '''Tries to create an action based on p_statementGroup. If the statement
is not correct, r_ is -1. Else, r_ is the index of the element within is not correct, r_ is -1. Else, r_ is the index of the element within
@ -432,12 +444,9 @@ class MemoryBuffer(Buffer):
self.action = ForAction(statementName, self, subExpr, podElem, self.action = ForAction(statementName, self, subExpr, podElem,
minus, iter, source, fromClause) minus, iter, source, fromClause)
elif actionType == 'with': elif actionType == 'with':
varRes = MemoryBuffer.varRex.match(subExpr.strip()) variables = self._getVariables(subExpr)
if not varRes: self.action = VariablesAction(statementName, self, podElem,
raise ParsingError(BAD_VAR_EXPRESSION % subExpr) minus, variables, source, fromClause)
varName, subExpr = varRes.groups()
self.action = VariableAction(statementName, self, subExpr,
podElem, minus, varName, source, fromClause)
else: # null action else: # null action
if not fromClause: if not fromClause:
raise ParsingError(NULL_ACTION_ERROR) raise ParsingError(NULL_ACTION_ERROR)
@ -460,6 +469,10 @@ class MemoryBuffer(Buffer):
elif actionType == 'if': elif actionType == 'if':
self.action = IfAction('if', self, statement, elem, False, self.action = IfAction('if', self, statement, elem, False,
'buffer', None) 'buffer', None)
elif actionType == 'var':
variables = self._getVariables(statement)
self.action = VariablesAction('var', self, elem, False, variables,
'buffer', None)
return res return res
def cut(self, index, keepFirstPart): def cut(self, index, keepFirstPart):

View file

@ -3,9 +3,7 @@
Python and XML.''' Python and XML.'''
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
from UserDict import UserDict
from px_parser import PxParser, PxEnvironment from px_parser import PxParser, PxEnvironment
from appy.pod.renderer import BAD_CONTEXT
# Exception class -------------------------------------------------------------- # Exception class --------------------------------------------------------------
class PxError(Exception): pass class PxError(Exception): pass
@ -37,16 +35,8 @@ class Px:
self.parser.parse(self.content) self.parser.parse(self.content)
def __call__(self, context): def __call__(self, context):
# Get the context in a standardized form. # p_context must be a dict. Store it in the PX environment.
evalContext = {} self.parser.env.context = context
if hasattr(context, '__dict__'):
evalContext.update(context.__dict__)
elif isinstance(context, dict) or isinstance(context, UserDict):
evalContext.update(context)
else:
raise PxError(BAD_CONTEXT)
# Store the context on the PX environment
self.parser.env.context = evalContext
# Render the PX result and return it # Render the PX result and return it
env = self.parser.env env = self.parser.env
env.ast.evaluate() env.ast.evaluate()

View file

@ -56,6 +56,8 @@ class PxEnvironment(XmlEnvironment):
class PxParser(XmlParser): class PxParser(XmlParser):
'''PX parser that is specific for parsing PX data.''' '''PX parser that is specific for parsing PX data.'''
pxAttributes = ('var', 'for', 'if')
def __init__(self, env, caller=None): def __init__(self, env, caller=None):
XmlParser.__init__(self, env, caller) XmlParser.__init__(self, env, caller)
@ -63,14 +65,14 @@ class PxParser(XmlParser):
'''A start p_elem with p_attrs is encountered in the PX.''' '''A start p_elem with p_attrs is encountered in the PX.'''
e = self.env e = self.env
self.currentElem = elem self.currentElem = elem
if attrs.has_key('for'): # See if we have a PX attribute among p_attrs.
# Dump the element in a new sub-buffer for name in self.pxAttributes:
e.addSubBuffer() if attrs.has_key(name):
# Create the action for this buffer # Dump the element in a new sub-buffer
e.currentBuffer.createPxAction(elem, 'for', attrs['for']) e.addSubBuffer()
elif attrs.has_key('if'): # Create the action for this buffer
e.addSubBuffer() e.currentBuffer.createPxAction(elem, name, attrs[name])
e.currentBuffer.createPxAction(elem, 'if', attrs['if']) break
if e.isActionElem(elem): if e.isActionElem(elem):
# Add a temp element in the buffer (that will be unreferenced # Add a temp element in the buffer (that will be unreferenced
# later). This way, when encountering the corresponding end element, # later). This way, when encountering the corresponding end element,