mirror of https://github.com/django/django.git
Fixed #24399 -- Made filesystem loaders use more specific exceptions.
This commit is contained in:
parent
85757d0e79
commit
70123cf084
|
@ -2,6 +2,7 @@
|
||||||
Wrapper for loading templates from the filesystem.
|
Wrapper for loading templates from the filesystem.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import errno
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from django.core.exceptions import SuspiciousFileOperation
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
|
@ -37,6 +38,7 @@ class Loader(BaseLoader):
|
||||||
try:
|
try:
|
||||||
with io.open(filepath, encoding=self.engine.file_charset) as fp:
|
with io.open(filepath, encoding=self.engine.file_charset) as fp:
|
||||||
return fp.read(), filepath
|
return fp.read(), filepath
|
||||||
except IOError:
|
except IOError as e:
|
||||||
pass
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
raise TemplateDoesNotExist(template_name)
|
raise TemplateDoesNotExist(template_name)
|
||||||
|
|
|
@ -269,10 +269,6 @@ class ExceptionReporter(object):
|
||||||
def format_path_status(self, path):
|
def format_path_status(self, path):
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
return "File does not exist"
|
return "File does not exist"
|
||||||
if not os.path.isfile(path):
|
|
||||||
return "Not a file"
|
|
||||||
if not os.access(path, os.R_OK):
|
|
||||||
return "File is not readable"
|
|
||||||
return "File exists"
|
return "File exists"
|
||||||
|
|
||||||
def get_traceback_data(self):
|
def get_traceback_data(self):
|
||||||
|
|
|
@ -224,6 +224,19 @@ the keyword argument ``clear=True``.
|
||||||
assignment for many-to-many relations and as a consequence now use the new
|
assignment for many-to-many relations and as a consequence now use the new
|
||||||
behavior.
|
behavior.
|
||||||
|
|
||||||
|
Filesystem-based template loaders catch more specific exceptions
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
When using the :class:`filesystem.Loader <django.template.loaders.filesystem.Loader>`
|
||||||
|
or :class:`app_directories.Loader <django.template.loaders.app_directories.Loader>`
|
||||||
|
template loaders, earlier versions of Django raised a
|
||||||
|
:exc:`~django.template.TemplateDoesNotExist` error if a template source existed
|
||||||
|
but was unreadable. This could happen under many circumstances, such as if
|
||||||
|
Django didn't have permissions to open the file, or if the template source was
|
||||||
|
a directory. Now, Django only silences the exception if the template source
|
||||||
|
does not exist. All other situations result in the original ``IOError`` being
|
||||||
|
raised.
|
||||||
|
|
||||||
Miscellaneous
|
Miscellaneous
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import types
|
import types
|
||||||
import unittest
|
import unittest
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
@ -176,7 +177,16 @@ class EggLoaderTests(SimpleTestCase):
|
||||||
class FileSystemLoaderTests(SimpleTestCase):
|
class FileSystemLoaderTests(SimpleTestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.engine = Engine()
|
self.engine = Engine(dirs=[TEMPLATE_DIR])
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def set_dirs(self, dirs):
|
||||||
|
original_dirs = self.engine.dirs
|
||||||
|
self.engine.dirs = dirs
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
self.engine.dirs = original_dirs
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def source_checker(self, dirs):
|
def source_checker(self, dirs):
|
||||||
|
@ -189,12 +199,8 @@ class FileSystemLoaderTests(SimpleTestCase):
|
||||||
expected_sources,
|
expected_sources,
|
||||||
)
|
)
|
||||||
|
|
||||||
original_dirs = self.engine.dirs
|
with self.set_dirs(dirs):
|
||||||
self.engine.dirs = dirs
|
|
||||||
try:
|
|
||||||
yield check_sources
|
yield check_sources
|
||||||
finally:
|
|
||||||
self.engine.dirs = original_dirs
|
|
||||||
|
|
||||||
def test_directory_security(self):
|
def test_directory_security(self):
|
||||||
with self.source_checker(['/dir1', '/dir2']) as check_sources:
|
with self.source_checker(['/dir1', '/dir2']) as check_sources:
|
||||||
|
@ -238,6 +244,27 @@ class FileSystemLoaderTests(SimpleTestCase):
|
||||||
check_sources('index.html', ['/dir1/index.html', '/DIR2/index.html'])
|
check_sources('index.html', ['/dir1/index.html', '/DIR2/index.html'])
|
||||||
check_sources('/DIR1/index.HTML', ['/DIR1/index.HTML'])
|
check_sources('/DIR1/index.HTML', ['/DIR1/index.HTML'])
|
||||||
|
|
||||||
|
def test_file_does_not_exist(self):
|
||||||
|
with self.assertRaises(TemplateDoesNotExist):
|
||||||
|
self.engine.get_template('doesnotexist.html')
|
||||||
|
|
||||||
|
@unittest.skipIf(
|
||||||
|
sys.platform == 'win32',
|
||||||
|
"Python on Windows doesn't have working os.chmod().",
|
||||||
|
)
|
||||||
|
def test_permissions_error(self):
|
||||||
|
with tempfile.NamedTemporaryFile() as tmpfile:
|
||||||
|
tmpdir = os.path.dirname(tmpfile.name)
|
||||||
|
tmppath = os.path.join(tmpdir, tmpfile.name)
|
||||||
|
os.chmod(tmppath, 0o0222)
|
||||||
|
with self.set_dirs([tmpdir]):
|
||||||
|
with self.assertRaisesMessage(IOError, 'Permission denied'):
|
||||||
|
self.engine.get_template(tmpfile.name)
|
||||||
|
|
||||||
|
def test_notafile_error(self):
|
||||||
|
with self.assertRaisesMessage(IOError, 'Is a directory'):
|
||||||
|
self.engine.get_template('first')
|
||||||
|
|
||||||
|
|
||||||
class AppDirectoriesLoaderTest(SimpleTestCase):
|
class AppDirectoriesLoaderTest(SimpleTestCase):
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import importlib
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import shutil
|
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from unittest import skipIf
|
from unittest import skipIf
|
||||||
|
@ -152,36 +151,6 @@ class DebugViewTests(TestCase):
|
||||||
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
|
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
|
||||||
self.assertContains(response, "%s (File does not exist)" % template_path, status_code=500, count=1)
|
self.assertContains(response, "%s (File does not exist)" % template_path, status_code=500, count=1)
|
||||||
|
|
||||||
@skipIf(sys.platform == "win32", "Python on Windows doesn't have working os.chmod() and os.access().")
|
|
||||||
def test_template_loader_postmortem_notreadable(self):
|
|
||||||
"""Tests for not readable file"""
|
|
||||||
with tempfile.NamedTemporaryFile() as tmpfile:
|
|
||||||
template_name = tmpfile.name
|
|
||||||
tempdir = os.path.dirname(tmpfile.name)
|
|
||||||
template_path = os.path.join(tempdir, template_name)
|
|
||||||
os.chmod(template_path, 0o0222)
|
|
||||||
with override_settings(TEMPLATES=[{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [tempdir],
|
|
||||||
}]):
|
|
||||||
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
|
|
||||||
self.assertContains(response, "%s (File is not readable)" % template_path, status_code=500, count=1)
|
|
||||||
|
|
||||||
def test_template_loader_postmortem_notafile(self):
|
|
||||||
"""Tests for not being a file"""
|
|
||||||
try:
|
|
||||||
template_path = tempfile.mkdtemp()
|
|
||||||
template_name = os.path.basename(template_path)
|
|
||||||
tempdir = os.path.dirname(template_path)
|
|
||||||
with override_settings(TEMPLATES=[{
|
|
||||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
|
||||||
'DIRS': [tempdir],
|
|
||||||
}]):
|
|
||||||
response = self.client.get(reverse('raises_template_does_not_exist', kwargs={"path": template_name}))
|
|
||||||
self.assertContains(response, "%s (Not a file)" % template_path, status_code=500, count=1)
|
|
||||||
finally:
|
|
||||||
shutil.rmtree(template_path)
|
|
||||||
|
|
||||||
def test_no_template_source_loaders(self):
|
def test_no_template_source_loaders(self):
|
||||||
"""
|
"""
|
||||||
Make sure if you don't specify a template, the debug view doesn't blow up.
|
Make sure if you don't specify a template, the debug view doesn't blow up.
|
||||||
|
|
Loading…
Reference in New Issue