from __future__ import absolute_import

import six
import sys

from typing import Any, Dict, List, Optional, Union, Text
if False:
    from mypy_extensions import NoReturn

import jinja2
from django.test.signals import template_rendered
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
from django.http import HttpRequest


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.
    """

    def __init__(self, params, *args, **kwargs):
        # type: (Dict[str, Any], *Any, **Any) -> None
        # We need to remove `context_processors` from `OPTIONS` because
        # `Environment` doesn't expect it
        context_processors = params['OPTIONS'].pop('context_processors', [])
        debug = params['OPTIONS'].pop('debug', False)
        super(Jinja2, self).__init__(params, *args, **kwargs)

        # We need to create these two properties after calling the __init__
        # of base class so that they are not overridden.
        self.context_processors = context_processors
        self.debug = debug

    def get_template(self, template_name):
        # type: (str) -> Template
        try:
            return Template(self.env.get_template(template_name), self)
        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])

    def from_string(self, template_code):
        # type: (str) -> Template
        return Template(self.env.from_string(template_code), self)


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.
        """
        def __init__(self, template, backend):
            # type: (django_jinja2.Template, django_jinja2.BaseEngine) -> None
            # TODO: Remove this function once we shift to Django 1.11 completely.
            # This is only intended for backward compatibility.
            self.template = template
            self.backend = backend
            self.origin = django_jinja2.Origin(
                name=template.filename, template_name=template.name,
            )

        def render(self, context=None, request=None):
            # type: (Optional[Union[Dict[str, Any], Context]], Optional[HttpRequest]) -> Text
            if context is None:
                context = {}

            if isinstance(context, Context):
                # 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

            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.backend.context_processors:
                    cp = import_string(context_processor)
                    context.update(cp(request))

            if self.backend.debug:
                template_rendered.send(sender=self, template=self,
                                       context=context)

            return self.template.render(context)

@@ -374,138 +374,8 @@
 ext%0A
-from django.utils.module_loading import import_string%0Afrom django.template.backends.utils import csrf_input_lazy, csrf_token_lazy%0A
 from
@@ -1417,32 +1417,362 @@
 tr) -%3E Template%0A
+        %22%22%22%0A        The only we need to override this function is to use our own Template%0A        class. Jinja2 backend doesn't allow the usage of custom Template%0A        class.%0A%0A        If in the future Django adds a method through which we can get a%0A        custom Template class, this method can be safely removed.%0A        %22%22%22%0A
         try:%0A   
@@ -2266,106 +2266,86 @@
     
-return Template(self.env.from_string(template_code), self)%0A%0A%0Aclass Template(django_jinja2.
+%22%22%22%0A        The only need to override this function is to use our own 
 Template
 ):%0A 
@@ -2340,18 +2340,16 @@
 Template
-):
 %0A       
@@ -2353,35 +2353,63 @@
     
-%22%22%22Context processors aware
+class. Jinja2 backend doesn't allow the usage of custom
  Tem
@@ -2413,18 +2413,16 @@
 Template
-.%0A
 %0A       
@@ -2426,626 +2426,438 @@
     
-This class upgrades the default %60Template%60 to apply context%0A        processors to the context before passing it to the %60render%60%0A        function.%0A        %22%22%22%0A        def __init__(self, template, backend):%0A            # type: (django_jinja2.Template, django_jinja2.BaseEngine) -%3E None%0A
+class.%0A%0A        If in the future Django adds a method through which we can get a%0A        custom Template class, this method can be safely removed.%0A        %22%22%22%0A        return Template(self.env.from_string(template_code), self)%0A%0A%0Aclass Template(django_jinja2.Template):%0A   
      
+%22%22%22%0A
        
-# TODO: Remove this function once we shift to Django 1.11 completely.%0A            # This is only intended for backward compatibility.%0A            self.template = template%0A            self.backend = backend%0A            self.origin = django_jinja2.Origin(%0A                name=template.filename, template_name=template.name,
+ We need this class so that we can send the template_rendered signal,%0A        and to flatten the context if it is an instance of Context class.
 %0A   
@@ -2865,14 +2865,11 @@
     
-    )%0A
+%22%22%22
 %0A   
@@ -3538,386 +3538,129 @@
     
-if request is not None:%0A                context%5B'request'%5D = request%0A                context%5B'csrf_input'%5D = csrf_input_lazy(request)%0A                context%5B'csrf_token'%5D = csrf_token_lazy(request)%0A%0A                for context_processor in self.backend.context_processors:%0A                    cp = import_string(context_processor)%0A                    context.update(cp(
+result = super(Template, self).render(context=context,%0A                                                  request=
 request)
 )%0A%0A 
@@ -3655,17 +3655,16 @@
 request)
-)
 %0A%0A      
@@ -3839,34 +3839,11 @@
 urn 
-self.template.render(context)
+result
 %0A

Support only Django version >= 1.11.