Skip to content

Commit

Permalink
Merge branch 'master' of git://github.com/coffin/coffin
Browse files Browse the repository at this point in the history
Conflicts:
	coffin/common.py
	coffin/template/loaders.py
  • Loading branch information
GaretJax committed Dec 20, 2012
2 parents 9305129 + df814b1 commit cb6f016
Show file tree
Hide file tree
Showing 16 changed files with 397 additions and 110 deletions.
10 changes: 9 additions & 1 deletion CHANGES
@@ -1,3 +1,11 @@
0.3.7 (2012-09-29)
- Support for Django 1.3 class-based views (Kirill Zaitsev,
Dmitry Panteleev)
- Loader for *.jinja files (Aarni Koskela)
- makemessages command now supports whitespace stripping tags
(Dmitry Panteleev)
- Now supports Django's CachedLoader.

0.3.6 (2011-09-09)
- Re-release of 0.3.5, containing the correct repository
- Re-release of 0.3.5, containing the correct repository
state this time.
4 changes: 2 additions & 2 deletions coffin/__init__.py
Expand Up @@ -2,7 +2,7 @@
Coffin
~~~~~~
`Coffin <http://www.github.com/dcramer/coffin>` is a package that resolves the
`Coffin <http://www.github.com/coffin/coffin>` is a package that resolves the
impedance mismatch between `Django <http://www.djangoproject.com/>` and `Jinja2
<http://jinja.pocoo.org/2/>` through various adapters. The aim is to use Coffin
as a drop-in replacement for Django's template system to whatever extent is
Expand All @@ -14,7 +14,7 @@


__all__ = ('__version__', '__build__', '__docformat__', 'get_revision')
__version__ = (0, 3, '7', 'dev')
__version__ = (0, 3, '8', 'dev')
__docformat__ = 'restructuredtext en'

import os
Expand Down
71 changes: 55 additions & 16 deletions coffin/common.py
Expand Up @@ -28,7 +28,7 @@ def __init__(self, filters={}, globals={}, tests={}, loader=None, extensions=[],
# the proper priority), so we want to assign to these attributes.
self.filters = all_ext['filters'].copy()
self.filters.update(filters)
self.globals = all_ext['globals'].copy()
self.globals.update(all_ext['globals'])
self.globals.update(globals)
self.tests = all_ext['tests'].copy()
self.tests.update(tests)
Expand All @@ -50,15 +50,26 @@ def _get_loaders(self):
from coffin.template.loaders import jinja_loader_from_django_loader

from django.conf import settings
for loader in settings.JINJA2_TEMPLATE_LOADERS:
if isinstance(loader, basestring):
loader_obj = jinja_loader_from_django_loader(loader)
if loader_obj:
loaders.append(loader_obj)
else:
warnings.warn('Cannot translate loader: %s' % loader)
else: # It's assumed to be a Jinja2 loader instance.
_loaders = getattr(settings, 'JINJA2_TEMPLATE_LOADERS', settings.TEMPLATE_LOADERS)
for loader in _loaders:
if isinstance(loader, JinjaLoader):
loaders.append(loader)
else:
loader_name = args = None
if isinstance(loader, basestring):
loader_name = loader
args = []
elif isinstance(loader, (tuple, list)):
loader_name = loader[0]
args = loader[1]

if loader_name:
loader_obj = jinja_loader_from_django_loader(loader_name, args)
if loader_obj:
loaders.append(loader_obj)
continue

warnings.warn('Cannot translate loader: %s' % loader)
return loaders


Expand Down Expand Up @@ -142,23 +153,35 @@ def _load_lib(lib):

# Next, add the globally defined extensions
extensions.extend(list(getattr(settings, 'JINJA2_EXTENSIONS', [])))
def from_setting(setting):
def from_setting(setting, values_must_be_callable = False):
retval = {}
setting = getattr(settings, setting, {})
if isinstance(setting, dict):
for key, value in setting.iteritems():
retval[key] = callable(value) and value or get_callable(value)
if values_must_be_callable and not callable(value):
value = get_callable(value)
retval[key] = value
else:
for value in setting:
value = callable(value) and value or get_callable(value)
if values_must_be_callable and not callable(value):
value = get_callable(value)
retval[value.__name__] = value
return retval
filters.update(from_setting('JINJA2_FILTERS'))

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


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

# Load custom libraries.
from django.template import get_library
for libname in getattr(settings, 'JINJA2_DJANGO_TEMPLATETAG_LIBRARIES', ()):
libraries.append(get_library(libname))

for lib in libraries:
_load_lib(lib)
attrs.update(getattr(lib, 'jinja2_environment_attrs', {}))

Expand All @@ -180,6 +203,22 @@ def get_env():
'autoescape': True,
}
kwargs.update(getattr(settings, 'JINJA2_ENVIRONMENT_OPTIONS', {}))
return CoffinEnvironment(**kwargs)

result = CoffinEnvironment(**kwargs)
# Hook Jinja's i18n extension up to Django's translation backend
# if i18n is enabled; note that we differ here from Django, in that
# Django always has it's i18n functionality available (that is, if
# enabled in a template via {% load %}), but uses a null backend if
# the USE_I18N setting is disabled. Jinja2 provides something similar
# (install_null_translations), but instead we are currently not
# enabling the extension at all when USE_I18N=False.
# While this is basically an incompatibility with Django, currently
# the i18n tags work completely differently anyway, so for now, I
# don't think it matters.
if settings.USE_I18N:
from django.utils import translation
result.install_gettext_translations(translation)

return result

env = get_env()
47 changes: 47 additions & 0 deletions coffin/contrib/loader.py
@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
"""
A Django template loader wrapper for Coffin that intercepts
requests for "*.jinja" templates, rendering them with Coffin
instead of Django templates.
Usage:
TEMPLATE_LOADERS = (
'coffin.contrib.loader.AppLoader',
'coffin.contrib.loader.FileSystemLoader',
)
"""

from os.path import splitext
from coffin.common import env
from django.conf import settings
from django.template.loaders import app_directories, filesystem


JINJA2_DEFAULT_TEMPLATE_EXTENSION = getattr(settings,
'JINJA2_DEFAULT_TEMPLATE_EXTENSION', ('.jinja',))

if isinstance(JINJA2_DEFAULT_TEMPLATE_EXTENSION, basestring):
JINJA2_DEFAULT_TEMPLATE_EXTENSION = (JINJA2_DEFAULT_TEMPLATE_EXTENSION,)


class LoaderMixin(object):
is_usable = True

def load_template(self, template_name, template_dirs=None):
extension = splitext(template_name)[1]

if not extension in JINJA2_DEFAULT_TEMPLATE_EXTENSION:
return super(LoaderMixin, self).load_template(template_name,
template_dirs)
template = env.get_template(template_name)
return template, template.filename


class FileSystemLoader(LoaderMixin, filesystem.Loader):
pass


class AppLoader(LoaderMixin, app_directories.Loader):
pass
22 changes: 19 additions & 3 deletions coffin/management/commands/makemessages.py
Expand Up @@ -27,24 +27,40 @@
import re
from django.core.management.commands import makemessages
from django.utils.translation import trans_real
from django.template import BLOCK_TAG_START, BLOCK_TAG_END

strip_whitespace_right = re.compile(r"(%s-?\s*(trans|pluralize).*?-%s)\s+" % (BLOCK_TAG_START, BLOCK_TAG_END), re.U)
strip_whitespace_left = re.compile(r"\s+(%s-\s*(endtrans|pluralize).*?-?%s)" % (BLOCK_TAG_START, BLOCK_TAG_END), re.U)

def strip_whitespaces(src):
src = strip_whitespace_left.sub(r'\1', src)
src = strip_whitespace_right.sub(r'\1', src)
return src

class Command(makemessages.Command):

def handle(self, *args, **options):
old_endblock_re = trans_real.endblock_re
old_block_re = trans_real.block_re
old_templatize = trans_real.templatize
# Extend the regular expressions that are used to detect
# translation blocks with an "OR jinja-syntax" clause.
trans_real.endblock_re = re.compile(
trans_real.endblock_re.pattern + '|' + r"""^\s*endtrans$""")
trans_real.endblock_re.pattern + '|' + r"""^-?\s*endtrans\s*-?$""")
trans_real.block_re = re.compile(
trans_real.block_re.pattern + '|' + r"""^\s*trans(?:\s+(?!'|")(?=.*?=.*?)|$)""")
trans_real.block_re.pattern + '|' + r"""^-?\s*trans(?:\s+(?!'|")(?=.*?=.*?)|-?$)""")
trans_real.plural_re = re.compile(
trans_real.plural_re.pattern + '|' + r"""^\s*pluralize(?:\s+.+|$)""")
trans_real.plural_re.pattern + '|' + r"""^-?\s*pluralize(?:\s+.+|-?$)""")

def my_templatize(src, origin=None):
new_src = strip_whitespaces(src)
return old_templatize(new_src, origin)

trans_real.templatize = my_templatize

try:
super(Command, self).handle(*args, **options)
finally:
trans_real.endblock_re = old_endblock_re
trans_real.block_re = old_block_re
trans_real.templatize = old_templatize
28 changes: 27 additions & 1 deletion coffin/shortcuts/__init__.py
Expand Up @@ -5,7 +5,7 @@
from django.shortcuts import *


__all__ = ('render_to_string', 'render_to_response',)
__all__ = ('render_to_string', 'render_to_response', 'render')


# Is within ``template.loader`` as per Django specification -
Expand All @@ -23,3 +23,29 @@ def render_to_response(template_name, dictionary=None, context_instance=None,
"""
rendered = render_to_string(template_name, dictionary, context_instance)
return HttpResponse(rendered, mimetype=mimetype)


def render(request, *args, **kwargs):
"""
Returns a HttpResponse whose content is filled with the result of calling
coffin.template.loader.render_to_string() with the passed arguments.
Uses a RequestContext by default.
"""
httpresponse_kwargs = {
'content_type': kwargs.pop('content_type', None),
'status': kwargs.pop('status', None),
}

if 'context_instance' in kwargs:
context_instance = kwargs.pop('context_instance')
if kwargs.get('current_app', None):
raise ValueError('If you provide a context_instance you must '
'set its current_app before calling render()')
else:
current_app = kwargs.pop('current_app', None)
context_instance = RequestContext(request, current_app=current_app)

kwargs['context_instance'] = context_instance

return HttpResponse(render_to_string(*args, **kwargs),
**httpresponse_kwargs)

0 comments on commit cb6f016

Please sign in to comment.