From 2577ae6a082e8a6abc964f999757e908257eafc1 Mon Sep 17 00:00:00 2001 From: Aymeric Augustin Date: Sat, 15 Nov 2014 18:35:02 +0100 Subject: [PATCH] Moved all template loaders under django.template.loaders. Reformatted the code of base.Loader according to modern standards. Turned the test template loader into a regular locmem.Loader -- but didn't document it. Added a normal deprecation path for BaseLoader which is a public API. Added an accelerated deprecation path for TestTemplateLoader which is a private API. --- django/template/loader.py | 56 +++++++--------------- django/template/loaders/app_directories.py | 3 +- django/template/loaders/base.py | 47 ++++++++++++++++++ django/template/loaders/cached.py | 4 +- django/template/loaders/eggs.py | 3 +- django/template/loaders/filesystem.py | 3 +- django/template/loaders/locmem.py | 22 +++++++++ django/test/utils.py | 30 +++++------- docs/internals/deprecation.txt | 3 ++ docs/ref/templates/api.txt | 7 ++- docs/releases/1.8.txt | 7 +++ tests/template_tests/tests.py | 4 +- 12 files changed, 125 insertions(+), 64 deletions(-) create mode 100644 django/template/loaders/base.py create mode 100644 django/template/loaders/locmem.py diff --git a/django/template/loader.py b/django/template/loader.py index ecd978d305..e341b5fcb9 100644 --- a/django/template/loader.py +++ b/django/template/loader.py @@ -25,54 +25,18 @@ # Python eggs) sets is_usable to False if the "pkg_resources" module isn't # installed, because pkg_resources is necessary to read eggs. +import warnings + from django.core.exceptions import ImproperlyConfigured from django.template.base import Origin, Template, Context, TemplateDoesNotExist from django.conf import settings +from django.utils.deprecation import RemovedInDjango20Warning from django.utils.module_loading import import_string from django.utils import six template_source_loaders = None -class BaseLoader(object): - is_usable = False - - def __init__(self, *args, **kwargs): - pass - - def __call__(self, template_name, template_dirs=None): - return self.load_template(template_name, template_dirs) - - def load_template(self, template_name, template_dirs=None): - source, display_name = self.load_template_source(template_name, template_dirs) - origin = make_origin(display_name, self.load_template_source, template_name, template_dirs) - try: - 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): - """ - Returns a tuple containing the source and origin for the given template - name. - - """ - raise NotImplementedError('subclasses of BaseLoader 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 - - class LoaderOrigin(Origin): def __init__(self, display_name, loader, name, dirs): super(LoaderOrigin, self).__init__(display_name) @@ -199,3 +163,17 @@ def select_template(template_name_list, dirs=None): continue # If we get here, none of the templates could be loaded raise TemplateDoesNotExist(', '.join(not_found)) + + +# This line must remain at the bottom to avoid import loops. +from .loaders import base + + +class BaseLoader(base.Loader): + + def __init__(self, *args, **kwargs): + warnings.warn( + "django.template.loader.BaseLoader was renamed to " + "django.template.loaders.base.Loader.", + RemovedInDjango20Warning, stacklevel=2) + super(BaseLoader, self).__init__(*args, **kwargs) diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py index db23f7226f..929c62f217 100644 --- a/django/template/loaders/app_directories.py +++ b/django/template/loaders/app_directories.py @@ -11,10 +11,11 @@ from django.apps import apps from django.conf import settings from django.core.exceptions import SuspiciousFileOperation from django.template.base import TemplateDoesNotExist -from django.template.loader import BaseLoader from django.utils._os import safe_join from django.utils import six +from .base import Loader as BaseLoader + def calculate_app_template_dirs(): if six.PY2: diff --git a/django/template/loaders/base.py b/django/template/loaders/base.py new file mode 100644 index 0000000000..03c58f7535 --- /dev/null +++ b/django/template/loaders/base.py @@ -0,0 +1,47 @@ +from django.template.base import TemplateDoesNotExist +from django.template.loader import get_template_from_string, make_origin + + +class Loader(object): + is_usable = False + + def __init__(self, *args, **kwargs): + # XXX dropping arguments silently may not be the best idea. + pass + + def __call__(self, template_name, template_dirs=None): + return self.load_template(template_name, template_dirs) + + def load_template(self, template_name, template_dirs=None): + source, display_name = self.load_template_source( + template_name, template_dirs) + origin = make_origin( + display_name, self.load_template_source, + template_name, template_dirs) + + try: + template = get_template_from_string(source, 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 of the actual template that does not exist. + return source, display_name + else: + return template, None + + def load_template_source(self, template_name, template_dirs=None): + """ + 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 diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py index 1ed7c7f96a..e217efc93f 100644 --- a/django/template/loaders/cached.py +++ b/django/template/loaders/cached.py @@ -5,9 +5,11 @@ to load templates from them in order, caching the result. import hashlib from django.template.base import TemplateDoesNotExist -from django.template.loader import BaseLoader, get_template_from_string, find_template_loader, make_origin +from django.template.loader import get_template_from_string, find_template_loader, make_origin from django.utils.encoding import force_bytes +from .base import Loader as BaseLoader + class Loader(BaseLoader): is_usable = True diff --git a/django/template/loaders/eggs.py b/django/template/loaders/eggs.py index bb49b302c9..8b5c92ba62 100644 --- a/django/template/loaders/eggs.py +++ b/django/template/loaders/eggs.py @@ -9,9 +9,10 @@ except ImportError: from django.apps import apps from django.conf import settings from django.template.base import TemplateDoesNotExist -from django.template.loader import BaseLoader from django.utils import six +from .base import Loader as BaseLoader + class Loader(BaseLoader): is_usable = resource_string is not None diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py index 935154e40c..6646073093 100644 --- a/django/template/loaders/filesystem.py +++ b/django/template/loaders/filesystem.py @@ -7,9 +7,10 @@ import io from django.conf import settings from django.core.exceptions import SuspiciousFileOperation from django.template.base import TemplateDoesNotExist -from django.template.loader import BaseLoader from django.utils._os import safe_join +from .base import Loader as BaseLoader + class Loader(BaseLoader): is_usable = True diff --git a/django/template/loaders/locmem.py b/django/template/loaders/locmem.py new file mode 100644 index 0000000000..6cb1c599c6 --- /dev/null +++ b/django/template/loaders/locmem.py @@ -0,0 +1,22 @@ +""" +Wrapper for loading templates from a plain Python dict. +""" + +from django.template.base import TemplateDoesNotExist + +from .base import Loader as BaseLoader + + +class Loader(BaseLoader): + is_usable = True + + def __init__(self, templates_dict): + self.templates_dict = templates_dict + + def load_template_source(self, template_name, template_dirs=None, + skip_template=None): + try: + return (self.templates_dict[template_name], + "test:%s" % template_name) + except KeyError: + raise TemplateDoesNotExist(template_name) diff --git a/django/test/utils.py b/django/test/utils.py index 9e09e97372..72466bec58 100644 --- a/django/test/utils.py +++ b/django/test/utils.py @@ -14,8 +14,8 @@ from django.core import mail from django.core.signals import request_started from django.db import reset_queries from django.http import request -from django.template import Template, loader, TemplateDoesNotExist -from django.template.loaders import cached +from django.template import Template, loader +from django.template.loaders import cached, locmem from django.test.signals import template_rendered, setting_changed from django.utils import six from django.utils.decorators import ContextDecorator @@ -189,20 +189,14 @@ class override_template_loaders(ContextDecorator): delattr(loader, RESTORE_LOADERS_ATTR) -class TestTemplateLoader(loader.BaseLoader): - "A custom template loader that loads templates from a dictionary." - is_usable = True +class TestTemplateLoader(locmem.Loader): - def __init__(self, templates_dict): - self.templates_dict = templates_dict - - def load_template_source(self, template_name, template_dirs=None, - skip_template=None): - try: - return (self.templates_dict[template_name], - "test:%s" % template_name) - except KeyError: - raise TemplateDoesNotExist(template_name) + def __init__(self, *args, **kwargs): + warnings.warn( + "django.test.utils.TestTemplateLoader was renamed to " + "django.template.loaders.locmem.Loader.", + RemovedInDjango19Warning, stacklevel=2) + super(TestTemplateLoader, self).__init__(*args, **kwargs) class override_with_test_loader(override_template_loaders): @@ -238,11 +232,11 @@ class override_with_test_loader(override_template_loaders): @classmethod def _get_loader(cls, templates_dict, use_cached_loader=False): if use_cached_loader: - loader = cached.Loader(['TestTemplateLoader']) - loader._cached_loaders = [TestTemplateLoader(templates_dict)] + loader = cached.Loader(['django.template.loaders.locmem.Loader']) + loader._cached_loaders = [locmem.Loader(templates_dict)] return loader else: - return TestTemplateLoader(templates_dict) + return locmem.Loader(templates_dict) class override_settings(object): diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 0bba849713..666ff1f492 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -82,6 +82,9 @@ details on these changes. * The backwards compatibility shim to allow ``FormMixin.get_form()`` to be defined with no default value for its ``form_class`` argument will be removed. +* The backwards compatibility alias ``django.template.loader.BaseLoader`` will + be removed. + .. _deprecation-removed-in-1.9: 1.9 diff --git a/docs/ref/templates/api.txt b/docs/ref/templates/api.txt index 08e001b039..6a3f160af3 100644 --- a/docs/ref/templates/api.txt +++ b/docs/ref/templates/api.txt @@ -967,11 +967,16 @@ with the Django loading and rendering system! The next step is to write a ``Loader`` class that returns instances of our custom template class instead of the default :class:`~django.template.Template`. Custom ``Loader`` -classes should inherit from ``django.template.loader.BaseLoader`` and override +classes should inherit from ``django.template.loaders.base.Loader`` and override the ``load_template_source()`` method, which takes a ``template_name`` argument, loads the template from disk (or elsewhere), and returns a tuple: ``(template_string, template_origin)``. +.. versionchanged:: 1.8 + + ``django.template.loaders.base.Loader`` used to be defined at + ``django.template.loader.BaseLoader``. + The ``load_template()`` method of the ``Loader`` class retrieves the template string by calling ``load_template_source()``, instantiates a ``Template`` from the template source, and returns a tuple: ``(template, template_origin)``. Since diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index dc1ced8be6..fb513591a7 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -1029,6 +1029,13 @@ The decorators :func:`~django.test.override_settings` and class decorators. As a consequence, when overriding ``setUpClass()`` or ``tearDownClass()``, the ``super`` implementation should always be called. +``django.template.loader.BaseLoader`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``django.template.loader.BaseLoader`` was renamed to +``django.template.loaders.base.Loader``. If you've written a custom template +loader that inherits ``BaseLoader``, you must inherit ``Loader`` instead. + .. removed-features-1.8: Features removed in 1.8 diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index cfcde1d912..41663c678a 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -221,7 +221,7 @@ class TemplateLoaderTests(TestCase): # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to # point to a directory containing a login.html file. Also that # the file system and app directories loaders both inherit the - # load_template method from the BaseLoader class, so we only need + # load_template method from the base Loader class, so we only need # to test one of them. load_name = 'login.html' template = loader.get_template(load_name) @@ -306,7 +306,7 @@ class TemplateLoaderTests(TestCase): def test_extends_include_missing_cachedloader(self): """ Same as test_extends_include_missing_baseloader, only tests - behavior of the cached loader instead of BaseLoader. + behavior of the cached loader instead of base loader. """ cache_loader = cached.Loader(('',)) cache_loader._cached_loaders = (app_directories.Loader(),)