diff --git a/django/contrib/admindocs/views.py b/django/contrib/admindocs/views.py
index bf706d0307..2c77a24f63 100644
--- a/django/contrib/admindocs/views.py
+++ b/django/contrib/admindocs/views.py
@@ -3,7 +3,6 @@ import inspect
import os
import re
-from django import template
from django.apps import apps
from django.conf import settings
from django.contrib import admin
@@ -13,6 +12,8 @@ from django.core.exceptions import ViewDoesNotExist
from django.http import Http404
from django.core import urlresolvers
from django.contrib.admindocs import utils
+from django.template.base import (builtins, get_library,
+ get_templatetags_modules, InvalidTemplateLibrary, libraries)
from django.template.engine import Engine
from django.utils.decorators import method_decorator
from django.utils._os import upath
@@ -61,8 +62,8 @@ class TemplateTagIndexView(BaseAdminDocsView):
load_all_installed_template_libraries()
tags = []
- app_libs = list(six.iteritems(template.libraries))
- builtin_libs = [(None, lib) for lib in template.builtins]
+ app_libs = list(six.iteritems(libraries))
+ builtin_libs = [(None, lib) for lib in builtins]
for module_name, library in builtin_libs + app_libs:
for tag_name, tag_func in library.tags.items():
title, body, metadata = utils.parse_docstring(tag_func.__doc__)
@@ -72,7 +73,7 @@ class TemplateTagIndexView(BaseAdminDocsView):
body = utils.parse_rst(body, 'tag', _('tag:') + tag_name)
for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'tag', _('tag:') + tag_name)
- if library in template.builtins:
+ if library in builtins:
tag_library = ''
else:
tag_library = module_name.split('.')[-1]
@@ -94,8 +95,8 @@ class TemplateFilterIndexView(BaseAdminDocsView):
load_all_installed_template_libraries()
filters = []
- app_libs = list(six.iteritems(template.libraries))
- builtin_libs = [(None, lib) for lib in template.builtins]
+ app_libs = list(six.iteritems(libraries))
+ builtin_libs = [(None, lib) for lib in builtins]
for module_name, library in builtin_libs + app_libs:
for filter_name, filter_func in library.filters.items():
title, body, metadata = utils.parse_docstring(filter_func.__doc__)
@@ -105,7 +106,7 @@ class TemplateFilterIndexView(BaseAdminDocsView):
body = utils.parse_rst(body, 'filter', _('filter:') + filter_name)
for key in metadata:
metadata[key] = utils.parse_rst(metadata[key], 'filter', _('filter:') + filter_name)
- if library in template.builtins:
+ if library in builtins:
tag_library = ''
else:
tag_library = module_name.split('.')[-1]
@@ -313,7 +314,7 @@ class TemplateDetailView(BaseAdminDocsView):
def load_all_installed_template_libraries():
# Load/register all template tag libraries from installed apps.
- for module_name in template.get_templatetags_modules():
+ for module_name in get_templatetags_modules():
mod = import_module(module_name)
if not hasattr(mod, '__file__'):
# e.g. packages installed as eggs
@@ -330,8 +331,8 @@ def load_all_installed_template_libraries():
else:
for library_name in libraries:
try:
- template.get_library(library_name)
- except template.InvalidTemplateLibrary:
+ get_library(library_name)
+ except InvalidTemplateLibrary:
pass
diff --git a/django/template/__init__.py b/django/template/__init__.py
index a43ff50f01..24a51ac20d 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -1,80 +1,17 @@
-"""
-This is the Django template system.
-
-How it works:
-
-The Lexer.tokenize() function converts a template string (i.e., a string containing
-markup with custom template tags) to tokens, which can be either plain text
-(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
-
-The Parser() class takes a list of tokens in its constructor, and its parse()
-method returns a compiled template -- which is, under the hood, a list of
-Node objects.
-
-Each Node is responsible for creating some sort of output -- e.g. simple text
-(TextNode), variable values in a given context (VariableNode), results of basic
-logic (IfNode), results of looping (ForNode), or anything else. The core Node
-types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can
-define their own custom node types.
-
-Each Node has a render() method, which takes a Context and returns a string of
-the rendered node. For example, the render() method of a Variable Node returns
-the variable's value as a string. The render() method of a ForNode returns the
-rendered output of whatever was inside the loop, recursively.
-
-The Template class is a convenient wrapper that takes care of template
-compilation and rendering.
-
-Usage:
-
-The only thing you should ever use directly in this file is the Template class.
-Create a compiled template object with a template_string, then call render()
-with a context. In the compilation stage, the TemplateSyntaxError exception
-will be raised if the template doesn't have proper syntax.
-
-Sample code:
-
->>> from django import template
->>> s = u'{% if test %}
{{ varvalue }}
{% endif %}'
->>> t = template.Template(s)
-
-(t is now a compiled template, and its render() method can be called multiple
-times with multiple contexts)
-
->>> c = template.Context({'test':True, 'varvalue': 'Hello'})
->>> t.render(c)
-u'Hello
'
->>> c = template.Context({'test':False, 'varvalue': 'Hello'})
->>> t.render(c)
-u''
-"""
-
-# Template lexing symbols
-from django.template.base import (ALLOWED_VARIABLE_CHARS, BLOCK_TAG_END, # NOQA
- BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START,
- FILTER_ARGUMENT_SEPARATOR, FILTER_SEPARATOR, SINGLE_BRACE_END,
- SINGLE_BRACE_START, TOKEN_BLOCK, TOKEN_COMMENT, TOKEN_TEXT, TOKEN_VAR,
- TRANSLATOR_COMMENT_MARK, UNKNOWN_SOURCE, VARIABLE_ATTRIBUTE_SEPARATOR,
- VARIABLE_TAG_END, VARIABLE_TAG_START, filter_re, tag_re)
-
-# Exceptions
-from django.template.base import (ContextPopException, InvalidTemplateLibrary, # NOQA
- TemplateDoesNotExist, TemplateEncodingError, TemplateSyntaxError,
- VariableDoesNotExist)
+# Public exceptions
+from .base import (TemplateDoesNotExist, TemplateSyntaxError, # NOQA
+ VariableDoesNotExist)
+from .context import ContextPopException # NOQA
# Template parts
-from django.template.base import (Context, FilterExpression, Lexer, Node, # NOQA
- NodeList, Parser, RequestContext, Origin, StringOrigin, Template,
- TextNode, Token, TokenParser, Variable, VariableNode, constant_string,
- filter_raw_string)
+from .base import (Context, Node, NodeList, RequestContext, # NOQA
+ StringOrigin, Template, Variable)
-# Compiling templates
-from django.template.base import (resolve_variable, # NOQA
- unescape_string_literal, generic_tag_compiler)
+# Deprecated in Django 1.8, will be removed in Django 2.0.
+from .base import resolve_variable # NOQA
# Library management
-from django.template.base import (Library, add_to_builtins, builtins, # NOQA
- get_library, get_templatetags_modules, get_text_list, import_library,
- libraries)
+from .base import Library # NOQA
+
__all__ = ('Template', 'Context', 'RequestContext')
diff --git a/django/template/base.py b/django/template/base.py
index 4ac20b13e1..2917d8323d 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -1,3 +1,54 @@
+"""
+This is the Django template system.
+
+How it works:
+
+The Lexer.tokenize() function converts a template string (i.e., a string containing
+markup with custom template tags) to tokens, which can be either plain text
+(TOKEN_TEXT), variables (TOKEN_VAR) or block statements (TOKEN_BLOCK).
+
+The Parser() class takes a list of tokens in its constructor, and its parse()
+method returns a compiled template -- which is, under the hood, a list of
+Node objects.
+
+Each Node is responsible for creating some sort of output -- e.g. simple text
+(TextNode), variable values in a given context (VariableNode), results of basic
+logic (IfNode), results of looping (ForNode), or anything else. The core Node
+types are TextNode, VariableNode, IfNode and ForNode, but plugin modules can
+define their own custom node types.
+
+Each Node has a render() method, which takes a Context and returns a string of
+the rendered node. For example, the render() method of a Variable Node returns
+the variable's value as a string. The render() method of a ForNode returns the
+rendered output of whatever was inside the loop, recursively.
+
+The Template class is a convenient wrapper that takes care of template
+compilation and rendering.
+
+Usage:
+
+The only thing you should ever use directly in this file is the Template class.
+Create a compiled template object with a template_string, then call render()
+with a context. In the compilation stage, the TemplateSyntaxError exception
+will be raised if the template doesn't have proper syntax.
+
+Sample code:
+
+>>> from django import template
+>>> s = u'{% if test %}{{ varvalue }}
{% endif %}'
+>>> t = template.Template(s)
+
+(t is now a compiled template, and its render() method can be called multiple
+times with multiple contexts)
+
+>>> c = template.Context({'test':True, 'varvalue': 'Hello'})
+>>> t.render(c)
+u'Hello
'
+>>> c = template.Context({'test':False, 'varvalue': 'Hello'})
+>>> t.render(c)
+u''
+"""
+
from __future__ import unicode_literals
import re
diff --git a/django/templatetags/i18n.py b/django/templatetags/i18n.py
index 0bfb4a9eff..ecaf84a035 100644
--- a/django/templatetags/i18n.py
+++ b/django/templatetags/i18n.py
@@ -3,9 +3,8 @@ import re
import sys
from django.conf import settings
-from django.template import (Node, Variable, TemplateSyntaxError,
- TokenParser, Library, TOKEN_TEXT, TOKEN_VAR)
-from django.template.base import render_value_in_context
+from django.template import Library, Node, TemplateSyntaxError, Variable
+from django.template.base import render_value_in_context, TokenParser, TOKEN_TEXT, TOKEN_VAR
from django.template.defaulttags import token_kwargs
from django.utils import six
from django.utils import translation
diff --git a/django/utils/translation/trans_real.py b/django/utils/translation/trans_real.py
index 2af2f1dd35..1d4d100206 100644
--- a/django/utils/translation/trans_real.py
+++ b/django/utils/translation/trans_real.py
@@ -544,8 +544,8 @@ def templatize(src, origin=None):
does so by translating the Django translation tags into standard gettext
function invocations.
"""
- from django.template import (Lexer, TOKEN_TEXT, TOKEN_VAR, TOKEN_BLOCK,
- TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
+ from django.template.base import (Lexer, TOKEN_TEXT, TOKEN_VAR,
+ TOKEN_BLOCK, TOKEN_COMMENT, TRANSLATOR_COMMENT_MARK)
src = force_text(src, settings.FILE_CHARSET)
out = StringIO('')
message_context = None
diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt
index 6a164821b6..a4bc14332c 100644
--- a/docs/releases/1.8.txt
+++ b/docs/releases/1.8.txt
@@ -832,6 +832,14 @@ Django previously closed database connections between each test within a
``TestCase`` within a transaction. If some of your tests relied on the old
behavior, you should have them inherit from ``TransactionTestCase`` instead.
+Cleanup of the ``django.template`` namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you've been relying on private APIs exposed in the ``django.template``
+module, you may have to import them from ``django.template.base`` instead.
+
+Also ``django.template.base.compile_string()`` was removed.
+
Miscellaneous
~~~~~~~~~~~~~
@@ -906,8 +914,6 @@ Miscellaneous
delete a key if ``set()`` fails. This is necessary to ensure the ``cache_db``
session store always fetches the most current session data.
-* Private API ``django.template.compile_string`` was removed.
-
* Private APIs ``override_template_loaders`` and ``override_with_test_loader``
in ``django.test.utils`` were removed. Override ``TEMPLATE_LOADERS`` with
``override_settings`` instead.
diff --git a/tests/template_tests/test_nodelist.py b/tests/template_tests/test_nodelist.py
index bce75aeecb..836d1b6db9 100644
--- a/tests/template_tests/test_nodelist.py
+++ b/tests/template_tests/test_nodelist.py
@@ -1,6 +1,7 @@
from unittest import TestCase
-from django.template import Context, Template, VariableNode
+from django.template import Context, Template
+from django.template.base import VariableNode
from django.test import override_settings
diff --git a/tests/template_tests/test_parser.py b/tests/template_tests/test_parser.py
index 017e78a863..348428513b 100644
--- a/tests/template_tests/test_parser.py
+++ b/tests/template_tests/test_parser.py
@@ -5,8 +5,8 @@ from __future__ import unicode_literals
from unittest import TestCase
-from django.template import (TokenParser, FilterExpression, Parser, Variable,
- Template, TemplateSyntaxError, Library)
+from django.template import Library, Template, TemplateSyntaxError
+from django.template.base import FilterExpression, Parser, TokenParser, Variable
from django.test import override_settings
from django.utils import six
diff --git a/tests/template_tests/test_unicode.py b/tests/template_tests/test_unicode.py
index 1f333bfed2..02d3d0a51d 100644
--- a/tests/template_tests/test_unicode.py
+++ b/tests/template_tests/test_unicode.py
@@ -3,7 +3,8 @@ from __future__ import unicode_literals
from unittest import TestCase
-from django.template import Template, TemplateEncodingError, Context
+from django.template import Template, Context
+from django.template.base import TemplateEncodingError
from django.utils.safestring import SafeData
from django.utils import six
diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py
index 2ba1cdb18a..80f926ea6f 100644
--- a/tests/template_tests/tests.py
+++ b/tests/template_tests/tests.py
@@ -8,7 +8,8 @@ import unittest
from django import template
from django.contrib.auth.models import Group
from django.core import urlresolvers
-from django.template import loader, Context, RequestContext, Template, TemplateSyntaxError
+from django.template import (base as template_base, loader,
+ Context, RequestContext, Template, TemplateSyntaxError)
from django.template.engine import Engine
from django.template.loaders import app_directories, filesystem
from django.test import RequestFactory, SimpleTestCase
@@ -245,7 +246,7 @@ class TemplateRegressionTests(SimpleTestCase):
def test_token_smart_split(self):
# Regression test for #7027
- token = template.Token(template.TOKEN_BLOCK, 'sometag _("Page not found") value|yesno:_("yes,no")')
+ token = template_base.Token(template_base.TOKEN_BLOCK, 'sometag _("Page not found") value|yesno:_("yes,no")')
split = token.split_contents()
self.assertEqual(split, ["sometag", '_("Page not found")', 'value|yesno:_("yes,no")'])
diff --git a/tests/template_tests/utils.py b/tests/template_tests/utils.py
index 168eca73f8..ba8dcdd46f 100644
--- a/tests/template_tests/utils.py
+++ b/tests/template_tests/utils.py
@@ -6,7 +6,7 @@ import functools
from django import template
from django.template import Library
-from django.template.base import Context
+from django.template.base import Context, libraries
from django.template.engine import Engine
from django.template.loader import get_template
from django.test.utils import override_settings
@@ -100,9 +100,11 @@ def upper(value):
def register_test_tags(func):
@functools.wraps(func)
def inner(self):
- template.libraries['testtags'] = register
- func(self)
- del template.libraries['testtags']
+ libraries['testtags'] = register
+ try:
+ func(self)
+ finally:
+ del libraries['testtags']
return inner