This commit is contained in:
parent
d1df1fd2bb
commit
adff499e47
|
@ -1,11 +1,14 @@
|
|||
# Since this package contains a "django" module, this is required on Python 2.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template.context import Context, RequestContext, make_context
|
||||
from django.template.engine import Engine, _dirs_undefined
|
||||
from django.utils import six
|
||||
from django.utils.deprecation import RemovedInDjango20Warning
|
||||
|
||||
from .base import BaseEngine
|
||||
|
@ -24,21 +27,23 @@ class DjangoTemplates(BaseEngine):
|
|||
self.engine = Engine(self.dirs, self.app_dirs, **options)
|
||||
|
||||
def from_string(self, template_code):
|
||||
return Template(self.engine.from_string(template_code))
|
||||
return Template(self.engine.from_string(template_code), self)
|
||||
|
||||
def get_template(self, template_name, dirs=_dirs_undefined):
|
||||
return Template(self.engine.get_template(template_name, dirs))
|
||||
try:
|
||||
return Template(self.engine.get_template(template_name, dirs), self)
|
||||
except TemplateDoesNotExist as exc:
|
||||
reraise(exc, self)
|
||||
|
||||
|
||||
class Template(object):
|
||||
|
||||
def __init__(self, template):
|
||||
def __init__(self, template, backend):
|
||||
self.template = template
|
||||
self.backend = backend
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
# TODO: define the Origin API. For now simply forwarding to the
|
||||
# underlying Template preserves backwards-compatibility.
|
||||
return self.template.origin
|
||||
|
||||
def render(self, context=None, request=None):
|
||||
|
@ -71,4 +76,17 @@ class Template(object):
|
|||
else:
|
||||
context = make_context(context, request)
|
||||
|
||||
try:
|
||||
return self.template.render(context)
|
||||
except TemplateDoesNotExist as exc:
|
||||
reraise(exc, self.backend)
|
||||
|
||||
|
||||
def reraise(exc, backend):
|
||||
"""
|
||||
Reraise TemplateDoesNotExist while maintaining template debug information.
|
||||
"""
|
||||
new = exc.__class__(*exc.args, tried=exc.tried, backend=backend)
|
||||
if hasattr(exc, 'template_debug'):
|
||||
new.template_debug = exc.template_debug
|
||||
six.reraise(exc.__class__, new, sys.exc_info()[2])
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
# Since this package contains a "django" module, this is required on Python 2.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import errno
|
||||
import io
|
||||
import string
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template import Origin, TemplateDoesNotExist
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
from .base import BaseEngine
|
||||
|
@ -29,17 +30,24 @@ class TemplateStrings(BaseEngine):
|
|||
return Template(template_code)
|
||||
|
||||
def get_template(self, template_name):
|
||||
tried = []
|
||||
for template_file in self.iter_template_filenames(template_name):
|
||||
try:
|
||||
with io.open(template_file, encoding=settings.FILE_CHARSET) as fp:
|
||||
template_code = fp.read()
|
||||
except IOError:
|
||||
except IOError as e:
|
||||
if e.errno == errno.ENOENT:
|
||||
tried.append((
|
||||
Origin(template_file, template_name, self),
|
||||
'Source does not exist',
|
||||
))
|
||||
continue
|
||||
raise
|
||||
|
||||
return Template(template_code)
|
||||
|
||||
else:
|
||||
raise TemplateDoesNotExist(template_name)
|
||||
raise TemplateDoesNotExist(template_name, tried=tried, backend=self)
|
||||
|
||||
|
||||
class Template(string.Template):
|
||||
|
|
|
@ -41,17 +41,24 @@ class Jinja2(BaseEngine):
|
|||
try:
|
||||
return Template(self.env.get_template(template_name))
|
||||
except jinja2.TemplateNotFound as exc:
|
||||
six.reraise(TemplateDoesNotExist, TemplateDoesNotExist(exc.args),
|
||||
sys.exc_info()[2])
|
||||
six.reraise(
|
||||
TemplateDoesNotExist,
|
||||
TemplateDoesNotExist(exc.name, backend=self),
|
||||
sys.exc_info()[2],
|
||||
)
|
||||
except jinja2.TemplateSyntaxError as exc:
|
||||
six.reraise(TemplateSyntaxError, TemplateSyntaxError(exc.args),
|
||||
sys.exc_info()[2])
|
||||
new = TemplateSyntaxError(exc.args)
|
||||
new.template_debug = get_exception_info(exc)
|
||||
six.reraise(TemplateSyntaxError, new, sys.exc_info()[2])
|
||||
|
||||
|
||||
class Template(object):
|
||||
|
||||
def __init__(self, template):
|
||||
self.template = template
|
||||
self.origin = Origin(
|
||||
name=template.filename, template_name=template.name,
|
||||
)
|
||||
|
||||
def render(self, context=None, request=None):
|
||||
if context is None:
|
||||
|
@ -61,3 +68,40 @@ class Template(object):
|
|||
context['csrf_input'] = csrf_input_lazy(request)
|
||||
context['csrf_token'] = csrf_token_lazy(request)
|
||||
return self.template.render(context)
|
||||
|
||||
|
||||
class Origin(object):
|
||||
"""
|
||||
A container to hold debug information as described in the template API
|
||||
documentation.
|
||||
"""
|
||||
def __init__(self, name, template_name):
|
||||
self.name = name
|
||||
self.template_name = template_name
|
||||
|
||||
|
||||
def get_exception_info(exception):
|
||||
"""
|
||||
Formats exception information for display on the debug page using the
|
||||
structure described in the template API documentation.
|
||||
"""
|
||||
context_lines = 10
|
||||
lineno = exception.lineno
|
||||
lines = list(enumerate(exception.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)
|
||||
|
||||
return {
|
||||
'name': exception.filename,
|
||||
'message': exception.message,
|
||||
'source_lines': lines[top:bottom],
|
||||
'line': lineno,
|
||||
'before': '',
|
||||
'during': during,
|
||||
'after': '',
|
||||
'total': total,
|
||||
'top': top,
|
||||
'bottom': bottom,
|
||||
}
|
||||
|
|
|
@ -135,13 +135,27 @@ class TemplateSyntaxError(Exception):
|
|||
|
||||
class TemplateDoesNotExist(Exception):
|
||||
"""
|
||||
This exception is used when template loaders are unable to find a
|
||||
template. The tried argument is an optional list of tuples containing
|
||||
(origin, status), where origin is an Origin object and status is a string
|
||||
with the reason the template wasn't found.
|
||||
The exception used by backends when a template does not exist. Accepts the
|
||||
following optional arguments:
|
||||
|
||||
backend
|
||||
The template backend class used when raising this exception.
|
||||
|
||||
tried
|
||||
A list of sources that were tried when finding the template. This
|
||||
is formatted as a list of tuples containing (origin, status), where
|
||||
origin is an Origin object and status is a string with the reason the
|
||||
template wasn't found.
|
||||
|
||||
chain
|
||||
A list of intermediate TemplateDoesNotExist exceptions. This is used to
|
||||
encapsulate multiple exceptions when loading templates from multiple
|
||||
engines.
|
||||
"""
|
||||
def __init__(self, msg, tried=None):
|
||||
def __init__(self, msg, tried=None, backend=None, chain=None):
|
||||
self.backend = backend
|
||||
self.tried = tried or []
|
||||
self.chain = chain or []
|
||||
super(TemplateDoesNotExist, self).__init__(msg)
|
||||
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
|
|||
|
||||
Raises TemplateDoesNotExist if no such template exists.
|
||||
"""
|
||||
tried = []
|
||||
chain = []
|
||||
engines = _engine_list(using)
|
||||
for engine in engines:
|
||||
try:
|
||||
|
@ -33,9 +33,9 @@ def get_template(template_name, dirs=_dirs_undefined, using=None):
|
|||
else:
|
||||
return engine.get_template(template_name)
|
||||
except TemplateDoesNotExist as e:
|
||||
tried.extend(e.tried)
|
||||
chain.append(e)
|
||||
|
||||
raise TemplateDoesNotExist(template_name, tried=tried)
|
||||
raise TemplateDoesNotExist(template_name, chain=chain)
|
||||
|
||||
|
||||
def select_template(template_name_list, dirs=_dirs_undefined, using=None):
|
||||
|
@ -46,7 +46,7 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
|
|||
|
||||
Raises TemplateDoesNotExist if no such template exists.
|
||||
"""
|
||||
tried = []
|
||||
chain = []
|
||||
engines = _engine_list(using)
|
||||
for template_name in template_name_list:
|
||||
for engine in engines:
|
||||
|
@ -63,10 +63,10 @@ def select_template(template_name_list, dirs=_dirs_undefined, using=None):
|
|||
else:
|
||||
return engine.get_template(template_name)
|
||||
except TemplateDoesNotExist as e:
|
||||
tried.extend(e.tried)
|
||||
chain.append(e)
|
||||
|
||||
if template_name_list:
|
||||
raise TemplateDoesNotExist(', '.join(template_name_list), tried=tried)
|
||||
raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
|
||||
else:
|
||||
raise TemplateDoesNotExist("No template names provided")
|
||||
|
||||
|
@ -92,7 +92,7 @@ def render_to_string(template_name, context=None,
|
|||
return template.render(context, request)
|
||||
|
||||
else:
|
||||
tried = []
|
||||
chain = []
|
||||
# Some deprecated arguments were passed - use the legacy code path
|
||||
for engine in _engine_list(using):
|
||||
try:
|
||||
|
@ -124,13 +124,13 @@ def render_to_string(template_name, context=None,
|
|||
"method doesn't support the dictionary argument." %
|
||||
engine.name, stacklevel=2)
|
||||
except TemplateDoesNotExist as e:
|
||||
tried.extend(e.tried)
|
||||
chain.append(e)
|
||||
continue
|
||||
|
||||
if template_name:
|
||||
if isinstance(template_name, (list, tuple)):
|
||||
template_name = ', '.join(template_name)
|
||||
raise TemplateDoesNotExist(template_name, tried=tried)
|
||||
raise TemplateDoesNotExist(template_name, chain=chain)
|
||||
else:
|
||||
raise TemplateDoesNotExist("No template names provided")
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.core.urlresolvers import Resolver404, resolve
|
|||
from django.http import (
|
||||
HttpRequest, HttpResponse, HttpResponseNotFound, build_request_repr,
|
||||
)
|
||||
from django.template import Context, Engine, TemplateDoesNotExist, engines
|
||||
from django.template import Context, Engine, TemplateDoesNotExist
|
||||
from django.template.defaultfilters import force_escape, pprint
|
||||
from django.utils import lru_cache, six, timezone
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
@ -276,25 +276,7 @@ class ExceptionReporter(object):
|
|||
"""Return a dictionary containing traceback information."""
|
||||
if self.exc_type and issubclass(self.exc_type, TemplateDoesNotExist):
|
||||
self.template_does_not_exist = True
|
||||
postmortem = []
|
||||
|
||||
# TODO: add support for multiple template engines (#24120).
|
||||
# TemplateDoesNotExist should carry all the information, including
|
||||
# the backend, rather than looping through engines.all.
|
||||
for engine in engines.all():
|
||||
if hasattr(engine, 'engine'):
|
||||
e = engine.engine
|
||||
else:
|
||||
e = engine
|
||||
|
||||
postmortem.append(dict(
|
||||
engine=engine,
|
||||
tried=[
|
||||
entry for entry in self.exc_value.tried if
|
||||
entry[0].loader.engine == e
|
||||
],
|
||||
))
|
||||
self.postmortem = postmortem
|
||||
self.postmortem = self.exc_value.chain or [self.exc_value]
|
||||
|
||||
frames = self.get_traceback_frames()
|
||||
for i, frame in enumerate(frames):
|
||||
|
@ -751,7 +733,7 @@ TECHNICAL_500_TEMPLATE = ("""
|
|||
{% if postmortem %}
|
||||
<p class="append-bottom">Django tried loading these templates, in this order:</p>
|
||||
{% for entry in postmortem %}
|
||||
<p class="postmortem-section">Using engine <code>{{ entry.engine.name }}</code>:</p>
|
||||
<p class="postmortem-section">Using engine <code>{{ entry.backend.name }}</code>:</p>
|
||||
<ul>
|
||||
{% if entry.tried %}
|
||||
{% for attempt in entry.tried %}
|
||||
|
@ -890,7 +872,7 @@ Installed Middleware:
|
|||
{% if template_does_not_exist %}Template loader postmortem
|
||||
{% if postmortem %}Django tried loading these templates, in this order:
|
||||
{% for entry in postmortem %}
|
||||
Using engine {{ entry.engine.name }}:
|
||||
Using engine {{ entry.backend.name }}:
|
||||
{% if entry.tried %}{% for attempt in entry.tried %} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
|
||||
{% endfor %}{% else %} This engine did not provide a list of tried templates.
|
||||
{% endif %}{% endfor %}
|
||||
|
@ -1083,7 +1065,7 @@ Installed Middleware:
|
|||
{% if template_does_not_exist %}Template loader postmortem
|
||||
{% if postmortem %}Django tried loading these templates, in this order:
|
||||
{% for entry in postmortem %}
|
||||
Using engine {{ entry.engine.name }}:
|
||||
Using engine {{ entry.backend.name }}:
|
||||
{% if entry.tried %}{% for attempt in entry.tried %} * {{ attempt.0.loader_name }}: {{ attempt.0.name }} ({{ attempt.1 }})
|
||||
{% endfor %}{% else %} This engine did not provide a list of tried templates.
|
||||
{% endif %}{% endfor %}
|
||||
|
|
|
@ -249,6 +249,9 @@ Templates
|
|||
* The debug page template postmortem now include output from each engine that
|
||||
is installed.
|
||||
|
||||
* :ref:`Debug page integration <template-debug-integration>` for custom
|
||||
template engines was added.
|
||||
|
||||
Requests and Responses
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
|
@ -152,11 +152,32 @@ The ``django.template.loader`` module defines two functions to load templates.
|
|||
If loading a template fails, the following two exceptions, defined in
|
||||
``django.template``, may be raised:
|
||||
|
||||
.. exception:: TemplateDoesNotExist
|
||||
.. exception:: TemplateDoesNotExist(msg, tried=None, backend=None, chain=None)
|
||||
|
||||
This exception is raised when a template cannot be found.
|
||||
This exception is raised when a template cannot be found. It accepts the
|
||||
following optional arguments for populating the :ref:`template postmortem
|
||||
<template-postmortem>` on the debug page:
|
||||
|
||||
.. exception:: TemplateSyntaxError
|
||||
``backend``
|
||||
The template backend instance from which the exception originated.
|
||||
|
||||
``tried``
|
||||
A list of sources that were tried when finding the template. This is
|
||||
formatted as a list of tuples containing ``(origin, status)``, where
|
||||
``origin`` is an :ref:`origin-like <template-origin-api>` object and
|
||||
``status`` is a string with the reason the template wasn't found.
|
||||
|
||||
``chain``
|
||||
A list of intermediate :exc:`~django.template.TemplateDoesNotExist`
|
||||
exceptions raised when trying to load a template. This is used by
|
||||
functions, such as :func:`~django.template.loader.get_template`, that
|
||||
try to load a given template from multiple engines.
|
||||
|
||||
.. versionadded:: 1.9
|
||||
|
||||
The ``backend``, ``tried``, and ``chain`` arguments were added.
|
||||
|
||||
.. exception:: TemplateSyntaxError(msg)
|
||||
|
||||
This exception is raised when a template was found but contains errors.
|
||||
|
||||
|
@ -478,7 +499,6 @@ fictional ``foobar`` template library::
|
|||
|
||||
self.engine = foobar.Engine(**options)
|
||||
|
||||
|
||||
def from_string(self, template_code):
|
||||
try:
|
||||
return Template(self.engine.from_string(template_code))
|
||||
|
@ -489,7 +509,7 @@ fictional ``foobar`` template library::
|
|||
try:
|
||||
return Template(self.engine.get_template(template_name))
|
||||
except foobar.TemplateNotFound as exc:
|
||||
raise TemplateDoesNotExist(exc.args)
|
||||
raise TemplateDoesNotExist(exc.args, backend=self)
|
||||
except foobar.TemplateCompilationFailed as exc:
|
||||
raise TemplateSyntaxError(exc.args)
|
||||
|
||||
|
@ -510,6 +530,117 @@ fictional ``foobar`` template library::
|
|||
|
||||
See `DEP 182`_ for more information.
|
||||
|
||||
.. _template-debug-integration:
|
||||
|
||||
Debug integration for custom engines
|
||||
------------------------------------
|
||||
|
||||
.. versionadded:: 1.9
|
||||
|
||||
Debug page integration for non-Django template engines was added.
|
||||
|
||||
The Django debug page has hooks to provide detailed information when a template
|
||||
error arises. Custom template engines can use these hooks to enhance the
|
||||
traceback information that appears to users. The following hooks are available:
|
||||
|
||||
.. _template-postmortem:
|
||||
|
||||
Template postmortem
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The postmortem appears when :exc:`~django.template.TemplateDoesNotExist` is
|
||||
raised. It lists the template engines and loaders that were used when trying
|
||||
to find a given template. For example, if two Django engines are configured,
|
||||
the postmortem will appear like:
|
||||
|
||||
.. image:: _images/postmortem.png
|
||||
|
||||
Custom engines can populate the postmortem by passing the ``backend`` and
|
||||
``tried`` arguments when raising :exc:`~django.template.TemplateDoesNotExist`.
|
||||
Backends that use the postmortem :ref:`should specify an origin
|
||||
<template-origin-api>` on the template object.
|
||||
|
||||
Contextual line information
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If an error happens during template parsing or rendering, Django can display
|
||||
the line the error happened on. For example:
|
||||
|
||||
.. image:: _images/template-lines.png
|
||||
|
||||
Custom engines can populate this information by setting a ``template_debug``
|
||||
attribute on exceptions raised during parsing and rendering. This attribute
|
||||
is a :class:`dict` with the following values:
|
||||
|
||||
* ``'name'``: The name of the template in which the exception occurred.
|
||||
|
||||
* ``'message'``: The exception message.
|
||||
|
||||
* ``'source_lines'``: The lines before, after, and including the line the
|
||||
exception occurred on. This is for context, so it shouldn't contain more than
|
||||
20 lines or so.
|
||||
|
||||
* ``'line'``: The line number on which the exception occurred.
|
||||
|
||||
* ``'before'``: The content on the error line before the token that raised the
|
||||
error.
|
||||
|
||||
* ``'during'``: The token that raised the error.
|
||||
|
||||
* ``'after'``: The content on the error line after the token that raised the
|
||||
error.
|
||||
|
||||
* ``'total'``: The number of lines in ``source_lines``.
|
||||
|
||||
* ``'top'``: The line number where ``source_lines`` starts.
|
||||
|
||||
* ``'bottom'``: The line number where ``source_lines`` ends.
|
||||
|
||||
Given the above template error, ``template_debug`` would look like::
|
||||
|
||||
{
|
||||
'name': '/path/to/template.html',
|
||||
'message': "Invalid block tag: 'syntax'",
|
||||
'source_lines': [
|
||||
(1, 'some\n'),
|
||||
(2, 'lines\n'),
|
||||
(3, 'before\n'),
|
||||
(4, 'Hello {% syntax error %} {{ world }}\n'),
|
||||
(5, 'some\n'),
|
||||
(6, 'lines\n'),
|
||||
(7, 'after\n'),
|
||||
(8, ''),
|
||||
],
|
||||
'line': 4,
|
||||
'before': 'Hello ',
|
||||
'during': '{% syntax error %}',
|
||||
'after': ' {{ world }}\n',
|
||||
'total': 9,
|
||||
'bottom': 9,
|
||||
'top': 1,
|
||||
}
|
||||
|
||||
.. _template-origin-api:
|
||||
|
||||
Origin API and 3rd-party integration
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Django templates have an :class:`~django.template.base.Origin` object available
|
||||
through the ``template.origin`` attribute. This enables debug information to be
|
||||
displayed in the :ref:`template postmortem <template-postmortem>`, as well as
|
||||
in 3rd-party libraries, like the `Django Debug Toolbar`_.
|
||||
|
||||
Custom engines can provide their own ``template.origin`` information by
|
||||
creating an object that specifies the following attributes:
|
||||
|
||||
* ``'name'``: The full path to the template.
|
||||
|
||||
* ``'template_name'``: The relative path to the template as passed into the
|
||||
the template loading methods.
|
||||
|
||||
* ``'loader_name'``: An optional string identifying the function or class used
|
||||
to load the template, e.g. ``django.template.loaders.filesystem.Loader``.
|
||||
|
||||
.. currentmodule:: django.template
|
||||
|
||||
.. _template-language-intro:
|
||||
|
@ -687,3 +818,4 @@ Implementing a custom context processor is as simple as defining a function.
|
|||
|
||||
.. _Jinja2: http://jinja.pocoo.org/
|
||||
.. _DEP 182: https://github.com/django/deps/blob/master/accepted/0182-multiple-template-engines.rst
|
||||
.. _Django Debug Toolbar: https://github.com/django-debug-toolbar/django-debug-toolbar
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
||||
{% block %}
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
10
|
||||
11
|
||||
12
|
||||
13
|
||||
14
|
||||
15
|
|
@ -37,8 +37,9 @@ class TemplateStringsTests(SimpleTestCase):
|
|||
self.assertEqual(content, "Hello world!\n")
|
||||
|
||||
def test_get_template_non_existing(self):
|
||||
with self.assertRaises(TemplateDoesNotExist):
|
||||
with self.assertRaises(TemplateDoesNotExist) as e:
|
||||
self.engine.get_template('template_backends/non_existing.html')
|
||||
self.assertEqual(e.exception.backend, self.engine)
|
||||
|
||||
def test_get_template_syntax_error(self):
|
||||
# There's no way to trigger a syntax error with the dummy backend.
|
||||
|
|
|
@ -4,6 +4,8 @@ from __future__ import absolute_import
|
|||
|
||||
from unittest import skipIf
|
||||
|
||||
from django.template import TemplateSyntaxError
|
||||
|
||||
from .test_dummy import TemplateStringsTests
|
||||
|
||||
try:
|
||||
|
@ -22,6 +24,16 @@ class Jinja2Tests(TemplateStringsTests):
|
|||
backend_name = 'jinja2'
|
||||
options = {'keep_trailing_newline': True}
|
||||
|
||||
def test_origin(self):
|
||||
template = self.engine.get_template('template_backends/hello.html')
|
||||
self.assertTrue(template.origin.name.endswith('hello.html'))
|
||||
self.assertEqual(template.origin.template_name, 'template_backends/hello.html')
|
||||
|
||||
def test_origin_from_string(self):
|
||||
template = self.engine.from_string('Hello!\n')
|
||||
self.assertEqual(template.origin.name, '<template>')
|
||||
self.assertEqual(template.origin.template_name, None)
|
||||
|
||||
def test_self_context(self):
|
||||
"""
|
||||
Using 'self' in the context should not throw errors (#24538).
|
||||
|
@ -32,3 +44,33 @@ class Jinja2Tests(TemplateStringsTests):
|
|||
template = self.engine.from_string('hello {{ foo }}!')
|
||||
content = template.render(context={'self': 'self', 'foo': 'world'})
|
||||
self.assertEqual(content, 'hello world!')
|
||||
|
||||
def test_exception_debug_info_min_context(self):
|
||||
with self.assertRaises(TemplateSyntaxError) as e:
|
||||
self.engine.get_template('template_backends/syntax_error.html')
|
||||
debug = e.exception.template_debug
|
||||
self.assertEqual(debug['after'], '')
|
||||
self.assertEqual(debug['before'], '')
|
||||
self.assertEqual(debug['during'], '{% block %}')
|
||||
self.assertEqual(debug['bottom'], 1)
|
||||
self.assertEqual(debug['top'], 0)
|
||||
self.assertEqual(debug['line'], 1)
|
||||
self.assertEqual(debug['total'], 1)
|
||||
self.assertEqual(len(debug['source_lines']), 1)
|
||||
self.assertTrue(debug['name'].endswith('syntax_error.html'))
|
||||
self.assertTrue('message' in debug)
|
||||
|
||||
def test_exception_debug_info_max_context(self):
|
||||
with self.assertRaises(TemplateSyntaxError) as e:
|
||||
self.engine.get_template('template_backends/syntax_error2.html')
|
||||
debug = e.exception.template_debug
|
||||
self.assertEqual(debug['after'], '')
|
||||
self.assertEqual(debug['before'], '')
|
||||
self.assertEqual(debug['during'], '{% block %}')
|
||||
self.assertEqual(debug['bottom'], 26)
|
||||
self.assertEqual(debug['top'], 5)
|
||||
self.assertEqual(debug['line'], 16)
|
||||
self.assertEqual(debug['total'], 31)
|
||||
self.assertEqual(len(debug['source_lines']), 21)
|
||||
self.assertTrue(debug['name'].endswith('syntax_error2.html'))
|
||||
self.assertTrue('message' in debug)
|
||||
|
|
|
@ -36,9 +36,10 @@ class TemplateLoaderTests(SimpleTestCase):
|
|||
with self.assertRaises(TemplateDoesNotExist) as e:
|
||||
get_template("template_loader/unknown.html")
|
||||
self.assertEqual(
|
||||
e.exception.tried[-1][0].template_name,
|
||||
e.exception.chain[-1].tried[0][0].template_name,
|
||||
'template_loader/unknown.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[-1].backend.name, 'django')
|
||||
|
||||
def test_select_template_first_engine(self):
|
||||
template = select_template(["template_loader/unknown.html",
|
||||
|
@ -64,13 +65,15 @@ class TemplateLoaderTests(SimpleTestCase):
|
|||
select_template(["template_loader/unknown.html",
|
||||
"template_loader/missing.html"])
|
||||
self.assertEqual(
|
||||
e.exception.tried[0][0].template_name,
|
||||
e.exception.chain[0].tried[0][0].template_name,
|
||||
'template_loader/unknown.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[0].backend.name, 'dummy')
|
||||
self.assertEqual(
|
||||
e.exception.tried[-1][0].template_name,
|
||||
e.exception.chain[-1].tried[0][0].template_name,
|
||||
'template_loader/missing.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[-1].backend.name, 'django')
|
||||
|
||||
def test_select_template_tries_all_engines_before_names(self):
|
||||
template = select_template(["template_loader/goodbye.html",
|
||||
|
@ -98,9 +101,10 @@ class TemplateLoaderTests(SimpleTestCase):
|
|||
with self.assertRaises(TemplateDoesNotExist) as e:
|
||||
render_to_string("template_loader/unknown.html")
|
||||
self.assertEqual(
|
||||
e.exception.tried[-1][0].template_name,
|
||||
e.exception.chain[-1].tried[0][0].template_name,
|
||||
'template_loader/unknown.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[-1].backend.name, 'django')
|
||||
|
||||
def test_render_to_string_with_list_first_engine(self):
|
||||
content = render_to_string(["template_loader/unknown.html",
|
||||
|
@ -126,13 +130,25 @@ class TemplateLoaderTests(SimpleTestCase):
|
|||
render_to_string(["template_loader/unknown.html",
|
||||
"template_loader/missing.html"])
|
||||
self.assertEqual(
|
||||
e.exception.tried[0][0].template_name,
|
||||
e.exception.chain[0].tried[0][0].template_name,
|
||||
'template_loader/unknown.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[0].backend.name, 'dummy')
|
||||
self.assertEqual(
|
||||
e.exception.tried[-1][0].template_name,
|
||||
e.exception.chain[1].tried[0][0].template_name,
|
||||
'template_loader/unknown.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[1].backend.name, 'django')
|
||||
self.assertEqual(
|
||||
e.exception.chain[2].tried[0][0].template_name,
|
||||
'template_loader/missing.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[2].backend.name, 'dummy')
|
||||
self.assertEqual(
|
||||
e.exception.chain[3].tried[0][0].template_name,
|
||||
'template_loader/missing.html',
|
||||
)
|
||||
self.assertEqual(e.exception.chain[3].backend.name, 'django')
|
||||
|
||||
def test_render_to_string_with_list_tries_all_engines_before_names(self):
|
||||
content = render_to_string(["template_loader/goodbye.html",
|
||||
|
|
Loading…
Reference in New Issue