[Checkins] SVN: z3c.pt/trunk/z3c/pt/ Added preliminary support for
specifying an expression translation class;
in this changeset, an implementation for a Python-expressions is
given.
Malthe Borch
mborch at gmail.com
Sun Mar 16 13:20:12 EDT 2008
Log message for revision 84711:
Added preliminary support for specifying an expression translation class; in this changeset, an implementation for a Python-expressions is given.
Changed:
U z3c.pt/trunk/z3c/pt/README.txt
U z3c.pt/trunk/z3c/pt/clauses.py
A z3c.pt/trunk/z3c/pt/configure.zcml
A z3c.pt/trunk/z3c/pt/expressions.py
A z3c.pt/trunk/z3c/pt/generation.py
U z3c.pt/trunk/z3c/pt/i18n.txt
A z3c.pt/trunk/z3c/pt/interfaces.py
D z3c.pt/trunk/z3c/pt/io.py
D z3c.pt/trunk/z3c/pt/parsing.py
U z3c.pt/trunk/z3c/pt/template.py
A z3c.pt/trunk/z3c/pt/testing.py
U z3c.pt/trunk/z3c/pt/tests/test_doctests.py
U z3c.pt/trunk/z3c/pt/translation.py
U z3c.pt/trunk/z3c/pt/translation.txt
-=-
Modified: z3c.pt/trunk/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/README.txt 2008-03-16 17:20:11 UTC (rev 84711)
@@ -28,6 +28,13 @@
``ViewTextTemplate``, ``ViewTextTemplateFile``
See above.
+Setup
+-----
+
+ >>> from zope.configuration.xmlconfig import XMLConfig
+ >>> import z3c.pt
+ >>> XMLConfig('configure.zcml', z3c.pt)()
+
Page template classes
---------------------
Modified: z3c.pt/trunk/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/z3c/pt/clauses.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/clauses.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -3,8 +3,8 @@
class Assign(object):
"""
- >>> from z3c.pt.io import CodeIO; stream = CodeIO()
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+ >>> from z3c.pt.testing import value
Simple assignment:
@@ -61,8 +61,8 @@
class Define(object):
"""
- >>> from z3c.pt.io import CodeIO; stream = CodeIO()
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+ >>> from z3c.pt.testing import value
Variable scope:
@@ -208,8 +208,8 @@
class Condition(object):
"""
- >>> from z3c.pt.io import CodeIO
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO
+ >>> from z3c.pt.testing import value
Unlimited scope:
@@ -325,8 +325,8 @@
class Tag(object):
"""
- >>> from z3c.pt.io import CodeIO
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO
+ >>> from z3c.pt.testing import value
>>> from StringIO import StringIO
>>> from cgi import escape as _escape
@@ -424,8 +424,8 @@
class Repeat(object):
"""
- >>> from z3c.pt.io import CodeIO; stream = CodeIO()
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+ >>> from z3c.pt.testing import value
We need to set up the repeat object.
@@ -482,8 +482,8 @@
class Write(object):
"""
- >>> from z3c.pt.io import CodeIO; stream = CodeIO()
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+ >>> from z3c.pt.testing import value
>>> from StringIO import StringIO
>>> _out = StringIO()
@@ -539,8 +539,8 @@
class Out(object):
"""
- >>> from z3c.pt.io import CodeIO; stream = CodeIO()
- >>> from z3c.pt.parsing import value
+ >>> from z3c.pt.generation import CodeIO; stream = CodeIO()
+ >>> from z3c.pt.testing import value
>>> from StringIO import StringIO
>>> _out = StringIO()
Added: z3c.pt/trunk/z3c/pt/configure.zcml
===================================================================
--- z3c.pt/trunk/z3c/pt/configure.zcml (rev 0)
+++ z3c.pt/trunk/z3c/pt/configure.zcml 2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,10 @@
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <include package="zope.component" file="meta.zcml" />
+
+ <utility
+ name="python"
+ factory=".expressions.PythonTranslation" />
+
+</configure>
+
Added: z3c.pt/trunk/z3c/pt/expressions.py
===================================================================
--- z3c.pt/trunk/z3c/pt/expressions.py (rev 0)
+++ z3c.pt/trunk/z3c/pt/expressions.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,276 @@
+import zope.interface
+import parser
+
+from interfaces import IExpressionTranslation
+
+class ExpressionTranslation(object):
+ zope.interface.implements(IExpressionTranslation)
+
+ def name(self, string):
+ return string
+
+ def value(self, string):
+ return NotImplementedError("Must be implemented by subclass.")
+
+ def search(self, string):
+ """
+ We need to implement a ``value``-method. Let's define that an
+ expression is valid if it contains an odd number of
+ characters.
+
+ >>> class MockExpressionTranslation(ExpressionTranslation):
+ ... def value(self, string):
+ ... if len(string) % 2 == 0: raise SyntaxError()
+
+ >>> search = MockExpressionTranslation().search
+ >>> search("a")
+ 'a'
+ >>> search("ab")
+ 'a'
+ >>> search("abc")
+ 'abc'
+
+ """
+
+ left = 0
+ right = left + 1
+
+ current = None
+
+ while right <= len(string):
+ expression = string[left:right]
+
+ try:
+ e = self.value(expression)
+ current = expression
+ except SyntaxError, e:
+ if right == len(string):
+ if current is not None:
+ return current
+
+ raise e
+
+ right += 1
+
+ return current
+
+ def variable(self, string):
+ """
+ >>> variable = ExpressionTranslation().variable
+
+ Single variable:
+
+ >>> variable("variable")
+ ('variable',)
+
+ Multiple variables:
+
+ >>> variable("variable1, variable2")
+ ('variable1', 'variable2')
+ """
+
+ variables = []
+ for var in string.split(', '):
+ var = var.strip()
+
+ if var in ('repeat',):
+ raise ValueError, "Invalid variable name '%s' (reserved)." % variable
+
+ if var.startswith('_') and not var.startswith('_tmp'):
+ raise ValueError(
+ "Invalid variable name '%s' (starts with an underscore)." % var)
+
+ variables.append(var)
+
+ return tuple(variables)
+
+ def mapping(self, string):
+ """
+ >>> mapping = ExpressionTranslation().mapping
+
+ >>> mapping("abc def")
+ [('abc', 'def')]
+
+ >>> mapping("abc")
+ [('abc', None)]
+
+ >>> mapping("abc; def ghi")
+ [('abc', None), ('def', 'ghi')]
+
+ """
+
+ defs = string.split(';')
+ mappings = []
+ for d in defs:
+ d = d.strip()
+ while ' ' in d:
+ d = d.replace(' ', ' ')
+
+ parts = d.split(' ')
+ if len(parts) == 1:
+ mappings.append((d, None))
+ elif len(parts) == 2:
+ mappings.append((parts[0], parts[1]))
+ else:
+ raise ValueError, "Invalid mapping (%s)." % string
+
+ return mappings
+
+ def definitions(self, string):
+ """
+ We need to subclass the parser and implement a simple
+ ``value``-method.
+
+ >>> class MockExpressionTranslation(ExpressionTranslation):
+ ... def value(self, string):
+ ... return (string.strip(),)
+
+ >>> definitions = MockExpressionTranslation().definitions
+
+ Single define:
+
+ >>> definitions("variable expression")
+ ((['variable'], ('expression',)),)
+
+ Multiple defines:
+
+ >>> definitions("variable1 expression1; variable2 expression2")
+ ((['variable1'], ('expression1',)), (['variable2'], ('expression2',)))
+
+ Tuple define:
+
+ >>> definitions("(variable1, variable2) (expression1, expression2)")
+ ((['variable1', 'variable2'], ('(expression1, expression2)',)),)
+
+ A define clause that ends in a semicolon:
+
+ >>> definitions("variable expression;")
+ ((['variable'], ('expression',)),)
+
+ A define clause with a trivial expression (we do allow this):
+
+ >>> definitions("variable")
+ ((['variable'], None),)
+
+ A proper define clause following one with a trivial expression:
+
+ >>> definitions("variable1 expression; variable2")
+ ((['variable1'], ('expression',)), (['variable2'], None))
+
+ """
+
+ string = string.replace('\n', '').strip()
+
+ defines = []
+
+ i = 0
+ while i < len(string):
+ while string[i] == ' ':
+ i += 1
+
+ # get variable definition
+ if string[i] == '(':
+ j = string.find(')', i+1)
+ if j == -1:
+ raise ValueError, "Invalid variable tuple definition (%s)." % string
+ var = self.variable(string[i+1:j])
+ j += 1
+ else:
+ j = string.find(' ', i + 1)
+ if j == -1:
+ var = self.variable(string[i:])
+ j = len(string)
+ else:
+ var = self.variable(string[i:j])
+
+ # get expression
+ i = j
+ while j < len(string):
+ j = string.find(';', j+1)
+ if j == -1:
+ j = len(string)
+
+ try:
+ expr = self.value(string[i:j])
+ except SyntaxError, e:
+ if j < len(string):
+ continue
+ raise e
+ break
+ else:
+ expr = None
+
+ defines.append((list(var), expr))
+
+ i = j + 1
+
+ return tuple(defines)
+
+ def definition(self, string):
+ defs = self.definitions(string)
+ if len(defs) != 1:
+ raise ValueError, "Multiple definitions not allowed."
+
+ return defs[0]
+
+class PythonTranslation(ExpressionTranslation):
+ def value(self, string):
+ """
+ Specification:
+
+ value :: = python_expression [ |* value ]
+ python_expresion ::= a python expression string
+
+ *) Using | as logical or is not supported.
+
+ >>> value = PythonTranslation().value
+
+ >>> value("4 + 5")
+ ['4 + 5']
+
+ Complex expressions:
+
+ >>> value("a.non_defined_method() | 1")
+ ['a.non_defined_method() ', '1']
+
+ Expression with non-semantic horizontal bar.
+
+ >>> value("'|'")
+ ["'|'"]
+
+ Expression with non-semantic horizontal bar and semantic bar.
+
+ >>> value("a.non_defined_method() | '|'")
+ ['a.non_defined_method() ', "'|'"]
+
+ """
+
+ string = string.replace('\n', '').strip()
+
+ if not string:
+ return []
+
+ expressions = []
+
+ i = j = 0
+ while i < len(string):
+ j = string.find('|', j + 1)
+ if j == -1:
+ j = len(string)
+
+ expr = string[i:j].lstrip()
+
+ try:
+ # we use the ``parser`` module to determine if
+ # an expression is a valid python expression
+ parser.expr(expr.encode('utf-8'))
+ except SyntaxError, e:
+ if j < len(string):
+ continue
+
+ raise e
+
+ expressions.append(expr)
+ i = j + 1
+
+ return expressions
Copied: z3c.pt/trunk/z3c/pt/generation.py (from rev 84456, z3c.pt/trunk/z3c/pt/io.py)
===================================================================
--- z3c.pt/trunk/z3c/pt/generation.py (rev 0)
+++ z3c.pt/trunk/z3c/pt/generation.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,96 @@
+from StringIO import StringIO
+
+wrapper = """\
+def render(%starget_language=None):
+\tglobal utils
+
+\t_out = utils.initialize_stream()
+
+\t(_attributes, repeat) = utils.initialize_tal()
+\t(_domain, _translate) = utils.initialize_i18n()
+\t(_escape, _marker) = utils.initialize_helpers()
+
+\t_target_language = target_language
+%s
+\treturn _out.getvalue().decode('utf-8')
+"""
+
+class CodeIO(StringIO):
+ """
+ A high-level I/O class to write Python code to a stream.
+ Indentation is managed using ``indent`` and ``outdent``.
+
+ Also:
+
+ * Convenience methods for keeping track of temporary
+ variables (see ``save``, ``restore`` and ``getvariable``).
+
+ * Methods to process clauses (see ``begin`` and ``end``).
+
+ """
+
+ t_prefix = '_tmp'
+ v_prefix = '_var'
+
+ def __init__(self, indentation=0, indentation_string="\t"):
+ StringIO.__init__(self)
+ self.indentation = indentation
+ self.indentation_string = indentation_string
+ self.queue = u''
+ self.scope = [set()]
+
+ self._variables = {}
+ self.t_counter = 0
+
+ def save(self):
+ self.t_counter += 1
+ return "%s%d" % (self.t_prefix, self.t_counter)
+
+ def restore(self):
+ var = "%s%d" % (self.t_prefix, self.t_counter)
+ self.t_counter -= 1
+ return var
+
+ def indent(self, amount=1):
+ if amount > 0:
+ self.cook()
+ self.indentation += amount
+
+ def outdent(self, amount=1):
+ if amount > 0:
+ self.cook()
+ self.indentation -= amount
+
+ def out(self, string):
+ self.queue += string
+
+ def cook(self):
+ if self.queue:
+ queue = self.queue
+ self.queue = ''
+ self.write("_out.write('%s')" %
+ queue.replace('\n', '\\n').replace("'", "\\'"))
+
+ def write(self, string):
+ self.cook()
+ StringIO.write(
+ self, self.indentation_string * self.indentation + string + '\n')
+
+ def getvalue(self):
+ self.cook()
+ return StringIO.getvalue(self)
+
+ def begin(self, clauses):
+ if isinstance(clauses, (list, tuple)):
+ for clause in clauses:
+ self.begin(clause)
+ else:
+ clauses.begin(self)
+
+ def end(self, clauses):
+ if isinstance(clauses, (list, tuple)):
+ for clause in reversed(clauses):
+ self.end(clause)
+ else:
+ clauses.end(self)
+
Modified: z3c.pt/trunk/z3c/pt/i18n.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/i18n.txt 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/i18n.txt 2008-03-16 17:20:11 UTC (rev 84711)
@@ -31,6 +31,12 @@
Translation of tag contents
---------------------------
+We'll first register the Python expression translator.
+
+ >>> from z3c.pt.expressions import PythonTranslation
+ >>> from zope.component import provideUtility
+ >>> provideUtility(PythonTranslation(), name="python")
+
First, a simple example:
>>> body = """\
Added: z3c.pt/trunk/z3c/pt/interfaces.py
===================================================================
--- z3c.pt/trunk/z3c/pt/interfaces.py (rev 0)
+++ z3c.pt/trunk/z3c/pt/interfaces.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,40 @@
+from zope import interface
+
+class IExpressionTranslation(interface.Interface):
+ """This interface defines an expression translation utility."""
+
+ def name(string):
+ """Should interpret ``string`` as a name. This method will
+ usually be the identity function."""
+
+ def value(string):
+ """Translate ``string`` to a Python value-expression."""
+
+ def search(string):
+ """Extracts the longest valid expression from the beginning of
+ the provided string."""
+
+ def variable(string):
+ """A variable definition.
+
+ Specification:
+
+ variables :: = variable_name [, variables]
+
+ """
+
+ def mapping(string):
+ """A mapping definition."""
+
+ def definition(string):
+ """A definition."""
+
+ def definitions(string):
+ """Multiple definitions.
+
+ Specification:
+
+ argument ::= define_var [';' define_var]
+ define_var ::= Name python_expression
+
+ """
Deleted: z3c.pt/trunk/z3c/pt/io.py
===================================================================
--- z3c.pt/trunk/z3c/pt/io.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/io.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,81 +0,0 @@
-from StringIO import StringIO
-
-class CodeIO(StringIO):
- """
- A high-level I/O class to write Python code to a stream.
- Indentation is managed using ``indent`` and ``outdent``.
-
- Also:
-
- * Convenience methods for keeping track of temporary
- variables (see ``save``, ``restore`` and ``getvariable``).
-
- * Methods to process clauses (see ``begin`` and ``end``).
-
- """
-
- t_prefix = '_tmp'
- v_prefix = '_var'
-
- def __init__(self, indentation=0, indentation_string="\t"):
- StringIO.__init__(self)
- self.indentation = indentation
- self.indentation_string = indentation_string
- self.queue = u''
- self.scope = [set()]
-
- self._variables = {}
- self.t_counter = 0
-
- def save(self):
- self.t_counter += 1
- return "%s%d" % (self.t_prefix, self.t_counter)
-
- def restore(self):
- var = "%s%d" % (self.t_prefix, self.t_counter)
- self.t_counter -= 1
- return var
-
- def indent(self, amount=1):
- if amount > 0:
- self.cook()
- self.indentation += amount
-
- def outdent(self, amount=1):
- if amount > 0:
- self.cook()
- self.indentation -= amount
-
- def out(self, string):
- self.queue += string
-
- def cook(self):
- if self.queue:
- queue = self.queue
- self.queue = ''
- self.write("_out.write('%s')" %
- queue.replace('\n', '\\n').replace("'", "\\'"))
-
- def write(self, string):
- self.cook()
- StringIO.write(
- self, self.indentation_string * self.indentation + string + '\n')
-
- def getvalue(self):
- self.cook()
- return StringIO.getvalue(self)
-
- def begin(self, clauses):
- if isinstance(clauses, (list, tuple)):
- for clause in clauses:
- self.begin(clause)
- else:
- clauses.begin(self)
-
- def end(self, clauses):
- if isinstance(clauses, (list, tuple)):
- for clause in reversed(clauses):
- self.end(clause)
- else:
- clauses.end(self)
-
Deleted: z3c.pt/trunk/z3c/pt/parsing.py
===================================================================
--- z3c.pt/trunk/z3c/pt/parsing.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/parsing.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,263 +0,0 @@
-import parser
-
-def name(string):
- return string
-
-def search(string):
- """
- Extracts the longest valid Python-expression from the beginning of
- the provided string..
-
- >>> search("'Hello World'")
- "'Hello World'"
-
- >>> search("'Hello World'}")
- "'Hello World'"
-
- >>> search("'Hello World' + '")
- "'Hello World' "
-
- """
-
- left = 0
- right = left + 1
-
- current = None
-
- while right <= len(string):
- expression = string[left:right]
-
- try:
- e = value(expression)
- current = expression
- except SyntaxError, e:
- if right == len(string):
- if current is not None:
- return current
-
- raise e
-
- right += 1
-
- return current
-
-def value(string):
- """
- Specification:
-
- value :: = python_expression [ |* value ]
- python_expresion ::= a python expression string
-
- *) Using | as logical or is not supported.
-
- >>> value("4 + 5")
- ['4 + 5']
-
- Complex expressions:
-
- >>> value("a.non_defined_method() | 1")
- ['a.non_defined_method() ', '1']
-
- Expression with non-semantic horizontal bar.
-
- >>> value("'|'")
- ["'|'"]
-
- Expression with non-semantic horizontal bar and semantic bar.
-
- >>> value("a.non_defined_method() | '|'")
- ['a.non_defined_method() ', "'|'"]
-
- """
-
- string = string.replace('\n', '').strip()
-
- if not string:
- return []
-
- expressions = []
-
- i = j = 0
- while i < len(string):
- j = string.find('|', j + 1)
- if j == -1:
- j = len(string)
-
- expr = string[i:j].lstrip()
-
- try:
- # we use the ``parser`` module to determine if
- # an expression is a valid python expression
- parser.expr(expr.encode('utf-8'))
- except SyntaxError, e:
- if j < len(string):
- continue
-
- raise e
-
- expressions.append(expr)
- i = j + 1
-
- return expressions
-
-def variable(string):
- """
- Specification:
-
- variables :: = variable_name [, variables]
-
- Single variable:
-
- >>> variable("variable")
- ('variable',)
-
- Multiple variables:
-
- >>> variable("variable1, variable2")
- ('variable1', 'variable2')
-
- """
-
- variables = []
- for var in string.split(', '):
- var = var.strip()
-
- if var in ('repeat',):
- raise ValueError, "Invalid variable name '%s' (reserved)." % variable
-
- if var.startswith('_') and not var.startswith('_tmp'):
- raise ValueError(
- "Invalid variable name '%s' (starts with an underscore)." % var)
-
- variables.append(var)
-
- return tuple(variables)
-
-def mapping(string):
- """
-
- >>> mapping("abc def")
- [('abc', 'def')]
-
- >>> mapping("abc")
- [('abc', None)]
-
- >>> mapping("abc; def ghi")
- [('abc', None), ('def', 'ghi')]
-
- """
-
- defs = string.split(';')
- mappings = []
- for d in defs:
- d = d.strip()
- while ' ' in d:
- d = d.replace(' ', ' ')
-
- parts = d.split(' ')
- if len(parts) == 1:
- mappings.append((d, None))
- elif len(parts) == 2:
- mappings.append((parts[0], parts[1]))
- else:
- raise ValueError, "Invalid mapping (%s)." % string
-
- return mappings
-
-def definitions(string):
- """
- Specification:
-
- argument ::= define_var [';' define_var]
- define_var ::= Name python_expression
-
- Single define:
-
- >>> definitions("variable expression")
- [(['variable'], ['expression'])]
-
- Multiple defines:
-
- >>> definitions("variable1 expression1; variable2 expression2")
- [(['variable1'], ['expression1']), (['variable2'], ['expression2'])]
-
- Tuple define:
-
- >>> definitions("(variable1, variable2) (expression1, expression2)")
- [(['variable1', 'variable2'], ['(expression1, expression2)'])]
-
- Use of unescaped semicolon in an expression:
-
- >>> definitions("variable ';'")
- [(['variable'], ["';'"])]
-
- A define clause that ends in a semicolon:
-
- >>> definitions("variable expression;")
- [(['variable'], ['expression'])]
-
- A define clause with a trivial expression (we do allow this):
-
- >>> definitions("variable")
- [(['variable'], None)]
-
- A proper define clause following one with a trivial expression:
-
- >>> definitions("variable1 expression; variable2")
- [(['variable1'], ['expression']), (['variable2'], None)]
-
- """
-
- string = string.replace('\n', '').strip()
-
- defines = []
-
- i = 0
- while i < len(string):
- while string[i] == ' ':
- i += 1
-
- # get variable definition
- if string[i] == '(':
- j = string.find(')', i+1)
- if j == -1:
- raise ValueError, "Invalid variable tuple definition (%s)." % string
- var = variable(string[i+1:j])
- j += 1
- else:
- j = string.find(' ', i + 1)
- if j == -1:
- var = variable(string[i:])
- j = len(string)
- else:
- var = variable(string[i:j])
-
- # get expression
- i = j
- while j < len(string):
- j = string.find(';', j+1)
- if j == -1:
- j = len(string)
-
- try:
- expr = value(string[i:j])
- except SyntaxError, e:
- if j < len(string):
- continue
- raise e
- break
- else:
- expr = None
-
- defines.append((list(var), expr))
-
- i = j + 1
-
- return defines
-
-def definition(string):
- defs = definitions(string)
- if len(defs) != 1:
- raise ValueError, "Multiple definitions not allowed."
-
- return defs[0]
Modified: z3c.pt/trunk/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/z3c/pt/template.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/template.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -4,7 +4,8 @@
class BaseTemplate(object):
registry = {}
-
+ default_expression = 'python'
+
def __init__(self, body):
self.body = body
self.signature = hash(body)
@@ -14,7 +15,8 @@
return NotImplementedError("Must be implemented by subclass.")
def cook(self, params):
- source, _globals = self.translate(self.body, params)
+ source, _globals = self.translate(
+ self.body, params=params, default_expression=self.default_expression)
suite = codegen.Suite(source)
self.source = source
Added: z3c.pt/trunk/z3c/pt/testing.py
===================================================================
--- z3c.pt/trunk/z3c/pt/testing.py (rev 0)
+++ z3c.pt/trunk/z3c/pt/testing.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -0,0 +1,5 @@
+import expressions
+
+def value(string):
+ translator = expressions.PythonTranslation()
+ return translator.value(string)
Modified: z3c.pt/trunk/z3c/pt/tests/test_doctests.py
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/test_doctests.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/tests/test_doctests.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,15 +1,14 @@
import zope.testing
import unittest
-OPTIONFLAGS = (#zope.testing.doctest.REPORT_ONLY_FIRST_FAILURE |
- zope.testing.doctest.ELLIPSIS |
+OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
zope.testing.doctest.NORMALIZE_WHITESPACE)
import zope.component.testing
def test_suite():
filesuites = ['README.txt', 'BENCHMARKS.txt', 'translation.txt', 'i18n.txt', 'codegen.txt']
- testsuites = ['z3c.pt.translation', 'z3c.pt.parsing', 'z3c.pt.clauses']
+ testsuites = ['z3c.pt.translation', 'z3c.pt.clauses', 'z3c.pt.expressions']
return unittest.TestSuite(
[zope.testing.doctest.DocTestSuite(doctest,
Modified: z3c.pt/trunk/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.py 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/translation.py 2008-03-16 17:20:11 UTC (rev 84711)
@@ -1,3 +1,5 @@
+from zope import component
+
from StringIO import StringIO
import lxml.etree
import re
@@ -2,38 +4,22 @@
-import io
+import generation
import utils
-import parsing
+import expressions
import clauses
+import interfaces
-# set up namespace
-lookup = lxml.etree.ElementNamespaceClassLookup()
-parser = lxml.etree.XMLParser()
-parser.setElementClassLookup(lookup)
-
-xhtml = lxml.etree.Namespace('http://www.w3.org/1999/xhtml')
-tal = lxml.etree.Namespace('http://xml.zope.org/namespaces/tal')
-
-wrapper = """\
-def render(%starget_language=None):
-\tglobal utils
-
-\t_out = utils.initialize_stream()
-
-\t(_attributes, repeat) = utils.initialize_tal()
-\t(_domain, _translate) = utils.initialize_i18n()
-\t(_escape, _marker) = utils.initialize_helpers()
-
-\t_target_language = target_language
-%s
-\treturn _out.getvalue().decode('utf-8')
-"""
interpolation_regex = re.compile(r'([^\\]\$|^\$){(?P<expression>.*)}')
-def attribute(ns, factory, default=None):
+def attribute(ns, factory=None, default=None):
def get(self):
value = self.attrib.get(ns)
if value is not None:
- return factory(value)
+ if factory is None:
+ return value
+
+ f = factory(self._translator())
+ return f(value)
elif default is not None:
return default
+
def set(self, value):
@@ -43,13 +29,13 @@
return property(get, set)
-def interpolate(string):
+def interpolate(string, translator):
m = interpolation_regex.search(string)
if m is None:
return None
left = m.start()
- exp = parsing.search(string[left+3:])
+ exp = translator.search(string[left+3:])
right = left+4+len(exp)
m = interpolation_regex.search(string[:right])
@@ -65,10 +51,10 @@
class Element(lxml.etree.ElementBase):
def begin(self, stream):
stream.scope.append(set())
- stream.begin(self.clauses())
+ stream.begin(self._clauses())
def end(self, stream):
- stream.end(self.clauses())
+ stream.end(self._clauses())
stream.scope.pop()
def body(self, stream):
@@ -103,9 +89,10 @@
def interpolate(self, stream):
# interpolate text
+ translator = self._translator()
if self.text is not None:
while self.text:
- m = interpolate(self.text)
+ m = interpolate(self.text, translator)
if m is None:
break
@@ -119,7 +106,7 @@
# interpolate tail
if self.tail is not None:
while self.tail:
- m = interpolate(self.tail)
+ m = interpolate(self.tail, translator)
if m is None:
break
@@ -143,7 +130,7 @@
while value:
string = value[i:]
- m = interpolate(string)
+ m = interpolate(string, translator)
if m is None:
break
@@ -157,7 +144,7 @@
format += '%s%s'
exp = m.group('expression')
- if len(parsing.value(exp)) == 1:
+ if len(translator.value(exp)) == 1:
terms.extend(("'%s'" % text.replace("'", "\\'"), exp))
else:
var = stream.save()
@@ -188,7 +175,7 @@
else:
self.attrib[define] = '%s %s' % (name, expression)
- def clauses(self):
+ def _clauses(self):
_ = []
# i18n domain
@@ -272,7 +259,7 @@
subclauses = []
subclauses.append(clauses.Define('_out', ['utils.initialize_stream()']))
- subclauses.append(clauses.Group(element.clauses()))
+ subclauses.append(clauses.Group(element._clauses()))
subclauses.append(clauses.Assign(['_out.getvalue()'],
"%s['%s']" % (mapping, name)))
@@ -380,36 +367,48 @@
return attributes
+ def _translator(self):
+ while self.default_expression is None:
+ self = self.getparent()
+ if self is None:
+ raise ValueError("Default expression not set.")
+
+ return component.getUtility(
+ interfaces.IExpressionTranslation, name=self.default_expression)
+
define = attribute(
- "{http://xml.zope.org/namespaces/tal}define", parsing.definitions)
+ "{http://xml.zope.org/namespaces/tal}define", lambda p: p.definitions)
condition = attribute(
- "{http://xml.zope.org/namespaces/tal}condition", parsing.value)
+ "{http://xml.zope.org/namespaces/tal}condition", lambda p: p.value)
repeat = attribute(
- "{http://xml.zope.org/namespaces/tal}repeat", parsing.definition)
+ "{http://xml.zope.org/namespaces/tal}repeat", lambda p: p.definition)
attributes = attribute(
- "{http://xml.zope.org/namespaces/tal}attributes", parsing.definitions)
+ "{http://xml.zope.org/namespaces/tal}attributes", lambda p: p.definitions)
content = attribute(
- "{http://xml.zope.org/namespaces/tal}content", parsing.value)
+ "{http://xml.zope.org/namespaces/tal}content", lambda p: p.value)
replace = attribute(
- "{http://xml.zope.org/namespaces/tal}replace", parsing.value)
+ "{http://xml.zope.org/namespaces/tal}replace", lambda p: p.value)
omit = attribute(
- "{http://xml.zope.org/namespaces/tal}omit-tag", parsing.value)
+ "{http://xml.zope.org/namespaces/tal}omit-tag", lambda p: p.value)
i18n_translate = attribute(
- "{http://xml.zope.org/namespaces/i18n}translate", parsing.name)
+ "{http://xml.zope.org/namespaces/i18n}translate")
i18n_attributes = attribute(
- "{http://xml.zope.org/namespaces/i18n}attributes", parsing.mapping)
+ "{http://xml.zope.org/namespaces/i18n}attributes", lambda p: p.mapping)
i18n_domain = attribute(
- "{http://xml.zope.org/namespaces/i18n}domain", parsing.name)
+ "{http://xml.zope.org/namespaces/i18n}domain")
i18n_name = attribute(
- "{http://xml.zope.org/namespaces/i18n}name", parsing.name)
-
+ "{http://xml.zope.org/namespaces/i18n}name")
+ default_expression = attribute(
+ "{http://xml.zope.org/namespaces/tal}default-expression")
+
class TALElement(Element):
- define = attribute("define", parsing.definitions)
- replace = attribute("replace", parsing.value)
- repeat = attribute("repeat", parsing.definition)
- attributes = attribute("attributes", parsing.value)
- content = attribute("content", parsing.value)
- omit = attribute("omit-tag", parsing.value, u"")
+ define = attribute("define", lambda p: p.definitions)
+ replace = attribute("replace", lambda p: p.value)
+ repeat = attribute("repeat", lambda p: p.definition)
+ attributes = attribute("attributes", lambda p: p.value)
+ content = attribute("content", lambda p: p.value)
+ omit = attribute("omit-tag", lambda p: p.value, u"")
+ default_expression = attribute("default-expression", lambda p: p.name)
def _static_attributes(self):
attributes = {}
@@ -428,38 +427,50 @@
return attributes
-xhtml[None] = Element
-tal[None] = TALElement
+# set up namespaces for XML parsing
+lookup = lxml.etree.ElementNamespaceClassLookup()
+parser = lxml.etree.XMLParser()
+parser.setElementClassLookup(lookup)
-def translate_xml(body, params=[]):
+lxml.etree.Namespace('http://www.w3.org/1999/xhtml')[None] = Element
+lxml.etree.Namespace('http://xml.zope.org/namespaces/tal')[None] = TALElement
+
+def translate_xml(body, *args, **kwargs):
tree = lxml.etree.parse(StringIO(body), parser)
root = tree.getroot()
- return translate_etree(root, params=params)
+ return translate_etree(root, *args, **kwargs)
-def translate_etree(root, params=[]):
+def translate_etree(root, params=[], default_expression='python'):
if None not in root.nsmap:
raise ValueError, "Must set default namespace."
-
- stream = io.CodeIO(indentation=1, indentation_string="\t")
+ # set up code generation stream
+ stream = generation.CodeIO(indentation=1, indentation_string="\t")
stream.scope.append(set(params + ['_out']))
+ # set default expression name
+ key = '{http://xml.zope.org/namespaces/tal}default-expression'
+ if key not in root.attrib:
+ root.attrib[key] = default_expression
+
+ # visit root
root.interpolate(stream)
root.visit(stream)
- code = stream.getvalue()
+ # prepare template arguments
args = ', '.join(params)
if args: args += ', '
-
- return wrapper % (args, code), {'utils': utils}
-def translate_text(body, params=[]):
+ code = stream.getvalue()
+ return generation.wrapper % (args, code), {'utils': utils}
+
+def translate_text(body, *args, **kwargs):
xml = parser.makeelement(
'{http://www.w3.org/1999/xhtml}text',
nsmap={None: 'http://www.w3.org/1999/xhtml'})
xml.text = body
xml.attrib['{http://xml.zope.org/namespaces/tal}omit-tag'] = ''
- return translate_etree(xml, params=params)
+ return translate_etree(xml, *args, **kwargs)
def _translate(expressions, mapping=None, default=None):
return [("_translate(%s, domain=_domain, mapping=%s, " + \
Modified: z3c.pt/trunk/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.txt 2008-03-16 15:56:23 UTC (rev 84710)
+++ z3c.pt/trunk/z3c/pt/translation.txt 2008-03-16 17:20:11 UTC (rev 84711)
@@ -11,15 +11,21 @@
... exec source in _globals, _locals
... return _locals['render'](**kwargs)
+We'll first register the Python expression translator.
+
+ >>> from z3c.pt.expressions import PythonTranslation
+ >>> from zope.component import provideUtility
+ >>> provideUtility(PythonTranslation(), name="python")
+
TAL templates
-------------
- >>> from z3c.pt.translation import translate_xml
-
For the functional testing of TAL templates we provide a complex
template that covers most cases. It's been developed alongside
development and is not meant to be easy on the eye.
-
+
+ >>> from z3c.pt.translation import translate_xml
+
>>> body = """\
... <div xmlns="http://www.w3.org/1999/xhtml"
... xmlns:tal="http://xml.zope.org/namespaces/tal">
More information about the Checkins
mailing list