Chris McDonough
2012-08-29 345bc79dcdbacb4cd2abd1227b5f851482511064
Merge branch 'feature.macrorenderers'
5 files modified
99 ■■■■ changed files
docs/narr/templates.rst 38 ●●●● patch | view | raw | blame | history
pyramid/chameleon_text.py 2 ●●● patch | view | raw | blame | history
pyramid/chameleon_zpt.py 19 ●●●● patch | view | raw | blame | history
pyramid/renderers.py 13 ●●●● patch | view | raw | blame | history
pyramid/tests/test_renderers.py 27 ●●●●● patch | view | raw | blame | history
docs/narr/templates.rst
@@ -534,6 +534,30 @@
      </span>
    </html>
Using A Chameleon Macro Name Within a Chameleon ZPT Template
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sommetime you'd like to render a macro inside of a Chameleon ZPT template
instead of the full Chameleon ZPT template. To render the content of a
``define-macro`` field inside a Chameleon ZPT template, given a Chameleon
template file named ``foo.pt`` and a macro named ``bar`` defined within it
(e.g. ``<div metal:define-macro="bar">...</div>``, you can configure the
template as a :term:`renderer` like so:
.. code-block:: python
   :linenos:
   from pyramid.view import view_config
   @view_config(renderer='foo#bar.pt')
   def my_view(request):
       return {'project':'my project'}
The above will render the ``bar`` macro from within the ``foo.pt`` template
instead of the entire template.
.. index::
   single: Chameleon text templates
@@ -714,12 +738,13 @@
:term:`renderer globals`.  See the `the Mako documentation
<http://www.makotemplates.org/>`_ to use more advanced features.
Using def inside Mako Templates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Using A Mako def name Within a Renderer Name
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To use a def inside a Mako template, given a :term:`Mako` template file named
``foo.mak`` and a def named ``bar``, you can configure the template as a
:term:`renderer` like so:
Sommetime you'd like to render a ``def`` inside of a Mako template instead of
the full Mako template. To render a def inside a Mako template, given a
:term:`Mako` template file named ``foo.mak`` and a def named ``bar``, you can
configure the template as a :term:`renderer` like so:
.. code-block:: python
   :linenos:
@@ -730,6 +755,9 @@
   def my_view(request):
       return {'project':'my project'}
The above will render the ``bar`` def from within the ``foo.mak`` template
instead of the entire template.
.. index::
   single: automatic reloading of templates
   single: template automatic reload
pyramid/chameleon_text.py
@@ -27,7 +27,7 @@
@implementer(ITemplateRenderer)
class TextTemplateRenderer(object):
    def __init__(self, path, lookup):
    def __init__(self, path, lookup, macro=None):
        self.path = path
        self.lookup = lookup
pyramid/chameleon_zpt.py
@@ -26,25 +26,34 @@
@implementer(ITemplateRenderer)
class ZPTTemplateRenderer(object):
    def __init__(self, path, lookup):
    def __init__(self, path, lookup, macro=None):
        self.path = path
        self.lookup = lookup
        self.macro = macro
    @reify # avoid looking up reload_templates before manager pushed
    def template(self):
        if sys.platform.startswith('java'): # pragma: no cover
            raise RuntimeError(
                'Chameleon templates are not compatible with Jython')
        return PageTemplateFile(self.path,
                                auto_reload=self.lookup.auto_reload,
                                debug=self.lookup.debug,
                                translate=self.lookup.translate)
        tf = PageTemplateFile(self.path,
                              auto_reload=self.lookup.auto_reload,
                              debug=self.lookup.debug,
                              translate=self.lookup.translate)
        if self.macro:
            # render only the portion of the template included in a
            # define-macro named the value of self.macro
            macro_renderer = tf.macros[self.macro].include
            tf._render = macro_renderer
        return tf
    def implementation(self):
        return self.template
    
    def __call__(self, value, system):
        try:
            system['renderer'] = self
            system['template'] = self.template
            system.update(value)
        except (TypeError, ValueError):
            raise ValueError('renderer was passed non-dictionary as value')
pyramid/renderers.py
@@ -1,5 +1,6 @@
import json
import os
import re
import pkg_resources
import threading
@@ -426,7 +427,7 @@
                raise ValueError('Missing template file: %s' % spec)
            renderer = registry.queryUtility(ITemplateRenderer, name=spec)
            if renderer is None:
                renderer = self.impl(spec, self)
                renderer = self.impl(spec, self, macro=None)
                # cache the template
                try:
                    self.lock.acquire()
@@ -438,6 +439,14 @@
            # spec is a package:relpath asset spec
            renderer = registry.queryUtility(ITemplateRenderer, name=spec)
            if renderer is None:
                p = re.compile(
                    r'(?P<asset>[\w_.:/]+)'
                    r'(?:\#(?P<defname>[\w_]+))?'
                    r'(\.(?P<ext>.*))'
                    )
                asset, macro, ext = p.match(spec).group(
                    'asset', 'defname', 'ext')
                spec = '%s.%s' % (asset, ext)
                try:
                    package_name, filename = spec.split(':', 1)
                except ValueError: # pragma: no cover
@@ -450,7 +459,7 @@
                if not pkg_resources.resource_exists(package_name, filename):
                    raise ValueError(
                        'Missing template asset: %s (%s)' % (spec, abspath))
                renderer = self.impl(abspath, self)
                renderer = self.impl(abspath, self, macro=macro)
                settings = info.settings
                if not settings.get('reload_assets'):
                    # cache the template
pyramid/tests/test_renderers.py
@@ -292,7 +292,32 @@
        if path.endswith('.pyc'): # pragma: no cover
            path = path[:-1]
        self.assertTrue(factory.path.startswith(path))
        self.assertEqual(factory.kw, {})
        self.assertEqual(factory.kw, {'macro':None})
    def test___call__spec_withmacro(self):
        import os
        from pyramid import tests
        module_name = tests.__name__
        relpath = 'fixtures/withmacro#foo.pt'
        renderer = {}
        factory = DummyFactory(renderer)
        spec = '%s:%s' % (module_name, relpath)
        info = DummyRendererInfo({
            'name':spec,
            'package':None,
            'registry':self.config.registry,
            'settings':{},
            'type':'type',
            })
        lookup = self._makeOne(factory)
        result = lookup(info)
        self.assertTrue(result is renderer)
        path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            'fixtures',
            'withmacro.pt')
        self.assertTrue(factory.path.startswith(path))
        self.assertEqual(factory.kw, {'macro':'foo'})
    def test___call__reload_assets_true(self):
        import pyramid.tests