summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris McDonough <chrism@agendaless.com>2008-09-26 06:42:53 +0000
committerChris McDonough <chrism@agendaless.com>2008-09-26 06:42:53 +0000
commit01a6e567a20096f6033cc603667f4e900d2a44c3 (patch)
treea87431383a63dbafbb5cccdfa7679b9187bdfc29
parent26216e5526ca56d886d2348f9e1f09b86622aa72 (diff)
downloadpyramid-01a6e567a20096f6033cc603667f4e900d2a44c3.tar.gz
pyramid-01a6e567a20096f6033cc603667f4e900d2a44c3.tar.bz2
pyramid-01a6e567a20096f6033cc603667f4e900d2a44c3.zip
Move to Chameleon.
-rw-r--r--CHANGES.txt29
-rw-r--r--docs/api/template.rst63
-rw-r--r--docs/glossary.rst32
-rw-r--r--docs/narr/MyProject/myproject/views.py2
-rw-r--r--docs/narr/project.rst36
-rw-r--r--docs/narr/templates.rst42
-rw-r--r--docs/tutorials/cmf/skins.rst8
-rw-r--r--docs/tutorials/lxmlgraph/step03.rst17
-rw-r--r--docs/tutorials/lxmlgraph/step03/myapp/views.py4
-rw-r--r--docs/tutorials/lxmlgraph/step04/myapp/views.py2
-rw-r--r--repoze/bfg/chameleon_genshi.py82
-rw-r--r--repoze/bfg/chameleon_zpt.py81
-rw-r--r--repoze/bfg/configure.zcml4
-rw-r--r--repoze/bfg/includes/configure.zcml4
-rw-r--r--repoze/bfg/paster_template/+package+/views.py_tmpl2
-rw-r--r--repoze/bfg/push.py6
-rw-r--r--repoze/bfg/template.py151
-rw-r--r--repoze/bfg/tests/fixtures/minimal.genshi3
-rw-r--r--repoze/bfg/tests/test_chameleon_genshi.py134
-rw-r--r--repoze/bfg/tests/test_chameleon_zpt.py139
-rw-r--r--repoze/bfg/tests/test_push.py2
-rw-r--r--repoze/bfg/tests/test_template.py61
-rw-r--r--repoze/bfg/tests/test_xslt.py14
-rw-r--r--repoze/bfg/xslt.py85
-rw-r--r--repoze/bfg/zcml.py2
-rw-r--r--setup.py44
26 files changed, 753 insertions, 296 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 4378fba85..2a0160998 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -13,6 +13,35 @@ Next release
- Add ``principals_allowed_by_permission`` API to security module.
+ - Replace ``z3c.pt`` support with support for ``chameleon.zpt``.
+ Chameleon is the new name for the package that used to be named
+ ``z3c.pt``. NOTE: If you update a ``repoze.bfg`` SVN checkout
+ that you're using for development, you will need to run "setup.py
+ install" or "setup.py develop" again in order to obtain the
+ proper Chameleon packages. ``z3c.pt`` is no longer supported by
+ ``repoze.bfg``. All API functions that used to render ``z3c.pt``
+ templates will work fine with the new packages, and your
+ templates should render almost identically.
+
+ - Add a ``repoze.bfg.chameleon_zpt`` module. This module provides
+ Chameleon ZPT support.
+
+ - Add a ``repoze.bfg.xslt`` module. This module provides XSLT
+ support.
+
+ - Add a ``repoze.bfg.chameleon_genshi`` module. This provides
+ direct Genshi support, which did not exist previously.
+
+ Deprecations
+
+ - Importing API functions directly from ``repoze.bfg.template`` is
+ now deprecated. The ``get_template``, ``render_template``,
+ ``render_template_to_response`` functions should now be imported
+ from ``repoze.chameleon_zpt``. The ``render_transform``, and
+ ``render_transform_to_response`` functions should now be imported
+ from ``repoze.bfg.xslt``. The ``repoze.bfg.template`` module
+ will remain around "forever" to support backwards compatibility.
+
0.3.7 (09/09/2008)
Features
diff --git a/docs/api/template.rst b/docs/api/template.rst
index 64e25a37c..8cf63c91b 100644
--- a/docs/api/template.rst
+++ b/docs/api/template.rst
@@ -1,9 +1,56 @@
.. _template_module:
-:mod:`repoze.bfg.template`
---------------------------
+:mod:`repoze.bfg` Built-in Templating Facilties
+===============================================
-.. automodule:: repoze.bfg.template
+Three templating facilities are provided by :mod:`repoze.bfg` "out of
+the box": :term:`ZPT` -style, :term:`Genshi` -style, and :term:`XSLT`
+templating.
+
+ZPT-style and Genshi-style templates are in :mod:`repoze.bfg` are
+supported by the :term:`Chameleon` (nee :term:`z3c.pt`) templating
+engine, which contains alternate implementations of both the ZPT and
+Genshi language specifications.
+
+XSLT templating is supported by the use of :term:`lxml`.
+
+Below is API documentation for each of those facilities. Each
+facility is similar to the other, but to use a particular facility,
+you must import the API function from a specific module. For
+instance, to render a ZPT-style template to a response, you would
+import the ``render_template_to_response`` function from
+``repoze.bfg.chameleon_zpt`` while you would import
+``render_template_to_response`` from ``repoze.bfg.chameleon_genshi``
+in order to render a Genshi-style template to a response. While these
+functions have the same name, each will only operate on template files
+that match the style in which the template file itself is written. If
+you need to import API functions from two templating facilities within
+the same module, use the ``as`` feature of the Python import
+statement, e.g.:
+
+.. code-block:: python
+
+ from repoze.chameleon_zpt import render_template as zpt_render
+ from repoze.chameleon_genshi import render_template as genshi_render
+
+:mod:`repoze.bfg.chameleon_zpt`
+-------------------------------
+
+.. automodule:: repoze.bfg.chameleon_zpt
+
+ .. autofunction:: get_template
+
+ .. autofunction:: render_template
+
+ .. autofunction:: render_template_to_response
+
+.. note:: For backwards compatibility purposes, these functions may
+ also be imported from ``repoze.bfg.template``.
+
+:mod:`repoze.bfg.chameleon_genshi`
+----------------------------------
+
+.. automodule:: repoze.bfg.chameleon_genshi
.. autofunction:: get_template
@@ -11,7 +58,17 @@
.. autofunction:: render_template_to_response
+:mod:`repoze.bfg.xslt`
+----------------------
+
+.. automodule:: repoze.bfg.xslt
+
+ .. autofunction:: get_transform
+
.. autofunction:: render_transform
.. autofunction:: render_transform_to_response
+.. note:: For backwards compatibility purposes, these functions may
+ also be imported from ``repoze.bfg.template``.
+
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 51eb8c2fa..6d335ec51 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -199,22 +199,36 @@ Glossary
XSLT
`XSL Transformations <http://www.w3.org/TR/xslt>`_. A language
for transforming XML documents into other XML documents.
+ Chameleon
+ `chameleon <http://pypi.python.org/pypi/chameleon.core>`_ is an
+ attribute language template compiler which supports both the
+ :term:`ZPT` and :term:`Genshi` templating specifications. It is
+ written and maintained by Malthe Borch. It has serveral
+ extensions, such as the ability to use bracketed (Genshi-style)
+ ``${name}`` syntax, even within ZPT. It is also much faster than
+ the reference implementations of both ZPT and Genshi.
+ :mod:`repoze.bfg` offers Chameleon templating out of the box in
+ both ZPT and Genshi "flavors".
+ chameleon.zpt
+ ``chameleon.zpt`` is the package which provides :term:`ZPT`
+ templating support under the :term:`Chameleon` templating engine.
+ chameleon.genshi
+ ``chameleon.genshi`` is the package which provides :term:`Genshi`
+ templating support under the :term:`Chameleon` templating engine.
z3c.pt
- `z3c.pt <http://pypi.python.org/pypi/z3c.pt>`_ is an
- implementation of :term:`ZPT` by Malthe Borch. It has serveral
- extensions, such as the ability to use bracketed- ``${name}``
- syntax. It is also much faster than the reference implementation
- of ZPT. :mod:`repoze.bfg` offers z3c.pt templating out of the
- box.
+ This was the previous name for :term:`Chameleon`, and is now a
+ Zope 3 compatibility package for Chameleon.
ZPT
The `Zope Page Template <http://wiki.zope.org/ZPT/FrontPage>`_
templating language.
+ Genshi
+ `Genshi <http://genshi.edgewall.org/>`_ is an attribute-based XML
+ templating language similar to ZPT. Its syntax is supported
+ within :mod:`repoze.bfg` via :term:`Chameleon`.
METAL
`Macro Expansion for TAL <http://wiki.zope.org/ZPT/METAL>`_, a
part of :term:`ZPT` which makes it possible to share common look
- and feel between templates. :term:`z3c.pt`, the implementation of
- ZPT that :mod:`repoze.bfg` ships with does not implement the METAL
- specification.
+ and feel between templates.
Routes
A `system by Ben Bangert <http://routes.groovie.org/>`_ which
parses URLs and compares them against a number of user defined
diff --git a/docs/narr/MyProject/myproject/views.py b/docs/narr/MyProject/myproject/views.py
index 6e1be6190..67dbd00b6 100644
--- a/docs/narr/MyProject/myproject/views.py
+++ b/docs/narr/MyProject/myproject/views.py
@@ -1,4 +1,4 @@
-from repoze.bfg.template import render_template_to_response
+from repoze.bfg.chameleon_zpt import render_template_to_response
def my_view(context, request):
return render_template_to_response('templates/mytemplate.pt',
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 7360053dd..3bca363e8 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -299,7 +299,7 @@ your application by requiring more settings in this section.
The ``reload_templates`` setting in the ``[app:main]`` section is a
:mod:`repoze.bfg`-specific setting which is passed into the framework.
-If it exists, and is ``true``, :term:`z3c.pt` and XSLT template
+If it exists, and is ``true``, :term:`Chameleon` and XSLT template
changes will not require an application restart to be detected.
.. warning:: The ``reload_templates`` option should be turned off for
@@ -418,8 +418,8 @@ The ``myproject`` :term:`package` lives inside the ``MyProject``
#. A ``run.py`` module, which contains code that helps users run the
application.
-#. A ``templates`` directory, which is full of :term:`z3c.pt` and/or
- :term:`XSLT` templates.
+#. A ``templates`` directory, which contains :term:`Chameleon` (or
+ other types of) templates.
#. A ``tests.py`` module, which contains unit test code for the
application.
@@ -485,16 +485,17 @@ in the model, and the HTML given back to the browser.
dispatch`). The *request* is an instance of the :term:`WebOb`
``Request`` class representing the browser's request to our server.
-#. The view renders a :term:`template` and returns the result as the
- :term:`response`. Note that because our ``MyProject.ini`` has a
- ``reload_templates = true`` directive indicating that templates
- should be reloaded when they change, you won't need to restart the
- application server to see changes you make to templates. During
- development, this is handy. If this directive had been ``false``
- (or if the directive did not exist), you would need to restart the
- application server for each template change. For production
- applications, you should set your project's ``reload_templates`` to
- ``false`` to increase the speed at which templates may be rendered.
+#. The view renders a :term:`Chameleon` template and returns the
+ result as the :term:`response`. Note that because our
+ ``MyProject.ini`` has a ``reload_templates = true`` directive
+ indicating that templates should be reloaded when they change, you
+ won't need to restart the application server to see changes you
+ make to templates. During development, this is handy. If this
+ directive had been ``false`` (or if the directive did not exist),
+ you would need to restart the application server for each template
+ change. For production applications, you should set your project's
+ ``reload_templates`` to ``false`` to increase the speed at which
+ templates may be rendered.
.. note::
@@ -569,16 +570,15 @@ without the PasteDeploy configuration file:
``templates/mytemplate.pt``
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The single :term:`template` in the project looks like so:
+The single :term:`Chameleon` template in the project looks like so:
.. literalinclude:: MyProject/myproject/templates/mytemplate.pt
:linenos:
:language: xml
-This is a :term:`z3c.pt` template. It displays the current project
-name when it is rendered. It is referenced by the ``my_view``
-function in the ``views.py`` module. Templates are accessed and used
-by view functions.
+It displays the current project name when it is rendered. It is
+referenced by the ``my_view`` function in the ``views.py`` module.
+Templates are accessed and used by view functions.
``tests.py``
~~~~~~~~~~~~
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index 80b530a8c..dce66ae3d 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -5,25 +5,35 @@ A :term:`template` is a usually file on disk which can be used to
render data provided by a :term:`view`, surrounded by more static
information.
-Templating With :term:`z3c.pt` (ZPT) Page Templates
----------------------------------------------------
+Templating With :term:`Chameleon` (:term:`chameleon.zpt`) Page Templates
+------------------------------------------------------------------------
Like Zope, :mod:`repoze.bfg` uses Zope Page Templates (:term:`ZPT`) as
-its default templating language. However, :mod:`repoze.bfg` uses a
-different implementation of the :term:`ZPT` specification than Zope
-does: the :term:`z3c.pt` templating engine. This templating engine
+its default and best-supported templating language. However,
+:mod:`repoze.bfg` uses a different implementation of the :term:`ZPT`
+specification than Zope does: the :term:`Chameleon`
+:term:`chameleon.zpt` templating engine. This templating engine
complies with the `Zope Page Template
<http://wiki.zope.org/ZPT/FrontPage>`_ template specification and is
significantly faster.
-Given that there is a :term:`z3c.pt` template named ``foo.html`` in a
-directory in your application named ``templates``, you can render the
-template from a view like so:
+.. note:: :mod:`repoze.bfg` can also allow for the use of Genshi-style
+ templates via the ``chameleon.genshi`` package, support for which
+ is built-in to :mod:`repoze.bfg`. The :mod:`repoze.bfg` API
+ functions for getting and rendering Chameleon Genshi-style
+ templates mirrors the Chameleon ZPT-style API completely; only the
+ template files themselves must differ. See :ref:`template_module`
+ for more information about using Genshi-style templates within
+ :mod:`repoze.bfg`.
+
+Given that there is a :term:`chameleon.zpt` template named
+``foo.html`` in a directory in your application named ``templates``,
+you can render the template from a view like so:
.. code-block:: python
:linenos:
- from repoze.bfg.template import render_template_to_response
+ from repoze.bfg.chameleon_zpt import render_template_to_response
def sample_view(context, request):
return render_template_to_response('templates/foo.html', foo=1, bar=2)
@@ -35,18 +45,18 @@ Relative to the directory in which the ``views.py`` file which names
it lives, which is usually the :mod:`repoze.bfg` application's
:term:`package` directory.
-``render_template_to_response`` always renders a :term:`z3c.pt`
+``render_template_to_response`` always renders a :term:`chameleon.zpt`
template, and always returns a Response object which has a *status
code* of ``200 OK`` and a *content-type* of ``text-html``. If you
need more control over the status code and content-type, use the
-``render_template`` function instead, which also renders a z3c.pt
-template but returns a string instead of a Response. You can use
-the string manually as a response body:
+``render_template`` function instead, which also renders a ZPT
+template but returns a string instead of a Response. You can use the
+string manually as a response body:
.. code-block:: python
:linenos:
- from repoze.bfg.template import render_template
+ from repoze.bfg.chameleon_zpt import render_template
from webob import Response
def sample_view(context, request):
result = render_template('templates/foo.html', foo=1, bar=2)
@@ -71,7 +81,7 @@ an XSLT as follows:
.. code-block:: python
:linenos:
- from repoze.bfg.template import render_transform_to_response
+ from repoze.bfg.xslt import render_transform_to_response
from lxml import etree
node = etree.Element("root")
return render_transform_to_response('templates/foo.xsl', node)
@@ -85,7 +95,7 @@ You can also pass XSLT parameters in as keyword arguments:
.. code-block:: python
:linenos:
- from repoze.bfg.template import render_transform_to_response
+ from repoze.bfg.xslt import render_transform_to_response
from lxml import etree
node = etree.Element("root")
value1 = "'app1'"
diff --git a/docs/tutorials/cmf/skins.rst b/docs/tutorials/cmf/skins.rst
index 2e444c4db..cbc28bfb5 100644
--- a/docs/tutorials/cmf/skins.rst
+++ b/docs/tutorials/cmf/skins.rst
@@ -14,9 +14,9 @@ particular skin to provide the site with additional features.
:mod:`repoze.bfg` itself has no such concept, and no package provides
a direct replacement, but bfg :term:`view` code combined with
differing :term:`request type` attributes can provide a good deal of
-the same sort of behavior. The `vudo.bfg <http://docs.vudo.me/>`_
-package is an attempt to allow directories on disk to represent
-collections of templates, each of which can be thought of as a minimal
-skin.
+the same sort of behavior. The `repoze.skins
+<http://svn.repoze.org/repoze.skins/>`_ package is an attempt to allow
+directories on disk to represent collections of templates, each of
+which can be thought of as a minimal skin.
diff --git a/docs/tutorials/lxmlgraph/step03.rst b/docs/tutorials/lxmlgraph/step03.rst
index 9edc77af3..0f54f8761 100644
--- a/docs/tutorials/lxmlgraph/step03.rst
+++ b/docs/tutorials/lxmlgraph/step03.rst
@@ -52,7 +52,7 @@ Also add a function in ``views.py`` that looks like the following:
.. code-block:: python
:linenos:
- from repoze.bfg.template import render_template_to_response
+ from repoze.bfg.chameleon_zpt import render_template_to_response
def zpt_view(context, request):
return render_template_to_response('templates/default.pt',
name=context.__name__,
@@ -61,8 +61,8 @@ Also add a function in ``views.py`` that looks like the following:
This function is relatively simple:
#. Line 1 imports a :mod:`repoze.bfg` function that renders ZPT
- templates to a response. :mod:`repoze.bfg` uses the ``z3c.pt`` ZPT
- engine.
+ templates to a response. :mod:`repoze.bfg` uses the
+ :term:`chameleon.zpt` ZPT engine.
#. Line 2, like our other view functions, gets passed a ``context``
(the current hop in the URL) and WebOb ``request`` object.
@@ -92,10 +92,11 @@ Life is better with templating:
``render_template_to_response``.
#. Line 6 looks interesting. It uses the ``node`` that we passed in
- via ``render_template_to_response``. Since ``z3c.pt`` uses Python
- as its expession language, we can put anything Python-legal between
- the braces. And since ``node`` is an ``lxml`` ``Element`` object,
- we just ask for its ``.tag``, like regular Python ``lxml`` code.
+ via ``render_template_to_response``. Since :term:`chameleon.zpt`
+ uses Python as its expession language, we can put anything
+ Python-legal between the braces. And since ``node`` is an ``lxml``
+ ``Element`` object, we just ask for its ``.tag``, like regular
+ Python ``lxml`` code.
Viewing the ZPT
------------------
@@ -119,7 +120,7 @@ model using the ZPT templating language.
XSLT Templates
-====================
+==============
So that's the ZPT way of rendering HTML for an XML document. We can
additonally use XSLT to do templating. How might XSLT look?
diff --git a/docs/tutorials/lxmlgraph/step03/myapp/views.py b/docs/tutorials/lxmlgraph/step03/myapp/views.py
index 0ac33ba83..6eb4e376c 100644
--- a/docs/tutorials/lxmlgraph/step03/myapp/views.py
+++ b/docs/tutorials/lxmlgraph/step03/myapp/views.py
@@ -1,5 +1,5 @@
-from repoze.bfg.template import render_template_to_response
-from repoze.bfg.template import render_transform_to_response
+from repoze.bfg.chameleon_zpt import render_template_to_response
+from repoze.bfg.chameleon_zpt import render_transform_to_response
def zpt_view(context, request):
return render_template_to_response("templates/default.pt",
diff --git a/docs/tutorials/lxmlgraph/step04/myapp/views.py b/docs/tutorials/lxmlgraph/step04/myapp/views.py
index fd8650e14..f079cea8c 100644
--- a/docs/tutorials/lxmlgraph/step04/myapp/views.py
+++ b/docs/tutorials/lxmlgraph/step04/myapp/views.py
@@ -1,4 +1,4 @@
-from repoze.bfg.template import render_transform_to_response
+from repoze.bfg.xslt import render_transform_to_response
# Some constants
XML_NAMESPACE='http://www.w3.org/XML/1998/namespace'
diff --git a/repoze/bfg/chameleon_genshi.py b/repoze/bfg/chameleon_genshi.py
new file mode 100644
index 000000000..fbcc519e6
--- /dev/null
+++ b/repoze/bfg/chameleon_genshi.py
@@ -0,0 +1,82 @@
+import os
+
+from webob import Response
+
+from zope.component import queryUtility
+from zope.component.interfaces import ComponentLookupError
+from zope.component import getSiteManager
+
+from zope.interface import classProvides
+from zope.interface import implements
+
+from repoze.bfg.path import caller_path
+from repoze.bfg.interfaces import ITemplateFactory
+from repoze.bfg.interfaces import ITemplate
+from repoze.bfg.interfaces import ISettings
+
+from chameleon.genshi.template import GenshiTemplateFile
+
+class GenshiTemplateFactory(object):
+ classProvides(ITemplateFactory)
+ implements(ITemplate)
+
+ def __init__(self, path, auto_reload=False):
+ try:
+ self.template = GenshiTemplateFile(path, auto_reload=auto_reload)
+ except ImportError, why:
+ why = str(why)
+ if 'z3c.pt' in why:
+ # unpickling error due to move from z3c.pt -> chameleon
+ cachefile = '%s.cache' % path
+ if os.path.isfile(cachefile):
+ os.remove(cachefile)
+ self.template = GenshiTemplateFile(path,
+ auto_reload=auto_reload)
+ else:
+ raise
+
+ def __call__(self, **kw):
+ result = self.template.render(**kw)
+ return result
+
+def _get_template(path, **kw):
+ # XXX use pkg_resources
+ template = queryUtility(ITemplate, path)
+
+ if template is None:
+ if not os.path.exists(path):
+ raise ValueError('Missing template file: %s' % path)
+ settings = queryUtility(ISettings)
+ auto_reload = settings and settings.reload_templates
+ template = GenshiTemplateFactory(path, auto_reload)
+ try:
+ sm = getSiteManager()
+ except ComponentLookupError:
+ pass
+ else:
+ sm.registerUtility(template, ITemplate, name=path)
+
+ return template
+
+def get_template(path):
+ """ Return a ``chameleon.genshi`` template object at the
+ package-relative path (may also be absolute)"""
+ path = caller_path(path)
+ return _get_template(path).template
+
+def render_template(path, **kw):
+ """ Render a ``chameleon.genshi`` template at the package-relative
+ path (may also be absolute) using the kwargs in ``*kw`` as
+ top-level names and return a string."""
+ path = caller_path(path)
+ template = get_template(path)
+ return template(**kw)
+
+def render_template_to_response(path, **kw):
+ """ Render a ``chameleon.genshi`` template at the package-relative
+ path (may also be absolute) using the kwargs in ``*kw`` as
+ top-level names and return a Response object."""
+ path = caller_path(path)
+ result = render_template(path, **kw)
+ return Response(result)
+
diff --git a/repoze/bfg/chameleon_zpt.py b/repoze/bfg/chameleon_zpt.py
new file mode 100644
index 000000000..80669c009
--- /dev/null
+++ b/repoze/bfg/chameleon_zpt.py
@@ -0,0 +1,81 @@
+import os
+
+from webob import Response
+
+from zope.component import queryUtility
+from zope.component.interfaces import ComponentLookupError
+from zope.component import getSiteManager
+
+from zope.interface import classProvides
+from zope.interface import implements
+
+from repoze.bfg.path import caller_path
+from repoze.bfg.interfaces import ITemplateFactory
+from repoze.bfg.interfaces import ITemplate
+from repoze.bfg.interfaces import ISettings
+
+from chameleon.zpt.template import PageTemplateFile
+
+class ZPTTemplateFactory(object):
+ classProvides(ITemplateFactory)
+ implements(ITemplate)
+
+ def __init__(self, path, auto_reload=False):
+ try:
+ self.template = PageTemplateFile(path, auto_reload=auto_reload)
+ except ImportError, why:
+ why = str(why)
+ if 'z3c.pt' in why:
+ # unpickling error due to move from z3c.pt -> chameleon
+ cachefile = '%s.cache' % path
+ if os.path.isfile(cachefile):
+ os.remove(cachefile)
+ self.template = PageTemplateFile(path, auto_reload=auto_reload)
+ else:
+ raise
+
+ def __call__(self, **kw):
+ result = self.template.render(**kw)
+ return result
+
+def _get_template(path, **kw):
+ # XXX use pkg_resources
+ template = queryUtility(ITemplate, path)
+
+ if template is None:
+ if not os.path.exists(path):
+ raise ValueError('Missing template file: %s' % path)
+ settings = queryUtility(ISettings)
+ auto_reload = settings and settings.reload_templates
+ template = ZPTTemplateFactory(path, auto_reload)
+ try:
+ sm = getSiteManager()
+ except ComponentLookupError:
+ pass
+ else:
+ sm.registerUtility(template, ITemplate, name=path)
+
+ return template
+
+def get_template(path):
+ """ Return a ``chameleon.zpt`` template object at the
+ package-relative path (may also be absolute)"""
+ path = caller_path(path)
+ return _get_template(path).template
+
+def render_template(path, **kw):
+ """ Render a ``chameleon.zpt`` template at the package-relative
+ path (may also be absolute) using the kwargs in ``*kw`` as
+ top-level names and return a string."""
+ path = caller_path(path)
+ template = get_template(path)
+ return template(**kw)
+
+def render_template_to_response(path, **kw):
+ """ Render a ``chameleon.zpt`` template at the package-relative
+ path (may also be absolute) using the kwargs in ``*kw`` as
+ top-level names and return a Response object."""
+ path = caller_path(path)
+ result = render_template(path, **kw)
+ return Response(result)
+
diff --git a/repoze/bfg/configure.zcml b/repoze/bfg/configure.zcml
index 355795b7c..517b5fd54 100644
--- a/repoze/bfg/configure.zcml
+++ b/repoze/bfg/configure.zcml
@@ -3,7 +3,9 @@
<!-- superseded by includes/configure.zcml -->
- <include package="z3c.pt" />
+ <include package="zope.component" file="meta.zcml" />
+
+ <include package="chameleon.zpt" file="configure.zcml"/>
<adapter
factory=".traversal.ModelGraphTraverser"
diff --git a/repoze/bfg/includes/configure.zcml b/repoze/bfg/includes/configure.zcml
index e25d749ca..e758fb192 100644
--- a/repoze/bfg/includes/configure.zcml
+++ b/repoze/bfg/includes/configure.zcml
@@ -1,7 +1,9 @@
<configure xmlns="http://namespaces.zope.org/zope"
i18n_domain="repoze.bfg">
- <include package="z3c.pt" />
+ <include package="zope.component" file="meta.zcml" />
+
+ <include package="chameleon.zpt" file="configure.zcml"/>
<adapter
factory="repoze.bfg.traversal.ModelGraphTraverser"
diff --git a/repoze/bfg/paster_template/+package+/views.py_tmpl b/repoze/bfg/paster_template/+package+/views.py_tmpl
index d3824826a..8a2836171 100644
--- a/repoze/bfg/paster_template/+package+/views.py_tmpl
+++ b/repoze/bfg/paster_template/+package+/views.py_tmpl
@@ -1,4 +1,4 @@
-from repoze.bfg.template import render_template_to_response
+from repoze.bfg.chameleon_zpt import render_template_to_response
def my_view(context, request):
return render_template_to_response('templates/mytemplate.pt',
diff --git a/repoze/bfg/push.py b/repoze/bfg/push.py
index 61b2f9cf0..9b3197cf6 100644
--- a/repoze/bfg/push.py
+++ b/repoze/bfg/push.py
@@ -1,8 +1,8 @@
import os.path
-from repoze.bfg.template import render_template_to_response
+from repoze.bfg.chameleon_zpt import render_template_to_response
class pushpage(object):
- """ Decorator for functions which return ZPT template namespaces.
+ """ Decorator for functions which return Chameleon template namespaces.
E.g.::
@@ -12,7 +12,7 @@ class pushpage(object):
Equates to::
- from repoze.bfg.template import render_template_to_response
+ from repoze.bfg.chameleon import render_template_to_response
def my_view(context, request):
return render_template_to_response('www/my_template.pt', a=1, b=())
diff --git a/repoze/bfg/template.py b/repoze/bfg/template.py
index e501b2592..2f3017bd6 100644
--- a/repoze/bfg/template.py
+++ b/repoze/bfg/template.py
@@ -1,138 +1,13 @@
-import os
-
-from webob import Response
-
-from zope.component import queryUtility
-from zope.component.interfaces import ComponentLookupError
-from zope.component import getSiteManager
-
-from zope.interface import classProvides
-from zope.interface import implements
-
-from repoze.bfg.path import caller_path
-from repoze.bfg.interfaces import ITemplateFactory
-from repoze.bfg.interfaces import ITemplate
-from repoze.bfg.interfaces import INodeTemplate
-from repoze.bfg.interfaces import ISettings
-
-class Z3CPTTemplateFactory(object):
- classProvides(ITemplateFactory)
- implements(ITemplate)
-
- def __init__(self, path, auto_reload=False):
- try:
- from z3c.pt import PageTempateFile
- except ImportError:
- # after 1.0a7
- from z3c.pt.pagetemplate import PageTemplateFile
- try:
- self.template = PageTemplateFile(path, auto_reload=auto_reload)
- except TypeError:
- # z3c.pt before 1.0
- self.template = PageTemplateFile(path)
-
- def __call__(self, **kw):
- result = self.template.render(**kw)
- return result
-
-class XSLTemplateFactory(object):
- classProvides(ITemplateFactory)
- implements(INodeTemplate)
-
- def __init__(self, path, auto_reload=False):
- self.path = path
- self.auto_reload = auto_reload
-
- def __call__(self, node, **kw):
- processor = get_processor(self.path, self.auto_reload)
- result = str(processor(node, **kw))
- return result
-
-# Manage XSLT processors on a per-thread basis
-import threading
-from lxml import etree
-xslt_pool = threading.local()
-def get_processor(xslt_fn, auto_reload=False):
- if not auto_reload:
- try:
- return xslt_pool.processors[xslt_fn]
- except AttributeError:
- xslt_pool.processors = {}
- except KeyError:
- pass
-
- # Make a processor and add it to the pool
- source = etree.ElementTree(file=xslt_fn)
- proc = etree.XSLT(source)
- xslt_pool.processors[xslt_fn] = proc
- return proc
-
-def registerTemplate(type, template, path):
- try:
- sm = getSiteManager()
- except ComponentLookupError:
- pass
- else:
- sm.registerUtility(template, type, name=path)
-
-def _get_template(path, **kw):
- # XXX use pkg_resources
- template = queryUtility(ITemplate, path)
-
- if template is None:
- if not os.path.exists(path):
- raise ValueError('Missing template file: %s' % path)
- settings = queryUtility(ISettings)
- auto_reload = settings and settings.reload_templates
- template = Z3CPTTemplateFactory(path, auto_reload)
- registerTemplate(ITemplate, template, path)
-
- return template
-
-def get_template(path):
- """ Return a z3c.pt template object at the package-relative path
- (may also be absolute) """
- path = caller_path(path)
- return _get_template(path).template
-
-def render_template(path, **kw):
- """ Render a z3c.pt (ZPT) template at the package-relative path
- (may also be absolute) using the kwargs in ``*kw`` as top-level
- names and return a string. """
- path = caller_path(path)
- template = get_template(path)
- return template(**kw)
-
-def render_template_to_response(path, **kw):
- """ Render a z3c.pt (ZPT) template at the package-relative path
- (may also be absolute) using the kwargs in ``*kw`` as top-level
- names and return a Response object. """
- path = caller_path(path)
- result = render_template(path, **kw)
- return Response(result)
-
-def render_transform(path, node, **kw):
- """ Render a XSL template at the package-relative path (may also
- be absolute) using the kwargs in ``*kw`` as top-level names and
- return a string."""
- # Render using XSLT
- path = caller_path(path)
-
- template = queryUtility(INodeTemplate, path)
- if template is None:
- if not os.path.exists(path):
- raise ValueError('Missing template file: %s' % path)
- template = XSLTemplateFactory(path)
- registerTemplate(INodeTemplate, template, path)
-
- return template(node, **kw)
-
-def render_transform_to_response(path, node, **kw):
- """ Render a XSL template at the package-relative path (may also
- be absolute) using the kwargs in ``*kw`` as top-level names and
- return a Response object."""
- path = caller_path(path)
- result = render_transform(path, node, **kw)
- return Response(result)
-
-
+# The definitions in this module are import aliases for backwards
+# compatibility; there are no plans to make this module itself go
+# away, but the ``get_template``, ``render_template``, and
+# ``render_template_to_response`` APIs should be imported from
+# ``repoze.bfg.chameleon_zpt`` in the future for optimum correctness,
+# while the ``render_transform`` and ``render_transform_to_response``
+# APIs should be imported from ``repoze.bfg.xsl``.
+
+from repoze.bfg.chameleon_zpt import get_template
+from repoze.bfg.chameleon_zpt import render_template
+from repoze.bfg.chameleon_zpt import render_template_to_response
+from repoze.bfg.xslt import render_transform
+from repoze.bfg.xslt import render_transform_to_response
diff --git a/repoze/bfg/tests/fixtures/minimal.genshi b/repoze/bfg/tests/fixtures/minimal.genshi
new file mode 100644
index 000000000..bd619a9da
--- /dev/null
+++ b/repoze/bfg/tests/fixtures/minimal.genshi
@@ -0,0 +1,3 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/">
+</div>
diff --git a/repoze/bfg/tests/test_chameleon_genshi.py b/repoze/bfg/tests/test_chameleon_genshi.py
new file mode 100644
index 000000000..0f0fc88a1
--- /dev/null
+++ b/repoze/bfg/tests/test_chameleon_genshi.py
@@ -0,0 +1,134 @@
+import unittest
+
+from zope.component.testing import PlacelessSetup
+
+class Base(PlacelessSetup):
+ def setUp(self):
+ PlacelessSetup.setUp(self)
+
+ def tearDown(self):
+ PlacelessSetup.tearDown(self)
+
+ def _zcmlConfigure(self):
+ import repoze.bfg
+ import zope.configuration.xmlconfig
+ zope.configuration.xmlconfig.file('configure.zcml', package=repoze.bfg)
+
+ def _getTemplatePath(self, name):
+ import os
+ here = os.path.abspath(os.path.dirname(__file__))
+ return os.path.join(here, 'fixtures', name)
+
+class GenshiTemplateFactoryTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getTargetClass(self):
+ from repoze.bfg.chameleon_genshi import GenshiTemplateFactory
+ return GenshiTemplateFactory
+
+ def _makeOne(self, *arg, **kw):
+ klass = self._getTargetClass()
+ return klass(*arg, **kw)
+
+ def test_instance_implements_ITemplate(self):
+ from zope.interface.verify import verifyObject
+ from repoze.bfg.interfaces import ITemplate
+ path = self._getTemplatePath('minimal.genshi')
+ verifyObject(ITemplate, self._makeOne(path))
+
+ def test_class_implements_ITemplate(self):
+ from zope.interface.verify import verifyClass
+ from repoze.bfg.interfaces import ITemplate
+ verifyClass(ITemplate, self._getTargetClass())
+
+ def test_call(self):
+ self._zcmlConfigure()
+ minimal = self._getTemplatePath('minimal.genshi')
+ instance = self._makeOne(minimal)
+ result = instance()
+ self.failUnless(isinstance(result, str))
+ self.assertEqual(result, '<div>\n</div>\n')
+
+class RenderTemplateTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_genshi import render_template
+ return render_template
+
+ def test_it(self):
+ minimal = self._getTemplatePath('minimal.genshi')
+ render = self._getFUT()
+ result = render(minimal)
+ self.failUnless(isinstance(result, str))
+ self.assertEqual(result, '<div>\n</div>\n')
+
+class RenderTemplateToResponseTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_genshi import render_template_to_response
+ return render_template_to_response
+
+ def test_it(self):
+ minimal = self._getTemplatePath('minimal.genshi')
+ render = self._getFUT()
+ result = render(minimal)
+ from webob import Response
+ self.failUnless(isinstance(result, Response))
+ self.assertEqual(result.app_iter, ['<div>\n</div>\n'])
+ self.assertEqual(result.status, '200 OK')
+ self.assertEqual(len(result.headerlist), 2)
+
+class GetTemplateTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_genshi import get_template
+ return get_template
+
+ def test_nonabs_registered(self):
+ from zope.component import getGlobalSiteManager
+ from zope.component import queryUtility
+ from repoze.bfg.chameleon_genshi import GenshiTemplateFactory
+ from repoze.bfg.interfaces import ITemplate
+ minimal = self._getTemplatePath('minimal.genshi')
+ utility = GenshiTemplateFactory(minimal)
+ gsm = getGlobalSiteManager()
+ gsm.registerUtility(utility, ITemplate, name=minimal)
+ get = self._getFUT()
+ result = get(minimal)
+ self.assertEqual(result.filename, minimal)
+ self.assertEqual(queryUtility(ITemplate, minimal), utility)
+
+ def test_nonabs_unregistered(self):
+ from zope.component import getGlobalSiteManager
+ from zope.component import queryUtility
+ from repoze.bfg.chameleon_genshi import GenshiTemplateFactory
+ from repoze.bfg.interfaces import ITemplate
+ minimal = self._getTemplatePath('minimal.genshi')
+ self.assertEqual(queryUtility(ITemplate, minimal), None)
+ utility = GenshiTemplateFactory(minimal)
+ gsm = getGlobalSiteManager()
+ gsm.registerUtility(utility, ITemplate, name=minimal)
+ get = self._getFUT()
+ result = get(minimal)
+ self.assertEqual(result.filename, minimal)
+ self.assertEqual(queryUtility(ITemplate, minimal), utility)
+
diff --git a/repoze/bfg/tests/test_chameleon_zpt.py b/repoze/bfg/tests/test_chameleon_zpt.py
new file mode 100644
index 000000000..3b977bcbf
--- /dev/null
+++ b/repoze/bfg/tests/test_chameleon_zpt.py
@@ -0,0 +1,139 @@
+import unittest
+
+from zope.component.testing import PlacelessSetup
+
+class Base(PlacelessSetup):
+ def setUp(self):
+ PlacelessSetup.setUp(self)
+
+ def tearDown(self):
+ PlacelessSetup.tearDown(self)
+
+ def _zcmlConfigure(self):
+ import repoze.bfg
+ import zope.configuration.xmlconfig
+ zope.configuration.xmlconfig.file('configure.zcml', package=repoze.bfg)
+
+ def _getTemplatePath(self, name):
+ import os
+ here = os.path.abspath(os.path.dirname(__file__))
+ return os.path.join(here, 'fixtures', name)
+
+class ZPTTemplateFactoryTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getTargetClass(self):
+ from repoze.bfg.chameleon_zpt import ZPTTemplateFactory
+ return ZPTTemplateFactory
+
+ def _makeOne(self, *arg, **kw):
+ klass = self._getTargetClass()
+ return klass(*arg, **kw)
+
+ def test_instance_implements_ITemplate(self):
+ from zope.interface.verify import verifyObject
+ from repoze.bfg.interfaces import ITemplate
+ path = self._getTemplatePath('minimal.pt')
+ verifyObject(ITemplate, self._makeOne(path))
+
+ def test_class_implements_ITemplate(self):
+ from zope.interface.verify import verifyClass
+ from repoze.bfg.interfaces import ITemplate
+ verifyClass(ITemplate, self._getTargetClass())
+
+ def test_call(self):
+ self._zcmlConfigure()
+ minimal = self._getTemplatePath('minimal.pt')
+ instance = self._makeOne(minimal)
+ result = instance()
+ self.failUnless(isinstance(result, str))
+ self.assertEqual(result, '<div>\n</div>\n')
+
+class RenderTemplateTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_zpt import render_template
+ return render_template
+
+ def test_it(self):
+ self._zcmlConfigure()
+ minimal = self._getTemplatePath('minimal.pt')
+ render = self._getFUT()
+ result = render(minimal)
+ self.failUnless(isinstance(result, str))
+ self.assertEqual(result, '<div>\n</div>\n')
+
+class RenderTemplateToResponseTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_zpt import render_template_to_response
+ return render_template_to_response
+
+ def test_it(self):
+ self._zcmlConfigure()
+ minimal = self._getTemplatePath('minimal.pt')
+ render = self._getFUT()
+ result = render(minimal)
+ from webob import Response
+ self.failUnless(isinstance(result, Response))
+ self.assertEqual(result.app_iter, ['<div>\n</div>\n'])
+ self.assertEqual(result.status, '200 OK')
+ self.assertEqual(len(result.headerlist), 2)
+
+class GetTemplateTests(unittest.TestCase, Base):
+ def setUp(self):
+ Base.setUp(self)
+
+ def tearDown(self):
+ Base.tearDown(self)
+
+ def _getFUT(self):
+ from repoze.bfg.chameleon_zpt import get_template
+ return get_template
+
+ def test_nonabs_registered(self):
+ self._zcmlConfigure()
+ from zope.component import getGlobalSiteManager
+ from zope.component import queryUtility
+ from repoze.bfg.chameleon_zpt import ZPTTemplateFactory
+ from repoze.bfg.interfaces import ITemplate
+ minimal = self._getTemplatePath('minimal.pt')
+ utility = ZPTTemplateFactory(minimal)
+ gsm = getGlobalSiteManager()
+ gsm.registerUtility(utility, ITemplate, name=minimal)
+ get = self._getFUT()
+ result = get(minimal)
+ self.assertEqual(result.filename, minimal)
+ self.assertEqual(queryUtility(ITemplate, minimal), utility)
+
+ def test_nonabs_unregistered(self):
+ self._zcmlConfigure()
+ from zope.component import getGlobalSiteManager
+ from zope.component import queryUtility
+ from repoze.bfg.chameleon_zpt import ZPTTemplateFactory
+ from repoze.bfg.interfaces import ITemplate
+ minimal = self._getTemplatePath('minimal.pt')
+ self.assertEqual(queryUtility(ITemplate, minimal), None)
+ utility = ZPTTemplateFactory(minimal)
+ gsm = getGlobalSiteManager()
+ gsm.registerUtility(utility, ITemplate, name=minimal)
+ get = self._getFUT()
+ result = get(minimal)
+ self.assertEqual(result.filename, minimal)
+ self.assertEqual(queryUtility(ITemplate, minimal), utility)
+
+
diff --git a/repoze/bfg/tests/test_push.py b/repoze/bfg/tests/test_push.py
index 907503983..3dc456b58 100644
--- a/repoze/bfg/tests/test_push.py
+++ b/repoze/bfg/tests/test_push.py
@@ -31,7 +31,7 @@ class Test_pushpage(unittest.TestCase, PlacelessSetup):
pp = self._makeOne('pp.pt')
wrapped = pp(to_wrap)
response = wrapped(object(), object())
- self.assertEqual(response.body, '<p>WRAPPED</p>')
+ self.assertEqual(response.body, '<p>WRAPPED</p>\n')
def to_wrap(context, request):
return {'wrapped': 'WRAPPED'}
diff --git a/repoze/bfg/tests/test_template.py b/repoze/bfg/tests/test_template.py
index ba3745132..42baa6492 100644
--- a/repoze/bfg/tests/test_template.py
+++ b/repoze/bfg/tests/test_template.py
@@ -19,40 +19,6 @@ class Base(PlacelessSetup):
here = os.path.abspath(os.path.dirname(__file__))
return os.path.join(here, 'fixtures', name)
-class Z3CPTTemplateFactoryTests(unittest.TestCase, Base):
- def setUp(self):
- Base.setUp(self)
-
- def tearDown(self):
- Base.tearDown(self)
-
- def _getTargetClass(self):
- from repoze.bfg.template import Z3CPTTemplateFactory
- return Z3CPTTemplateFactory
-
- def _makeOne(self, *arg, **kw):
- klass = self._getTargetClass()
- return klass(*arg, **kw)
-
- def test_instance_implements_ITemplate(self):
- from zope.interface.verify import verifyObject
- from repoze.bfg.interfaces import ITemplate
- path = self._getTemplatePath('minimal.pt')
- verifyObject(ITemplate, self._makeOne(path))
-
- def test_class_implements_ITemplate(self):
- from zope.interface.verify import verifyClass
- from repoze.bfg.interfaces import ITemplate
- verifyClass(ITemplate, self._getTargetClass())
-
- def test_call(self):
- self._zcmlConfigure()
- minimal = self._getTemplatePath('minimal.pt')
- instance = self._makeOne(minimal)
- result = instance()
- self.failUnless(isinstance(result, str))
- self.assertEqual(result, '<div>\n</div>')
-
class RenderTemplateTests(unittest.TestCase, Base):
def setUp(self):
Base.setUp(self)
@@ -70,7 +36,7 @@ class RenderTemplateTests(unittest.TestCase, Base):
render = self._getFUT()
result = render(minimal)
self.failUnless(isinstance(result, str))
- self.assertEqual(result, '<div>\n</div>')
+ self.assertEqual(result, '<div>\n</div>\n')
class RenderTemplateToResponseTests(unittest.TestCase, Base):
def setUp(self):
@@ -90,7 +56,7 @@ class RenderTemplateToResponseTests(unittest.TestCase, Base):
result = render(minimal)
from webob import Response
self.failUnless(isinstance(result, Response))
- self.assertEqual(result.app_iter, ['<div>\n</div>'])
+ self.assertEqual(result.app_iter, ['<div>\n</div>\n'])
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
@@ -109,10 +75,10 @@ class GetTemplateTests(unittest.TestCase, Base):
self._zcmlConfigure()
from zope.component import getGlobalSiteManager
from zope.component import queryUtility
- from repoze.bfg.template import Z3CPTTemplateFactory
+ from repoze.bfg.chameleon_zpt import ZPTTemplateFactory
from repoze.bfg.interfaces import ITemplate
minimal = self._getTemplatePath('minimal.pt')
- utility = Z3CPTTemplateFactory(minimal)
+ utility = ZPTTemplateFactory(minimal)
gsm = getGlobalSiteManager()
gsm.registerUtility(utility, ITemplate, name=minimal)
get = self._getFUT()
@@ -124,11 +90,11 @@ class GetTemplateTests(unittest.TestCase, Base):
self._zcmlConfigure()
from zope.component import getGlobalSiteManager
from zope.component import queryUtility
- from repoze.bfg.template import Z3CPTTemplateFactory
+ from repoze.bfg.chameleon_zpt import ZPTTemplateFactory
from repoze.bfg.interfaces import ITemplate
minimal = self._getTemplatePath('minimal.pt')
self.assertEqual(queryUtility(ITemplate, minimal), None)
- utility = Z3CPTTemplateFactory(minimal)
+ utility = ZPTTemplateFactory(minimal)
gsm = getGlobalSiteManager()
gsm.registerUtility(utility, ITemplate, name=minimal)
get = self._getFUT()
@@ -136,18 +102,3 @@ class GetTemplateTests(unittest.TestCase, Base):
self.assertEqual(result.filename, minimal)
self.assertEqual(queryUtility(ITemplate, minimal), utility)
- def test_nonabs_registered(self):
- self._zcmlConfigure()
- from zope.component import getGlobalSiteManager
- from zope.component import queryUtility
- from repoze.bfg.template import Z3CPTTemplateFactory
- from repoze.bfg.interfaces import ITemplate
- minimal = self._getTemplatePath('minimal.pt')
- utility = Z3CPTTemplateFactory(minimal)
- gsm = getGlobalSiteManager()
- gsm.registerUtility(utility, ITemplate, name=minimal)
- get = self._getFUT()
- result = get(minimal)
- self.assertEqual(result.filename, minimal)
- self.assertEqual(queryUtility(ITemplate, minimal), utility)
-
diff --git a/repoze/bfg/tests/test_xslt.py b/repoze/bfg/tests/test_xslt.py
index 01e5befba..0a858e08b 100644
--- a/repoze/bfg/tests/test_xslt.py
+++ b/repoze/bfg/tests/test_xslt.py
@@ -27,7 +27,7 @@ class XSLTemplateFactoryTests(unittest.TestCase, Base):
Base.tearDown(self)
def _getTargetClass(self):
- from repoze.bfg.template import XSLTemplateFactory
+ from repoze.bfg.xslt import XSLTemplateFactory
return XSLTemplateFactory
def _makeOne(self, *arg, **kw):
@@ -64,7 +64,7 @@ class RenderTransformToResponseTests(unittest.TestCase, Base):
Base.tearDown(self)
def _getFUT(self):
- from repoze.bfg.template import render_transform_to_response
+ from repoze.bfg.xslt import render_transform_to_response
return render_transform_to_response
def test_nonabs_unregistered(self):
@@ -83,7 +83,7 @@ class RenderTransformToResponseTests(unittest.TestCase, Base):
self.assertEqual(result.app_iter, [resultstr])
self.assertEqual(result.status, '200 OK')
self.assertEqual(len(result.headerlist), 2)
- from repoze.bfg.template import XSLTemplateFactory
+ from repoze.bfg.xslt import XSLTemplateFactory
self.failUnless(isinstance(queryUtility(INodeTemplate, minimal),
XSLTemplateFactory))
@@ -91,7 +91,7 @@ class RenderTransformToResponseTests(unittest.TestCase, Base):
self._zcmlConfigure()
from zope.component import getGlobalSiteManager
from zope.component import queryUtility
- from repoze.bfg.template import XSLTemplateFactory
+ from repoze.bfg.xslt import XSLTemplateFactory
from repoze.bfg.interfaces import INodeTemplate
minimal = self._getTemplatePath('minimal.xsl')
utility = XSLTemplateFactory(minimal)
@@ -117,7 +117,7 @@ class RenderTransformTests(unittest.TestCase, Base):
Base.tearDown(self)
def _getFUT(self):
- from repoze.bfg.template import render_transform
+ from repoze.bfg.xslt import render_transform
return render_transform
def test_nonabs_unregistered(self):
@@ -133,7 +133,7 @@ class RenderTransformTests(unittest.TestCase, Base):
self.failUnless(isinstance(result, str))
resultstr = """<?xml version="1.0"?>\n<div/>\n"""
self.assertEqual(result, resultstr)
- from repoze.bfg.template import XSLTemplateFactory
+ from repoze.bfg.xslt import XSLTemplateFactory
self.failUnless(isinstance(queryUtility(INodeTemplate, minimal),
XSLTemplateFactory))
@@ -141,7 +141,7 @@ class RenderTransformTests(unittest.TestCase, Base):
self._zcmlConfigure()
from zope.component import getGlobalSiteManager
from zope.component import queryUtility
- from repoze.bfg.template import XSLTemplateFactory
+ from repoze.bfg.xslt import XSLTemplateFactory
from repoze.bfg.interfaces import INodeTemplate
minimal = self._getTemplatePath('minimal.xsl')
utility = XSLTemplateFactory(minimal)
diff --git a/repoze/bfg/xslt.py b/repoze/bfg/xslt.py
new file mode 100644
index 000000000..949bde6da
--- /dev/null
+++ b/repoze/bfg/xslt.py
@@ -0,0 +1,85 @@
+import os
+
+from webob import Response
+
+from zope.component import queryUtility
+from zope.component import getSiteManager
+from zope.component.interfaces import ComponentLookupError
+
+from zope.interface import classProvides
+from zope.interface import implements
+
+from repoze.bfg.path import caller_path
+
+from repoze.bfg.interfaces import INodeTemplate
+from repoze.bfg.interfaces import ITemplateFactory
+
+def get_transform(path, node):
+ """ Return a callable transform object. When called, the
+ transform will return a string. The ``path`` argument should be a
+ package-relative path (also may be absolute) to an XSLT file.
+ When called, the transform will use the kwargs in ``*kw`` as top
+ level names and the lxml node at ``node``."""
+ # Render using XSLT
+ path = caller_path(path)
+
+ template = queryUtility(INodeTemplate, path)
+ if template is None:
+ if not os.path.exists(path):
+ raise ValueError('Missing template file: %s' % path)
+ template = XSLTemplateFactory(path)
+ try:
+ sm = getSiteManager()
+ except ComponentLookupError:
+ pass
+ else:
+ sm.registerUtility(template, INodeTemplate, name=path)
+ return template
+
+def render_transform(path, node, **kw):
+ """ Render a XSL template at the package-relative path (may also
+ be absolute) using the kwargs in ``*kw`` as top-level names and
+ the lxml node at ``node`` and return a string."""
+ path = caller_path(path)
+ template = get_transform(path, node)
+ return template(node, **kw)
+
+def render_transform_to_response(path, node, **kw):
+ """ Render a XSL template at the package-relative path (may also
+ be absolute) using the kwargs in ``*kw`` as top-level names and
+ the lxml node at ``node`` and return a Response object."""
+ path = caller_path(path)
+ result = render_transform(path, node, **kw)
+ return Response(result)
+
+class XSLTemplateFactory(object):
+ classProvides(ITemplateFactory)
+ implements(INodeTemplate)
+
+ def __init__(self, path, auto_reload=False):
+ self.path = path
+ self.auto_reload = auto_reload
+
+ def __call__(self, node, **kw):
+ processor = get_processor(self.path, self.auto_reload)
+ result = str(processor(node, **kw))
+ return result
+
+# Manage XSLT processors on a per-thread basis
+import threading
+from lxml import etree
+xslt_pool = threading.local()
+def get_processor(xslt_fn, auto_reload=False):
+ if not auto_reload:
+ try:
+ return xslt_pool.processors[xslt_fn]
+ except AttributeError:
+ xslt_pool.processors = {}
+ except KeyError:
+ pass
+
+ # Make a processor and add it to the pool
+ source = etree.ElementTree(file=xslt_fn)
+ proc = etree.XSLT(source)
+ xslt_pool.processors[xslt_fn] = proc
+ return proc
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index a2a063603..b5de0584a 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -118,7 +118,7 @@ def zcml_configure(name, package, load=cPickle.load):
try:
vers, ptime, actions = load(open(pckname, 'rb'))
except (IOError, cPickle.UnpicklingError, EOFError, TypeError, ValueError,
- AttributeError, NameError):
+ AttributeError, NameError, ImportError):
return file_configure(name, package)
if vers != PVERSION:
diff --git a/setup.py b/setup.py
index 6ee7cbb8e..3aa1aa78f 100644
--- a/setup.py
+++ b/setup.py
@@ -12,7 +12,7 @@
#
##############################################################################
-__version__ = '0.3.7'
+__version__ = '0.3.8'
import os
@@ -25,6 +25,21 @@ here = os.path.abspath(os.path.dirname(__file__))
README = open(os.path.join(here, 'README.txt')).read()
CHANGES = open(os.path.join(here, 'CHANGES.txt')).read()
+install_requires=[
+ 'zope.interface',
+ 'zope.component',
+ 'zope.testing',
+ 'zope.hookable',
+ 'zope.event',
+ 'WebOb',
+ 'PasteScript',
+ 'chameleon.core [lxml]',
+ 'chameleon.zpt',
+ 'chameleon.genshi',
+ 'Routes',
+ 'setuptools',
+ ]
+
setup(name='repoze.bfg',
version=__version__,
description='A web framework for WSGI',
@@ -47,31 +62,8 @@ setup(name='repoze.bfg',
include_package_data=True,
namespace_packages=['repoze', 'repoze.bfg'],
zip_safe=False,
- install_requires=[
- 'zope.interface',
- 'zope.component',
- 'zope.testing',
- 'zope.hookable',
- 'zope.event',
- 'WebOb',
- 'PasteScript',
- 'z3c.pt [lxml]',
- 'Routes',
- 'setuptools',
- ],
- tests_require=[
- 'zope.interface',
- 'zope.component',
- 'zope.testing',
- 'zope.hookable',
- 'zope.event',
- 'WebOb',
- 'z3c.pt [lxml]',
- 'Paste',
- 'Routes',
- 'Sphinx',
- 'docutils',
- ],
+ install_requires = install_requires,
+ tests_require= install_requires + ['Sphinx', 'docutils'],
test_suite="repoze.bfg.tests",
entry_points = """\
[paste.paster_create_template]