[Checkins] SVN: z3c.pt/trunk/ Implemented 'nocall',
'structure' and 'not'-pragmas.
Malthe Borch
mborch at gmail.com
Sun Mar 16 20:54:46 EDT 2008
Log message for revision 84726:
Implemented 'nocall', 'structure' and 'not'-pragmas.
Changed:
U z3c.pt/trunk/README.txt
U z3c.pt/trunk/docs/HISTORY.txt
U z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
U z3c.pt/trunk/z3c/pt/README.txt
U z3c.pt/trunk/z3c/pt/clauses.py
U z3c.pt/trunk/z3c/pt/configure.zcml
U z3c.pt/trunk/z3c/pt/expressions.py
U z3c.pt/trunk/z3c/pt/generation.py
U z3c.pt/trunk/z3c/pt/pagetemplate.py
U z3c.pt/trunk/z3c/pt/template.py
U z3c.pt/trunk/z3c/pt/translation.py
U z3c.pt/trunk/z3c/pt/translation.txt
U z3c.pt/trunk/z3c/pt/utils.py
-=-
Modified: z3c.pt/trunk/README.txt
===================================================================
--- z3c.pt/trunk/README.txt 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/README.txt 2008-03-17 00:54:31 UTC (rev 84726)
@@ -25,36 +25,31 @@
The template and expression language is based loosely on the TAL 1.4
specification*. Some notable changes:
-1. Only Python-expressions are allowed. Expressions can have
- try-except fallbacks using the vertical bar syntax:
+1. Tuples are allowed in the tal:define statement:
- tal:content="<expression> | <first fallback> | <second fallback> | ..."
-
-2. Tuples are allowed in the tal:define statement:
-
tal:define="(a, b, c) [1, 2, 3]"
-3. Generators are allowed in tal:repeat statements. Note that the
+2. Generators are allowed in tal:repeat statements. Note that the
repeat variable is not available in this case.
tal:repeat="i <some generator>"
-4. Attribute-access to dictionary entries is allowed, e.g.
+3. Attribute-access to dictionary entries is allowed, e.g.
dictionary.key
can be used instead of ``dictionary['key']``.
-5. Expressions that return a callable are called.
+4. Expressions that return a callable are called.
-6. Expression interpolation is allowed:
+5. Expression interpolation is allowed:
<a href="mailto:${context.email}">${context.email}</a>
-7. Attribute-values are always escaped; document expressions are
+6. Attribute-values are always escaped; document expressions are
never.
-8. Default expression type can be set using ``tal:default-expression``.
+7. Default expression type can be set using ``tal:default-expression``.
This is an alternative to providing the expression type before each
expression.
Modified: z3c.pt/trunk/docs/HISTORY.txt
===================================================================
--- z3c.pt/trunk/docs/HISTORY.txt 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/docs/HISTORY.txt 2008-03-17 00:54:31 UTC (rev 84726)
@@ -1,6 +1,17 @@
Changelog
---------
+Version 0.8dev
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Result of 'replace' and 'content' is now escaped by default.
+
+- Added support for 'nocall', 'not' and 'structure'-pragmas.
+
+- Added support for path-expressions.
+
+- Abstracted expression translation engine.
+
Version 0.7 - March 10, 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Modified: z3c.pt/trunk/z3c/pt/BENCHMARKS.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/BENCHMARKS.txt 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/BENCHMARKS.txt 2008-03-17 00:54:31 UTC (rev 84726)
@@ -10,7 +10,7 @@
zope.pagetemplate z3c.pt pure python
Hello World 3.6 1 0.02
-1000 x 10 table 12.7* 1 0.53
+1000 x 10 table 10.6* 1 0.53
There's a setup cost in using a template language which explains the
50x factor in the 'Hello World' benchmark versus a pure python
@@ -22,6 +22,17 @@
UTF-8. There's a penalty of 10-15% when using an encoding that does
not coerce unicode gracefully to strings.
+Setup
+-----
+
+ >>> from zope.component import provideUtility
+
+ >>> from z3c.pt.expressions import PythonTranslation
+ >>> provideUtility(PythonTranslation(), name="python")
+
+ >>> from z3c.pt.expressions import PathTranslation
+ >>> provideUtility(PathTranslation(), name="path")
+
Benchmark source code
---------------------
Modified: z3c.pt/trunk/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/README.txt 2008-03-17 00:54:31 UTC (rev 84726)
@@ -95,7 +95,7 @@
>>> print view.view_page_template_file(test=u'test')
<div>
- <span><ViewPageTemplateView object at ...</span>
+ <span><ViewPageTemplateView object at ...></span>
<span>context</span>
<span>request</span>
<span>test</span>
@@ -105,7 +105,7 @@
>>> print view.view_page_template(test=u'test')
<div>
- <span><ViewPageTemplateView object at ...</span>
+ <span><ViewPageTemplateView object at ...></span>
<span>context</span>
<span>request</span>
<span>test</span>
Modified: z3c.pt/trunk/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/z3c/pt/clauses.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/clauses.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -210,6 +210,7 @@
"""
>>> from z3c.pt.generation import CodeIO
>>> from z3c.pt.testing import value
+ >>> from cgi import escape as _escape
Unlimited scope:
@@ -260,15 +261,23 @@
"""
- def __init__(self, expression, clauses=None, finalize=True):
- self.assign = Assign(expression)
+ def __init__(self, value, clauses=None, finalize=True):
+ self.assign = Assign(value)
+ try:
+ self.inverse = value.options.get('not')
+ except:
+ import pdb; pdb.set_trace()
+
self.clauses = clauses
self.finalize = finalize
def begin(self, stream):
temp = stream.save()
self.assign.begin(stream, temp)
- stream.write("if %s:" % temp)
+ if self.inverse:
+ stream.write("if not(%s):" % temp)
+ else:
+ stream.write("if %s:" % temp)
stream.indent()
if self.clauses:
for clause in self.clauses:
@@ -381,16 +390,17 @@
temp = stream.save()
- for attribute, expression in dynamic:
- assign = Assign(expression)
+ for attribute, value in dynamic:
+ assign = Assign(value)
assign.begin(stream, temp)
# only include attribute if value is non-trivial
stream.write("if %s is not None:" % temp)
stream.indent()
- # if callable, evaluate method
- stream.write("if callable(%s): %s = %s()" % (temp, temp, temp))
+ if not value.options.get('nocall'):
+ # if callable, evaluate method
+ stream.write("if callable(%s): %s = %s()" % (temp, temp, temp))
if unicode_required_flag:
stream.write("if isinstance(%s, unicode):" % temp)
@@ -485,7 +495,8 @@
>>> from z3c.pt.generation import CodeIO; stream = CodeIO()
>>> from z3c.pt.testing import value
>>> from StringIO import StringIO
-
+ >>> from cgi import escape as _escape
+
>>> _out = StringIO()
>>> write = Write(value("'New York'"))
>>> write.begin(stream)
@@ -502,22 +513,25 @@
'New York, New York!'
"""
- def __init__(self, expressions):
- self.assign = Assign(expressions)
- self.expressions = expressions
- self.count = len(expressions)
+ def __init__(self, value):
+ self.assign = Assign(value)
+ self.value = value
+ self.count = len(value)
def begin(self, stream):
temp = stream.save()
if self.count == 1:
- expr = self.expressions[0]
+ expr = self.value[0]
else:
self.assign.begin(stream, temp)
expr = temp
stream.write("_urf = %s" % expr)
- stream.write("if callable(_urf): _urf = _urf()")
+
+ if not self.value.options.get('nocall'):
+ stream.write("if callable(_urf): _urf = _urf()")
+
stream.write("if _urf is None: _urf = ''")
if unicode_required_flag:
@@ -527,10 +541,16 @@
stream.outdent()
stream.write("else:")
stream.indent()
- stream.write("_out.write(str(_urf))")
+ if self.value.options.get('structure'):
+ stream.write("_out.write(str(_urf))")
+ else:
+ stream.write("_out.write(_escape(str(_urf)))")
stream.outdent()
else:
- stream.write("_out.write(str(_urf))")
+ if self.value.options.get('structure'):
+ stream.write("_out.write(str(_urf))")
+ else:
+ stream.write("_out.write(_escape(str(_urf)))")
def end(self, stream):
if self.count != 1:
Modified: z3c.pt/trunk/z3c/pt/configure.zcml
===================================================================
--- z3c.pt/trunk/z3c/pt/configure.zcml 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/configure.zcml 2008-03-17 00:54:31 UTC (rev 84726)
@@ -5,6 +5,10 @@
<utility
name="python"
factory=".expressions.PythonTranslation" />
-
+
+ <utility
+ name="path"
+ factory=".expressions.PathTranslation" />
+
</configure>
Modified: z3c.pt/trunk/z3c/pt/expressions.py
===================================================================
--- z3c.pt/trunk/z3c/pt/expressions.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/expressions.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -1,14 +1,18 @@
import zope.interface
+import zope.component
import zope.traversing.adapters
import parser
import re
from interfaces import IExpressionTranslation
+from utils import value
class ExpressionTranslation(object):
zope.interface.implements(IExpressionTranslation)
+ pragma = re.compile(r'^(?P<pragma>[a-z]+):')
+
def name(self, string):
return string
@@ -246,8 +250,34 @@
expressions = []
+ # reset pragmas
+ translator = self
+
+ options = {'nocall': False,
+ 'structure': False,
+ 'not': False}
+
i = j = 0
while i < len(string):
+ match = self.pragma.match(string[i:])
+ if match is not None:
+ pragma = match.group('pragma').lower()
+
+ utility = zope.component.queryUtility(
+ IExpressionTranslation, name=pragma)
+
+ if utility is not None:
+ translator = utility
+ pragma = None
+
+ if pragma in options:
+ options[pragma] = True
+ pragma = None
+
+ if pragma is None:
+ i += match.end()
+ continue
+
j = string.find('|', j + 1)
if j == -1:
j = len(string)
@@ -255,17 +285,19 @@
expr = string[i:j].lstrip()
try:
- self.validate(expr)
+ translator.validate(expr)
except Exception, e:
if j < len(string):
continue
raise e
- expressions.append(self.translate(expr))
+ expressions.append(translator.translate(expr))
+ translator = self
+
i = j + 1
- return tuple(expressions)
+ return value(options, expressions)
class PythonTranslation(ExpressionTranslation):
def validate(self, string):
Modified: z3c.pt/trunk/z3c/pt/generation.py
===================================================================
--- z3c.pt/trunk/z3c/pt/generation.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/generation.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -1,22 +1,44 @@
-from StringIO import StringIO
+import zope.i18n
+import cgi
+import StringIO
+import cStringIO
+
+import expressions
+import utils
+
wrapper = """\
def render(%starget_language=None):
-\tglobal utils
+\tglobal generation
-\t_out = utils.initialize_stream()
+\t_out = generation.initialize_stream()
-\t(_attributes, repeat) = utils.initialize_tal()
-\t(_domain, _translate) = utils.initialize_i18n()
-\t(_escape, _marker) = utils.initialize_helpers()
-\t_path = utils.initialize_traversal()
+\t(_attributes, repeat) = generation.initialize_tal()
+\t(_domain, _translate) = generation.initialize_i18n()
+\t(_escape, _marker) = generation.initialize_helpers()
+\t_path = generation.initialize_traversal()
\t_target_language = target_language
%s
\treturn _out.getvalue().decode('utf-8')
"""
-class CodeIO(StringIO):
+def initialize_i18n():
+ return (None, zope.i18n.translate)
+
+def initialize_tal():
+ return ({}, utils.repeatdict())
+
+def initialize_helpers():
+ return (cgi.escape, object())
+
+def initialize_stream():
+ return cStringIO.StringIO()
+
+def initialize_traversal():
+ return expressions.PathTranslation.traverse
+
+class CodeIO(StringIO.StringIO):
"""
A high-level I/O class to write Python code to a stream.
Indentation is managed using ``indent`` and ``outdent``.
@@ -34,7 +56,7 @@
v_prefix = '_var'
def __init__(self, indentation=0, indentation_string="\t"):
- StringIO.__init__(self)
+ StringIO.StringIO.__init__(self)
self.indentation = indentation
self.indentation_string = indentation_string
self.queue = u''
@@ -74,12 +96,12 @@
def write(self, string):
self.cook()
- StringIO.write(
+ StringIO.StringIO.write(
self, self.indentation_string * self.indentation + string + '\n')
def getvalue(self):
self.cook()
- return StringIO.getvalue(self)
+ return StringIO.StringIO.getvalue(self)
def begin(self, clauses):
if isinstance(clauses, (list, tuple)):
Modified: z3c.pt/trunk/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/pagetemplate.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -15,8 +15,8 @@
return translation.translate_xml
class ViewPageTemplate(property):
- def __init__(self, body):
- self.template = PageTemplate(body)
+ def __init__(self, body, **kwargs):
+ self.template = PageTemplate(body, **kwargs)
property.__init__(self, self.render)
def render(self, view):
@@ -28,6 +28,6 @@
return template
class ViewPageTemplateFile(ViewPageTemplate):
- def __init__(self, filename):
- self.template = PageTemplateFile(filename)
+ def __init__(self, filename, **kwargs):
+ self.template = PageTemplateFile(filename, **kwargs)
property.__init__(self, self.render)
Modified: z3c.pt/trunk/z3c/pt/template.py
===================================================================
--- z3c.pt/trunk/z3c/pt/template.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/template.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -6,10 +6,13 @@
registry = {}
default_expression = 'python'
- def __init__(self, body):
+ def __init__(self, body, default_expression=None):
self.body = body
self.signature = hash(body)
+ if default_expression:
+ self.default_expression = default_expression
+
@property
def translate(self):
return NotImplementedError("Must be implemented by subclass.")
@@ -18,9 +21,9 @@
source, _globals = self.translate(
self.body, params=params, default_expression=self.default_expression)
suite = codegen.Suite(source)
+
+ self.source = source
- self.source = source
-
_globals.update(suite._globals)
_locals = {}
Modified: z3c.pt/trunk/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/translation.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -5,10 +5,10 @@
import re
import generation
-import utils
import expressions
import clauses
import interfaces
+import utils
interpolation_regex = re.compile(r'([^\\]\$|^\$){(?P<expression>.*)}')
@@ -180,7 +180,8 @@
# i18n domain
if self.i18n_domain is not None:
- _.append(clauses.Define("_domain", [repr(self.i18n_domain)]))
+ _.append(clauses.Define(
+ "_domain", utils.value({}, (repr(self.i18n_domain),))))
# defines
if self.define is not None:
@@ -258,10 +259,11 @@
name = element.i18n_name
subclauses = []
- subclauses.append(clauses.Define('_out', ['utils.initialize_stream()']))
+ subclauses.append(clauses.Define(
+ '_out', utils.value({}, ('generation.initialize_stream()',))))
subclauses.append(clauses.Group(element._clauses()))
- subclauses.append(clauses.Assign(['_out.getvalue()'],
- "%s['%s']" % (mapping, name)))
+ subclauses.append(clauses.Assign(
+ utils.value({}, ('_out.getvalue()',)), "%s['%s']" % (mapping, name)))
_.append(clauses.Group(subclauses))
@@ -271,18 +273,19 @@
# write translation to output if successful, otherwise
# fallback to default rendition;
+ result = utils.value({}, ('_result',))
+ condition = utils.value({}, ('_result is not _marker',))
+ _.append(clauses.Condition(condition, [clauses.Write(result)]))
- _.append(clauses.Condition(['_result is not _marker'],
- [clauses.Write(['_result'])]))
-
subclauses = []
if self.text:
subclauses.append(clauses.Out(self.text))
for element in self:
name = element.i18n_name
if name:
- subclauses.append(clauses.Write(["%s['%s']" % (mapping, name)]))
- #subclauses.append(clauses.Out(element.tail))
+ value = utils.value(
+ {'structure': True}, ("%s['%s']" % (mapping, name),))
+ subclauses.append(clauses.Write(value))
else:
subclauses.append(clauses.Out(lxml.etree.tostring(element)))
@@ -462,7 +465,7 @@
if args: args += ', '
code = stream.getvalue()
- return generation.wrapper % (args, code), {'utils': utils}
+ return generation.wrapper % (args, code), {'generation': generation}
def translate_text(body, *args, **kwargs):
xml = parser.makeelement(
@@ -473,9 +476,11 @@
return translate_etree(xml, *args, **kwargs)
def _translate(expressions, mapping=None, default=None):
- return [("_translate(%s, domain=_domain, mapping=%s, " + \
- "target_language=_target_language, default=%s)") %
- (exp, mapping, default) for exp in expressions]
+ format = "_translate(%s, domain=_domain, mapping=%s, "\
+ "target_language=_target_language, default=%s)"
+
+ return utils.value(
+ {}, tuple(format % (exp, mapping, default) for exp in expressions))
def _not(expressions):
- return ["not (%s)" % exp for exp in expressions]
+ return utils.value({}, tuple("not (%s)" % exp for exp in expressions))
Modified: z3c.pt/trunk/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.txt 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/translation.txt 2008-03-17 00:54:31 UTC (rev 84726)
@@ -63,6 +63,7 @@
... <span tal:attributes="class lambda: 'Hello'"
... tal:content="lambda: 'World'" />
... <img tal:attributes="title '%sHello%s' % (chr(60), chr(62))" />
+ ... <span tal:replace="structure: '%sbr /%s' % (chr(60), chr(62))" />
... <span tal:replace="'%sbr /%s' % (chr(60), chr(62))" />
... <span tal:content="unicode('La Pe\xc3\xb1a', 'utf-8')" />
... <span>inter${'pol' + 'ati'}on</span>is ${int('test') | 'convenient'}!
@@ -81,8 +82,14 @@
... <span tal:replace="mydict/a" />
... <span tal:replace="mydict/b|mydict/a" />
... <span tal:replace="mydict/c/a" />
+ ... <span tal:replace="python: 5+5" />
... </div>
+ ... <span tal:replace="path: mydict/a" />
+ ... <span tal:replace="path: mydict/b|True" />
+ ... <span tal:replace="int('a')|path: mydict/a" />
... </tal:path-expression-testing>
+ ... <span tal:replace="nocall: dir" />
+ ... <span tal:condition="not: False">Hello World</span>
... </div>
... """
@@ -126,6 +133,7 @@
<span class="Hello">World</span>
<img title="<Hello>" />
<br />
+ <br />
<span>La Peña</span>
<span>interpolation</span>is convenient!
<span class="Hello World!" />
@@ -140,8 +148,14 @@
1
1
2
+ 10
</div>
+ 1
+ True
+ 1
<BLANKLINE>
+ <built-in function dir>
+ <span>Hello World</span>
</div>
Text templates
Modified: z3c.pt/trunk/z3c/pt/utils.py
===================================================================
--- z3c.pt/trunk/z3c/pt/utils.py 2008-03-17 00:14:13 UTC (rev 84725)
+++ z3c.pt/trunk/z3c/pt/utils.py 2008-03-17 00:54:31 UTC (rev 84726)
@@ -1,11 +1,5 @@
-import zope.i18n
-
-from cStringIO import StringIO
-
import sys
-import cgi
import logging
-import expressions
# check if we're able to coerce unicode to str
try:
@@ -29,24 +23,6 @@
return g
return decorate
-def initialize_i18n():
- return (None, zope.i18n.translate)
-
-def initialize_tal():
- return ({}, repeatdict())
-
-def initialize_helpers():
- return (cgi.escape, object())
-
-def initialize_stream():
- return StringIO()
-
-def initialize_traversal():
- return expressions.PathTranslation.traverse
-
-def getLanguage(request):
- return ''
-
s_counter = 0
class scope(list):
@@ -58,6 +34,12 @@
def __hash__(self):
return self.hash
+class value(tuple):
+ def __new__(cls, options, *args):
+ inst = tuple.__new__(cls, *args)
+ inst.options = options
+ return inst
+
class repeatitem(object):
def __init__(self, iterator, length):
self.length = length
More information about the Checkins
mailing list