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