[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>&lt;ViewPageTemplateView object at ...&gt;</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>&lt;ViewPageTemplateView object at ...&gt;</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="&lt;Hello&gt;" />
     <br />
+    &lt;br /&gt;
     <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>
+    &lt;built-in function dir&gt;
+    <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