diff --git a/pod/actions.py b/pod/actions.py index 7e80303..84708fb 100644 --- a/pod/actions.py +++ b/pod/actions.py @@ -53,28 +53,38 @@ class BufferAction: # We store the result of evaluation of expr and fromExpr self.exprResult = None self.fromExprResult = None + def writeError(self, errorMessage, dumpTb=True): # Empty the buffer self.buffer.__init__(self.buffer.env, self.buffer.parent) PodError.dump(self.buffer, errorMessage, withinElement=self.elem, dumpTb=dumpTb) 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): # Check that if minus is set, we have an element which can accept it if self.minus and isinstance(self.elem, Table) and \ (not self.elem.tableInfo.isOneCell()): self.writeError(TABLE_NOT_ONE_CELL % self.expr) else: - errorOccurred = False + error = False if self.expr: - try: - self.exprResult = eval(self.expr, self.buffer.env.context) - except: - self.exprResult = None - self.writeError(EVAL_ERROR % self.expr) - errorOccurred = True - if not errorOccurred: + self.exprResult, error = self.evaluateExpression(self.expr) + if not error: self.do() + def evaluateBuffer(self): if self.source == 'buffer': self.buffer.evaluate(removeMainElems = self.minus) @@ -243,27 +253,48 @@ class NullAction(BufferAction): def do(self): self.evaluateBuffer() -class VariableAction(BufferAction): - '''Action that allows to define a variable somewhere in the template.''' - def __init__(self, name, buffer, expr, elem, minus, varName, source, - fromExpr): - BufferAction.__init__(self, name, buffer, expr, elem, minus, source, +class VariablesAction(BufferAction): + '''Action that allows to define a set of variables somewhere in the + template.''' + def __init__(self, name, buffer, elem, minus, variables, source, fromExpr): + # 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) - 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): context = self.buffer.env.context - # Remember the variable hidden by our variable definition, if any - hasHiddenVariable = False - if context.has_key(self.varName): - hiddenVariable = context[self.varName] - hasHiddenVariable = True - # Add the variable to the context - context[self.varName] = self.exprResult + # Evaluate the variables' expressions: because there are several + # expressions, we did not use the standard, single-expression-minded + # BufferAction code for evaluating the expression. + # Also: remember the names and values of the variables that we will hide + # in the context: after execution of this buffer we will restore those + # values in the context. + 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 self.evaluateBuffer() - # Restore hidden variable if any - if hasHiddenVariable: - context[self.varName] = hiddenVariable - else: - del context[self.varName] + # Restore hidden variables if any + if hidden: context.update(hidden) + # Delete not-hidden variables + for name in self.variables.iterkeys(): + if hidden and (name in hidden): continue + del context[name] # ------------------------------------------------------------------------------ diff --git a/pod/buffers.py b/pod/buffers.py index 0c7df0a..929d2f1 100644 --- a/pod/buffers.py +++ b/pod/buffers.py @@ -23,7 +23,7 @@ from xml.sax.saxutils import quoteattr from appy.shared.xml_parser import xmlPrologue, escapeXml from appy.pod import PodError from appy.pod.elements import * -from appy.pod.actions import IfAction, ElseAction, ForAction, VariableAction, \ +from appy.pod.actions import IfAction, ElseAction, ForAction, VariablesAction, \ NullAction # ------------------------------------------------------------------------------ @@ -359,6 +359,18 @@ class MemoryBuffer(Buffer): self.content += u' ' 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): '''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 @@ -432,12 +444,9 @@ class MemoryBuffer(Buffer): self.action = ForAction(statementName, self, subExpr, podElem, minus, iter, source, fromClause) elif actionType == 'with': - varRes = MemoryBuffer.varRex.match(subExpr.strip()) - if not varRes: - raise ParsingError(BAD_VAR_EXPRESSION % subExpr) - varName, subExpr = varRes.groups() - self.action = VariableAction(statementName, self, subExpr, - podElem, minus, varName, source, fromClause) + variables = self._getVariables(subExpr) + self.action = VariablesAction(statementName, self, podElem, + minus, variables, source, fromClause) else: # null action if not fromClause: raise ParsingError(NULL_ACTION_ERROR) @@ -460,6 +469,10 @@ class MemoryBuffer(Buffer): elif actionType == 'if': self.action = IfAction('if', self, statement, elem, False, 'buffer', None) + elif actionType == 'var': + variables = self._getVariables(statement) + self.action = VariablesAction('var', self, elem, False, variables, + 'buffer', None) return res def cut(self, index, keepFirstPart): diff --git a/px/__init__.py b/px/__init__.py index 37f564e..f572baa 100644 --- a/px/__init__.py +++ b/px/__init__.py @@ -3,9 +3,7 @@ Python and XML.''' # ------------------------------------------------------------------------------ -from UserDict import UserDict from px_parser import PxParser, PxEnvironment -from appy.pod.renderer import BAD_CONTEXT # Exception class -------------------------------------------------------------- class PxError(Exception): pass @@ -37,16 +35,8 @@ class Px: self.parser.parse(self.content) def __call__(self, context): - # Get the context in a standardized form. - evalContext = {} - 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 + # p_context must be a dict. Store it in the PX environment. + self.parser.env.context = context # Render the PX result and return it env = self.parser.env env.ast.evaluate() diff --git a/px/px_parser.py b/px/px_parser.py index fd84119..16c72a6 100644 --- a/px/px_parser.py +++ b/px/px_parser.py @@ -56,6 +56,8 @@ class PxEnvironment(XmlEnvironment): class PxParser(XmlParser): '''PX parser that is specific for parsing PX data.''' + pxAttributes = ('var', 'for', 'if') + def __init__(self, env, caller=None): XmlParser.__init__(self, env, caller) @@ -63,14 +65,14 @@ class PxParser(XmlParser): '''A start p_elem with p_attrs is encountered in the PX.''' e = self.env self.currentElem = elem - if attrs.has_key('for'): - # Dump the element in a new sub-buffer - e.addSubBuffer() - # Create the action for this buffer - e.currentBuffer.createPxAction(elem, 'for', attrs['for']) - elif attrs.has_key('if'): - e.addSubBuffer() - e.currentBuffer.createPxAction(elem, 'if', attrs['if']) + # See if we have a PX attribute among p_attrs. + for name in self.pxAttributes: + if attrs.has_key(name): + # Dump the element in a new sub-buffer + e.addSubBuffer() + # Create the action for this buffer + e.currentBuffer.createPxAction(elem, name, attrs[name]) + break if e.isActionElem(elem): # Add a temp element in the buffer (that will be unreferenced # later). This way, when encountering the corresponding end element,