[px] PXs can be called by other PXs.

This commit is contained in:
Gaetan Delannay 2013-03-22 12:52:24 +01:00
parent bfbf9bea82
commit 2a145ac890
5 changed files with 52 additions and 23 deletions

View file

@ -1,4 +1,4 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <!DOCTYPE html>
<html metal:define-macro="main" <html metal:define-macro="main"
tal:define="user tool/getUser; tal:define="user tool/getUser;
isAnon tool/userIsAnon; isAnon tool/userIsAnon;

View file

@ -69,7 +69,12 @@ class BufferAction:
if self.buffer.caller() == 'px': if self.buffer.caller() == 'px':
# Add in the error message the line nb where the errors occurs # Add in the error message the line nb where the errors occurs
# within the PX. # within the PX.
import pdb; pdb.set_trace() locator = self.buffer.env.parser.locator
# The column number may not be given.
col = locator.getColumnNumber()
if col == None: col = ''
else: col = ', column %d' % col
errorMessage += ' (line %s%s)' % (locator.getLineNumber(), col)
raise Exception(errorMessage) raise Exception(errorMessage)
# Empty the buffer # Empty the buffer
self.buffer.__init__(self.buffer.env, self.buffer.parent) self.buffer.__init__(self.buffer.env, self.buffer.parent)
@ -274,7 +279,7 @@ class VariablesAction(BufferAction):
# every defined variable. # every defined variable.
BufferAction.__init__(self, name, buffer, None, elem, minus, source, BufferAction.__init__(self, name, buffer, None, elem, minus, source,
fromExpr) fromExpr)
# Definitions of variables: ~{s_name: s_expr}~ # Definitions of variables: ~[(s_name, s_expr)]~
self.variables = variables self.variables = variables
def do(self): def do(self):
@ -286,7 +291,7 @@ class VariablesAction(BufferAction):
# hide in the context: after execution of this buffer we will restore # hide in the context: after execution of this buffer we will restore
# those values. # those values.
hidden = None hidden = None
for name, expr in self.variables.iteritems(): for name, expr in self.variables:
# Evaluate the expression # Evaluate the expression
result, error = self.evaluateExpression(expr) result, error = self.evaluateExpression(expr)
if error: return if error: return
@ -303,7 +308,7 @@ class VariablesAction(BufferAction):
# Restore hidden variables if any # Restore hidden variables if any
if hidden: context.update(hidden) if hidden: context.update(hidden)
# Delete not-hidden variables # Delete not-hidden variables
for name in self.variables.iterkeys(): for name, expr in self.variables:
if hidden and (name in hidden): continue if hidden and (name in hidden): continue
del context[name] del context[name]
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -140,7 +140,7 @@ class Buffer:
def getLength(self): pass # To be overridden def getLength(self): pass # To be overridden
def dumpStartElement(self, elem, attrs={}, ignoreAttrs=(), def dumpStartElement(self, elem, attrs={}, ignoreAttrs=(),
insertAttributesHook=False): insertAttributesHook=False, noEndTag=False):
'''Inserts into this buffer the start tag p_elem, with its p_attrs, '''Inserts into this buffer the start tag p_elem, with its p_attrs,
excepted those listed in p_ignoreAttrs. If p_insertAttributesHook excepted those listed in p_ignoreAttrs. If p_insertAttributesHook
is True (works only for MemoryBuffers), we will insert an Attributes is True (works only for MemoryBuffers), we will insert an Attributes
@ -162,7 +162,12 @@ class Buffer:
res = self.addAttributes() res = self.addAttributes()
else: else:
res = None res = None
self.write('>') # Close the tag
if noEndTag:
suffix = '/>'
else:
suffix = '>'
self.write(suffix)
return res return res
def dumpEndElement(self, elem): def dumpEndElement(self, elem):
@ -201,7 +206,9 @@ class FileBuffer(Buffer):
def addExpression(self, expression, tiedHook=None): def addExpression(self, expression, tiedHook=None):
# At 2013-02-06, this method was not called within the whole test suite. # At 2013-02-06, this method was not called within the whole test suite.
try: try:
self.dumpContent(Expression(expression).evaluate(self.env.context)) res, escape = Expression(expression).evaluate(self.env.context)
if escape: self.dumpContent(res)
else: self.write(res)
except Exception, e: except Exception, e:
PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False) PodError.dump(self, EVAL_EXPR_ERROR % (expression, e), dumpTb=False)
@ -360,15 +367,15 @@ class MemoryBuffer(Buffer):
return attrs return attrs
def _getVariables(self, expr): def _getVariables(self, expr):
'''Returns variable definitions in p_expr as a dict '''Returns variable definitions in p_expr as a list
~{s_varName: s_expr}~.''' ~[(s_varName, s_expr)]~.'''
exprs = expr.strip().split(';') exprs = expr.strip().split(';')
res = {} res = []
for sub in exprs: for sub in exprs:
varRes = MemoryBuffer.varRex.match(sub) varRes = MemoryBuffer.varRex.match(sub)
if not varRes: if not varRes:
raise ParsingError(BAD_VAR_EXPRESSION % sub) raise ParsingError(BAD_VAR_EXPRESSION % sub)
res[varRes.group(1)] = varRes.group(2) res.append(varRes.groups())
return res return res
def createAction(self, statementGroup): def createAction(self, statementGroup):
@ -624,7 +631,9 @@ class MemoryBuffer(Buffer):
currentIndex = index + 1 currentIndex = index + 1
if isinstance(evalEntry, Expression): if isinstance(evalEntry, Expression):
try: try:
result.dumpContent(evalEntry.evaluate(self.env.context)) res, escape = evalEntry.evaluate(self.env.context)
if escape: result.dumpContent(res)
else: result.write(res)
except Exception, e: except Exception, e:
if self.caller() == 'pod': if self.caller() == 'pod':
PodError.dump(result, EVAL_EXPR_ERROR % ( PodError.dump(result, EVAL_EXPR_ERROR % (

View file

@ -89,7 +89,13 @@ class Expression(PodElement):
def evaluate(self, context): def evaluate(self, context):
'''Evaluates the Python expression (self.expr) with a given '''Evaluates the Python expression (self.expr) with a given
p_context.''' p_context, and returns the result. More precisely, it returns a
tuple (result, escapeXml). Boolean escapeXml indicates if XML chars
must be escaped or not. For example, if the expression's result is a
PX, the result of evaluating it (a chunk of XHTML) must be inserted
as is, unescaped, into the buffer. In most situations, XML escaping
will be enabled.'''
escapeXml = True
# Evaluate the expression, or get it from self.result if it has already # Evaluate the expression, or get it from self.result if it has already
# been computed. # been computed.
if self.evaluated: if self.evaluated:
@ -103,16 +109,22 @@ class Expression(PodElement):
# Evaluates the Python expression # Evaluates the Python expression
res = self.result = eval(self.expr, context) res = self.result = eval(self.expr, context)
# Converts the expression result to a string that can be inserted into # Converts the expression result to a string that can be inserted into
# the POD result. # the POD/PX result.
if res == None: resultType = res.__class__.__name__
if resultType == 'NoneType':
res = u'' res = u''
elif isinstance(res, str): elif resultType == 'str':
res = unicode(res.decode('utf-8')) res = res.decode('utf-8')
elif isinstance(res, unicode): elif resultType == 'unicode':
pass pass # Don't perform any conversion, unicode is the target type.
elif resultType == 'Px':
# A PX that must be called within the current PX. Call it with the
# current context.
res = res(context)
escapeXml = False
else: else:
res = unicode(res) res = unicode(res)
return res return res, escapeXml
class Attributes(PodElement): class Attributes(PodElement):
'''Represents a bunch of XML attributes that will be dumped for a given tag '''Represents a bunch of XML attributes that will be dumped for a given tag

View file

@ -54,6 +54,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') pxAttributes = ('var', 'for', 'if')
# No-end tags
noEndTags = ('br', 'img')
def __init__(self, env, caller=None): def __init__(self, env, caller=None):
XmlParser.__init__(self, env, caller) XmlParser.__init__(self, env, caller)
@ -78,7 +80,7 @@ class PxParser(XmlParser):
e.currentBuffer.addElement(elem, elemType='px') e.currentBuffer.addElement(elem, elemType='px')
if elem != 'x': if elem != 'x':
e.currentBuffer.dumpStartElement(elem, attrs, e.currentBuffer.dumpStartElement(elem, attrs,
ignoreAttrs=self.pxAttributes) ignoreAttrs=self.pxAttributes, noEndTag=elem in self.noEndTags)
def endElement(self, elem): def endElement(self, elem):
e = self.env e = self.env
@ -88,7 +90,8 @@ class PxParser(XmlParser):
e.currentBuffer.addExpression(e.currentContent) e.currentBuffer.addExpression(e.currentContent)
e.currentContent = '' e.currentContent = ''
# Dump the end element into the current buffer # Dump the end element into the current buffer
if elem != 'x': e.currentBuffer.dumpEndElement(elem) if (elem != 'x') and (elem not in self.noEndTags):
e.currentBuffer.dumpEndElement(elem)
# If this element is the main element of the current buffer, we must # If this element is the main element of the current buffer, we must
# pop it and continue to work in the parent buffer. # pop it and continue to work in the parent buffer.
if e.isActionElem(elem): if e.isActionElem(elem):