[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