2016-04-21 08:48:33 +02:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2017-03-07 07:24:01 +01:00
|
|
|
import six
|
2016-04-21 08:48:33 +02:00
|
|
|
import sys
|
2017-03-07 07:24:01 +01:00
|
|
|
|
2017-03-03 19:01:52 +01:00
|
|
|
from typing import Any, Dict, List, Optional, Union, Text
|
2017-03-05 00:18:18 +01:00
|
|
|
if False:
|
|
|
|
from mypy_extensions import NoReturn
|
2016-04-21 08:48:33 +02:00
|
|
|
|
|
|
|
import jinja2
|
2016-05-23 15:44:07 +02:00
|
|
|
from django.test.signals import template_rendered
|
2016-04-21 08:48:33 +02:00
|
|
|
from django.template.backends import jinja2 as django_jinja2
|
|
|
|
from django.template import TemplateDoesNotExist, TemplateSyntaxError, Context
|
|
|
|
from django.utils.module_loading import import_string
|
|
|
|
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy
|
2016-08-08 11:07:35 +02:00
|
|
|
from django.http import HttpRequest
|
2016-04-21 08:48:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Jinja2(django_jinja2.Jinja2):
|
|
|
|
"""Context processors aware Jinja2 backend.
|
|
|
|
|
|
|
|
The default Jinja2 backend in Django is not aware of context
|
|
|
|
processors so we just derive from the default Jinja2 backend
|
|
|
|
and add the functionality to pass the context processors to
|
|
|
|
the `Template` object.
|
|
|
|
"""
|
2016-11-29 07:22:02 +01:00
|
|
|
|
2016-04-21 08:48:33 +02:00
|
|
|
def __init__(self, params, *args, **kwargs):
|
2016-08-08 11:07:35 +02:00
|
|
|
# type: (Dict[str, Any], *Any, **Any) -> None
|
2016-04-21 08:48:33 +02:00
|
|
|
# We need to remove `context_processors` from `OPTIONS` because
|
|
|
|
# `Environment` doesn't expect it
|
|
|
|
self.context_processors = params['OPTIONS'].pop('context_processors', [])
|
2016-05-23 15:44:07 +02:00
|
|
|
self.debug = params['OPTIONS'].pop('debug', False)
|
2016-04-21 08:48:33 +02:00
|
|
|
super(Jinja2, self).__init__(params, *args, **kwargs)
|
|
|
|
|
|
|
|
def get_template(self, template_name):
|
2017-03-07 07:24:01 +01:00
|
|
|
# type: (str) -> Template
|
2016-04-21 08:48:33 +02:00
|
|
|
try:
|
|
|
|
return Template(self.env.get_template(template_name),
|
2016-05-23 15:44:07 +02:00
|
|
|
self.context_processors,
|
|
|
|
self.debug)
|
2016-04-21 08:48:33 +02:00
|
|
|
except jinja2.TemplateNotFound as exc:
|
|
|
|
six.reraise(TemplateDoesNotExist, TemplateDoesNotExist(exc.args),
|
|
|
|
sys.exc_info()[2])
|
|
|
|
except jinja2.TemplateSyntaxError as exc:
|
|
|
|
six.reraise(TemplateSyntaxError, TemplateSyntaxError(exc.args),
|
|
|
|
sys.exc_info()[2])
|
|
|
|
|
2017-05-18 03:29:31 +02:00
|
|
|
def from_string(self, template_code):
|
|
|
|
# type: (str) -> Template
|
|
|
|
return Template(self.env.from_string(template_code),
|
|
|
|
self.context_processors,
|
|
|
|
self.debug)
|
|
|
|
|
2016-04-21 08:48:33 +02:00
|
|
|
|
|
|
|
class Template(django_jinja2.Template):
|
|
|
|
"""Context processors aware Template.
|
|
|
|
|
|
|
|
This class upgrades the default `Template` to apply context
|
|
|
|
processors to the context before passing it to the `render`
|
|
|
|
function.
|
|
|
|
"""
|
2016-11-29 07:22:02 +01:00
|
|
|
|
2016-05-23 15:44:07 +02:00
|
|
|
def __init__(self, template, context_processors, debug, *args, **kwargs):
|
2016-08-08 11:07:35 +02:00
|
|
|
# type: (str, List[str], bool, *Any, **Any) -> None
|
2016-04-21 08:48:33 +02:00
|
|
|
self.context_processors = context_processors
|
2016-05-23 15:44:07 +02:00
|
|
|
self.debug = debug
|
2016-04-21 08:48:33 +02:00
|
|
|
super(Template, self).__init__(template, *args, **kwargs)
|
|
|
|
|
|
|
|
def render(self, context=None, request=None):
|
2016-12-08 05:06:51 +01:00
|
|
|
# type: (Optional[Union[Dict[str, Any], Context]], Optional[HttpRequest]) -> Text
|
2016-04-21 08:48:33 +02:00
|
|
|
if context is None:
|
|
|
|
context = {}
|
|
|
|
|
|
|
|
if isinstance(context, Context):
|
2016-08-08 11:07:35 +02:00
|
|
|
# Jinja2 expects a dictionary
|
|
|
|
# This condition makes sure that `flatten` is called only when
|
|
|
|
# `context` is an instance of `Context`.
|
|
|
|
#
|
|
|
|
# Note: If we don't ignore then mypy complains about missing
|
|
|
|
# `flatten` attribute in some members of union.
|
|
|
|
context = context.flatten() # type: ignore
|
2016-04-21 08:48:33 +02:00
|
|
|
|
|
|
|
if request is not None:
|
|
|
|
context['request'] = request
|
|
|
|
context['csrf_input'] = csrf_input_lazy(request)
|
|
|
|
context['csrf_token'] = csrf_token_lazy(request)
|
|
|
|
|
|
|
|
for context_processor in self.context_processors:
|
|
|
|
cp = import_string(context_processor)
|
|
|
|
context.update(cp(request))
|
|
|
|
|
2016-05-23 15:44:07 +02:00
|
|
|
if self.debug:
|
|
|
|
template_rendered.send(sender=self, template=self,
|
|
|
|
context=context)
|
|
|
|
|
2016-04-21 08:48:33 +02:00
|
|
|
return self.template.render(context)
|