Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Template loader behavior change #33

wants to merge 3 commits into from

3 participants


This is a change on the behavior of the template loader to enable it to load templates relatively to the package of the caller module, this way it will match the current behavior of the pyramid's chameleon template loader.

I have also added a test case for it.


Thanks for the patch -- it looks great overall.

Could you please update 'docs/index.rst' to add a description of the new
lookup behavior?


I just changed docs/index.rst to reflect the new lookup behavior.


Could you also add a changelog entry and add your name to CONTRIBUTORS.txt?




Merged. Thanks :beers:

@domenkozar domenkozar closed this
@domenkozar domenkozar reopened this

While this works for templates rendered through Pyramid API, those rendered as part of inheritance or similar will not render relative.

To cover all templates, we need to solve this here:

The question is, how to retrieve package there :)


Is it in the environment? How does the included Chameleon template loader deals with that?


Needs some research, but to me it looks handling of template lookups needs to be rethought from scratch and most imporantly - edge case tested. Needs a hero or my spare time someday.

@domenkozar domenkozar closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 31, 2012
  1. @jpfarias
  2. @jpfarias
Commits on Sep 11, 2012
  1. @jpfarias

    Added changes to changelog.

    jpfarias authored
This page is out of date. Refresh to see the latest.
3  CHANGES.txt
@@ -5,6 +5,9 @@ Next release
- Add ``dev`` and ``docs`` aliases (ala Pyramid).
+- Changed template loading relative to package calling the renderer so
+ it works like the Chameleon template loader.
1.3 (2011-12-14)
@@ -115,3 +115,6 @@ Blaise Laflamme, 2010/11/14
Rocky Burt, 2011/02/13
Kiall Mac Innes, 2012/05/13
+Joao Paulo F Farias, 2012/09/11
43 docs/index.rst
@@ -61,21 +61,50 @@ To setup the jinja2 search path either one of the following steps must be taken:
.. warning::
If you do not explicitly configure your jinja2 search path it will
- default to the root of your application. If configured in this way all
- subsequent paths will need to be specified relative to the root of your
- application's package. For example:
+ default to the root of your application. If the specified template
+ is not found in the root of your application and you did not specify
+ a package on the template path it will then try to load the template
+ path relative to the module's caller package. For example:
Without the search path configured:
.. code-block:: text
With the search path configured:
- .. code-block:: text
+ .. code-block:: text
+ If you view module is in app.module.view and your template is
+ under app/module/templates/mytemplate.jinja2 you can access
+ that asset in a few different ways.
+ Using the full path:
+ .. code-block:: text
+ @view_config(renderer="module/templates/mytemplate.jinja2")
+ Using the package:
+ .. code-block:: text
+ @view_config(renderer="app.module:templates/mytemplate.jinja2")
+ Using the relative path to current package:
+ .. code-block:: text
+ @view_config(renderer="templates/mytemplate.jinja2")
+ You need to be careful when using relative paths though, if
+ there is an app/templates/mytemplate.jinja2 this will be
+ used instead as jinja2 lookup will first try the path relative
+ to the root of the app and then it will try the path relative
+ to the current package.
14 pyramid_jinja2/
@@ -275,7 +275,19 @@ def implementation(self):
def template(self):
- return self.environment.get_template(
+ info =
+ name =
+ name_with_package = name
+ if ':' not in name and hasattr(info, 'package') and info.package is not None:
+ package =
+ name_with_package = '%s:%s' % (package.__name__, name)
+ try:
+ return self.environment.get_template(name)
+ except TemplateNotFound:
+ if name != name_with_package:
+ return self.environment.get_template(name_with_package)
+ else:
+ raise
def __call__(self, value, system):
16 pyramid_jinja2/tests/
@@ -220,6 +220,22 @@ def test_render(self):
self.assertEqual(result, text_('\nHello föö', 'utf-8'))
+class TestIntegration2(unittest.TestCase):
+ def setUp(self):
+ import pyramid_jinja2
+ config = testing.setUp()
+ config.add_renderer('.jinja2',
+ pyramid_jinja2.renderer_factory)
+ def tearDown(self):
+ testing.tearDown()
+ def test_render_relative_to_package(self):
+ from pyramid.renderers import render
+ result = render('templates/helloworld.jinja2', {'a': 1})
+ self.assertEqual(result, text_('\nHello föö', 'utf-8'))
class Test_includeme(unittest.TestCase):
def test_it(self):
from pyramid.interfaces import IRendererFactory
Something went wrong with that request. Please try again.