[px] Made PX thread-safe.
This commit is contained in:
parent
086f93e845
commit
1d931cfb96
|
@ -37,6 +37,11 @@ class ToolMixin(BaseMixin):
|
||||||
if res in ('User', 'Group', 'Translation'): res = appName + res
|
if res in ('User', 'Group', 'Translation'): res = appName + res
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def home(self):
|
||||||
|
'''Returns the content of px ToolWrapper.pxHome.'''
|
||||||
|
tool = self.appy()
|
||||||
|
return tool.pxHome({'self': tool}).encode('utf-8')
|
||||||
|
|
||||||
def getHomePage(self):
|
def getHomePage(self):
|
||||||
'''Return the home page when a user hits the app.'''
|
'''Return the home page when a user hits the app.'''
|
||||||
# If the app defines a method "getHomePage", call it.
|
# If the app defines a method "getHomePage", call it.
|
||||||
|
|
|
@ -5,6 +5,7 @@ from appy.gen.mail import sendMail
|
||||||
from appy.shared.utils import executeCommand
|
from appy.shared.utils import executeCommand
|
||||||
from appy.gen.wrappers import AbstractWrapper
|
from appy.gen.wrappers import AbstractWrapper
|
||||||
from appy.gen.installer import loggedUsers
|
from appy.gen.installer import loggedUsers
|
||||||
|
from appy.px import Px
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
_PY = 'Please specify a file corresponding to a Python interpreter ' \
|
_PY = 'Please specify a file corresponding to a Python interpreter ' \
|
||||||
|
@ -19,6 +20,10 @@ NOT_UNO_ENABLED_PYTHON = '"%s" is not a UNO-enabled Python interpreter. ' \
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ToolWrapper(AbstractWrapper):
|
class ToolWrapper(AbstractWrapper):
|
||||||
|
|
||||||
|
pxHome = Px('''<p>Hello home</p>''',
|
||||||
|
template=AbstractWrapper.pxTemplate, hook='content')
|
||||||
|
|
||||||
def validPythonWithUno(self, value):
|
def validPythonWithUno(self, value):
|
||||||
'''This method represents the validator for field unoEnabledPython.'''
|
'''This method represents the validator for field unoEnabledPython.'''
|
||||||
if value:
|
if value:
|
||||||
|
|
|
@ -7,6 +7,7 @@ import appy.pod
|
||||||
from appy.gen import Type, Search, Ref, String, WorkflowAnonymous
|
from appy.gen import Type, Search, Ref, String, WorkflowAnonymous
|
||||||
from appy.gen.indexer import defaultIndexes
|
from appy.gen.indexer import defaultIndexes
|
||||||
from appy.gen.utils import createObject
|
from appy.gen.utils import createObject
|
||||||
|
from appy.px import Px
|
||||||
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
from appy.shared.utils import getOsTempFolder, executeCommand, \
|
||||||
normalizeString, sequenceTypes
|
normalizeString, sequenceTypes
|
||||||
from appy.shared.xml_parser import XmlMarshaller
|
from appy.shared.xml_parser import XmlMarshaller
|
||||||
|
@ -26,6 +27,15 @@ class AbstractWrapper(object):
|
||||||
'''Any real Appy-managed Zope object has a companion object that is an
|
'''Any real Appy-managed Zope object has a companion object that is an
|
||||||
instance of this class.'''
|
instance of this class.'''
|
||||||
|
|
||||||
|
pxTemplate = Px('''
|
||||||
|
<html>
|
||||||
|
<head><title>:self.title</title></head>
|
||||||
|
<body>
|
||||||
|
<x>:content</x>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
''', prologue=Px.xhtmlPrologue)
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
# Class methods
|
# Class methods
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
|
|
206
pod/actions.py
206
pod/actions.py
|
@ -1,20 +1,18 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Appy is a framework for building applications in the Python language.
|
# This file is part of Appy, a framework for building applications in the Python
|
||||||
# Copyright (C) 2007 Gaetan Delannay
|
# language. Copyright (C) 2007 Gaetan Delannay
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or
|
# Appy is free software; you can redistribute it and/or modify it under the
|
||||||
# modify it under the terms of the GNU General Public License
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
# as published by the Free Software Foundation; either version 2
|
# Foundation; either version 3 of the License, or (at your option) any later
|
||||||
# of the License, or (at your option) any later version.
|
# version.
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
# Appy is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License along with
|
||||||
# along with this program; if not, write to the Free Software
|
# Appy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from appy import Object
|
from appy import Object
|
||||||
|
@ -43,30 +41,24 @@ class BufferAction:
|
||||||
# case of a NullAction or ElseAction, for example)
|
# case of a NullAction or ElseAction, for example)
|
||||||
self.elem = elem # The element within the buffer that is the object
|
self.elem = elem # The element within the buffer that is the object
|
||||||
# of the action.
|
# of the action.
|
||||||
self.minus = minus # If True, the main buffer element(s) must not be
|
self.minus = minus # If True, the main elem(s) must not be dumped.
|
||||||
# dumped.
|
self.source = source # If 'buffer', we must dump the (evaluated) buffer
|
||||||
self.result = self.buffer.getRootBuffer()
|
|
||||||
self.source = source # if 'buffer', we must dump the (evaluated) buffer
|
|
||||||
# content. If 'from', we must dump what comes from the 'from' part of
|
# content. If 'from', we must dump what comes from the 'from' part of
|
||||||
# the action (='fromExpr')
|
# the action (='fromExpr')
|
||||||
self.fromExpr = fromExpr
|
self.fromExpr = fromExpr
|
||||||
# We store the result of evaluation of expr and fromExpr
|
# When an error occurs, must we raise it or write it into the buffer?
|
||||||
self.exprResult = None
|
self.raiseErrors = not self.buffer.pod
|
||||||
self.fromExprResult = None
|
|
||||||
# When an error is encountered, must we raise it or write it into the
|
|
||||||
# buffer?
|
|
||||||
self.raiseErrors = self.buffer.caller == 'px'
|
|
||||||
|
|
||||||
def getExceptionLine(self, e):
|
def getExceptionLine(self, e):
|
||||||
'''Gets the line describing exception p_e, containing the pathname of
|
'''Gets the line describing exception p_e, containing the pathname of
|
||||||
the exception class, the exception's message and line number.'''
|
the exception class, the exception's message and line number.'''
|
||||||
return '%s.%s: %s' % (e.__module__, e.__class__.__name__, str(e))
|
return '%s.%s: %s' % (e.__module__, e.__class__.__name__, str(e))
|
||||||
|
|
||||||
def writeError(self, errorMessage, dumpTb=True):
|
def manageError(self, result, context, errorMessage, dumpTb=True):
|
||||||
'''Write the encountered error into the buffer or raise an exception
|
'''Manage the encountered error: dump it into the buffer or raise an
|
||||||
if self.raiseErrors is True.'''
|
exception if self.raiseErrors is True.'''
|
||||||
if self.raiseErrors:
|
if self.raiseErrors:
|
||||||
if self.buffer.caller == 'px':
|
if not self.buffer.pod:
|
||||||
# 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.
|
||||||
locator = self.buffer.env.parser.locator
|
locator = self.buffer.env.parser.locator
|
||||||
|
@ -76,90 +68,95 @@ class BufferAction:
|
||||||
else: col = ', column %d' % col
|
else: col = ', column %d' % col
|
||||||
errorMessage += ' (line %s%s)' % (locator.getLineNumber(), col)
|
errorMessage += ' (line %s%s)' % (locator.getLineNumber(), col)
|
||||||
raise Exception(errorMessage)
|
raise Exception(errorMessage)
|
||||||
# Empty the buffer
|
# Empty the buffer (pod-only)
|
||||||
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(result, context)
|
||||||
|
|
||||||
def evaluateExpression(self, expr):
|
def evaluateExpression(self, result, context, expr):
|
||||||
'''Evaluates expression p_expr with the current context. Returns a tuple
|
'''Evaluates expression p_expr with the current p_context. Returns a
|
||||||
(result, errorOccurred).'''
|
tuple (result, errorOccurred).'''
|
||||||
try:
|
try:
|
||||||
res = eval(expr, self.buffer.env.context)
|
res = eval(expr, context)
|
||||||
error = False
|
error = False
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
res = None
|
res = None
|
||||||
self.writeError(EVAL_ERROR % (expr, self.getExceptionLine(e)))
|
errorMessage = EVAL_ERROR % (expr, self.getExceptionLine(e))
|
||||||
|
self.manageError(result, context, errorMessage)
|
||||||
error = True
|
error = True
|
||||||
return res, error
|
return res, error
|
||||||
|
|
||||||
def execute(self):
|
def execute(self, result, context):
|
||||||
|
'''Executes this action given some p_context and add the result to
|
||||||
|
p_result.'''
|
||||||
# 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.manageError(result, context, TABLE_NOT_ONE_CELL % self.expr)
|
||||||
else:
|
else:
|
||||||
error = False
|
error = False
|
||||||
|
# Evaluate self.expr in eRes.
|
||||||
|
eRes = None
|
||||||
if self.expr:
|
if self.expr:
|
||||||
self.exprResult, error = self.evaluateExpression(self.expr)
|
eRes,error = self.evaluateExpression(result, context, self.expr)
|
||||||
if not error:
|
if not error:
|
||||||
self.do()
|
# Trigger action-specific behaviour.
|
||||||
|
self.do(result, context, eRes)
|
||||||
|
|
||||||
def evaluateBuffer(self):
|
def evaluateBuffer(self, result, context):
|
||||||
if self.source == 'buffer':
|
if self.source == 'buffer':
|
||||||
self.buffer.evaluate(removeMainElems = self.minus)
|
self.buffer.evaluate(result, context, removeMainElems=self.minus)
|
||||||
else:
|
else:
|
||||||
# Evaluate fromExpr
|
# Evaluate self.fromExpr in feRes.
|
||||||
self.fromExprResult = None
|
feRes = None
|
||||||
error = False
|
error = False
|
||||||
try:
|
try:
|
||||||
self.fromExprResult= eval(self.fromExpr,self.buffer.env.context)
|
feRes = eval(self.fromExpr, context)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
msg= FROM_EVAL_ERROR % (self.fromExpr, self.getExceptionLine(e))
|
msg = FROM_EVAL_ERROR% (self.fromExpr, self.getExceptionLine(e))
|
||||||
self.writeError(msg, dumpTb=False)
|
self.manageError(result, context, msg, dumpTb=False)
|
||||||
error = True
|
error = True
|
||||||
if not error:
|
if not error:
|
||||||
self.result.write(self.fromExprResult)
|
result.write(feRes)
|
||||||
|
|
||||||
class IfAction(BufferAction):
|
class IfAction(BufferAction):
|
||||||
'''Action that determines if we must include the content of the buffer in
|
'''Action that determines if we must include the content of the buffer in
|
||||||
the result or not.'''
|
the result or not.'''
|
||||||
def do(self):
|
def do(self, result, context, exprRes):
|
||||||
if self.exprResult:
|
if exprRes:
|
||||||
self.evaluateBuffer()
|
self.evaluateBuffer(result, context)
|
||||||
else:
|
else:
|
||||||
if self.buffer.isMainElement(Cell.OD):
|
if self.buffer.isMainElement(Cell.OD):
|
||||||
# Don't leave the current row with a wrong number of cells
|
# Don't leave the current row with a wrong number of cells
|
||||||
self.result.dumpElement(Cell.OD.elem)
|
result.dumpElement(Cell.OD.elem)
|
||||||
|
|
||||||
class ElseAction(IfAction):
|
class ElseAction(IfAction):
|
||||||
'''Action that is linked to a previous "if" action. In fact, an "else"
|
'''Action that is linked to a previous "if" action. In fact, an "else"
|
||||||
action works exactly like an "if" action, excepted that instead of
|
action works exactly like an "if" action, excepted that instead of
|
||||||
defining a conditional expression, it is based on the negation of the
|
defining a conditional expression, it is based on the negation of the
|
||||||
conditional expression of the last defined "if" action.'''
|
conditional expression of the last defined "if" action.'''
|
||||||
def __init__(self, name, buffer, expr, elem, minus, source, fromExpr,
|
|
||||||
ifAction):
|
def __init__(self, name, buff, expr, elem, minus, src, fromExpr, ifAction):
|
||||||
IfAction.__init__(self, name, buffer, None, elem, minus, source,
|
IfAction.__init__(self, name, buff, None, elem, minus, src, fromExpr)
|
||||||
fromExpr)
|
|
||||||
self.ifAction = ifAction
|
self.ifAction = ifAction
|
||||||
def do(self):
|
|
||||||
# The result of this "else" action is "not <result from last execution
|
def do(self, result, context, exprRes):
|
||||||
# of linked 'if' action>".
|
# This action is executed if the tied "if" action is not executed.
|
||||||
self.exprResult = not self.ifAction.exprResult
|
ifAction = self.ifAction
|
||||||
IfAction.do(self)
|
iRes,error = ifAction.evaluateExpression(result, context, ifAction.expr)
|
||||||
|
IfAction.do(self, result, context, not iRes)
|
||||||
|
|
||||||
class ForAction(BufferAction):
|
class ForAction(BufferAction):
|
||||||
'''Actions that will include the content of the buffer as many times as
|
'''Actions that will include the content of the buffer as many times as
|
||||||
specified by the action parameters.'''
|
specified by the action parameters.'''
|
||||||
def __init__(self, name, buffer, expr, elem, minus, iter, source, fromExpr):
|
|
||||||
BufferAction.__init__(self, name, buffer, expr, elem, minus, source,
|
def __init__(self, name, buff, expr, elem, minus, iter, src, fromExpr):
|
||||||
fromExpr)
|
BufferAction.__init__(self, name, buff, expr, elem, minus, src,fromExpr)
|
||||||
self.iter = iter # Name of the iterator variable used in the each loop
|
self.iter = iter # Name of the iterator variable used in the each loop
|
||||||
|
|
||||||
def initialiseLoop(self):
|
def initialiseLoop(self, context, elems):
|
||||||
'''Initialises information about the loop, before entering into it.'''
|
'''Initialises information about the loop, before entering into it.'''
|
||||||
context = self.buffer.env.context
|
|
||||||
# The "loop" object, made available in the POD context, contains info
|
# The "loop" object, made available in the POD context, contains info
|
||||||
# about all currently walked loops. For every walked loop, a specific
|
# about all currently walked loops. For every walked loop, a specific
|
||||||
# object, le'ts name it curLoop, accessible at getattr(loop, self.iter),
|
# object, le'ts name it curLoop, accessible at getattr(loop, self.iter),
|
||||||
|
@ -182,21 +179,22 @@ class ForAction(BufferAction):
|
||||||
if 'loop' not in context:
|
if 'loop' not in context:
|
||||||
context['loop'] = Object()
|
context['loop'] = Object()
|
||||||
try:
|
try:
|
||||||
total = len(self.exprResult)
|
total = len(elems)
|
||||||
except:
|
except:
|
||||||
total = 0
|
total = 0
|
||||||
curLoop = Object(length=total)
|
curLoop = Object(length=total)
|
||||||
setattr(context['loop'], self.iter, curLoop)
|
setattr(context['loop'], self.iter, curLoop)
|
||||||
return curLoop
|
return curLoop
|
||||||
|
|
||||||
def do(self):
|
def do(self, result, context, elems):
|
||||||
context = self.buffer.env.context
|
'''Performs the "for" action. p_elems is the list of elements to
|
||||||
# Check self.exprResult type
|
walk, evaluated from self.expr.'''
|
||||||
|
# Check p_exprRes type.
|
||||||
try:
|
try:
|
||||||
# All "iterable" objects are OK.
|
# All "iterable" objects are OK.
|
||||||
iter(self.exprResult)
|
iter(elems)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
self.writeError(WRONG_SEQ_TYPE % self.expr)
|
self.manageError(result, context, WRONG_SEQ_TYPE % self.expr)
|
||||||
return
|
return
|
||||||
# Remember variable hidden by iter if any
|
# Remember variable hidden by iter if any
|
||||||
hasHiddenVariable = False
|
hasHiddenVariable = False
|
||||||
|
@ -211,14 +209,14 @@ class ForAction(BufferAction):
|
||||||
initialColIndex = self.elem.colIndex
|
initialColIndex = self.elem.colIndex
|
||||||
currentColIndex = initialColIndex
|
currentColIndex = initialColIndex
|
||||||
rowAttributes = self.elem.tableInfo.curRowAttrs
|
rowAttributes = self.elem.tableInfo.curRowAttrs
|
||||||
# If self.exprResult is empty, dump an empty cell to avoid
|
# If p_elems is empty, dump an empty cell to avoid having the wrong
|
||||||
# having the wrong number of cells for the current row
|
# number of cells for the current row.
|
||||||
if not self.exprResult:
|
if not elems:
|
||||||
self.result.dumpElement(Cell.OD.elem)
|
result.dumpElement(Cell.OD.elem)
|
||||||
# Enter the "for" loop
|
# Enter the "for" loop.
|
||||||
loop = self.initialiseLoop()
|
loop = self.initialiseLoop(context, elems)
|
||||||
i = -1
|
i = -1
|
||||||
for item in self.exprResult:
|
for item in elems:
|
||||||
i += 1
|
i += 1
|
||||||
loop.nb = i
|
loop.nb = i
|
||||||
loop.first = i == 0
|
loop.first = i == 0
|
||||||
|
@ -226,20 +224,20 @@ class ForAction(BufferAction):
|
||||||
context[self.iter] = item
|
context[self.iter] = item
|
||||||
# Cell: add a new row if we are at the end of a row
|
# Cell: add a new row if we are at the end of a row
|
||||||
if isCell and (currentColIndex == nbOfColumns):
|
if isCell and (currentColIndex == nbOfColumns):
|
||||||
self.result.dumpEndElement(Row.OD.elem)
|
result.dumpEndElement(Row.OD.elem)
|
||||||
self.result.dumpStartElement(Row.OD.elem, rowAttributes)
|
result.dumpStartElement(Row.OD.elem, rowAttributes)
|
||||||
currentColIndex = 0
|
currentColIndex = 0
|
||||||
self.evaluateBuffer()
|
self.evaluateBuffer(result, context)
|
||||||
# Cell: increment the current column index
|
# Cell: increment the current column index
|
||||||
if isCell:
|
if isCell:
|
||||||
currentColIndex += 1
|
currentColIndex += 1
|
||||||
# Cell: leave the last row with the correct number of cells
|
# Cell: leave the last row with the correct number of cells
|
||||||
if isCell and self.exprResult:
|
if isCell and elems:
|
||||||
wrongNbOfCells = (currentColIndex-1) - initialColIndex
|
wrongNbOfCells = (currentColIndex-1) - initialColIndex
|
||||||
if wrongNbOfCells < 0: # Too few cells for last row
|
if wrongNbOfCells < 0: # Too few cells for last row
|
||||||
for i in range(abs(wrongNbOfCells)):
|
for i in range(abs(wrongNbOfCells)):
|
||||||
context[self.iter] = ''
|
context[self.iter] = ''
|
||||||
self.buffer.evaluate(subElements=False)
|
self.buffer.evaluate(result, context, subElements=False)
|
||||||
# This way, the cell is dumped with the correct styles
|
# This way, the cell is dumped with the correct styles
|
||||||
elif wrongNbOfCells > 0: # Too many cells for last row
|
elif wrongNbOfCells > 0: # Too many cells for last row
|
||||||
# Finish current row
|
# Finish current row
|
||||||
|
@ -248,15 +246,15 @@ class ForAction(BufferAction):
|
||||||
nbOfMissingCells = nbOfColumns - currentColIndex
|
nbOfMissingCells = nbOfColumns - currentColIndex
|
||||||
context[self.iter] = ''
|
context[self.iter] = ''
|
||||||
for i in range(nbOfMissingCells):
|
for i in range(nbOfMissingCells):
|
||||||
self.buffer.evaluate(subElements=False)
|
self.buffer.evaluate(result, context, subElements=False)
|
||||||
self.result.dumpEndElement(Row.OD.elem)
|
result.dumpEndElement(Row.OD.elem)
|
||||||
# Create additional row with remaining cells
|
# Create additional row with remaining cells
|
||||||
self.result.dumpStartElement(Row.OD.elem, rowAttributes)
|
result.dumpStartElement(Row.OD.elem, rowAttributes)
|
||||||
nbOfRemainingCells = wrongNbOfCells + nbOfMissingCells
|
nbOfRemainingCells = wrongNbOfCells + nbOfMissingCells
|
||||||
nbOfMissingCellsLastLine = nbOfColumns - nbOfRemainingCells
|
nbOfMissingCellsLastLine = nbOfColumns - nbOfRemainingCells
|
||||||
context[self.iter] = ''
|
context[self.iter] = ''
|
||||||
for i in range(nbOfMissingCellsLastLine):
|
for i in range(nbOfMissingCellsLastLine):
|
||||||
self.buffer.evaluate(subElements=False)
|
self.buffer.evaluate(result, context, subElements=False)
|
||||||
# Delete the object representing info about the current loop.
|
# Delete the object representing info about the current loop.
|
||||||
try:
|
try:
|
||||||
delattr(context['loop'], self.iter)
|
delattr(context['loop'], self.iter)
|
||||||
|
@ -266,40 +264,40 @@ class ForAction(BufferAction):
|
||||||
if hasHiddenVariable:
|
if hasHiddenVariable:
|
||||||
context[self.iter] = hiddenVariable
|
context[self.iter] = hiddenVariable
|
||||||
else:
|
else:
|
||||||
if self.exprResult:
|
if elems:
|
||||||
if self.iter in context: # May not be the case on error.
|
if self.iter in context: # May not be the case on error.
|
||||||
del context[self.iter]
|
del context[self.iter]
|
||||||
|
|
||||||
class NullAction(BufferAction):
|
class NullAction(BufferAction):
|
||||||
'''Action that does nothing. Used in conjunction with a "from" clause, it
|
'''Action that does nothing. Used in conjunction with a "from" clause, it
|
||||||
allows to insert in a buffer arbitrary odt content.'''
|
allows to insert in a buffer arbitrary odt content.'''
|
||||||
def do(self):
|
def do(self, result, context, exprRes):
|
||||||
self.evaluateBuffer()
|
self.evaluateBuffer(result, context)
|
||||||
|
|
||||||
class VariablesAction(BufferAction):
|
class VariablesAction(BufferAction):
|
||||||
'''Action that allows to define a set of variables somewhere in the
|
'''Action that allows to define a set of variables somewhere in the
|
||||||
template.'''
|
template.'''
|
||||||
def __init__(self, name, buffer, elem, minus, variables, source, fromExpr):
|
def __init__(self, name, buff, elem, minus, variables, src, fromExpr):
|
||||||
# We do not use the default Buffer.expr attribute for storing the Python
|
# We do not use the default Buffer.expr attribute for storing the Python
|
||||||
# expression, because here we will have several expressions, one for
|
# expression, because here we will have several expressions, one for
|
||||||
# every defined variable.
|
# every defined variable.
|
||||||
BufferAction.__init__(self, name, buffer, None, elem, minus, source,
|
BufferAction.__init__(self,name, buff, None, elem, minus, src, 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, result, context, exprRes):
|
||||||
context = self.buffer.env.context
|
'''Evaluate the variables' expressions: because there are several
|
||||||
# Evaluate the variables' expressions: because there are several
|
expressions, we do not use the standard, single-expression-minded
|
||||||
# expressions, we did not use the standard, single-expression-minded
|
BufferAction code for evaluating our expressions.
|
||||||
# BufferAction code for evaluating our expressions.
|
|
||||||
# Also: we remember the names and values of the variables that we will
|
We remember the names and values of the variables that we will hide
|
||||||
# hide in the context: after execution of this buffer we will restore
|
in the context: after execution of this buffer we will restore those
|
||||||
# those values.
|
values.
|
||||||
|
'''
|
||||||
hidden = None
|
hidden = None
|
||||||
for name, expr in self.variables:
|
for name, expr in self.variables:
|
||||||
# Evaluate the expression
|
# Evaluate variable expression in vRes.
|
||||||
result, error = self.evaluateExpression(expr)
|
vRes, error = self.evaluateExpression(result, context, expr)
|
||||||
if error: return
|
if error: return
|
||||||
# Remember the variable previous value if already in the context
|
# Remember the variable previous value if already in the context
|
||||||
if name in context:
|
if name in context:
|
||||||
|
@ -308,9 +306,9 @@ class VariablesAction(BufferAction):
|
||||||
else:
|
else:
|
||||||
hidden[name] = context[name]
|
hidden[name] = context[name]
|
||||||
# Store the result into the context
|
# Store the result into the context
|
||||||
context[name] = result
|
context[name] = vRes
|
||||||
# Evaluate the buffer
|
# Evaluate the buffer
|
||||||
self.evaluateBuffer()
|
self.evaluateBuffer(result, context)
|
||||||
# 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
|
||||||
|
|
|
@ -1,24 +1,21 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Appy is a framework for building applications in the Python language.
|
# This file is part of Appy, a framework for building applications in the Python
|
||||||
# Copyright (C) 2007 Gaetan Delannay
|
# language. Copyright (C) 2007 Gaetan Delannay
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or
|
# Appy is free software; you can redistribute it and/or modify it under the
|
||||||
# modify it under the terms of the GNU General Public License
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
# as published by the Free Software Foundation; either version 2
|
# Foundation; either version 3 of the License, or (at your option) any later
|
||||||
# of the License, or (at your option) any later version.
|
# version.
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
# Appy is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License along with
|
||||||
# along with this program; if not, write to the Free Software
|
# Appy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from xml.sax.saxutils import quoteattr
|
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
|
||||||
|
@ -121,8 +118,8 @@ class Buffer:
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.subBuffers = {} # ~{i_bufferIndex: Buffer}~
|
self.subBuffers = {} # ~{i_bufferIndex: Buffer}~
|
||||||
self.env = env
|
self.env = env
|
||||||
# Are we computing for pod or for px ?
|
# Are we computing for pod (True) or px (False)
|
||||||
self.caller= (env.__class__.__name__=='PxEnvironment') and 'px' or 'pod'
|
self.pod = env.__class__.__name__ != 'PxEnvironment'
|
||||||
|
|
||||||
def addSubBuffer(self, subBuffer=None):
|
def addSubBuffer(self, subBuffer=None):
|
||||||
if not subBuffer:
|
if not subBuffer:
|
||||||
|
@ -184,7 +181,7 @@ class Buffer:
|
||||||
|
|
||||||
def dumpContent(self, content):
|
def dumpContent(self, content):
|
||||||
'''Dumps string p_content into the buffer.'''
|
'''Dumps string p_content into the buffer.'''
|
||||||
if self.caller == 'pod':
|
if self.pod:
|
||||||
# Take care of converting line breaks to odf line breaks.
|
# Take care of converting line breaks to odf line breaks.
|
||||||
content = escapeXml(content, format='odf',
|
content = escapeXml(content, format='odf',
|
||||||
nsText=self.env.namespaces[self.env.NS_TEXT])
|
nsText=self.env.namespaces[self.env.NS_TEXT])
|
||||||
|
@ -214,7 +211,8 @@ 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:
|
||||||
res, escape = Expression(expression).evaluate(self.env.context)
|
expr = Expression(expression, self.pod)
|
||||||
|
res, escape = expr.evaluate(self.env.context)
|
||||||
if escape: self.dumpContent(res)
|
if escape: self.dumpContent(res)
|
||||||
else: self.write(res)
|
else: self.write(res)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -329,7 +327,7 @@ class MemoryBuffer(Buffer):
|
||||||
# First unreference all elements
|
# First unreference all elements
|
||||||
for index in self.getElementIndexes(expressions=False):
|
for index in self.getElementIndexes(expressions=False):
|
||||||
del self.elements[index]
|
del self.elements[index]
|
||||||
self.evaluate()
|
self.evaluate(self.parent, self.env.context)
|
||||||
else:
|
else:
|
||||||
# Transfer content in itself
|
# Transfer content in itself
|
||||||
oldParentLength = self.parent.getLength()
|
oldParentLength = self.parent.getLength()
|
||||||
|
@ -337,7 +335,7 @@ class MemoryBuffer(Buffer):
|
||||||
# Transfer elements
|
# Transfer elements
|
||||||
for index, podElem in self.elements.iteritems():
|
for index, podElem in self.elements.iteritems():
|
||||||
self.parent.elements[oldParentLength + index] = podElem
|
self.parent.elements[oldParentLength + index] = podElem
|
||||||
# Transfer subBuffers
|
# Transfer sub-buffers
|
||||||
for index, buf in self.subBuffers.iteritems():
|
for index, buf in self.subBuffers.iteritems():
|
||||||
self.parent.subBuffers[oldParentLength + index] = buf
|
self.parent.subBuffers[oldParentLength + index] = buf
|
||||||
# Empty the buffer
|
# Empty the buffer
|
||||||
|
@ -360,7 +358,7 @@ class MemoryBuffer(Buffer):
|
||||||
|
|
||||||
def addExpression(self, expression, tiedHook=None):
|
def addExpression(self, expression, tiedHook=None):
|
||||||
# Create the POD expression
|
# Create the POD expression
|
||||||
expr = Expression(expression)
|
expr = Expression(expression, self.pod)
|
||||||
if tiedHook: tiedHook.tiedExpression = expr
|
if tiedHook: tiedHook.tiedExpression = expr
|
||||||
self.elements[self.getLength()] = expr
|
self.elements[self.getLength()] = expr
|
||||||
# To be sure that an expr and an elem can't be found at the same index
|
# To be sure that an expr and an elem can't be found at the same index
|
||||||
|
@ -621,8 +619,11 @@ class MemoryBuffer(Buffer):
|
||||||
|
|
||||||
reTagContent = re.compile('<(?P<p>[\w-]+):(?P<f>[\w-]+)(.*?)>.*</(?P=p):' \
|
reTagContent = re.compile('<(?P<p>[\w-]+):(?P<f>[\w-]+)(.*?)>.*</(?P=p):' \
|
||||||
'(?P=f)>', re.S)
|
'(?P=f)>', re.S)
|
||||||
def evaluate(self, subElements=True, removeMainElems=False):
|
def evaluate(self, result, context, subElements=True,
|
||||||
result = self.getRootBuffer()
|
removeMainElems=False):
|
||||||
|
'''Evaluates this buffer given the current p_context and add the result
|
||||||
|
into p_result. With pod, p_result is the root file buffer; with px
|
||||||
|
it is a memory buffer.'''
|
||||||
if not subElements:
|
if not subElements:
|
||||||
# Dump the root tag in this buffer, but not its content.
|
# Dump the root tag in this buffer, but not its content.
|
||||||
res = self.reTagContent.match(self.content.strip())
|
res = self.reTagContent.match(self.content.strip())
|
||||||
|
@ -639,20 +640,20 @@ class MemoryBuffer(Buffer):
|
||||||
currentIndex = index + 1
|
currentIndex = index + 1
|
||||||
if isinstance(evalEntry, Expression):
|
if isinstance(evalEntry, Expression):
|
||||||
try:
|
try:
|
||||||
res, escape = evalEntry.evaluate(self.env.context)
|
res, escape = evalEntry.evaluate(context)
|
||||||
if escape: result.dumpContent(res)
|
if escape: result.dumpContent(res)
|
||||||
else: result.write(res)
|
else: result.write(res)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
if self.caller == 'pod':
|
if self.pod:
|
||||||
PodError.dump(result, EVAL_EXPR_ERROR % (
|
PodError.dump(result, EVAL_EXPR_ERROR % (
|
||||||
evalEntry.expr, e), dumpTb=False)
|
evalEntry.expr, e), dumpTb=False)
|
||||||
else: # px
|
else: # px
|
||||||
raise Exception(EVAL_EXPR_ERROR %(evalEntry.expr,e))
|
raise Exception(EVAL_EXPR_ERROR %(evalEntry.expr,e))
|
||||||
elif isinstance(evalEntry, Attributes):
|
elif isinstance(evalEntry, Attributes):
|
||||||
result.write(evalEntry.evaluate(self.env.context))
|
result.write(evalEntry.evaluate(context))
|
||||||
else: # It is a subBuffer
|
else: # It is a subBuffer
|
||||||
if evalEntry.action:
|
if evalEntry.action:
|
||||||
evalEntry.action.execute()
|
evalEntry.action.execute(result, context)
|
||||||
else:
|
else:
|
||||||
result.write(evalEntry.content)
|
result.write(evalEntry.content)
|
||||||
stopIndex = self.getStopIndex(removeMainElems)
|
stopIndex = self.getStopIndex(removeMainElems)
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Appy is a framework for building applications in the Python language.
|
# This file is part of Appy, a framework for building applications in the Python
|
||||||
# Copyright (C) 2007 Gaetan Delannay
|
# language. Copyright (C) 2007 Gaetan Delannay
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or
|
# Appy is free software; you can redistribute it and/or modify it under the
|
||||||
# modify it under the terms of the GNU General Public License
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
# as published by the Free Software Foundation; either version 2
|
# Foundation; either version 3 of the License, or (at your option) any later
|
||||||
# of the License, or (at your option) any later version.
|
# version.
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
# Appy is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License along with
|
||||||
# along with this program; if not, write to the Free Software
|
# Appy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from xml.sax.saxutils import quoteattr
|
from xml.sax.saxutils import quoteattr
|
||||||
|
@ -28,8 +26,8 @@ class PodElement:
|
||||||
'table': 'Table', 'table-row': 'Row', 'table-cell': 'Cell',
|
'table': 'Table', 'table-row': 'Row', 'table-cell': 'Cell',
|
||||||
None: 'Expression'}
|
None: 'Expression'}
|
||||||
POD_ELEMS = ('text', 'title', 'section', 'table', 'row', 'cell')
|
POD_ELEMS = ('text', 'title', 'section', 'table', 'row', 'cell')
|
||||||
MINUS_ELEMS = ('section', 'table') # Elements for which the '-' operator can
|
# Elements for which the '-' operator can be applied.
|
||||||
# be applied
|
MINUS_ELEMS = ('section', 'table')
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def create(elem):
|
def create(elem):
|
||||||
'''Used to create any POD elem that has an equivalent OD element. Not
|
'''Used to create any POD elem that has an equivalent OD element. Not
|
||||||
|
@ -38,8 +36,9 @@ class PodElement:
|
||||||
|
|
||||||
class Text(PodElement):
|
class Text(PodElement):
|
||||||
OD = XmlElement('p', nsUri=ns.NS_TEXT)
|
OD = XmlElement('p', nsUri=ns.NS_TEXT)
|
||||||
subTags = [] # When generating an error we may need to surround the error
|
# When generating an error we may need to surround it with a given tag and
|
||||||
# with a given tag and subtags
|
# sub-tags.
|
||||||
|
subTags = []
|
||||||
|
|
||||||
class Title(PodElement):
|
class Title(PodElement):
|
||||||
OD = XmlElement('h', nsUri=ns.NS_TEXT)
|
OD = XmlElement('h', nsUri=ns.NS_TEXT)
|
||||||
|
@ -48,8 +47,9 @@ class Title(PodElement):
|
||||||
class Section(PodElement):
|
class Section(PodElement):
|
||||||
OD = XmlElement('section', nsUri=ns.NS_TEXT)
|
OD = XmlElement('section', nsUri=ns.NS_TEXT)
|
||||||
subTags = [Text.OD]
|
subTags = [Text.OD]
|
||||||
DEEPEST_TO_REMOVE = OD # When we must remove the Section element from a
|
# When we must remove the Section element from a buffer, the deepest element
|
||||||
# buffer, the deepest element to remove is the Section element itself
|
# to remove is the Section element itself.
|
||||||
|
DEEPEST_TO_REMOVE = OD
|
||||||
|
|
||||||
class Cell(PodElement):
|
class Cell(PodElement):
|
||||||
OD = XmlElement('table-cell', nsUri=ns.NS_TABLE)
|
OD = XmlElement('table-cell', nsUri=ns.NS_TABLE)
|
||||||
|
@ -65,19 +65,19 @@ class Row(PodElement):
|
||||||
class Table(PodElement):
|
class Table(PodElement):
|
||||||
OD = XmlElement('table', nsUri=ns.NS_TABLE)
|
OD = XmlElement('table', nsUri=ns.NS_TABLE)
|
||||||
subTags = [Row.OD, Cell.OD, Text.OD]
|
subTags = [Row.OD, Cell.OD, Text.OD]
|
||||||
DEEPEST_TO_REMOVE = Cell.OD # When we must remove the Table element from a
|
# When we must remove the Table element from a buffer, the deepest element
|
||||||
# buffer, the deepest element to remove is the Cell (it can only be done for
|
# to remove is the Cell (it can only be done for one-row, one-cell tables).
|
||||||
# one-row, one-cell tables
|
DEEPEST_TO_REMOVE = Cell.OD
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.tableInfo = None # ~OdTable~
|
self.tableInfo = None # ~OdTable~
|
||||||
|
|
||||||
class Expression(PodElement):
|
class Expression(PodElement):
|
||||||
'''Instances of this class represent Python expressions that are inserted
|
'''Represents a Python expression that is found in a pod or px.'''
|
||||||
into a POD template.'''
|
|
||||||
OD = None
|
OD = None
|
||||||
def __init__(self, pyExpr):
|
def __init__(self, pyExpr, pod):
|
||||||
# The Python expression
|
# The Python expression
|
||||||
self.expr = pyExpr.strip()
|
self.expr = pyExpr.strip()
|
||||||
|
self.pod = pod # True if I work for pod, False if I work for px.
|
||||||
# Must we, when evaluating the expression, escape XML special chars
|
# Must we, when evaluating the expression, escape XML special chars
|
||||||
# or not?
|
# or not?
|
||||||
if self.expr.startswith(':'):
|
if self.expr.startswith(':'):
|
||||||
|
@ -85,14 +85,18 @@ class Expression(PodElement):
|
||||||
self.escapeXml = False
|
self.escapeXml = False
|
||||||
else:
|
else:
|
||||||
self.escapeXml = True
|
self.escapeXml = True
|
||||||
# We will store here the expression's true result (before being
|
if self.pod:
|
||||||
# converted to a string)
|
# pod-only: store here the expression's true result (before being
|
||||||
|
# converted to a string).
|
||||||
self.result = None
|
self.result = None
|
||||||
# This boolean indicates if this Expression instance has already been
|
# pod-only: the following bool indicates if this Expression instance
|
||||||
# evaluated or not. Expressions which are tied to attribute hooks are
|
# has already been evaluated or not. Expressions which are tied to
|
||||||
# already evaluated when the tied hook is evaluated: this boolean
|
# attribute hooks are already evaluated when the tied hook is
|
||||||
# prevents the expression from being evaluated twice.
|
# evaluated: this boolean prevents the expression from being
|
||||||
|
# evaluated twice.
|
||||||
self.evaluated = False
|
self.evaluated = False
|
||||||
|
# self.result and self.evaluated are not used by PX, because they
|
||||||
|
# are not thread-safe.
|
||||||
|
|
||||||
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
|
||||||
|
@ -102,7 +106,7 @@ class Expression(PodElement):
|
||||||
escapeXml = self.escapeXml
|
escapeXml = self.escapeXml
|
||||||
# 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.pod and self.evaluated:
|
||||||
res = self.result
|
res = self.result
|
||||||
# It can happen only once, to ask to evaluate an expression that
|
# It can happen only once, to ask to evaluate an expression that
|
||||||
# was already evaluated (from the tied hook). We reset here the
|
# was already evaluated (from the tied hook). We reset here the
|
||||||
|
@ -111,9 +115,11 @@ class Expression(PodElement):
|
||||||
self.evaluated = False
|
self.evaluated = False
|
||||||
else:
|
else:
|
||||||
# Evaluates the Python expression
|
# Evaluates the Python expression
|
||||||
res = self.result = eval(self.expr, context)
|
res = eval(self.expr, context)
|
||||||
# Converts the expression result to a string that can be inserted into
|
# pod-only: cache the expression result.
|
||||||
# the POD/PX result.
|
if self.pod: self.result = res
|
||||||
|
# Converts the expr result to a string that can be inserted in the
|
||||||
|
# pod/px result.
|
||||||
resultType = res.__class__.__name__
|
resultType = res.__class__.__name__
|
||||||
if resultType == 'NoneType':
|
if resultType == 'NoneType':
|
||||||
res = u''
|
res = u''
|
||||||
|
@ -124,7 +130,7 @@ class Expression(PodElement):
|
||||||
elif resultType == 'Px':
|
elif resultType == 'Px':
|
||||||
# A PX that must be called within the current PX. Call it with the
|
# A PX that must be called within the current PX. Call it with the
|
||||||
# current context.
|
# current context.
|
||||||
res = res(context)
|
res = res(context, applyTemplate=False)
|
||||||
# Force escapeXml to False.
|
# Force escapeXml to False.
|
||||||
escapeXml = False
|
escapeXml = False
|
||||||
else:
|
else:
|
||||||
|
@ -133,7 +139,7 @@ class Expression(PodElement):
|
||||||
|
|
||||||
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
|
||||||
in the result.'''
|
in the result. pod-only.'''
|
||||||
OD = None
|
OD = None
|
||||||
floatTypes = ('int', 'long', 'float')
|
floatTypes = ('int', 'long', 'float')
|
||||||
dateTypes = ('DateTime',)
|
dateTypes = ('DateTime',)
|
||||||
|
@ -166,7 +172,7 @@ class Attributes(PodElement):
|
||||||
# Evaluate first the tied expression, in order to determine its type.
|
# Evaluate first the tied expression, in order to determine its type.
|
||||||
try:
|
try:
|
||||||
self.tiedExpression.evaluate(context)
|
self.tiedExpression.evaluate(context)
|
||||||
self.evaluated = True
|
self.tiedExpression.evaluated = True
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
# Don't set "evaluated" to True. This way, when the buffer will
|
# Don't set "evaluated" to True. This way, when the buffer will
|
||||||
# evaluate the expression directly, we will really evaluate it, so
|
# evaluate the expression directly, we will really evaluate it, so
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Appy is a framework for building applications in the Python language.
|
# This file is part of Appy, a framework for building applications in the Python
|
||||||
# Copyright (C) 2007 Gaetan Delannay
|
# language. Copyright (C) 2007 Gaetan Delannay
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or
|
# Appy is free software; you can redistribute it and/or modify it under the
|
||||||
# modify it under the terms of the GNU General Public License
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
# as published by the Free Software Foundation; either version 2
|
# Foundation; either version 3 of the License, or (at your option) any later
|
||||||
# of the License, or (at your option) any later version.
|
# version.
|
||||||
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
# Appy is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
# GNU General Public License for more details.
|
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License along with
|
||||||
# along with this program; if not, write to the Free Software
|
# Appy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA.
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import re
|
import re
|
||||||
|
@ -65,17 +63,13 @@ class PodEnvironment(OdfEnvironment):
|
||||||
# continue to dump it in the current buffer
|
# continue to dump it in the current buffer
|
||||||
ADD_IN_BUFFER = 0
|
ADD_IN_BUFFER = 0
|
||||||
# ADD_IN_SUBBUFFER: when encountering an impactable element, we must
|
# ADD_IN_SUBBUFFER: when encountering an impactable element, we must
|
||||||
# create a new subbuffer and dump it in it.
|
# create a new sub-buffer and dump it in it.
|
||||||
ADD_IN_SUBBUFFER = 1
|
ADD_IN_SUBBUFFER = 1
|
||||||
# Possible states
|
# Possible states
|
||||||
IGNORING = 0 # We are ignoring what we are currently reading
|
IGNORING = 0 # We are ignoring what we are currently reading
|
||||||
READING_CONTENT = 1 # We are reading "normal" content
|
READING_CONTENT = 1 # We are reading "normal" content
|
||||||
READING_STATEMENT = 2
|
READING_STATEMENT = 2 # We are reading a POD statement (for, if...)
|
||||||
# We are reading a POD statement (for, if...), which is located within a
|
READING_EXPRESSION = 3 # We are reading a POD expression.
|
||||||
# office:annotation element
|
|
||||||
READING_EXPRESSION = 3
|
|
||||||
# We are reading a POD expression, which is located between
|
|
||||||
# a text:change-start and a text:change-end elements
|
|
||||||
def __init__(self, context, inserts=[]):
|
def __init__(self, context, inserts=[]):
|
||||||
OdfEnvironment.__init__(self)
|
OdfEnvironment.__init__(self)
|
||||||
# Buffer where we must dump the content we are currently reading
|
# Buffer where we must dump the content we are currently reading
|
||||||
|
@ -335,7 +329,8 @@ class PodParser(OdfParser):
|
||||||
if isinstance(parent, FileBuffer):
|
if isinstance(parent, FileBuffer):
|
||||||
# Execute buffer action and delete the
|
# Execute buffer action and delete the
|
||||||
# buffer.
|
# buffer.
|
||||||
e.currentBuffer.action.execute()
|
e.currentBuffer.action.execute(parent,
|
||||||
|
e.context)
|
||||||
parent.removeLastSubBuffer()
|
parent.removeLastSubBuffer()
|
||||||
e.currentBuffer = parent
|
e.currentBuffer = parent
|
||||||
e.mode = e.ADD_IN_SUBBUFFER
|
e.mode = e.ADD_IN_SUBBUFFER
|
||||||
|
|
3637
pod/test/Tests.rtf
3637
pod/test/Tests.rtf
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
from px_parser import PxParser, PxEnvironment
|
from px_parser import PxParser, PxEnvironment
|
||||||
|
from appy.pod.buffers import MemoryBuffer
|
||||||
|
|
||||||
# Exception class --------------------------------------------------------------
|
# Exception class --------------------------------------------------------------
|
||||||
class PxError(Exception): pass
|
class PxError(Exception): pass
|
||||||
|
@ -11,11 +12,23 @@ class PxError(Exception): pass
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class Px:
|
class Px:
|
||||||
'''Represents a (chunk of) PX code.'''
|
'''Represents a (chunk of) PX code.'''
|
||||||
def __init__(self, content, isFileName=False, partial=True):
|
xhtmlPrologue = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" '\
|
||||||
|
'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
|
||||||
|
|
||||||
|
def __init__(self, content, isFileName=False, partial=True,
|
||||||
|
template=None, hook=None, prologue=None):
|
||||||
'''p_content is the PX code, as a string, or a file name if p_isFileName
|
'''p_content is the PX code, as a string, or a file name if p_isFileName
|
||||||
is True. If this code represents a complete XML file, p_partial is
|
is True. If this code represents a complete XML file, p_partial is
|
||||||
False. Else, we must surround p_content with a root tag to be able
|
False. Else, we must surround p_content with a root tag to be able
|
||||||
to parse it with a SAX parser.'''
|
to parse it with a SAX parser.
|
||||||
|
|
||||||
|
If this PX is based on another PX template, specify the PX template
|
||||||
|
in p_template and the name of the p_hook where to insert this PX into
|
||||||
|
the template PX.
|
||||||
|
|
||||||
|
If a p_prologue is specified, it will be rendered at the start of the
|
||||||
|
PX result.
|
||||||
|
'''
|
||||||
# Get the PX content
|
# Get the PX content
|
||||||
if isFileName:
|
if isFileName:
|
||||||
f = file(content)
|
f = file(content)
|
||||||
|
@ -33,15 +46,35 @@ class Px:
|
||||||
# Parses self.content (a PX code in a string) with self.parser, to
|
# Parses self.content (a PX code in a string) with self.parser, to
|
||||||
# produce a tree of memory buffers.
|
# produce a tree of memory buffers.
|
||||||
self.parser.parse(self.content)
|
self.parser.parse(self.content)
|
||||||
|
# Is this PX based on a template PX?
|
||||||
|
self.template = template
|
||||||
|
self.hook = hook
|
||||||
|
# Is there some (XML, XHTML...) prologue to dump?
|
||||||
|
self.prologue = prologue
|
||||||
|
|
||||||
def __call__(self, context):
|
def __call__(self, context, applyTemplate=True):
|
||||||
# p_context must be a dict. Store it in the PX environment.
|
'''Renders the PX.
|
||||||
self.parser.env.context = context
|
|
||||||
# Render the PX result and return it
|
If the PX is based on a template PX, we have 2 possibilities.
|
||||||
|
1. p_applyTemplate is True. This case corresponds to the initial
|
||||||
|
call to the current PX. In this case we call the template with a
|
||||||
|
context containing, in the hook variable, the current PX.
|
||||||
|
2. p_applyTemplate is False. In this case, we are currently executing
|
||||||
|
the PX template, and, at the hook, we must include the current PX,
|
||||||
|
as is, without re-applying the template (else, an infinite
|
||||||
|
recursion would occur).
|
||||||
|
'''
|
||||||
|
if self.hook and applyTemplate:
|
||||||
|
# Call the template PX, filling the hook with the current PX.
|
||||||
|
context[self.hook] = self
|
||||||
|
return self.template(context)
|
||||||
|
else:
|
||||||
|
# Create a Memory buffer for storing the result.
|
||||||
env = self.parser.env
|
env = self.parser.env
|
||||||
env.ast.evaluate()
|
result = MemoryBuffer(env, None)
|
||||||
res = env.result.content
|
env.ast.evaluate(result, context)
|
||||||
# Clean the res, for the next evaluation
|
res = result.content
|
||||||
env.result.clean()
|
if self.prologue:
|
||||||
|
res = self.prologue + res
|
||||||
return res
|
return res
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -7,28 +7,19 @@ class PxEnvironment(XmlEnvironment):
|
||||||
'''Environment for the PX parser.'''
|
'''Environment for the PX parser.'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# We try to mimic POD. POD has a root buffer that is a FileBuffer, which
|
# In the following buffer, we will create a single memory sub-buffer
|
||||||
# is the final result buffer, into which the result of evaluating all
|
# that will hold the result of parsing the PX = a hierarchy of memory
|
||||||
# memory buffers, defined as sub-buffers of this file buffer, is
|
# buffers = PX's AST (Abstract Syntax Tree).
|
||||||
# generated. For PX, we will define a result buffer, but as a memory
|
|
||||||
# buffer instead of a file buffer.
|
|
||||||
self.result = MemoryBuffer(self, None)
|
|
||||||
# In this buffer, we will create a single memory sub-buffer that will
|
|
||||||
# hold the result of parsing the PX = a hierarchy of memory buffers =
|
|
||||||
# PX's AST (Abstract Syntax Tree).
|
|
||||||
self.ast = MemoryBuffer(self, self.result)
|
|
||||||
# A major difference between POD and PX: POD creates the AST and
|
# A major difference between POD and PX: POD creates the AST and
|
||||||
# generates the result in the same step: one AST is generated, and then
|
# generates the result in the same step: one AST is generated, and then
|
||||||
# directly produces a single evaluation, in the root file buffer. PX
|
# directly produces a single evaluation, in the root file buffer. PX
|
||||||
# works in 2 steps: the AST is initially created in self.ast. Then,
|
# works in 2 steps: the AST is initially created in self.ast. Then,
|
||||||
# several evaluations can be generated, in self.result, without
|
# several (concurrent) evaluations can occur, without re-generating the
|
||||||
# re-generating the AST. After every evaluation, self.result will be
|
# AST.
|
||||||
# cleaned, to be reusable for the next evaluation.
|
self.ast = MemoryBuffer(self, None)
|
||||||
# Context will come afterwards
|
|
||||||
self.context = None
|
|
||||||
# Buffer where we must dump the content we are currently reading
|
# Buffer where we must dump the content we are currently reading
|
||||||
self.currentBuffer = self.ast
|
self.currentBuffer = self.ast
|
||||||
# Tag content we are currently reading. We will put soomething in this
|
# Tag content we are currently reading. We will put something in this
|
||||||
# attribute only if we encounter content that is Python code.
|
# attribute only if we encounter content that is Python code.
|
||||||
# Else, we will directly dump the parsed content into the current
|
# Else, we will directly dump the parsed content into the current
|
||||||
# buffer.
|
# buffer.
|
||||||
|
|
Loading…
Reference in a new issue