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.
This commit is contained in:
Aymeric Augustin 2014-11-15 18:35:02 +01:00
parent cffa559082
commit 2577ae6a08
12 changed files with 125 additions and 64 deletions

View File

@ -25,54 +25,18 @@
# Python eggs) sets is_usable to False if the "pkg_resources" module isn't # Python eggs) sets is_usable to False if the "pkg_resources" module isn't
# installed, because pkg_resources is necessary to read eggs. # installed, because pkg_resources is necessary to read eggs.
import warnings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.template.base import Origin, Template, Context, TemplateDoesNotExist from django.template.base import Origin, Template, Context, TemplateDoesNotExist
from django.conf import settings from django.conf import settings
from django.utils.deprecation import RemovedInDjango20Warning
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils import six from django.utils import six
template_source_loaders = None 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): class LoaderOrigin(Origin):
def __init__(self, display_name, loader, name, dirs): def __init__(self, display_name, loader, name, dirs):
super(LoaderOrigin, self).__init__(display_name) super(LoaderOrigin, self).__init__(display_name)
@ -199,3 +163,17 @@ def select_template(template_name_list, dirs=None):
continue continue
# If we get here, none of the templates could be loaded # If we get here, none of the templates could be loaded
raise TemplateDoesNotExist(', '.join(not_found)) 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)

View File

@ -11,10 +11,11 @@ from django.apps import apps
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation from django.core.exceptions import SuspiciousFileOperation
from django.template.base import TemplateDoesNotExist from django.template.base import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.utils._os import safe_join from django.utils._os import safe_join
from django.utils import six from django.utils import six
from .base import Loader as BaseLoader
def calculate_app_template_dirs(): def calculate_app_template_dirs():
if six.PY2: if six.PY2:

View File

@ -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

View File

@ -5,9 +5,11 @@ to load templates from them in order, caching the result.
import hashlib import hashlib
from django.template.base import TemplateDoesNotExist 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 django.utils.encoding import force_bytes
from .base import Loader as BaseLoader
class Loader(BaseLoader): class Loader(BaseLoader):
is_usable = True is_usable = True

View File

@ -9,9 +9,10 @@ except ImportError:
from django.apps import apps from django.apps import apps
from django.conf import settings from django.conf import settings
from django.template.base import TemplateDoesNotExist from django.template.base import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.utils import six from django.utils import six
from .base import Loader as BaseLoader
class Loader(BaseLoader): class Loader(BaseLoader):
is_usable = resource_string is not None is_usable = resource_string is not None

View File

@ -7,9 +7,10 @@ import io
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousFileOperation from django.core.exceptions import SuspiciousFileOperation
from django.template.base import TemplateDoesNotExist from django.template.base import TemplateDoesNotExist
from django.template.loader import BaseLoader
from django.utils._os import safe_join from django.utils._os import safe_join
from .base import Loader as BaseLoader
class Loader(BaseLoader): class Loader(BaseLoader):
is_usable = True is_usable = True

View File

@ -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)

View File

@ -14,8 +14,8 @@ from django.core import mail
from django.core.signals import request_started from django.core.signals import request_started
from django.db import reset_queries from django.db import reset_queries
from django.http import request from django.http import request
from django.template import Template, loader, TemplateDoesNotExist from django.template import Template, loader
from django.template.loaders import cached from django.template.loaders import cached, locmem
from django.test.signals import template_rendered, setting_changed from django.test.signals import template_rendered, setting_changed
from django.utils import six from django.utils import six
from django.utils.decorators import ContextDecorator from django.utils.decorators import ContextDecorator
@ -189,20 +189,14 @@ class override_template_loaders(ContextDecorator):
delattr(loader, RESTORE_LOADERS_ATTR) delattr(loader, RESTORE_LOADERS_ATTR)
class TestTemplateLoader(loader.BaseLoader): class TestTemplateLoader(locmem.Loader):
"A custom template loader that loads templates from a dictionary."
is_usable = True
def __init__(self, templates_dict): def __init__(self, *args, **kwargs):
self.templates_dict = templates_dict warnings.warn(
"django.test.utils.TestTemplateLoader was renamed to "
def load_template_source(self, template_name, template_dirs=None, "django.template.loaders.locmem.Loader.",
skip_template=None): RemovedInDjango19Warning, stacklevel=2)
try: super(TestTemplateLoader, self).__init__(*args, **kwargs)
return (self.templates_dict[template_name],
"test:%s" % template_name)
except KeyError:
raise TemplateDoesNotExist(template_name)
class override_with_test_loader(override_template_loaders): class override_with_test_loader(override_template_loaders):
@ -238,11 +232,11 @@ class override_with_test_loader(override_template_loaders):
@classmethod @classmethod
def _get_loader(cls, templates_dict, use_cached_loader=False): def _get_loader(cls, templates_dict, use_cached_loader=False):
if use_cached_loader: if use_cached_loader:
loader = cached.Loader(['TestTemplateLoader']) loader = cached.Loader(['django.template.loaders.locmem.Loader'])
loader._cached_loaders = [TestTemplateLoader(templates_dict)] loader._cached_loaders = [locmem.Loader(templates_dict)]
return loader return loader
else: else:
return TestTemplateLoader(templates_dict) return locmem.Loader(templates_dict)
class override_settings(object): class override_settings(object):

View File

@ -82,6 +82,9 @@ details on these changes.
* The backwards compatibility shim to allow ``FormMixin.get_form()`` to be * The backwards compatibility shim to allow ``FormMixin.get_form()`` to be
defined with no default value for its ``form_class`` argument will be removed. 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: .. _deprecation-removed-in-1.9:
1.9 1.9

View File

@ -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 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`` 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, the ``load_template_source()`` method, which takes a ``template_name`` argument,
loads the template from disk (or elsewhere), and returns a tuple: loads the template from disk (or elsewhere), and returns a tuple:
``(template_string, template_origin)``. ``(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 The ``load_template()`` method of the ``Loader`` class retrieves the template
string by calling ``load_template_source()``, instantiates a ``Template`` from string by calling ``load_template_source()``, instantiates a ``Template`` from
the template source, and returns a tuple: ``(template, template_origin)``. Since the template source, and returns a tuple: ``(template, template_origin)``. Since

View File

@ -1029,6 +1029,13 @@ The decorators :func:`~django.test.override_settings` and
class decorators. As a consequence, when overriding ``setUpClass()`` or class decorators. As a consequence, when overriding ``setUpClass()`` or
``tearDownClass()``, the ``super`` implementation should always be called. ``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: .. removed-features-1.8:
Features removed in 1.8 Features removed in 1.8

View File

@ -221,7 +221,7 @@ class TemplateLoaderTests(TestCase):
# We rely on the fact that runtests.py sets up TEMPLATE_DIRS to # We rely on the fact that runtests.py sets up TEMPLATE_DIRS to
# point to a directory containing a login.html file. Also that # point to a directory containing a login.html file. Also that
# the file system and app directories loaders both inherit the # 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. # to test one of them.
load_name = 'login.html' load_name = 'login.html'
template = loader.get_template(load_name) template = loader.get_template(load_name)
@ -306,7 +306,7 @@ class TemplateLoaderTests(TestCase):
def test_extends_include_missing_cachedloader(self): def test_extends_include_missing_cachedloader(self):
""" """
Same as test_extends_include_missing_baseloader, only tests 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.Loader(('',))
cache_loader._cached_loaders = (app_directories.Loader(),) cache_loader._cached_loaders = (app_directories.Loader(),)