Fixed #30425 -- Handled jinja2.TemplateSyntaxError when rendering a template.

Jinja raises jinja2.TemplateSyntaxError in render() not in
get_template() when it's in an included template.
This commit is contained in:
Hasan Ramezani 2019-11-17 20:34:48 +01:00 committed by Mariusz Felisiak
parent 1f817daa20
commit 8d32290279
3 changed files with 62 additions and 8 deletions

View File

@ -1,3 +1,5 @@
from pathlib import Path
import jinja2 import jinja2
from django.conf import settings from django.conf import settings
@ -68,7 +70,12 @@ class Template:
context['csrf_token'] = csrf_token_lazy(request) context['csrf_token'] = csrf_token_lazy(request)
for context_processor in self.backend.template_context_processors: for context_processor in self.backend.template_context_processors:
context.update(context_processor(request)) context.update(context_processor(request))
return self.template.render(context) 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: class Origin:
@ -88,12 +95,22 @@ def get_exception_info(exception):
""" """
context_lines = 10 context_lines = 10
lineno = exception.lineno lineno = exception.lineno
lines = list(enumerate(exception.source.strip().split("\n"), start=1)) source = exception.source
during = lines[lineno - 1][1] if source is None:
total = len(lines) exception_file = Path(exception.filename)
top = max(0, lineno - context_lines - 1) if exception_file.exists():
bottom = min(total, lineno + context_lines) with open(exception_file, 'r') as fp:
source = fp.read()
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 { return {
'name': exception.filename, 'name': exception.filename,
'message': exception.message, 'message': exception.message,

View File

@ -0,0 +1 @@
{% include "template_backends/syntax_error.html" %}

View File

@ -1,5 +1,5 @@
from pathlib import Path from pathlib import Path
from unittest import skipIf from unittest import mock, skipIf
from django.template import TemplateSyntaxError from django.template import TemplateSyntaxError
from django.test import RequestFactory from django.test import RequestFactory
@ -96,3 +96,39 @@ class Jinja2Tests(TemplateStringsTests):
}) })
template = engine.get_template('hello.html') template = engine.get_template('hello.html')
self.assertEqual(template.render({'name': 'Joe'}), 'Hello Joe!') self.assertEqual(template.render({'name': 'Joe'}), 'Hello Joe!')
def test_template_render_nested_error(self):
template = self.engine.get_template('template_backends/syntax_error_include.html')
with self.assertRaises(TemplateSyntaxError) as e:
template.render(context={})
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.assertIn('message', debug)
def test_template_render_error_nonexistent_source(self):
template = self.engine.get_template('template_backends/hello.html')
with mock.patch(
'jinja2.environment.Template.render',
side_effect=jinja2.TemplateSyntaxError('', 1, filename='nonexistent.html'),
):
with self.assertRaises(TemplateSyntaxError) as e:
template.render(context={})
debug = e.exception.template_debug
self.assertEqual(debug['after'], '')
self.assertEqual(debug['before'], '')
self.assertEqual(debug['during'], '')
self.assertEqual(debug['bottom'], 0)
self.assertEqual(debug['top'], 0)
self.assertEqual(debug['line'], 1)
self.assertEqual(debug['total'], 0)
self.assertEqual(len(debug['source_lines']), 0)
self.assertTrue(debug['name'].endswith('nonexistent.html'))
self.assertIn('message', debug)