[px] Made PX thread-safe.
This commit is contained in:
parent
086f93e845
commit
1d931cfb96
10 changed files with 2086 additions and 2037 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
# ------------------------------------------------------------------------------
|
||||
from px_parser import PxParser, PxEnvironment
|
||||
from appy.pod.buffers import MemoryBuffer
|
||||
|
||||
# Exception class --------------------------------------------------------------
|
||||
class PxError(Exception): pass
|
||||
|
@ -11,11 +12,23 @@ class PxError(Exception): pass
|
|||
# ------------------------------------------------------------------------------
|
||||
class Px:
|
||||
'''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
|
||||
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
|
||||
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
|
||||
if isFileName:
|
||||
f = file(content)
|
||||
|
@ -33,15 +46,35 @@ class Px:
|
|||
# Parses self.content (a PX code in a string) with self.parser, to
|
||||
# produce a tree of memory buffers.
|
||||
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):
|
||||
# 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()
|
||||
res = env.result.content
|
||||
# Clean the res, for the next evaluation
|
||||
env.result.clean()
|
||||
return res
|
||||
def __call__(self, context, applyTemplate=True):
|
||||
'''Renders the PX.
|
||||
|
||||
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
|
||||
result = MemoryBuffer(env, None)
|
||||
env.ast.evaluate(result, context)
|
||||
res = result.content
|
||||
if self.prologue:
|
||||
res = self.prologue + res
|
||||
return res
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -7,28 +7,19 @@ class PxEnvironment(XmlEnvironment):
|
|||
'''Environment for the PX parser.'''
|
||||
|
||||
def __init__(self):
|
||||
# We try to mimic POD. POD has a root buffer that is a FileBuffer, which
|
||||
# is the final result buffer, into which the result of evaluating all
|
||||
# memory buffers, defined as sub-buffers of this file buffer, is
|
||||
# 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)
|
||||
# In the following 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).
|
||||
# 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
|
||||
# directly produces a single evaluation, in the root file buffer. PX
|
||||
# works in 2 steps: the AST is initially created in self.ast. Then,
|
||||
# several evaluations can be generated, in self.result, without
|
||||
# re-generating the AST. After every evaluation, self.result will be
|
||||
# cleaned, to be reusable for the next evaluation.
|
||||
# Context will come afterwards
|
||||
self.context = None
|
||||
# works in 2 steps: the AST is initially created in self.ast. Then,
|
||||
# several (concurrent) evaluations can occur, without re-generating the
|
||||
# AST.
|
||||
self.ast = MemoryBuffer(self, None)
|
||||
# Buffer where we must dump the content we are currently reading
|
||||
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.
|
||||
# Else, we will directly dump the parsed content into the current
|
||||
# buffer.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue