Skip to content

Commit

Permalink
It turns out that 7cf94a7 did have
Browse files Browse the repository at this point in the history
side-effects: The Django builtins overwrote those provided by Jinja2.

This partly reverts the aforementioned commit in reintroducing Coffin's
own "builtin" list of libraries. However, the Django builtin filters are
still loaded as well. We now pay attention that we first load all three,
Django's builtins, Jinja2's builtins and our own, in this order, thus
ensuring the proper precedence order.
  • Loading branch information
miracle2k committed Aug 29, 2010
1 parent 719b589 commit c84529d
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 12 deletions.
33 changes: 23 additions & 10 deletions coffin/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django import dispatch
from jinja2 import Environment, loaders
from jinja2 import defaults as jinja2_defaults
from coffin.template import Library as CoffinLibrary

__all__ = ('env',)
Expand All @@ -20,11 +21,13 @@ def __init__(self, filters={}, globals={}, tests={}, loader=None, extensions=[],
extensions.extend(all_ext['extensions'])
super(CoffinEnvironment, self).__init__(extensions=extensions, loader=loader, **kwargs)

self.filters.update(all_ext['filters'])
# Note: all_ext already includes Jinja2's own builtins (with
# the proper priority), so we want to assign to these attributes.
self.filters = all_ext['filters'].copy()
self.filters.update(filters)
self.globals.update(all_ext['globals'])
self.globals = all_ext['globals'].copy()
self.globals.update(globals)
self.tests.update(all_ext['tests'])
self.tests = all_ext['tests'].copy()
self.tests.update(tests)
for key, value in all_ext['attrs'].items():
setattr(self, key, value)
Expand Down Expand Up @@ -90,7 +93,8 @@ def _get_templatelibs(self):

def _get_all_extensions(self):
from django.conf import settings
from django.template import builtins
from django.template import builtins as django_builtins
from coffin.template import builtins as coffin_builtins
from django.core.urlresolvers import get_callable

extensions, filters, globals, tests, attrs = [], {}, {}, {}, {}
Expand All @@ -107,16 +111,26 @@ def _load_lib(lib):
tests.update(getattr(lib, 'jinja2_tests', {}))
attrs.update(getattr(lib, 'jinja2_environment_attrs', {}))

# start with our builtins
for lib in builtins:
# Start with Django's builtins; this give's us all of Django's
# filters courtasy of our interop layer.
for lib in django_builtins:
_load_lib(lib)

# The stuff Jinja2 comes with by default should override Django.
filters.update(jinja2_defaults.DEFAULT_FILTERS)
tests.update(jinja2_defaults.DEFAULT_TESTS)
globals.update(jinja2_defaults.DEFAULT_NAMESPACE)

# Our own set of builtins are next, overwriting Jinja2's.
for lib in coffin_builtins:
_load_lib(lib)

# Optionally, include the i18n extension.
if settings.USE_I18N:
extensions.append(_JINJA_I18N_EXTENSION_NAME)

# add the globally defined extension list
# Next, add the globally defined extensions
extensions.extend(list(getattr(settings, 'JINJA2_EXTENSIONS', [])))

def from_setting(setting):
retval = {}
setting = getattr(settings, setting, {})
Expand All @@ -128,12 +142,11 @@ def from_setting(setting):
value = callable(value) and value or get_callable(value)
retval[value.__name__] = value
return retval

filters.update(from_setting('JINJA2_FILTERS'))
globals.update(from_setting('JINJA2_GLOBALS'))
tests.update(from_setting('JINJA2_TESTS'))

# add extensions defined in application's templatetag libraries
# Finally, add extensions defined in application's templatetag libraries
for lib in self._get_templatelibs():
_load_lib(lib)

Expand Down
27 changes: 25 additions & 2 deletions coffin/template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,30 @@ def dict_from_django_context(context):
return dict_


# Make available the ports of Django tags and filters that Coffin provides.
from django.template import add_to_builtins
# libraries to load by default for a new environment
builtins = []


def add_to_builtins(module_name):
"""Add the given module to both Coffin's list of default template
libraries as well as Django's. This makes sense, since Coffin
libs are compatible with Django libraries.
You can still use Django's own ``add_to_builtins`` to register
directly with Django and bypass Coffin.
TODO: Allow passing path to (or reference of) extensions and
filters directly. This would make it easier to use this function
with 3rd party Jinja extensions that do not know about Coffin and
thus will not provide a Library object.
XXX/TODO: Why do we need our own custom list of builtins? Our
Library object is compatible, remember!? We can just add them
directly to Django's own list of builtins.
"""
builtins.append(import_library(module_name))
django_add_to_builtins(module_name)


add_to_builtins('coffin.template.defaulttags')
add_to_builtins('coffin.template.defaultfilters')
9 changes: 9 additions & 0 deletions tests/test_defaultfilters.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ def test_django_builtins_available():
assert r('{{ unknown|get_digit("2") }}') == ''


def test_jinja2_builtins():
"""Ensure that the Jinja2 builtins are available, and take
precedence over the Django builtins (which we automatically convert
and install).
"""
# Django's default filter only accepts one argument.
assert r('{{ unknown|default("2", True) }}') == '2'


def test_url():
# project name is optional
assert r('{{ "urls_app.views.index"|url() }}') == '/url_test/'
Expand Down

0 comments on commit c84529d

Please sign in to comment.