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

View File

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

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

View File

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

View File

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

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.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):

View File

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

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

View File

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

View File

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