a container to hold debug information as described in the template api documentation format exception information for display on the debug page using the structure described in the template api documentation a container to hold debug information as described in the template api documentation format exception information for display on the debug page using the structure described in the template api documentation
from pathlib import Path

import jinja2

from django.conf import settings
from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.utils.functional import cached_property
from django.utils.module_loading import import_string

from .base import BaseEngine
from .utils import csrf_input_lazy, csrf_token_lazy


class Jinja2(BaseEngine):
    app_dirname = "jinja2"

    def __init__(self, params):
        params = params.copy()
        options = params.pop("OPTIONS").copy()
        super().__init__(params)

        self.context_processors = options.pop("context_processors", [])

        environment = options.pop("environment", "jinja2.Environment")
        environment_cls = import_string(environment)

        if "loader" not in options:
            options["loader"] = jinja2.FileSystemLoader(self.template_dirs)
        options.setdefault("autoescape", True)
        options.setdefault("auto_reload", settings.DEBUG)
        options.setdefault(
            "undefined", jinja2.DebugUndefined if settings.DEBUG else jinja2.Undefined
        )

        self.env = environment_cls(**options)

    def from_string(self, template_code):
        return Template(self.env.from_string(template_code), self)

    def get_template(self, template_name):
        try:
            return Template(self.env.get_template(template_name), self)
        except jinja2.TemplateNotFound as exc:
            raise TemplateDoesNotExist(exc.name, backend=self) from exc
        except jinja2.TemplateSyntaxError as exc:
            new = TemplateSyntaxError(exc.args)
            new.template_debug = get_exception_info(exc)
            raise new from exc

    @cached_property
    def template_context_processors(self):
        return [import_string(path) for path in self.context_processors]


class Template:
    def __init__(self, template, backend):
        self.template = template
        self.backend = backend
        self.origin = Origin(
            name=template.filename,
            template_name=template.name,
        )

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        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.template_context_processors:
                context.update(context_processor(request))
        try:
            return self.template.render(context)
        except jinja2.TemplateSyntaxError as exc:
            new = TemplateSyntaxError(exc.args)
            new.template_debug = get_exception_info(exc)
            raise new from exc


class Origin:
    

    def __init__(self, name, template_name):
        self.name = name
        self.template_name = template_name


def get_exception_info(exception):
    
    context_lines = 10
    lineno = exception.lineno
    source = exception.source
    if source is None:
        exception_file = Path(exception.filename)
        if exception_file.exists():
            source = exception_file.read_text()
    if source is not None:
        lines = list(enumerate(source.strip().split("\n"), start=1))
        during = lines[lineno - 1][1]
        total = len(lines)
        top = max(0, lineno - context_lines - 1)
        bottom = min(total, lineno + context_lines)
    else:
        during = ""
        lines = []
        total = top = bottom = 0
    return {
        "name": exception.filename,
        "message": exception.message,
        "source_lines": lines[top:bottom],
        "line": lineno,
        "before": "",
        "during": during,
        "after": "",
        "total": total,
        "top": top,
        "bottom": bottom,
    }