Fixed #12787: Correctly identify the template that does not exist when a template being extended includes another template that does not exist. Thanks to trigeek38 for the report.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@12792 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Karen Tracey 2010-03-16 14:34:57 +00:00
parent e89a5e06cf
commit 80e744945c
6 changed files with 117 additions and 36 deletions

View File

@ -44,8 +44,15 @@ class BaseLoader(object):
def load_template(self, template_name, template_dirs=None): def load_template(self, template_name, template_dirs=None):
source, display_name = self.load_template_source(template_name, template_dirs) source, display_name = self.load_template_source(template_name, template_dirs)
origin = make_origin(display_name, self.load_template_source, template_name, template_dirs) origin = make_origin(display_name, self.load_template_source, template_name, template_dirs)
template = get_template_from_string(source, origin, template_name) try:
return template, None template = get_template_from_string(source, origin, template_name)
return template, None
except TemplateDoesNotExist:
# If compiling the template we found raises TemplateDoesNotExist, back off to
# returning the source and display name for the template we were asked to load.
# This allows for correct identification (later) of the actual template that does
# not exist.
return source, display_name
def load_template_source(self, template_name, template_dirs=None): def load_template_source(self, template_name, template_dirs=None):
""" """

View File

@ -97,10 +97,7 @@ class ExtendsNode(Node):
raise TemplateSyntaxError(error_msg) raise TemplateSyntaxError(error_msg)
if hasattr(parent, 'render'): if hasattr(parent, 'render'):
return parent # parent is a Template object return parent # parent is a Template object
try: return get_template(parent)
return get_template(parent)
except TemplateDoesNotExist:
raise TemplateSyntaxError("Template %r cannot be extended, because it doesn't exist" % parent)
def render(self, context): def render(self, context):
compiled_parent = self.get_parent(context) compiled_parent = self.get_parent(context)

View File

@ -37,7 +37,14 @@ class Loader(BaseLoader):
if template_name not in self.template_cache: if template_name not in self.template_cache:
template, origin = self.find_template(template_name, template_dirs) template, origin = self.find_template(template_name, template_dirs)
if not hasattr(template, 'render'): if not hasattr(template, 'render'):
template = get_template_from_string(template, origin, template_name) try:
template = get_template_from_string(template, origin, template_name)
except TemplateDoesNotExist:
# If compiling the template we found raises TemplateDoesNotExist,
# back off to returning the source and display name for the template
# we were asked to load. This allows for correct identification (later)
# of the actual template that does not exist.
return template, origin
self.template_cache[template_name] = template self.template_cache[template_name] = template
return self.template_cache[template_name], None return self.template_cache[template_name], None

View File

@ -0,0 +1 @@
{% include "missing.html" %}

View File

@ -0,0 +1 @@
{% extends "broken_base.html" %}

View File

@ -160,38 +160,106 @@ class Templates(unittest.TestCase):
# Turn TEMPLATE_DEBUG on, so that the origin file name will be kept with # Turn TEMPLATE_DEBUG on, so that the origin file name will be kept with
# the compiled templates. # the compiled templates.
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
old_loaders = loader.template_source_loaders old_loaders = loader.template_source_loaders
loader.template_source_loaders = (filesystem.Loader(),)
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to try:
# point to a directory containing a 404.html file. Also that loader.template_source_loaders = (filesystem.Loader(),)
# the file system and app directories loaders both inherit the
# load_template method from the BaseLoader class, so we only need
# to test one of them.
load_name = '404.html'
template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name),
'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
# Aso test the cached loader, since it overrides load_template # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
cache_loader = cached.Loader(('',)) # point to a directory containing a 404.html file. Also that
cache_loader._cached_loaders = loader.template_source_loaders # the file system and app directories loaders both inherit the
loader.template_source_loaders = (cache_loader,) # load_template method from the BaseLoader class, so we only need
# to test one of them.
load_name = '404.html'
template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name),
'Template loaded by filesystem loader has incorrect name for debug page: %s' % template_name)
template = loader.get_template(load_name) # Aso test the cached loader, since it overrides load_template
template_name = template.nodelist[0].source[0].name cache_loader = cached.Loader(('',))
self.assertTrue(template_name.endswith(load_name), cache_loader._cached_loaders = loader.template_source_loaders
'Template loaded through cached loader has incorrect name for debug page: %s' % template_name) loader.template_source_loaders = (cache_loader,)
template = loader.get_template(load_name) template = loader.get_template(load_name)
template_name = template.nodelist[0].source[0].name template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name), self.assertTrue(template_name.endswith(load_name),
'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name) 'Template loaded through cached loader has incorrect name for debug page: %s' % template_name)
loader.template_source_loaders = old_loaders template = loader.get_template(load_name)
settings.TEMPLATE_DEBUG = old_td template_name = template.nodelist[0].source[0].name
self.assertTrue(template_name.endswith(load_name),
'Cached template loaded through cached loader has incorrect name for debug page: %s' % template_name)
finally:
loader.template_source_loaders = old_loaders
settings.TEMPLATE_DEBUG = old_td
def test_extends_include_missing_baseloader(self):
"""
Tests that the correct template is identified as not existing
when {% extends %} specifies a template that does exist, but
that template has an {% include %} of something that does not
exist. See #12787.
"""
# TEMPLATE_DEBUG must be true, otherwise the exception raised
# during {% include %} processing will be suppressed.
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
old_loaders = loader.template_source_loaders
try:
# Test the base loader class via the app loader. load_template
# from base is used by all shipped loaders excepting cached,
# which has its own test.
loader.template_source_loaders = (app_directories.Loader(),)
load_name = 'test_extends_error.html'
tmpl = loader.get_template(load_name)
r = None
try:
r = tmpl.render(template.Context({}))
except template.TemplateSyntaxError, e:
settings.TEMPLATE_DEBUG = old_td
self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
finally:
loader.template_source_loaders = old_loaders
settings.TEMPLATE_DEBUG = old_td
def test_extends_include_missing_cachedloader(self):
"""
Same as test_extends_include_missing_baseloader, only tests
behavior of the cached loader instead of BaseLoader.
"""
old_td, settings.TEMPLATE_DEBUG = settings.TEMPLATE_DEBUG, True
old_loaders = loader.template_source_loaders
try:
cache_loader = cached.Loader(('',))
cache_loader._cached_loaders = (app_directories.Loader(),)
loader.template_source_loaders = (cache_loader,)
load_name = 'test_extends_error.html'
tmpl = loader.get_template(load_name)
r = None
try:
r = tmpl.render(template.Context({}))
except template.TemplateSyntaxError, e:
self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
# For the cached loader, repeat the test, to ensure the first attempt did not cache a
# result that behaves incorrectly on subsequent attempts.
tmpl = loader.get_template(load_name)
try:
tmpl.render(template.Context({}))
except template.TemplateSyntaxError, e:
self.assertEqual(e.args[0], 'Caught TemplateDoesNotExist while rendering: missing.html')
self.assertEqual(r, None, 'Template rendering unexpectedly succeeded, produced: ->%r<-' % r)
finally:
loader.template_source_loaders = old_loaders
settings.TEMPLATE_DEBUG = old_td
def test_token_smart_split(self): def test_token_smart_split(self):
# Regression test for #7027 # Regression test for #7027
@ -269,7 +337,7 @@ class Templates(unittest.TestCase):
if isinstance(vals[2], tuple): if isinstance(vals[2], tuple):
normal_string_result = vals[2][0] normal_string_result = vals[2][0]
invalid_string_result = vals[2][1] invalid_string_result = vals[2][1]
if '%s' in invalid_string_result: if isinstance(invalid_string_result, basestring) and '%s' in invalid_string_result:
expected_invalid_str = 'INVALID %s' expected_invalid_str = 'INVALID %s'
invalid_string_result = invalid_string_result % vals[2][2] invalid_string_result = invalid_string_result % vals[2][2]
template.invalid_var_format_string = True template.invalid_var_format_string = True
@ -530,10 +598,10 @@ class Templates(unittest.TestCase):
### EXCEPTIONS ############################################################ ### EXCEPTIONS ############################################################
# Raise exception for invalid template name # Raise exception for invalid template name
'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateSyntaxError), 'exception01': ("{% extends 'nonexistent' %}", {}, template.TemplateDoesNotExist),
# Raise exception for invalid template name (in variable) # Raise exception for invalid template name (in variable)
'exception02': ("{% extends nonexistent %}", {}, template.TemplateSyntaxError), 'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)),
# Raise exception for extra {% extends %} tags # Raise exception for extra {% extends %} tags
'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError), 'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError),