diff --git a/django/template/engine.py b/django/template/engine.py index 729c484a67..154276a5b0 100644 --- a/django/template/engine.py +++ b/django/template/engine.py @@ -130,21 +130,11 @@ class Engine(object): def find_template(self, name, dirs=None, skip=None): tried = [] for loader in self.template_loaders: - if loader.supports_recursion: - try: - template = loader.get_template( - name, template_dirs=dirs, skip=skip, - ) - return template, template.origin - except TemplateDoesNotExist as e: - tried.extend(e.tried) - else: - # RemovedInDjango20Warning: Use old api for non-recursive - # loaders. - try: - return loader(name, dirs) - except TemplateDoesNotExist: - pass + try: + template = loader.get_template(name, skip=skip) + return template, template.origin + except TemplateDoesNotExist as e: + tried.extend(e.tried) raise TemplateDoesNotExist(name, tried=tried) def from_string(self, template_code): diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py index 2ed145c248..e5c44c88b0 100644 --- a/django/template/loader_tags.py +++ b/django/template/loader_tags.py @@ -19,10 +19,6 @@ BLOCK_CONTEXT_KEY = 'block_context' logger = logging.getLogger('django.template') -class ExtendsError(Exception): - pass - - class BlockContext(object): def __init__(self): # Dictionary of FIFO queues. @@ -107,23 +103,6 @@ class ExtendsNode(Node): passed as the skip argument. This enables extends to work recursively without extending the same template twice. """ - # RemovedInDjango20Warning: If any non-recursive loaders are installed - # do a direct template lookup. If the same template name appears twice, - # raise an exception to avoid system recursion. - for loader in context.template.engine.template_loaders: - if not loader.supports_recursion: - history = context.render_context.setdefault( - self.context_key, [context.template.origin.template_name], - ) - if template_name in history: - raise ExtendsError( - "Cannot extend templates recursively when using " - "non-recursive template loaders", - ) - template = context.template.engine.get_template(template_name) - history.append(template_name) - return template - history = context.render_context.setdefault( self.context_key, [context.template.origin], ) diff --git a/django/template/loaders/base.py b/django/template/loaders/base.py index 5d49f9bfba..cb1807fd7e 100644 --- a/django/template/loaders/base.py +++ b/django/template/loaders/base.py @@ -1,8 +1,4 @@ -import warnings - -from django.template import Origin, Template, TemplateDoesNotExist -from django.utils.deprecation import RemovedInDjango20Warning -from django.utils.inspect import func_supports_parameter +from django.template import Template, TemplateDoesNotExist class Loader(object): @@ -10,11 +6,7 @@ class Loader(object): def __init__(self, engine): self.engine = engine - def __call__(self, template_name, template_dirs=None): - # RemovedInDjango20Warning: Allow loaders to be called like functions. - return self.load_template(template_name, template_dirs) - - def get_template(self, template_name, template_dirs=None, skip=None): + def get_template(self, template_name, skip=None): """ Calls self.get_template_sources() and returns a Template object for the first template matching template_name. If skip is provided, @@ -23,13 +15,7 @@ class Loader(object): """ tried = [] - args = [template_name] - # RemovedInDjango20Warning: Add template_dirs for compatibility with - # old loaders - if func_supports_parameter(self.get_template_sources, 'template_dirs'): - args.append(template_dirs) - - for origin in self.get_template_sources(*args): + for origin in self.get_template_sources(template_name): if skip is not None and origin in skip: tried.append((origin, 'Skipped')) continue @@ -46,30 +32,6 @@ class Loader(object): raise TemplateDoesNotExist(template_name, tried=tried) - def load_template(self, template_name, template_dirs=None): - warnings.warn( - 'The load_template() method is deprecated. Use get_template() ' - 'instead.', RemovedInDjango20Warning, - ) - source, display_name = self.load_template_source( - template_name, template_dirs, - ) - origin = Origin( - name=display_name, - template_name=template_name, - loader=self, - ) - try: - template = Template(source, origin, template_name, self.engine) - 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 of the actual template that does not exist. - return source, display_name - else: - return template, None - def get_template_sources(self, template_name): """ An iterator that yields possible matching template paths for a @@ -79,26 +41,9 @@ class Loader(object): 'subclasses of Loader must provide a get_template_sources() method' ) - def load_template_source(self, template_name, template_dirs=None): - """ - RemovedInDjango20Warning: Returns a tuple containing the source and - origin for the given template name. - """ - raise NotImplementedError( - 'subclasses of Loader must provide a load_template_source() method' - ) - def reset(self): """ Resets any state maintained by the loader instance (e.g. cached templates or cached loader modules). """ pass - - @property - def supports_recursion(self): - """ - RemovedInDjango20Warning: This is an internal property used by the - ExtendsNode during the deprecation of non-recursive loaders. - """ - return hasattr(self, 'get_contents') diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py index 104a3356a7..1b9ee98c95 100644 --- a/django/template/loaders/cached.py +++ b/django/template/loaders/cached.py @@ -4,13 +4,10 @@ to load templates from them in order, caching the result. """ import hashlib -import warnings -from django.template import Origin, Template, TemplateDoesNotExist +from django.template import TemplateDoesNotExist from django.template.backends.django import copy_exception -from django.utils.deprecation import RemovedInDjango20Warning from django.utils.encoding import force_bytes, force_text -from django.utils.inspect import func_supports_parameter from .base import Loader as BaseLoader @@ -19,7 +16,6 @@ class Loader(BaseLoader): def __init__(self, engine, loaders): self.template_cache = {} - self.find_template_cache = {} # RemovedInDjango20Warning self.get_template_cache = {} self.loaders = engine.get_template_loaders(loaders) super(Loader, self).__init__(engine) @@ -27,7 +23,7 @@ class Loader(BaseLoader): def get_contents(self, origin): return origin.loader.get_contents(origin) - def get_template(self, template_name, template_dirs=None, skip=None): + def get_template(self, template_name, skip=None): """ Perform the caching that gives this loader its name. Often many of the templates attempted will be missing, so memory use is of concern here. @@ -46,7 +42,7 @@ class Loader(BaseLoader): memory leak. Thus, unraised copies of the exceptions are cached and copies of those copies are raised after they're fetched from the cache. """ - key = self.cache_key(template_name, template_dirs, skip) + key = self.cache_key(template_name, skip) cached = self.get_template_cache.get(key) if cached: if isinstance(cached, type) and issubclass(cached, TemplateDoesNotExist): @@ -56,9 +52,7 @@ class Loader(BaseLoader): return cached try: - template = super(Loader, self).get_template( - template_name, template_dirs, skip, - ) + template = super(Loader, self).get_template(template_name, skip) except TemplateDoesNotExist as e: self.get_template_cache[key] = copy_exception(e) if self.engine.debug else TemplateDoesNotExist raise @@ -67,17 +61,12 @@ class Loader(BaseLoader): return template - def get_template_sources(self, template_name, template_dirs=None): + def get_template_sources(self, template_name): for loader in self.loaders: - args = [template_name] - # RemovedInDjango20Warning: Add template_dirs for compatibility - # with old loaders - if func_supports_parameter(loader.get_template_sources, 'template_dirs'): - args.append(template_dirs) - for origin in loader.get_template_sources(*args): + for origin in loader.get_template_sources(template_name): yield origin - def cache_key(self, template_name, template_dirs, skip=None): + def cache_key(self, template_name, skip=None): """ Generate a cache key for the template name, dirs, and skip. @@ -97,78 +86,12 @@ class Loader(BaseLoader): if matching: skip_prefix = self.generate_hash(matching) - if template_dirs: - dirs_prefix = self.generate_hash(template_dirs) - return '-'.join(filter(bool, [force_text(template_name), skip_prefix, dirs_prefix])) def generate_hash(self, values): return hashlib.sha1(force_bytes('|'.join(values))).hexdigest() - @property - def supports_recursion(self): - """ - RemovedInDjango20Warning: This is an internal property used by the - ExtendsNode during the deprecation of non-recursive loaders. - """ - return all(hasattr(loader, 'get_contents') for loader in self.loaders) - - def find_template(self, name, dirs=None): - """ - RemovedInDjango20Warning: An internal method to lookup the template - name in all the configured loaders. - """ - key = self.cache_key(name, dirs) - try: - result = self.find_template_cache[key] - except KeyError: - result = None - for loader in self.loaders: - try: - template, display_name = loader(name, dirs) - except TemplateDoesNotExist: - pass - else: - origin = Origin( - name=display_name, - template_name=name, - loader=loader, - ) - result = template, origin - break - self.find_template_cache[key] = result - if result: - return result - else: - self.template_cache[key] = TemplateDoesNotExist - raise TemplateDoesNotExist(name) - - def load_template(self, template_name, template_dirs=None): - warnings.warn( - 'The load_template() method is deprecated. Use get_template() ' - 'instead.', RemovedInDjango20Warning, - ) - key = self.cache_key(template_name, template_dirs) - template_tuple = self.template_cache.get(key) - # A cached previous failure: - if template_tuple is TemplateDoesNotExist: - raise TemplateDoesNotExist(template_name) - elif template_tuple is None: - template, origin = self.find_template(template_name, template_dirs) - if not hasattr(template, 'render'): - try: - template = Template(template, origin, template_name, self.engine) - 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. - self.template_cache[key] = (template, origin) - self.template_cache[key] = (template, None) - return self.template_cache[key] - def reset(self): "Empty the template cache." self.template_cache.clear() - self.find_template_cache.clear() # RemovedInDjango20Warning self.get_template_cache.clear() diff --git a/django/template/loaders/eggs.py b/django/template/loaders/eggs.py index 90dfe685f0..3013a8586d 100644 --- a/django/template/loaders/eggs.py +++ b/django/template/loaders/eggs.py @@ -54,21 +54,3 @@ class Loader(BaseLoader): template_name=template_name, loader=self, ) - - def load_template_source(self, template_name, template_dirs=None): - """ - Loads templates from Python eggs via pkg_resource.resource_string. - - For every installed app, it tries to get the resource (app, template_name). - """ - warnings.warn( - 'The load_template_sources() method is deprecated. Use ' - 'get_template() or get_contents() instead.', - RemovedInDjango20Warning, - ) - for origin in self.get_template_sources(template_name): - try: - return self.get_contents(origin), origin.name - except TemplateDoesNotExist: - pass - raise TemplateDoesNotExist(template_name) diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py index da2c46ff08..bd126a0c6b 100644 --- a/django/template/loaders/filesystem.py +++ b/django/template/loaders/filesystem.py @@ -4,12 +4,10 @@ Wrapper for loading templates from the filesystem. import errno import io -import warnings from django.core.exceptions import SuspiciousFileOperation from django.template import Origin, TemplateDoesNotExist from django.utils._os import safe_join -from django.utils.deprecation import RemovedInDjango20Warning from .base import Loader as BaseLoader @@ -32,15 +30,13 @@ class Loader(BaseLoader): raise TemplateDoesNotExist(origin) raise - def get_template_sources(self, template_name, template_dirs=None): + def get_template_sources(self, template_name): """ Return an Origin object pointing to an absolute path in each directory in template_dirs. For security reasons, if a path doesn't lie inside one of the template_dirs it is excluded from the result set. """ - if not template_dirs: - template_dirs = self.get_dirs() - for template_dir in template_dirs: + for template_dir in self.get_dirs(): try: name = safe_join(template_dir, template_name) except SuspiciousFileOperation: @@ -53,16 +49,3 @@ class Loader(BaseLoader): template_name=template_name, loader=self, ) - - def load_template_source(self, template_name, template_dirs=None): - warnings.warn( - 'The load_template_sources() method is deprecated. Use ' - 'get_template() or get_contents() instead.', - RemovedInDjango20Warning, - ) - for origin in self.get_template_sources(template_name, template_dirs): - try: - return self.get_contents(origin), origin.name - except TemplateDoesNotExist: - pass - raise TemplateDoesNotExist(template_name) diff --git a/django/template/loaders/locmem.py b/django/template/loaders/locmem.py index a27dcd845b..1b77c9805b 100644 --- a/django/template/loaders/locmem.py +++ b/django/template/loaders/locmem.py @@ -2,10 +2,7 @@ Wrapper for loading templates from a plain Python dict. """ -import warnings - from django.template import Origin, TemplateDoesNotExist -from django.utils.deprecation import RemovedInDjango20Warning from .base import Loader as BaseLoader @@ -28,14 +25,3 @@ class Loader(BaseLoader): template_name=template_name, loader=self, ) - - def load_template_source(self, template_name, template_dirs=None): - warnings.warn( - 'The load_template_sources() method is deprecated. Use ' - 'get_template() or get_contents() instead.', - RemovedInDjango20Warning, - ) - try: - return self.templates_dict[template_name], template_name - except KeyError: - raise TemplateDoesNotExist(template_name) diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 2057c370f3..305945a856 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -1057,37 +1057,6 @@ Loader methods :meth:`get_contents` for custom template loaders. ``get_template()`` will usually not need to be overridden. - .. method:: load_template_source(template_name, template_dirs=None) - - Returns a tuple of (``template_string``, ``template_origin``), where - ``template_string`` is a string containing the template contents, - and ``template_origin`` is a string identifying the template source. - A filesystem-based loader may return the full path to the file as the - ``template_origin``, for example. - - ``template_dirs`` is an optional argument used to control which - directories the loader will search. - - This method is called automatically by :meth:`load_template` and should - be overridden when writing custom template loaders. - - .. deprecated:: 1.9 - - Custom loaders should use :meth:`get_template` and - :meth:`get_contents` instead. - - .. method:: load_template(template_name, template_dirs=None) - - Returns a tuple of (``template``, ``template_origin``), where ``template`` - is a ``Template`` object and ``template_origin`` is a string identifying - the template source. A filesystem-based loader may return the full - path to the file as the ``template_origin``, for example. - - .. deprecated:: 1.9 - - Custom loaders should use :meth:`get_template` and - :meth:`get_contents` instead. - .. admonition:: Building your own For examples, `read the source code for Django's built-in loaders`_. diff --git a/docs/releases/2.0.txt b/docs/releases/2.0.txt index 35859c05ca..a6d85697b5 100644 --- a/docs/releases/2.0.txt +++ b/docs/releases/2.0.txt @@ -265,3 +265,23 @@ these features. * The ``GeoManager`` and ``GeoQuerySet`` classes are removed. * The ``django.contrib.gis.geoip`` module is removed. + +* The ``supports_recursion`` check for template loaders is removed from: + + * ``django.template.engine.Engine.find_template()`` + * ``django.template.loader_tags.ExtendsNode.find_template()`` + * ``django.template.loaders.base.Loader.supports_recursion()`` + * ``django.template.loaders.cached.Loader.supports_recursion()`` + +* The ``load_template`` and ``load_template_sources`` template loader methods + are removed. + +* The ``template_dirs`` argument for template loaders is removed: + + * ``django.template.loaders.base.Loader.get_template()`` + * ``django.template.loaders.cached.Loader.cache_key()`` + * ``django.template.loaders.cached.Loader.get_template()`` + * ``django.template.loaders.cached.Loader.get_template_sources()`` + * ``django.template.loaders.filesystem.Loader.get_template_sources()`` + +* ``django.template.loaders.base.Loader.__call__()`` is removed. diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py index ca7ee6f5b0..1ef76c7011 100644 --- a/tests/template_tests/syntax_tests/test_include.py +++ b/tests/template_tests/syntax_tests/test_include.py @@ -248,9 +248,6 @@ class IncludeTests(SimpleTestCase): self.assertEqual(e.exception.args[0], 'missing.html') def test_extends_include_missing_cachedloader(self): - """ - Test the cache loader separately since it overrides load_template. - """ engine = Engine(debug=True, loaders=[ ('django.template.loaders.cached.Loader', [ 'django.template.loaders.app_directories.Loader', diff --git a/tests/template_tests/test_extends.py b/tests/template_tests/test_extends.py index 1d7e00b60d..270160a82a 100644 --- a/tests/template_tests/test_extends.py +++ b/tests/template_tests/test_extends.py @@ -1,10 +1,7 @@ import os from django.template import Context, Engine, TemplateDoesNotExist -from django.template.loader_tags import ExtendsError -from django.template.loaders.base import Loader -from django.test import SimpleTestCase, ignore_warnings -from django.utils.deprecation import RemovedInDjango20Warning +from django.test import SimpleTestCase from .utils import ROOT @@ -120,64 +117,3 @@ class ExtendsBehaviorTests(SimpleTestCase): template = engine.get_template('base.html') output = template.render(Context({})) self.assertEqual(output.strip(), 'loader2 loader1') - - -class NonRecursiveLoader(Loader): - - def __init__(self, engine, templates_dict): - self.templates_dict = templates_dict - super(NonRecursiveLoader, self).__init__(engine) - - def load_template_source(self, template_name, template_dirs=None): - try: - return self.templates_dict[template_name], template_name - except KeyError: - raise TemplateDoesNotExist(template_name) - - -@ignore_warnings(category=RemovedInDjango20Warning) -class NonRecursiveLoaderExtendsTests(SimpleTestCase): - - loaders = [ - ('template_tests.test_extends.NonRecursiveLoader', { - 'base.html': 'base', - 'index.html': '{% extends "base.html" %}', - 'recursive.html': '{% extends "recursive.html" %}', - 'other-recursive.html': '{% extends "recursive.html" %}', - 'a.html': '{% extends "b.html" %}', - 'b.html': '{% extends "a.html" %}', - }), - ] - - def test_extend(self): - engine = Engine(loaders=self.loaders) - output = engine.render_to_string('index.html') - self.assertEqual(output, 'base') - - def test_extend_cached(self): - engine = Engine(loaders=[ - ('django.template.loaders.cached.Loader', self.loaders), - ]) - output = engine.render_to_string('index.html') - self.assertEqual(output, 'base') - - cache = engine.template_loaders[0].template_cache - self.assertIn('base.html', cache) - self.assertIn('index.html', cache) - - # Render a second time from cache - output = engine.render_to_string('index.html') - self.assertEqual(output, 'base') - - def test_extend_error(self): - engine = Engine(loaders=self.loaders) - msg = 'Cannot extend templates recursively when using non-recursive template loaders' - - with self.assertRaisesMessage(ExtendsError, msg): - engine.render_to_string('recursive.html') - - with self.assertRaisesMessage(ExtendsError, msg): - engine.render_to_string('other-recursive.html') - - with self.assertRaisesMessage(ExtendsError, msg): - engine.render_to_string('a.html') diff --git a/tests/template_tests/test_loaders.py b/tests/template_tests/test_loaders.py index 22f32941fe..22fb1d812c 100644 --- a/tests/template_tests/test_loaders.py +++ b/tests/template_tests/test_loaders.py @@ -91,62 +91,6 @@ class CachedLoaderTests(SimpleTestCase): self.assertIsNone(e.__context__, error_msg) self.assertIsNone(e.__cause__, error_msg) - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_template(self): - loader = self.engine.template_loaders[0] - template, origin = loader.load_template('index.html') - self.assertEqual(template.origin.template_name, 'index.html') - - cache = self.engine.template_loaders[0].template_cache - self.assertEqual(cache['index.html'][0], template) - - # Run a second time from cache - loader = self.engine.template_loaders[0] - source, name = loader.load_template('index.html') - self.assertEqual(template.origin.template_name, 'index.html') - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_template_missing(self): - """ - #19949 -- TemplateDoesNotExist exceptions should be cached. - """ - loader = self.engine.template_loaders[0] - - self.assertNotIn('missing.html', loader.template_cache) - - with self.assertRaises(TemplateDoesNotExist): - loader.load_template("missing.html") - - self.assertEqual( - loader.template_cache["missing.html"], - TemplateDoesNotExist, - "Cached loader failed to cache the TemplateDoesNotExist exception", - ) - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_nonexistent_cached_template(self): - loader = self.engine.template_loaders[0] - template_name = 'nonexistent.html' - - # fill the template cache - with self.assertRaises(TemplateDoesNotExist): - loader.find_template(template_name) - - with self.assertRaisesMessage(TemplateDoesNotExist, template_name): - loader.get_template(template_name) - - def test_templatedir_caching(self): - """ - #13573 -- Template directories should be part of the cache key. - """ - # Retrieve a template specifying a template directory to check - t1, name = self.engine.find_template('test.html', (os.path.join(TEMPLATE_DIR, 'first'),)) - # Now retrieve the same template name, but from a different directory - t2, name = self.engine.find_template('test.html', (os.path.join(TEMPLATE_DIR, 'second'),)) - - # The two templates should not have the same content - self.assertNotEqual(t1.render(Context({})), t2.render(Context({}))) - def test_template_name_leading_dash_caching(self): """ #26536 -- A leading dash in a template name shouldn't be stripped @@ -239,20 +183,6 @@ class EggLoaderTests(SimpleTestCase): output = template.render(Context({})) self.assertEqual(output, "y") - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_template_source(self): - loader = self.engine.template_loaders[0] - templates = { - os.path.normcase('templates/y.html'): six.StringIO("y"), - } - - with self.create_egg('egg', templates): - with override_settings(INSTALLED_APPS=['egg']): - source, name = loader.load_template_source('y.html') - - self.assertEqual(source.strip(), 'y') - self.assertEqual(name, 'egg:egg:templates/y.html') - def test_non_existing(self): """ Template loading fails if the template is not in the egg. @@ -323,13 +253,6 @@ class FileSystemLoaderTests(SimpleTestCase): with self.assertRaises(TemplateDoesNotExist): engine.get_template('index.html') - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_template_source(self): - loader = self.engine.template_loaders[0] - source, name = loader.load_template_source('index.html') - self.assertEqual(source.strip(), 'index') - self.assertEqual(name, os.path.join(TEMPLATE_DIR, 'index.html')) - def test_directory_security(self): with self.source_checker(['/dir1', '/dir2']) as check_sources: check_sources('index.html', ['/dir1/index.html', '/dir2/index.html']) @@ -353,10 +276,10 @@ class FileSystemLoaderTests(SimpleTestCase): """ Invalid UTF-8 encoding in bytestrings should raise a useful error """ - engine = Engine() + engine = self.engine loader = engine.template_loaders[0] with self.assertRaises(UnicodeDecodeError): - list(loader.get_template_sources(b'\xc3\xc3', ['/dir1'])) + list(loader.get_template_sources(b'\xc3\xc3')) def test_unicode_dir_name(self): with self.source_checker([b'/Stra\xc3\x9fe']) as check_sources: @@ -410,14 +333,6 @@ class AppDirectoriesLoaderTests(SimpleTestCase): self.assertEqual(template.origin.template_name, 'index.html') self.assertEqual(template.origin.loader, self.engine.template_loaders[0]) - @ignore_warnings(category=RemovedInDjango20Warning) - @override_settings(INSTALLED_APPS=['template_tests']) - def test_load_template_source(self): - loader = self.engine.template_loaders[0] - source, name = loader.load_template_source('index.html') - self.assertEqual(source.strip(), 'index') - self.assertEqual(name, os.path.join(TEMPLATE_DIR, 'index.html')) - @override_settings(INSTALLED_APPS=[]) def test_not_installed(self): with self.assertRaises(TemplateDoesNotExist): @@ -440,10 +355,3 @@ class LocmemLoaderTests(SimpleTestCase): self.assertEqual(template.origin.name, 'index.html') self.assertEqual(template.origin.template_name, 'index.html') self.assertEqual(template.origin.loader, self.engine.template_loaders[0]) - - @ignore_warnings(category=RemovedInDjango20Warning) - def test_load_template_source(self): - loader = self.engine.template_loaders[0] - source, name = loader.load_template_source('index.html') - self.assertEqual(source.strip(), 'index') - self.assertEqual(name, 'index.html')