[Checkins] SVN: z3c.pt/trunk/ Added support for text templates.

Malthe Borch mborch at gmail.com
Sat Feb 23 22:21:19 EST 2008


Log message for revision 84207:
  Added support for text templates.

Changed:
  U   z3c.pt/trunk/README.txt
  U   z3c.pt/trunk/docs/HISTORY.txt
  U   z3c.pt/trunk/setup.py
  U   z3c.pt/trunk/z3c/pt/README.txt
  U   z3c.pt/trunk/z3c/pt/__init__.py
  U   z3c.pt/trunk/z3c/pt/clauses.py
  D   z3c.pt/trunk/z3c/pt/pagetemplate.py
  A   z3c.pt/trunk/z3c/pt/pagetemplate.py
  A   z3c.pt/trunk/z3c/pt/template.py
  A   z3c.pt/trunk/z3c/pt/tests/view.css
  A   z3c.pt/trunk/z3c/pt/texttemplate.py
  U   z3c.pt/trunk/z3c/pt/translation.py
  U   z3c.pt/trunk/z3c/pt/translation.txt

-=-
Modified: z3c.pt/trunk/README.txt
===================================================================
--- z3c.pt/trunk/README.txt	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/README.txt	2008-02-24 03:21:19 UTC (rev 84207)
@@ -2,7 +2,8 @@
 --------
 		      
 The z3c.pt package provides an alternative implementation of the TAL
-template language including i18n.
+template language including i18n. It also provides a simple text
+template class that allows expression interpolation.
 
 Casual benchmarks pegs it 12x more performant than ``zope.pagetemplate``.
 
@@ -12,9 +13,12 @@
 * Only Python-expressions are supported
 * Depends only on lxml
 * Adds support for expression interpolation
-  
-The METAL macro language is not supported.
+* Limited support for CSS, Javascript and other non-XML documents
 
+See the README.txt inside the package for instructions on usage.
+
+Note: The METAL macro language is not supported.
+
 Template and expression language
 --------------------------------
 
@@ -59,3 +63,4 @@
 ``z3c.pt==dev`` as your dependency.
 
 http://svn.zope.org/z3c.pt/trunk#egg=z3c.pt-dev
+

Modified: z3c.pt/trunk/docs/HISTORY.txt
===================================================================
--- z3c.pt/trunk/docs/HISTORY.txt	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/docs/HISTORY.txt	2008-02-24 03:21:19 UTC (rev 84207)
@@ -1,6 +1,13 @@
 Changelog
 ---------
 
+Version 0.6 - February 24, 2008
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Added support for text templates; these allow expression
+  interpolation in non-XML documents like CSS stylesheets and
+  javascript files.
+
 Version 0.5 - February 23, 2008
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

Modified: z3c.pt/trunk/setup.py
===================================================================
--- z3c.pt/trunk/setup.py	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/setup.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -1,6 +1,6 @@
 from setuptools import setup, find_packages
 
-version = '0.5'
+version = '0.6'
 
 setup(name='z3c.pt',
       version=version,

Modified: z3c.pt/trunk/z3c/pt/README.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/README.txt	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/README.txt	2008-02-24 03:21:19 UTC (rev 84207)
@@ -1,11 +1,38 @@
 z3c.pt
 ======
 
-Usage
------
+This document demonstrates the high-level API of the package.
 
-From a string:
+Overview
+--------
 
+Two types of TAL page template classes are provided:
+
+``PageTemplate``, ``PageTemlateFile``
+       These classes allow passing of arguments directly to the
+       template namespace.
+
+``ViewPageTemplate``, ``ViewPageTemplateFile``
+       These classes should be initialized as properties in a
+       view class and provide a set of default arguments to the
+       template.
+
+Templates structured as plain text are supported:
+
+``TextTemplate``, ``TextTemplateFile``
+       These classes work like their page template counterparts,
+       except they work with plain text documents, extending
+       to CSS stylesheets, javascript files and other non-XML
+       document. Only expression interpolation is supported.
+
+``ViewTextTemplate``, ``ViewTextTemplateFile``
+       See above.
+
+Page template classes
+---------------------
+
+Initialized with a string:
+
   >>> from z3c.pt import PageTemplate
   >>> template = PageTemplate("""\
   ... <div xmlns="http://www.w3.org/1999/xhtml"
@@ -19,7 +46,7 @@
      Hello World!
   </div>
 
-From a file:
+Providing the path to a template file:
 
   >>> from z3c.pt import PageTemplateFile
   >>> from z3c.pt import tests
@@ -39,10 +66,10 @@
 and for inline templates. The following symbols are defined for the
 template:
 
-  * view
-  * context
-  * request
-  * options
+* view
+* context
+* request
+* options
 
 Keyword-parameters are passed on to the template in a dictionary bound
 to the symbol ``options``.
@@ -77,4 +104,25 @@
     <span>test</span>
   </div>
 
+Text template classes
+---------------------
+
+  >>> from z3c.pt import ViewTextTemplate, ViewTextTemplateFile
   
+  >>> class ViewTextTemplateView(object):
+  ...     view_text_template_file = ViewTextTemplateFile(path+'/view.css')
+  ...     view_text_template = ViewTextTemplate(open(path+'/view.css').read())
+  ...     request = u'request'
+  ...     context = u'context'
+
+  >>> view = ViewTextTemplateView()
+
+  >>> print view.view_text_template_file(color=u'#ccc')
+  #region {
+      background: #ccc;
+  }
+
+  >>> print view.view_text_template(color=u'#ccc')
+  #region {
+      background: #ccc;
+  }

Modified: z3c.pt/trunk/z3c/pt/__init__.py
===================================================================
--- z3c.pt/trunk/z3c/pt/__init__.py	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/__init__.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -2,3 +2,8 @@
 from pagetemplate import PageTemplateFile
 from pagetemplate import ViewPageTemplate
 from pagetemplate import ViewPageTemplateFile
+
+from texttemplate import TextTemplate
+from texttemplate import TextTemplateFile
+from texttemplate import ViewTextTemplate
+from texttemplate import ViewTextTemplateFile

Modified: z3c.pt/trunk/z3c/pt/clauses.py
===================================================================
--- z3c.pt/trunk/z3c/pt/clauses.py	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/clauses.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -349,6 +349,7 @@
 
     def __init__(self, tag, attributes={}, selfclosing=False):
         i = tag.find('}')
+
         if i != -1:
             self.tag = tag[i+1:]
         else:

Deleted: z3c.pt/trunk/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/pagetemplate.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -1,104 +0,0 @@
-import os
-import translation
-import codegen
-import sys
-
-class PageTemplate(object):
-    registry = {}
-
-    def __init__(self, body):
-        self.body = body
-        self.signature = hash(body)
-        
-    def cook(self, params):
-        source, _globals = translation.translate(self.body, params)
-        suite = codegen.Suite(source)
-
-        self.source = source
-        
-        _globals.update(suite._globals)
-        _locals = {}
-
-        exec suite.code in _globals, _locals
-        
-        return _locals['render']
-
-    def render(self, **kwargs):
-        signature = self.signature + hash(",".join(kwargs.keys()))
-
-        template = self.registry.get(signature)
-        if not template:
-            self.registry[signature] = template = self.cook(kwargs.keys())
-
-        return template(**kwargs)
-            
-    def __call__(self, **kwargs):
-        return self.render(**kwargs)
-
-class PageTemplateFile(PageTemplate):
-    def __init__(self, filename):
-        self.filename = filename
-        
-    def _get_filename(self):
-        return getattr(self, '_filename', None)
-
-    def _set_filename(self, filename):
-        self._filename = filename
-        self._v_last_read = False
-
-    filename = property(_get_filename, _set_filename)
-
-    def render(self, **kwargs):
-        if self._cook_check():
-            self.body = open(self.filename, 'r').read()
-            self.signature = hash(self.body)
-            self._v_last_read = self.mtime()
-
-        return PageTemplate.render(self, **kwargs)
-            
-    def _cook_check(self):
-        if self._v_last_read and not __debug__:
-            return
-
-        if self.mtime() == self._v_last_read:
-            return
-
-        return True
-
-    def mtime(self):
-        try:
-            return os.path.getmtime(self.filename)
-        except OSError:
-            return 0
-
-class ViewPageTemplate(property):
-    def __init__(self, body):
-        self.template = PageTemplate(body)
-        property.__init__(self, self.render)
-
-    def render(self, view):
-        def template(**kwargs):
-            return self.template.render(view=view,
-                                        context=view.context,
-                                        request=view.request,
-                                        options=kwargs)
-        return template
-    
-class ViewPageTemplateFile(ViewPageTemplate):
-    def __init__(self, filename):
-        if not os.path.isabs(filename):
-            package_name = sys._getframe(1).f_globals['__name__']
-            module = sys.modules[package_name]
-            try:
-                path = module.__path__[0]
-            except AttributeError:
-                path = module.__file__
-                path = path[:path.rfind(os.sep)]
-                
-            filename = path + os.sep + filename
-
-        # make sure file exists
-        os.lstat(filename)
-            
-        self.template = PageTemplateFile(filename)
-        property.__init__(self, self.render)

Added: z3c.pt/trunk/z3c/pt/pagetemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/pagetemplate.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/pagetemplate.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -0,0 +1,25 @@
+import os
+import sys
+
+import translation
+import template
+
+class PageTemplate(template.BaseTemplate):
+    @property
+    def translate(self):
+        return translation.translate_xml
+
+class PageTemplateFile(template.BaseTemplateFile):
+    @property
+    def translate(self):
+        return translation.translate_xml
+
+class ViewPageTemplate(template.BaseViewTemplate):
+    def __init__(self, body):
+        super(ViewPageTemplate, self).__init__(body)
+        self.template = PageTemplate(body)
+    
+class ViewPageTemplateFile(template.BaseViewTemplateFile):
+    def __init__(self, filename):
+        super(ViewPageTemplateFile, self).__init__(filename)
+        self.template = PageTemplateFile(filename)

Copied: z3c.pt/trunk/z3c/pt/template.py (from rev 84206, z3c.pt/trunk/z3c/pt/pagetemplate.py)
===================================================================
--- z3c.pt/trunk/z3c/pt/template.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/template.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -0,0 +1,104 @@
+import os
+import codegen
+
+class BaseTemplate(object):
+    registry = {}
+
+    def __init__(self, body):
+        self.body = body
+        self.signature = hash(body)        
+
+    @property
+    def translate(self):
+        return NotImplementedError("Must be implemented by subclass.")
+
+    def cook(self, params):
+        source, _globals = self.translate(self.body, params)
+        suite = codegen.Suite(source)
+
+        self.source = source
+        
+        _globals.update(suite._globals)
+        _locals = {}
+
+        exec suite.code in _globals, _locals
+        
+        return _locals['render']
+
+    def render(self, **kwargs):
+        signature = self.signature + hash(",".join(kwargs.keys()))
+
+        template = self.registry.get(signature)
+        if not template:
+            self.registry[signature] = template = self.cook(kwargs.keys())
+
+        return template(**kwargs)
+            
+    def __call__(self, **kwargs):
+        return self.render(**kwargs)
+
+class BaseTemplateFile(BaseTemplate):
+    def __init__(self, filename):
+        self.filename = filename
+        
+    def _get_filename(self):
+        return getattr(self, '_filename', None)
+
+    def _set_filename(self, filename):
+        self._filename = filename
+        self._v_last_read = False
+
+    filename = property(_get_filename, _set_filename)
+
+    def render(self, **kwargs):
+        if self._cook_check():
+            self.body = open(self.filename, 'r').read()
+            self.signature = hash(self.body)
+            self._v_last_read = self.mtime()
+
+        return BaseTemplate.render(self, **kwargs)
+            
+    def _cook_check(self):
+        if self._v_last_read and not __debug__:
+            return
+
+        if self.mtime() == self._v_last_read:
+            return
+
+        return True
+
+    def mtime(self):
+        try:
+            return os.path.getmtime(self.filename)
+        except OSError:
+            return 0
+
+class BaseViewTemplate(property):
+    def __init__(self, body):
+        property.__init__(self, self.render)
+
+    def render(self, view):
+        def template(**kwargs):
+            return self.template.render(view=view,
+                                        context=view.context,
+                                        request=view.request,
+                                        options=kwargs)
+        return template
+
+class BaseViewTemplateFile(BaseViewTemplate):
+    def __init__(self, filename):
+        if not os.path.isabs(filename):
+            package_name = sys._getframe(1).f_globals['__name__']
+            module = sys.modules[package_name]
+            try:
+                path = module.__path__[0]
+            except AttributeError:
+                path = module.__file__
+                path = path[:path.rfind(os.sep)]
+                
+            filename = path + os.sep + filename
+
+        # make sure file exists
+        os.lstat(filename)
+
+        property.__init__(self, self.render)

Added: z3c.pt/trunk/z3c/pt/tests/view.css
===================================================================
--- z3c.pt/trunk/z3c/pt/tests/view.css	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/tests/view.css	2008-02-24 03:21:19 UTC (rev 84207)
@@ -0,0 +1,3 @@
+#region {
+    background: ${options.color};
+}
\ No newline at end of file

Added: z3c.pt/trunk/z3c/pt/texttemplate.py
===================================================================
--- z3c.pt/trunk/z3c/pt/texttemplate.py	                        (rev 0)
+++ z3c.pt/trunk/z3c/pt/texttemplate.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -0,0 +1,22 @@
+import template
+import translation
+
+class TextTemplate(template.BaseTemplate):
+    @property
+    def translate(self):
+        return translation.translate_text
+
+class TextTemplateFile(template.BaseTemplateFile):
+    @property
+    def translate(self):
+        return translation.translate_text
+
+class ViewTextTemplate(template.BaseViewTemplate):
+    def __init__(self, body):
+        super(ViewTextTemplate, self).__init__(body)
+        self.template = TextTemplate(body)
+    
+class ViewTextTemplateFile(template.BaseViewTemplateFile):
+    def __init__(self, filename):
+        super(ViewTextTemplateFile, self).__init__(filename)
+        self.template = TextTemplateFile(filename)

Modified: z3c.pt/trunk/z3c/pt/translation.py
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.py	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/translation.py	2008-02-24 03:21:19 UTC (rev 84207)
@@ -412,10 +412,12 @@
 xhtml[None] = Element
 tal[None] = TALElement
 
-def translate(body, params=[]):
+def translate_xml(body, params=[]):
     tree = lxml.etree.parse(StringIO(body), parser)
     root = tree.getroot()
+    return translate_etree(root, params=params)
 
+def translate_etree(root, params=[]):
     if None not in root.nsmap:
         raise ValueError, "Must set default namespace."
         
@@ -423,6 +425,7 @@
 
     stream.scope.append(set(params + ['_out']))
 
+    root.interpolate(stream)
     root.visit(stream)
 
     code = stream.getvalue()
@@ -431,6 +434,14 @@
     
     return wrapper % (args, code), {'utils': utils}
 
+def translate_text(body, params=[]):
+    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)    
+    
 def _translate(expressions, mapping=None, default=None):
     return [("_translate(%s, domain=_domain, mapping=%s, " + \
              "target_language=_target_language, default=%s)") %

Modified: z3c.pt/trunk/z3c/pt/translation.txt
===================================================================
--- z3c.pt/trunk/z3c/pt/translation.txt	2008-02-24 02:50:24 UTC (rev 84206)
+++ z3c.pt/trunk/z3c/pt/translation.txt	2008-02-24 03:21:19 UTC (rev 84207)
@@ -1,20 +1,25 @@
 Translation
 -----------
 
-This document contains functional template tests covering the TAL
-directives.
+This document contains functional template tests.
 
-  >>> from z3c.pt.translation import translate
+We use a generic render-method for convenience.
 
-  >>> def render(body, **kwargs):
-  ...    source, _globals = translate(body)
+  >>> def render(body, translator, **kwargs):
+  ...    source, _globals = translator(body)
   ...    _locals = {}
   ...    exec source in _globals, _locals
   ...    return _locals['render'](**kwargs)
+
+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.
   
-Canonical test template
------------------------
-
   >>> body = """\
   ... <div xmlns="http://www.w3.org/1999/xhtml"
   ...      xmlns:tal="http://xml.zope.org/namespaces/tal">
@@ -54,7 +59,7 @@
   ... </div>
   ... """
 
-  >>> print render(body)
+  >>> print render(body, translate_xml)
   <div>
     <span id="test" style="position: absolute" class="defabc">abcghi</span>
     <ul>
@@ -101,7 +106,31 @@
     <img alt="Hello La Peña!" />  
   </div>
 
-    
+Text templates
+--------------
+
+  >>> from z3c.pt.translation import translate_text
+
+An example with a CSS stylesheet document:
+  
+  >>> css = """\
+  ... #some-region {
+  ...    background: url(${'http://nohost/plone'}/logo.gif) no-repeat;
+  ... }"""
+
+  >>> print render(css, translate_text)
+  #some-region {
+     background: url(http://nohost/plone/logo.gif) no-repeat;
+  }
+
+A javascript document that prints out HTML:
+
+  >>> js = """\
+  ... print '<div class="description">Hello ${'World!'}</div>';"""
+
+  >>> print render(js, translate_text)
+  print '<div class="description">Hello World!</div>';
+
 Error handling
 --------------
 
@@ -112,7 +141,7 @@
   A default namespace must be explicitly declared for the parser to work.
   
   >>> body = '<br />'
-  >>> render(body)
+  >>> render(body, translate_xml)
   Traceback (most recent call last):
     ...
   ValueError: Must set default namespace.
@@ -121,7 +150,7 @@
   We expect the xml-parser to raise an exception.
   
   >>> body = '<div xmlns="http://www.w3.org/1999/xhtml"'
-  >>> render(body)
+  >>> render(body, translate_xml)
   Traceback (most recent call last):
     ...
   XMLSyntaxError: line 1: Couldn't find end of Start Tag div line 1
@@ -133,5 +162,5 @@
   ... <div xmlns="http://www.w3.org/1999/xhtml" tal:content="'Hello World'" />
   ... """
 
-  >>> print render(body)
+  >>> print render(body, translate_xml)
   <div content="'Hello World'" />



More information about the Checkins mailing list