summaryrefslogtreecommitdiff
path: root/django/template
diff options
context:
space:
mode:
authordjango-bot <ops@djangoproject.com>2022-02-03 20:24:19 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2022-02-07 20:37:05 +0100
commit9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch)
treef0506b668a013d0063e5fba3dbf4863b466713ba /django/template
parentf68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff)
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'django/template')
-rw-r--r--django/template/__init__.py22
-rw-r--r--django/template/autoreload.py14
-rw-r--r--django/template/backends/base.py16
-rw-r--r--django/template/backends/django.py37
-rw-r--r--django/template/backends/dummy.py24
-rw-r--r--django/template/backends/jinja2.py57
-rw-r--r--django/template/backends/utils.py3
-rw-r--r--django/template/base.py254
-rw-r--r--django/template/context.py30
-rw-r--r--django/template/context_processors.py35
-rw-r--r--django/template/defaultfilters.py169
-rw-r--r--django/template/defaulttags.py338
-rw-r--r--django/template/engine.py55
-rw-r--r--django/template/exceptions.py2
-rw-r--r--django/template/library.py128
-rw-r--r--django/template/loader.py8
-rw-r--r--django/template/loader_tags.py128
-rw-r--r--django/template/loaders/app_directories.py3
-rw-r--r--django/template/loaders/base.py12
-rw-r--r--django/template/loaders/cached.py15
-rw-r--r--django/template/loaders/filesystem.py1
-rw-r--r--django/template/loaders/locmem.py1
-rw-r--r--django/template/response.py41
-rw-r--r--django/template/smartif.py35
-rw-r--r--django/template/utils.py30
25 files changed, 845 insertions, 613 deletions
diff --git a/django/template/__init__.py b/django/template/__init__.py
index 7414d0fef5..adb431c00d 100644
--- a/django/template/__init__.py
+++ b/django/template/__init__.py
@@ -46,26 +46,30 @@ from .utils import EngineHandler
engines = EngineHandler()
-__all__ = ('Engine', 'engines')
+__all__ = ("Engine", "engines")
# Django Template Language
# Public exceptions
-from .base import VariableDoesNotExist # NOQA isort:skip
-from .context import Context, ContextPopException, RequestContext # NOQA isort:skip
-from .exceptions import TemplateDoesNotExist, TemplateSyntaxError # NOQA isort:skip
+from .base import VariableDoesNotExist # NOQA isort:skip
+from .context import Context, ContextPopException, RequestContext # NOQA isort:skip
+from .exceptions import TemplateDoesNotExist, TemplateSyntaxError # NOQA isort:skip
# Template parts
-from .base import ( # NOQA isort:skip
- Node, NodeList, Origin, Template, Variable,
+from .base import ( # NOQA isort:skip
+ Node,
+ NodeList,
+ Origin,
+ Template,
+ Variable,
)
# Library management
-from .library import Library # NOQA isort:skip
+from .library import Library # NOQA isort:skip
# Import the .autoreload module to trigger the registrations of signals.
-from . import autoreload # NOQA isort:skip
+from . import autoreload # NOQA isort:skip
-__all__ += ('Template', 'Context', 'RequestContext')
+__all__ += ("Template", "Context", "RequestContext")
diff --git a/django/template/autoreload.py b/django/template/autoreload.py
index 7242d68f2d..84c8554165 100644
--- a/django/template/autoreload.py
+++ b/django/template/autoreload.py
@@ -4,9 +4,7 @@ from django.dispatch import receiver
from django.template import engines
from django.template.backends.django import DjangoTemplates
from django.utils._os import to_path
-from django.utils.autoreload import (
- autoreload_started, file_changed, is_django_path,
-)
+from django.utils.autoreload import autoreload_started, file_changed, is_django_path
def get_template_directories():
@@ -22,7 +20,7 @@ def get_template_directories():
items.update(cwd / to_path(dir) for dir in backend.engine.dirs)
for loader in backend.engine.template_loaders:
- if not hasattr(loader, 'get_dirs'):
+ if not hasattr(loader, "get_dirs"):
continue
items.update(
cwd / to_path(directory)
@@ -40,15 +38,15 @@ def reset_loaders():
loader.reset()
-@receiver(autoreload_started, dispatch_uid='template_loaders_watch_changes')
+@receiver(autoreload_started, dispatch_uid="template_loaders_watch_changes")
def watch_for_template_changes(sender, **kwargs):
for directory in get_template_directories():
- sender.watch_dir(directory, '**/*')
+ sender.watch_dir(directory, "**/*")
-@receiver(file_changed, dispatch_uid='template_loaders_file_changed')
+@receiver(file_changed, dispatch_uid="template_loaders_file_changed")
def template_changed(sender, file_path, **kwargs):
- if file_path.suffix == '.py':
+ if file_path.suffix == ".py":
return
for template_dir in get_template_directories():
if template_dir in file_path.parents:
diff --git a/django/template/backends/base.py b/django/template/backends/base.py
index f1fa142362..240733e6f4 100644
--- a/django/template/backends/base.py
+++ b/django/template/backends/base.py
@@ -1,6 +1,4 @@
-from django.core.exceptions import (
- ImproperlyConfigured, SuspiciousFileOperation,
-)
+from django.core.exceptions import ImproperlyConfigured, SuspiciousFileOperation
from django.template.utils import get_app_template_dirs
from django.utils._os import safe_join
from django.utils.functional import cached_property
@@ -18,18 +16,20 @@ class BaseEngine:
`params` is a dict of configuration settings.
"""
params = params.copy()
- self.name = params.pop('NAME')
- self.dirs = list(params.pop('DIRS'))
- self.app_dirs = params.pop('APP_DIRS')
+ self.name = params.pop("NAME")
+ self.dirs = list(params.pop("DIRS"))
+ self.app_dirs = params.pop("APP_DIRS")
if params:
raise ImproperlyConfigured(
- "Unknown parameters: {}".format(", ".join(params)))
+ "Unknown parameters: {}".format(", ".join(params))
+ )
@property
def app_dirname(self):
raise ImproperlyConfigured(
"{} doesn't support loading templates from installed "
- "applications.".format(self.__class__.__name__))
+ "applications.".format(self.__class__.__name__)
+ )
def from_string(self, template_code):
"""
diff --git a/django/template/backends/django.py b/django/template/backends/django.py
index 48257e5e4e..218e5e0bc1 100644
--- a/django/template/backends/django.py
+++ b/django/template/backends/django.py
@@ -13,16 +13,16 @@ from .base import BaseEngine
class DjangoTemplates(BaseEngine):
- app_dirname = 'templates'
+ app_dirname = "templates"
def __init__(self, params):
params = params.copy()
- options = params.pop('OPTIONS').copy()
- options.setdefault('autoescape', True)
- options.setdefault('debug', settings.DEBUG)
- options.setdefault('file_charset', 'utf-8')
- libraries = options.get('libraries', {})
- options['libraries'] = self.get_templatetag_libraries(libraries)
+ options = params.pop("OPTIONS").copy()
+ options.setdefault("autoescape", True)
+ options.setdefault("debug", settings.DEBUG)
+ options.setdefault("file_charset", "utf-8")
+ libraries = options.get("libraries", {})
+ options["libraries"] = self.get_templatetag_libraries(libraries)
super().__init__(params)
self.engine = Engine(self.dirs, self.app_dirs, **options)
@@ -46,7 +46,6 @@ class DjangoTemplates(BaseEngine):
class Template:
-
def __init__(self, template, backend):
self.template = template
self.backend = backend
@@ -56,7 +55,9 @@ class Template:
return self.template.origin
def render(self, context=None, request=None):
- context = make_context(context, request, autoescape=self.backend.engine.autoescape)
+ context = make_context(
+ context, request, autoescape=self.backend.engine.autoescape
+ )
try:
return self.template.render(context)
except TemplateDoesNotExist as exc:
@@ -71,7 +72,7 @@ def copy_exception(exc, backend=None):
"""
backend = backend or exc.backend
new = exc.__class__(*exc.args, tried=exc.tried, backend=backend, chain=exc.chain)
- if hasattr(exc, 'template_debug'):
+ if hasattr(exc, "template_debug"):
new.template_debug = exc.template_debug
return new
@@ -89,10 +90,9 @@ def get_template_tag_modules():
Yield (module_name, module_path) pairs for all installed template tag
libraries.
"""
- candidates = ['django.templatetags']
+ candidates = ["django.templatetags"]
candidates.extend(
- f'{app_config.name}.templatetags'
- for app_config in apps.get_app_configs()
+ f"{app_config.name}.templatetags" for app_config in apps.get_app_configs()
)
for candidate in candidates:
@@ -102,9 +102,9 @@ def get_template_tag_modules():
# No templatetags package defined. This is safe to ignore.
continue
- if hasattr(pkg, '__path__'):
+ if hasattr(pkg, "__path__"):
for name in get_package_libraries(pkg):
- yield name[len(candidate) + 1:], name
+ yield name[len(candidate) + 1 :], name
def get_installed_libraries():
@@ -115,8 +115,7 @@ def get_installed_libraries():
django.templatetags.i18n is stored as i18n.
"""
return {
- module_name: full_name
- for module_name, full_name in get_template_tag_modules()
+ module_name: full_name for module_name, full_name in get_template_tag_modules()
}
@@ -125,7 +124,7 @@ def get_package_libraries(pkg):
Recursively yield template tag libraries defined in submodules of a
package.
"""
- for entry in walk_packages(pkg.__path__, pkg.__name__ + '.'):
+ for entry in walk_packages(pkg.__path__, pkg.__name__ + "."):
try:
module = import_module(entry[1])
except ImportError as e:
@@ -134,5 +133,5 @@ def get_package_libraries(pkg):
"trying to load '%s': %s" % (entry[1], e)
) from e
- if hasattr(module, 'register'):
+ if hasattr(module, "register"):
yield entry[1]
diff --git a/django/template/backends/dummy.py b/django/template/backends/dummy.py
index 6be05ca614..692382b6b1 100644
--- a/django/template/backends/dummy.py
+++ b/django/template/backends/dummy.py
@@ -10,14 +10,13 @@ from .utils import csrf_input_lazy, csrf_token_lazy
class TemplateStrings(BaseEngine):
- app_dirname = 'template_strings'
+ app_dirname = "template_strings"
def __init__(self, params):
params = params.copy()
- options = params.pop('OPTIONS').copy()
+ options = params.pop("OPTIONS").copy()
if options:
- raise ImproperlyConfigured(
- "Unknown options: {}".format(", ".join(options)))
+ raise ImproperlyConfigured("Unknown options: {}".format(", ".join(options)))
super().__init__(params)
def from_string(self, template_code):
@@ -27,26 +26,27 @@ class TemplateStrings(BaseEngine):
tried = []
for template_file in self.iter_template_filenames(template_name):
try:
- with open(template_file, encoding='utf-8') as fp:
+ with open(template_file, encoding="utf-8") as fp:
template_code = fp.read()
except FileNotFoundError:
- tried.append((
- Origin(template_file, template_name, self),
- 'Source does not exist',
- ))
+ tried.append(
+ (
+ Origin(template_file, template_name, self),
+ "Source does not exist",
+ )
+ )
else:
return Template(template_code)
raise TemplateDoesNotExist(template_name, tried=tried, backend=self)
class Template(string.Template):
-
def render(self, context=None, request=None):
if context is None:
context = {}
else:
context = {k: conditional_escape(v) for k, v in context.items()}
if request is not None:
- context['csrf_input'] = csrf_input_lazy(request)
- context['csrf_token'] = csrf_token_lazy(request)
+ context["csrf_input"] = csrf_input_lazy(request)
+ context["csrf_token"] = csrf_token_lazy(request)
return self.safe_substitute(context)
diff --git a/django/template/backends/jinja2.py b/django/template/backends/jinja2.py
index f0540d389c..199d62b429 100644
--- a/django/template/backends/jinja2.py
+++ b/django/template/backends/jinja2.py
@@ -12,24 +12,25 @@ from .base import BaseEngine
class Jinja2(BaseEngine):
- app_dirname = 'jinja2'
+ app_dirname = "jinja2"
def __init__(self, params):
params = params.copy()
- options = params.pop('OPTIONS').copy()
+ options = params.pop("OPTIONS").copy()
super().__init__(params)
- self.context_processors = options.pop('context_processors', [])
+ self.context_processors = options.pop("context_processors", [])
- environment = options.pop('environment', 'jinja2.Environment')
+ environment = options.pop("environment", "jinja2.Environment")
environment_cls = import_string(environment)
- if 'loader' not in options:
- options['loader'] = jinja2.FileSystemLoader(self.template_dirs)
- options.setdefault('autoescape', True)
- options.setdefault('auto_reload', settings.DEBUG)
- options.setdefault('undefined',
- jinja2.DebugUndefined if settings.DEBUG else jinja2.Undefined)
+ if "loader" not in options:
+ options["loader"] = jinja2.FileSystemLoader(self.template_dirs)
+ options.setdefault("autoescape", True)
+ options.setdefault("auto_reload", settings.DEBUG)
+ options.setdefault(
+ "undefined", jinja2.DebugUndefined if settings.DEBUG else jinja2.Undefined
+ )
self.env = environment_cls(**options)
@@ -52,22 +53,23 @@ class Jinja2(BaseEngine):
class Template:
-
def __init__(self, template, backend):
self.template = template
self.backend = backend
self.origin = Origin(
- name=template.filename, template_name=template.name,
+ name=template.filename,
+ template_name=template.name,
)
def render(self, context=None, request=None):
from .utils import csrf_input_lazy, csrf_token_lazy
+
if context is None:
context = {}
if request is not None:
- context['request'] = request
- context['csrf_input'] = csrf_input_lazy(request)
- context['csrf_token'] = csrf_token_lazy(request)
+ context["request"] = request
+ context["csrf_input"] = csrf_input_lazy(request)
+ context["csrf_token"] = csrf_token_lazy(request)
for context_processor in self.backend.template_context_processors:
context.update(context_processor(request))
try:
@@ -83,6 +85,7 @@ class Origin:
A container to hold debug information as described in the template API
documentation.
"""
+
def __init__(self, name, template_name):
self.name = name
self.template_name = template_name
@@ -101,24 +104,24 @@ def get_exception_info(exception):
if exception_file.exists():
source = exception_file.read_text()
if source is not None:
- lines = list(enumerate(source.strip().split('\n'), start=1))
+ lines = list(enumerate(source.strip().split("\n"), start=1))
during = lines[lineno - 1][1]
total = len(lines)
top = max(0, lineno - context_lines - 1)
bottom = min(total, lineno + context_lines)
else:
- during = ''
+ during = ""
lines = []
total = top = bottom = 0
return {
- 'name': exception.filename,
- 'message': exception.message,
- 'source_lines': lines[top:bottom],
- 'line': lineno,
- 'before': '',
- 'during': during,
- 'after': '',
- 'total': total,
- 'top': top,
- 'bottom': bottom,
+ "name": exception.filename,
+ "message": exception.message,
+ "source_lines": lines[top:bottom],
+ "line": lineno,
+ "before": "",
+ "during": during,
+ "after": "",
+ "total": total,
+ "top": top,
+ "bottom": bottom,
}
diff --git a/django/template/backends/utils.py b/django/template/backends/utils.py
index 1396ae7095..880959d6a1 100644
--- a/django/template/backends/utils.py
+++ b/django/template/backends/utils.py
@@ -7,7 +7,8 @@ from django.utils.safestring import SafeString
def csrf_input(request):
return format_html(
'<input type="hidden" name="csrfmiddlewaretoken" value="{}">',
- get_token(request))
+ get_token(request),
+ )
csrf_input_lazy = lazy(csrf_input, SafeString, str)
diff --git a/django/template/base.py b/django/template/base.py
index caadf89970..00f8283507 100644
--- a/django/template/base.py
+++ b/django/template/base.py
@@ -60,37 +60,35 @@ from django.utils.formats import localize
from django.utils.html import conditional_escape, escape
from django.utils.regex_helper import _lazy_re_compile
from django.utils.safestring import SafeData, SafeString, mark_safe
-from django.utils.text import (
- get_text_list, smart_split, unescape_string_literal,
-)
+from django.utils.text import get_text_list, smart_split, unescape_string_literal
from django.utils.timezone import template_localtime
from django.utils.translation import gettext_lazy, pgettext_lazy
from .exceptions import TemplateSyntaxError
# template syntax constants
-FILTER_SEPARATOR = '|'
-FILTER_ARGUMENT_SEPARATOR = ':'
-VARIABLE_ATTRIBUTE_SEPARATOR = '.'
-BLOCK_TAG_START = '{%'
-BLOCK_TAG_END = '%}'
-VARIABLE_TAG_START = '{{'
-VARIABLE_TAG_END = '}}'
-COMMENT_TAG_START = '{#'
-COMMENT_TAG_END = '#}'
-SINGLE_BRACE_START = '{'
-SINGLE_BRACE_END = '}'
+FILTER_SEPARATOR = "|"
+FILTER_ARGUMENT_SEPARATOR = ":"
+VARIABLE_ATTRIBUTE_SEPARATOR = "."
+BLOCK_TAG_START = "{%"
+BLOCK_TAG_END = "%}"
+VARIABLE_TAG_START = "{{"
+VARIABLE_TAG_END = "}}"
+COMMENT_TAG_START = "{#"
+COMMENT_TAG_END = "#}"
+SINGLE_BRACE_START = "{"
+SINGLE_BRACE_END = "}"
# what to report as the origin for templates that come from non-loader sources
# (e.g. strings)
-UNKNOWN_SOURCE = '<unknown source>'
+UNKNOWN_SOURCE = "<unknown source>"
# Match BLOCK_TAG_*, VARIABLE_TAG_*, and COMMENT_TAG_* tags and capture the
# entire tag, including start/end delimiters. Using re.compile() is faster
# than instantiating SimpleLazyObject with _lazy_re_compile().
-tag_re = re.compile(r'({%.*?%}|{{.*?}}|{#.*?#})')
+tag_re = re.compile(r"({%.*?%}|{{.*?}}|{#.*?#})")
-logger = logging.getLogger('django.template')
+logger = logging.getLogger("django.template")
class TokenType(Enum):
@@ -101,7 +99,6 @@ class TokenType(Enum):
class VariableDoesNotExist(Exception):
-
def __init__(self, msg, params=()):
self.msg = msg
self.params = params
@@ -120,20 +117,21 @@ class Origin:
return self.name
def __repr__(self):
- return '<%s name=%r>' % (self.__class__.__qualname__, self.name)
+ return "<%s name=%r>" % (self.__class__.__qualname__, self.name)
def __eq__(self, other):
return (
- isinstance(other, Origin) and
- self.name == other.name and
- self.loader == other.loader
+ isinstance(other, Origin)
+ and self.name == other.name
+ and self.loader == other.loader
)
@property
def loader_name(self):
if self.loader:
- return '%s.%s' % (
- self.loader.__module__, self.loader.__class__.__name__,
+ return "%s.%s" % (
+ self.loader.__module__,
+ self.loader.__class__.__name__,
)
@@ -145,6 +143,7 @@ class Template:
# e.g. Template('...').render(Context({...}))
if engine is None:
from .engine import Engine
+
engine = Engine.get_default()
if origin is None:
origin = Origin(UNKNOWN_SOURCE)
@@ -161,7 +160,7 @@ class Template:
def __repr__(self):
return '<%s template_string="%s...">' % (
self.__class__.__qualname__,
- self.source[:20].replace('\n', ''),
+ self.source[:20].replace("\n", ""),
)
def _render(self, context):
@@ -191,7 +190,9 @@ class Template:
tokens = lexer.tokenize()
parser = Parser(
- tokens, self.engine.template_libraries, self.engine.template_builtins,
+ tokens,
+ self.engine.template_libraries,
+ self.engine.template_builtins,
self.origin,
)
@@ -263,30 +264,30 @@ class Template:
try:
message = str(exception.args[0])
except (IndexError, UnicodeDecodeError):
- message = '(Could not get exception message)'
+ message = "(Could not get exception message)"
return {
- 'message': message,
- 'source_lines': source_lines[top:bottom],
- 'before': before,
- 'during': during,
- 'after': after,
- 'top': top,
- 'bottom': bottom,
- 'total': total,
- 'line': line,
- 'name': self.origin.name,
- 'start': start,
- 'end': end,
+ "message": message,
+ "source_lines": source_lines[top:bottom],
+ "before": before,
+ "during": during,
+ "after": after,
+ "top": top,
+ "bottom": bottom,
+ "total": total,
+ "line": line,
+ "name": self.origin.name,
+ "start": start,
+ "end": end,
}
def linebreak_iter(template_source):
yield 0
- p = template_source.find('\n')
+ p = template_source.find("\n")
while p >= 0:
yield p + 1
- p = template_source.find('\n', p + 1)
+ p = template_source.find("\n", p + 1)
yield len(template_source) + 1
@@ -316,8 +317,10 @@ class Token:
def __repr__(self):
token_name = self.token_type.name.capitalize()
- return ('<%s token: "%s...">' %
- (token_name, self.contents[:20].replace('\n', '')))
+ return '<%s token: "%s...">' % (
+ token_name,
+ self.contents[:20].replace("\n", ""),
+ )
def split_contents(self):
split = []
@@ -325,12 +328,12 @@ class Token:
for bit in bits:
# Handle translation-marked template pieces
if bit.startswith(('_("', "_('")):
- sentinel = bit[2] + ')'
+ sentinel = bit[2] + ")"
trans_bit = [bit]
while not bit.endswith(sentinel):
bit = next(bits)
trans_bit.append(bit)
- bit = ' '.join(trans_bit)
+ bit = " ".join(trans_bit)
split.append(bit)
return split
@@ -343,7 +346,7 @@ class Lexer:
def __repr__(self):
return '<%s template_string="%s...", verbatim=%s>' % (
self.__class__.__qualname__,
- self.template_string[:20].replace('\n', ''),
+ self.template_string[:20].replace("\n", ""),
self.verbatim,
)
@@ -357,7 +360,7 @@ class Lexer:
for token_string in tag_re.split(self.template_string):
if token_string:
result.append(self.create_token(token_string, None, lineno, in_tag))
- lineno += token_string.count('\n')
+ lineno += token_string.count("\n")
in_tag = not in_tag
return result
@@ -382,9 +385,9 @@ class Lexer:
return Token(TokenType.TEXT, token_string, position, lineno)
# Otherwise, the current verbatim block is ending.
self.verbatim = False
- elif content[:9] in ('verbatim', 'verbatim '):
+ elif content[:9] in ("verbatim", "verbatim "):
# Then a verbatim block is starting.
- self.verbatim = 'end%s' % content
+ self.verbatim = "end%s" % content
return Token(TokenType.BLOCK, content, position, lineno)
if not self.verbatim:
content = token_string[2:-2].strip()
@@ -425,7 +428,7 @@ class DebugLexer(Lexer):
for token_string, position in self._tag_re_split():
if token_string:
result.append(self.create_token(token_string, position, lineno, in_tag))
- lineno += token_string.count('\n')
+ lineno += token_string.count("\n")
in_tag = not in_tag
return result
@@ -450,7 +453,7 @@ class Parser:
self.origin = origin
def __repr__(self):
- return '<%s tokens=%r>' % (self.__class__.__qualname__, self.tokens)
+ return "<%s tokens=%r>" % (self.__class__.__qualname__, self.tokens)
def parse(self, parse_until=None):
"""
@@ -472,7 +475,9 @@ class Parser:
self.extend_nodelist(nodelist, TextNode(token.contents), token)
elif token_type == 1: # TokenType.VAR
if not token.contents:
- raise self.error(token, 'Empty variable tag on line %d' % token.lineno)
+ raise self.error(
+ token, "Empty variable tag on line %d" % token.lineno
+ )
try:
filter_expression = self.compile_filter(token.contents)
except TemplateSyntaxError as e:
@@ -483,7 +488,7 @@ class Parser:
try:
command = token.contents.split()[0]
except IndexError:
- raise self.error(token, 'Empty block tag on line %d' % token.lineno)
+ raise self.error(token, "Empty block tag on line %d" % token.lineno)
if command in parse_until:
# A matching token has been reached. Return control to
# the caller. Put the token back on the token list so the
@@ -524,7 +529,8 @@ class Parser:
# Check that non-text nodes don't appear before an extends tag.
if node.must_be_first and nodelist.contains_nontext:
raise self.error(
- token, '%r must be the first tag in the template.' % node,
+ token,
+ "%r must be the first tag in the template." % node,
)
if not isinstance(node, TextNode):
nodelist.contains_nontext = True
@@ -543,7 +549,7 @@ class Parser:
"""
if not isinstance(e, Exception):
e = TemplateSyntaxError(e)
- if not hasattr(e, 'token'):
+ if not hasattr(e, "token"):
e.token = token
return e
@@ -552,16 +558,17 @@ class Parser:
raise self.error(
token,
"Invalid block tag on line %d: '%s', expected %s. Did you "
- "forget to register or load this tag?" % (
+ "forget to register or load this tag?"
+ % (
token.lineno,
command,
- get_text_list(["'%s'" % p for p in parse_until], 'or'),
+ get_text_list(["'%s'" % p for p in parse_until], "or"),
),
)
raise self.error(
token,
"Invalid block tag on line %d: '%s'. Did you forget to register "
- "or load this tag?" % (token.lineno, command)
+ "or load this tag?" % (token.lineno, command),
)
def unclosed_block_tag(self, parse_until):
@@ -569,7 +576,7 @@ class Parser:
msg = "Unclosed tag on line %d: '%s'. Looking for one of: %s." % (
token.lineno,
command,
- ', '.join(parse_until),
+ ", ".join(parse_until),
)
raise self.error(token, msg)
@@ -608,10 +615,10 @@ constant_string = r"""
%(strdq)s|
%(strsq)s)
""" % {
- 'strdq': r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string
- 'strsq': r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string
- 'i18n_open': re.escape("_("),
- 'i18n_close': re.escape(")"),
+ "strdq": r'"[^"\\]*(?:\\.[^"\\]*)*"', # double-quoted string
+ "strsq": r"'[^'\\]*(?:\\.[^'\\]*)*'", # single-quoted string
+ "i18n_open": re.escape("_("),
+ "i18n_close": re.escape(")"),
}
constant_string = constant_string.replace("\n", "")
@@ -627,11 +634,11 @@ filter_raw_string = r"""
)
)?
)""" % {
- 'constant': constant_string,
- 'num': r'[-+\.]?\d[\d\.e]*',
- 'var_chars': r'\w\.',
- 'filter_sep': re.escape(FILTER_SEPARATOR),
- 'arg_sep': re.escape(FILTER_ARGUMENT_SEPARATOR),
+ "constant": constant_string,
+ "num": r"[-+\.]?\d[\d\.e]*",
+ "var_chars": r"\w\.",
+ "filter_sep": re.escape(FILTER_SEPARATOR),
+ "arg_sep": re.escape(FILTER_ARGUMENT_SEPARATOR),
}
filter_re = _lazy_re_compile(filter_raw_string, re.VERBOSE)
@@ -652,7 +659,7 @@ class FilterExpression:
<Variable: 'variable'>
"""
- __slots__ = ('token', 'filters', 'var', 'is_var')
+ __slots__ = ("token", "filters", "var", "is_var")
def __init__(self, token, parser):
self.token = token
@@ -663,12 +670,12 @@ class FilterExpression:
for match in matches:
start = match.start()
if upto != start:
- raise TemplateSyntaxError("Could not parse some characters: "
- "%s|%s|%s" %
- (token[:upto], token[upto:start],
- token[start:]))
+ raise TemplateSyntaxError(
+ "Could not parse some characters: "
+ "%s|%s|%s" % (token[:upto], token[upto:start], token[start:])
+ )
if var_obj is None:
- var, constant = match['var'], match['constant']
+ var, constant = match["var"], match["constant"]
if constant:
try:
var_obj = Variable(constant).resolve({})
@@ -681,9 +688,9 @@ class FilterExpression:
else:
var_obj = Variable(var)
else:
- filter_name = match['filter_name']
+ filter_name = match["filter_name"]
args = []
- constant_arg, var_arg = match['constant_arg'], match['var_arg']
+ constant_arg, var_arg = match["constant_arg"], match["var_arg"]
if constant_arg:
args.append((False, Variable(constant_arg).resolve({})))
elif var_arg:
@@ -693,8 +700,10 @@ class FilterExpression:
filters.append((filter_func, args))
upto = match.end()
if upto != len(token):
- raise TemplateSyntaxError("Could not parse the remainder: '%s' "
- "from '%s'" % (token[upto:], token))
+ raise TemplateSyntaxError(
+ "Could not parse the remainder: '%s' "
+ "from '%s'" % (token[upto:], token)
+ )
self.filters = filters
self.var = var_obj
@@ -710,7 +719,7 @@ class FilterExpression:
else:
string_if_invalid = context.template.engine.string_if_invalid
if string_if_invalid:
- if '%s' in string_if_invalid:
+ if "%s" in string_if_invalid:
return string_if_invalid % self.var
else:
return string_if_invalid
@@ -725,13 +734,13 @@ class FilterExpression:
arg_vals.append(mark_safe(arg))
else:
arg_vals.append(arg.resolve(context))
- if getattr(func, 'expects_localtime', False):
+ if getattr(func, "expects_localtime", False):
obj = template_localtime(obj, context.use_tz)
- if getattr(func, 'needs_autoescape', False):
+ if getattr(func, "needs_autoescape", False):
new_obj = func(obj, autoescape=context.autoescape, *arg_vals)
else:
new_obj = func(obj, *arg_vals)
- if getattr(func, 'is_safe', False) and isinstance(obj, SafeData):
+ if getattr(func, "is_safe", False) and isinstance(obj, SafeData):
obj = mark_safe(new_obj)
else:
obj = new_obj
@@ -749,10 +758,12 @@ class FilterExpression:
dlen = len(defaults or [])
# Not enough OR Too many
if plen < (alen - dlen) or plen > alen:
- raise TemplateSyntaxError("%s requires %d arguments, %d provided" %
- (name, alen - dlen, plen))
+ raise TemplateSyntaxError(
+ "%s requires %d arguments, %d provided" % (name, alen - dlen, plen)
+ )
return True
+
args_check = staticmethod(args_check)
def __str__(self):
@@ -781,7 +792,7 @@ class Variable:
(The example assumes VARIABLE_ATTRIBUTE_SEPARATOR is '.')
"""
- __slots__ = ('var', 'literal', 'lookups', 'translate', 'message_context')
+ __slots__ = ("var", "literal", "lookups", "translate", "message_context")
def __init__(self, var):
self.var = var
@@ -791,8 +802,7 @@ class Variable:
self.message_context = None
if not isinstance(var, str):
- raise TypeError(
- "Variable must be a string or number, got %s" % type(var))
+ raise TypeError("Variable must be a string or number, got %s" % type(var))
try:
# First try to treat this variable as a number.
#
@@ -802,16 +812,16 @@ class Variable:
# Try to interpret values containing a period or an 'e'/'E'
# (possibly scientific notation) as a float; otherwise, try int.
- if '.' in var or 'e' in var.lower():
+ if "." in var or "e" in var.lower():
self.literal = float(var)
# "2." is invalid
- if var[-1] == '.':
+ if var[-1] == ".":
raise ValueError
else:
self.literal = int(var)
except ValueError:
# A ValueError means that the variable isn't a number.
- if var[0:2] == '_(' and var[-1] == ')':
+ if var[0:2] == "_(" and var[-1] == ")":
# The result of the lookup should be translated at rendering
# time.
self.translate = True
@@ -823,10 +833,11 @@ class Variable:
except ValueError:
# Otherwise we'll set self.lookups so that resolve() knows we're
# dealing with a bonafide variable
- if VARIABLE_ATTRIBUTE_SEPARATOR + '_' in var or var[0] == '_':
- raise TemplateSyntaxError("Variables and attributes may "
- "not begin with underscores: '%s'" %
- var)
+ if VARIABLE_ATTRIBUTE_SEPARATOR + "_" in var or var[0] == "_":
+ raise TemplateSyntaxError(
+ "Variables and attributes may "
+ "not begin with underscores: '%s'" % var
+ )
self.lookups = tuple(var.split(VARIABLE_ATTRIBUTE_SEPARATOR))
def resolve(self, context):
@@ -839,7 +850,7 @@ class Variable:
value = self.literal
if self.translate:
is_safe = isinstance(value, SafeData)
- msgid = value.replace('%', '%%')
+ msgid = value.replace("%", "%%")
msgid = mark_safe(msgid) if is_safe else msgid
if self.message_context:
return pgettext_lazy(self.message_context, msgid)
@@ -872,7 +883,9 @@ class Variable:
except (TypeError, AttributeError, KeyError, ValueError, IndexError):
try: # attribute lookup
# Don't return class attributes if the class is the context:
- if isinstance(current, BaseContext) and getattr(type(current), bit):
+ if isinstance(current, BaseContext) and getattr(
+ type(current), bit
+ ):
raise AttributeError
current = getattr(current, bit)
except (TypeError, AttributeError):
@@ -881,18 +894,20 @@ class Variable:
raise
try: # list-index lookup
current = current[int(bit)]
- except (IndexError, # list index out of range
- ValueError, # invalid literal for int()
- KeyError, # current is a dict without `int(bit)` key
- TypeError): # unsubscriptable object
+ except (
+ IndexError, # list index out of range
+ ValueError, # invalid literal for int()
+ KeyError, # current is a dict without `int(bit)` key
+ TypeError,
+ ): # unsubscriptable object
raise VariableDoesNotExist(
"Failed lookup for key [%s] in %r",
(bit, current),
) # missing attribute
if callable(current):
- if getattr(current, 'do_not_call_in_templates', False):
+ if getattr(current, "do_not_call_in_templates", False):
pass
- elif getattr(current, 'alters_data', False):
+ elif getattr(current, "alters_data", False):
current = context.template.engine.string_if_invalid
else:
try: # method call (assuming no args required)
@@ -902,11 +917,13 @@ class Variable:
try:
signature.bind()
except TypeError: # arguments *were* required
- current = context.template.engine.string_if_invalid # invalid method call
+ current = (
+ context.template.engine.string_if_invalid
+ ) # invalid method call
else:
raise
except Exception as e:
- template_name = getattr(context, 'template_name', None) or 'unknown'
+ template_name = getattr(context, "template_name", None) or "unknown"
logger.debug(
"Exception while resolving variable '%s' in template '%s'.",
bit,
@@ -914,7 +931,7 @@ class Variable:
exc_info=True,
)
- if getattr(e, 'silent_variable_failure', False):
+ if getattr(e, "silent_variable_failure", False):
current = context.template.engine.string_if_invalid
else:
raise
@@ -926,7 +943,7 @@ class Node:
# Set this to True for nodes that must be first in the template (although
# they can be preceded by text nodes.
must_be_first = False
- child_nodelists = ('nodelist',)
+ child_nodelists = ("nodelist",)
token = None
def render(self, context):
@@ -947,14 +964,17 @@ class Node:
except Exception as e:
if context.template.engine.debug:
# Store the actual node that caused the exception.
- if not hasattr(e, '_culprit_node'):
+ if not hasattr(e, "_culprit_node"):
e._culprit_node = self
if (
- not hasattr(e, 'template_debug') and
- context.render_context.template.origin == e._culprit_node.origin
+ not hasattr(e, "template_debug")
+ and context.render_context.template.origin == e._culprit_node.origin
):
- e.template_debug = context.render_context.template.get_exception_info(
- e, e._culprit_node.token,
+ e.template_debug = (
+ context.render_context.template.get_exception_info(
+ e,
+ e._culprit_node.token,
+ )
)
raise
@@ -982,9 +1002,7 @@ class NodeList(list):
contains_nontext = False
def render(self, context):
- return SafeString(''.join([
- node.render_annotated(context) for node in self
- ]))
+ return SafeString("".join([node.render_annotated(context) for node in self]))
def get_nodes_by_type(self, nodetype):
"Return a list of all nodes of the given type"
@@ -1048,7 +1066,7 @@ class VariableNode(Node):
# Unicode conversion can fail sometimes for reasons out of our
# control (e.g. exception rendering). In that case, we fail
# quietly.
- return ''
+ return ""
return render_value_in_context(output, context)
@@ -1079,7 +1097,7 @@ def token_kwargs(bits, parser, support_legacy=False):
if not kwarg_format:
if not support_legacy:
return {}
- if len(bits) < 3 or bits[1] != 'as':
+ if len(bits) < 3 or bits[1] != "as":
return {}
kwargs = {}
@@ -1091,13 +1109,13 @@ def token_kwargs(bits, parser, support_legacy=False):
key, value = match.groups()
del bits[:1]
else:
- if len(bits) < 3 or bits[1] != 'as':
+ if len(bits) < 3 or bits[1] != "as":
return kwargs
key, value = bits[2], bits[0]
del bits[:3]
kwargs[key] = parser.compile_filter(value)
if bits and not kwarg_format:
- if bits[0] != 'and':
+ if bits[0] != "and":
return kwargs
del bits[:1]
return kwargs
diff --git a/django/template/context.py b/django/template/context.py
index f0a0cf2a00..ccf0b430dc 100644
--- a/django/template/context.py
+++ b/django/template/context.py
@@ -2,7 +2,7 @@ from contextlib import contextmanager
from copy import copy
# Hard-coded processor for easier use of CSRF protection.
-_builtin_context_processors = ('django.template.context_processors.csrf',)
+_builtin_context_processors = ("django.template.context_processors.csrf",)
class ContextPopException(Exception):
@@ -29,7 +29,7 @@ class BaseContext:
self._reset_dicts(dict_)
def _reset_dicts(self, value=None):
- builtins = {'True': True, 'False': False, 'None': None}
+ builtins = {"True": True, "False": False, "None": None}
self.dicts = [builtins]
if value is not None:
self.dicts.append(value)
@@ -132,6 +132,7 @@ class BaseContext:
class Context(BaseContext):
"A stack container for variable context"
+
def __init__(self, dict_=None, autoescape=True, use_l10n=None, use_tz=None):
self.autoescape = autoescape
self.use_l10n = use_l10n
@@ -160,8 +161,8 @@ class Context(BaseContext):
def update(self, other_dict):
"Push other_dict to the stack of dictionaries in the Context"
- if not hasattr(other_dict, '__getitem__'):
- raise TypeError('other_dict must be a mapping (dictionary-like) object.')
+ if not hasattr(other_dict, "__getitem__"):
+ raise TypeError("other_dict must be a mapping (dictionary-like) object.")
if isinstance(other_dict, BaseContext):
other_dict = other_dict.dicts[1:].pop()
return ContextDict(self, other_dict)
@@ -182,6 +183,7 @@ class RenderContext(BaseContext):
rendering of other templates as they would if they were stored in the normal
template context.
"""
+
template = None
def __iter__(self):
@@ -217,7 +219,16 @@ class RequestContext(Context):
Additional processors can be specified as a list of callables
using the "processors" keyword argument.
"""
- def __init__(self, request, dict_=None, processors=None, use_l10n=None, use_tz=None, autoescape=True):
+
+ def __init__(
+ self,
+ request,
+ dict_=None,
+ processors=None,
+ use_l10n=None,
+ use_tz=None,
+ autoescape=True,
+ ):
super().__init__(dict_, use_l10n=use_l10n, use_tz=use_tz, autoescape=autoescape)
self.request = request
self._processors = () if processors is None else tuple(processors)
@@ -237,8 +248,7 @@ class RequestContext(Context):
self.template = template
# Set context processors according to the template engine's settings.
- processors = (template.engine.template_context_processors +
- self._processors)
+ processors = template.engine.template_context_processors + self._processors
updates = {}
for processor in processors:
updates.update(processor(self.request))
@@ -255,7 +265,7 @@ class RequestContext(Context):
new_context = super().new(values)
# This is for backwards-compatibility: RequestContexts created via
# Context.new don't include values from context processors.
- if hasattr(new_context, '_processors_index'):
+ if hasattr(new_context, "_processors_index"):
del new_context._processors_index
return new_context
@@ -265,7 +275,9 @@ def make_context(context, request=None, **kwargs):
Create a suitable Context from a plain dict and optionally an HttpRequest.
"""
if context is not None and not isinstance(context, dict):
- raise TypeError('context must be a dict rather than %s.' % context.__class__.__name__)
+ raise TypeError(
+ "context must be a dict rather than %s." % context.__class__.__name__
+ )
if request is None:
context = Context(context, **kwargs)
else:
diff --git a/django/template/context_processors.py b/django/template/context_processors.py
index 25ac1f2661..32753032fc 100644
--- a/django/template/context_processors.py
+++ b/django/template/context_processors.py
@@ -19,17 +19,18 @@ def csrf(request):
Context processor that provides a CSRF token, or the string 'NOTPROVIDED' if
it has not been provided by either a view decorator or the middleware
"""
+
def _get_val():
token = get_token(request)
if token is None:
# In order to be able to provide debugging info in the
# case of misconfiguration, we use a sentinel value
# instead of returning an empty dict.
- return 'NOTPROVIDED'
+ return "NOTPROVIDED"
else:
return token
- return {'csrf_token': SimpleLazyObject(_get_val)}
+ return {"csrf_token": SimpleLazyObject(_get_val)}
def debug(request):
@@ -37,46 +38,52 @@ def debug(request):
Return context variables helpful for debugging.
"""
context_extras = {}
- if settings.DEBUG and request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS:
- context_extras['debug'] = True
+ if settings.DEBUG and request.META.get("REMOTE_ADDR") in settings.INTERNAL_IPS:
+ context_extras["debug"] = True
from django.db import connections
# Return a lazy reference that computes connection.queries on access,
# to ensure it contains queries triggered after this function runs.
- context_extras['sql_queries'] = lazy(
- lambda: list(itertools.chain.from_iterable(connections[x].queries for x in connections)),
- list
+ context_extras["sql_queries"] = lazy(
+ lambda: list(
+ itertools.chain.from_iterable(
+ connections[x].queries for x in connections
+ )
+ ),
+ list,
)
return context_extras
def i18n(request):
from django.utils import translation
+
return {
- 'LANGUAGES': settings.LANGUAGES,
- 'LANGUAGE_CODE': translation.get_language(),
- 'LANGUAGE_BIDI': translation.get_language_bidi(),
+ "LANGUAGES": settings.LANGUAGES,
+ "LANGUAGE_CODE": translation.get_language(),
+ "LANGUAGE_BIDI": translation.get_language_bidi(),
}
def tz(request):
from django.utils import timezone
- return {'TIME_ZONE': timezone.get_current_timezone_name()}
+
+ return {"TIME_ZONE": timezone.get_current_timezone_name()}
def static(request):
"""
Add static-related context variables to the context.
"""
- return {'STATIC_URL': settings.STATIC_URL}
+ return {"STATIC_URL": settings.STATIC_URL}
def media(request):
"""
Add media-related context variables to the context.
"""
- return {'MEDIA_URL': settings.MEDIA_URL}
+ return {"MEDIA_URL": settings.MEDIA_URL}
def request(request):
- return {'request': request}
+ return {"request": request}
diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py
index f78a96e3eb..46334791c6 100644
--- a/django/template/defaultfilters.py
+++ b/django/template/defaultfilters.py
@@ -12,14 +12,14 @@ from urllib.parse import quote
from django.utils import formats
from django.utils.dateformat import format, time_format
from django.utils.encoding import iri_to_uri
-from django.utils.html import (
- avoid_wrapping, conditional_escape, escape, escapejs,
- json_script as _json_script, linebreaks, strip_tags, urlize as _urlize,
-)
+from django.utils.html import avoid_wrapping, conditional_escape, escape, escapejs
+from django.utils.html import json_script as _json_script
+from django.utils.html import linebreaks, strip_tags
+from django.utils.html import urlize as _urlize
from django.utils.safestring import SafeData, mark_safe
-from django.utils.text import (
- Truncator, normalize_newlines, phone2numeric, slugify as _slugify, wrap,
-)
+from django.utils.text import Truncator, normalize_newlines, phone2numeric
+from django.utils.text import slugify as _slugify
+from django.utils.text import wrap
from django.utils.timesince import timesince, timeuntil
from django.utils.translation import gettext, ngettext
@@ -33,16 +33,18 @@ register = Library()
# STRING DECORATOR #
#######################
+
def stringfilter(func):
"""
Decorator for filters which should only receive strings. The object
passed as the first positional argument will be converted to a string.
"""
+
@wraps(func)
def _dec(first, *args, **kwargs):
first = str(first)
result = func(first, *args, **kwargs)
- if isinstance(first, SafeData) and getattr(unwrap(func), 'is_safe', False):
+ if isinstance(first, SafeData) and getattr(unwrap(func), "is_safe", False):
result = mark_safe(result)
return result
@@ -53,6 +55,7 @@ def stringfilter(func):
# STRINGS #
###################
+
@register.filter(is_safe=True)
@stringfilter
def addslashes(value):
@@ -61,7 +64,7 @@ def addslashes(value):
example. Less useful for escaping JavaScript; use the ``escapejs``
filter instead.
"""
- return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'")
+ return value.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'")
@register.filter(is_safe=True)
@@ -135,14 +138,14 @@ def floatformat(text, arg=-1):
use_l10n = True
if isinstance(arg, str):
last_char = arg[-1]
- if arg[-2:] in {'gu', 'ug'}:
+ if arg[-2:] in {"gu", "ug"}:
force_grouping = True
use_l10n = False
arg = arg[:-2] or -1
- elif last_char == 'g':
+ elif last_char == "g":
force_grouping = True
arg = arg[:-1] or -1
- elif last_char == 'u':
+ elif last_char == "u":
use_l10n = False
arg = arg[:-1] or -1
try:
@@ -152,7 +155,7 @@ def floatformat(text, arg=-1):
try:
d = Decimal(str(float(text)))
except (ValueError, InvalidOperation, TypeError):
- return ''
+ return ""
try:
p = int(arg)
except ValueError:
@@ -164,12 +167,14 @@ def floatformat(text, arg=-1):
return input_val
if not m and p < 0:
- return mark_safe(formats.number_format(
- '%d' % (int(d)),
- 0,
- use_l10n=use_l10n,
- force_grouping=force_grouping,
- ))
+ return mark_safe(
+ formats.number_format(
+ "%d" % (int(d)),
+ 0,
+ use_l10n=use_l10n,
+ force_grouping=force_grouping,
+ )
+ )
exp = Decimal(1).scaleb(-abs(p))
# Set the precision high enough to avoid an exception (#15789).
@@ -184,17 +189,19 @@ def floatformat(text, arg=-1):
sign, digits, exponent = rounded_d.as_tuple()
digits = [str(digit) for digit in reversed(digits)]
while len(digits) <= abs(exponent):
- digits.append('0')
- digits.insert(-exponent, '.')
+ digits.append("0")
+ digits.insert(-exponent, ".")
if sign and rounded_d:
- digits.append('-')
- number = ''.join(reversed(digits))
- return mark_safe(formats.number_format(
- number,
- abs(p),
- use_l10n=use_l10n,
- force_grouping=force_grouping,
- ))
+ digits.append("-")
+ number = "".join(reversed(digits))
+ return mark_safe(
+ formats.number_format(
+ number,
+ abs(p),
+ use_l10n=use_l10n,
+ force_grouping=force_grouping,
+ )
+ )
@register.filter(is_safe=True)
@@ -208,7 +215,7 @@ def iriencode(value):
@stringfilter
def linenumbers(value, autoescape=True):
"""Display text with line numbers."""
- lines = value.split('\n')
+ lines = value.split("\n")
# Find the maximum width of the line count, for use with zero padding
# string format command
width = str(len(str(len(lines))))
@@ -218,7 +225,7 @@ def linenumbers(value, autoescape=True):
else:
for i, line in enumerate(lines):
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
- return mark_safe('\n'.join(lines))
+ return mark_safe("\n".join(lines))
@register.filter(is_safe=True)
@@ -275,7 +282,7 @@ def stringformat(value, arg):
def title(value):
"""Convert a string into titlecase."""
t = re.sub("([a-z])'([A-Z])", lambda m: m[0].lower(), value.title())
- return re.sub(r'\d([A-Z])', lambda m: m[0].lower(), t)
+ return re.sub(r"\d([A-Z])", lambda m: m[0].lower(), t)
@register.filter(is_safe=True)
@@ -314,7 +321,7 @@ def truncatewords(value, arg):
length = int(arg)
except ValueError: # Invalid literal for int().
return value # Fail silently.
- return Truncator(value).words(length, truncate=' …')
+ return Truncator(value).words(length, truncate=" …")
@register.filter(is_safe=True)
@@ -328,7 +335,7 @@ def truncatewords_html(value, arg):
length = int(arg)
except ValueError: # invalid literal for int()
return value # Fail silently.
- return Truncator(value).words(length, html=True, truncate=' …')
+ return Truncator(value).words(length, html=True, truncate=" …")
@register.filter(is_safe=False)
@@ -351,7 +358,7 @@ def urlencode(value, safe=None):
"""
kwargs = {}
if safe is not None:
- kwargs['safe'] = safe
+ kwargs["safe"] = safe
return quote(value, **kwargs)
@@ -371,7 +378,9 @@ def urlizetrunc(value, limit, autoescape=True):
Argument: Length to truncate URLs to.
"""
- return mark_safe(_urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape))
+ return mark_safe(
+ _urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape)
+ )
@register.filter(is_safe=False)
@@ -414,8 +423,8 @@ def center(value, arg):
def cut(value, arg):
"""Remove all values of arg from the given string."""
safe = isinstance(value, SafeData)
- value = value.replace(arg, '')
- if safe and arg != ';':
+ value = value.replace(arg, "")
+ if safe and arg != ";":
return mark_safe(value)
return value
@@ -424,6 +433,7 @@ def cut(value, arg):
# HTML STRINGS #
###################
+
@register.filter("escape", is_safe=True)
@stringfilter
def escape_filter(value):
@@ -465,7 +475,7 @@ def linebreaksbr(value, autoescape=True):
value = normalize_newlines(value)
if autoescape:
value = escape(value)
- return mark_safe(value.replace('\n', '<br>'))
+ return mark_safe(value.replace("\n", "<br>"))
@register.filter(is_safe=True)
@@ -496,6 +506,7 @@ def striptags(value):
# LISTS #
###################
+
def _property_resolver(arg):
"""
When arg is convertible to float, behave like operator.itemgetter(arg)
@@ -517,8 +528,8 @@ def _property_resolver(arg):
try:
float(arg)
except ValueError:
- if VARIABLE_ATTRIBUTE_SEPARATOR + '_' in arg or arg[0] == '_':
- raise AttributeError('Access to private variables is forbidden.')
+ if VARIABLE_ATTRIBUTE_SEPARATOR + "_" in arg or arg[0] == "_":
+ raise AttributeError("Access to private variables is forbidden.")
parts = arg.split(VARIABLE_ATTRIBUTE_SEPARATOR)
def resolve(value):
@@ -543,7 +554,7 @@ def dictsort(value, arg):
try:
return sorted(value, key=_property_resolver(arg))
except (AttributeError, TypeError):
- return ''
+ return ""
@register.filter(is_safe=False)
@@ -555,7 +566,7 @@ def dictsortreversed(value, arg):
try:
return sorted(value, key=_property_resolver(arg), reverse=True)
except (AttributeError, TypeError):
- return ''
+ return ""
@register.filter(is_safe=False)
@@ -564,7 +575,7 @@ def first(value):
try:
return value[0]
except IndexError:
- return ''
+ return ""
@register.filter(is_safe=True, needs_autoescape=True)
@@ -585,7 +596,7 @@ def last(value):
try:
return value[-1]
except IndexError:
- return ''
+ return ""
@register.filter(is_safe=False)
@@ -603,7 +614,7 @@ def length_is(value, arg):
try:
return len(value) == int(arg)
except (ValueError, TypeError):
- return ''
+ return ""
@register.filter(is_safe=True)
@@ -619,7 +630,7 @@ def slice_filter(value, arg):
"""
try:
bits = []
- for x in str(arg).split(':'):
+ for x in str(arg).split(":"):
if not x:
bits.append(None)
else:
@@ -655,6 +666,7 @@ def unordered_list(value, autoescape=True):
if autoescape:
escaper = conditional_escape
else:
+
def escaper(x):
return x
@@ -683,16 +695,19 @@ def unordered_list(value, autoescape=True):
pass
def list_formatter(item_list, tabs=1):
- indent = '\t' * tabs
+ indent = "\t" * tabs
output = []
for item, children in walk_items(item_list):
- sublist = ''
+ sublist = ""
if children:
- sublist = '\n%s<ul>\n%s\n%s</ul>\n%s' % (
- indent, list_formatter(children, tabs + 1), indent, indent)
- output.append('%s<li>%s%s</li>' % (
- indent, escaper(item), sublist))
- return '\n'.join(output)
+ sublist = "\n%s<ul>\n%s\n%s</ul>\n%s" % (
+ indent,
+ list_formatter(children, tabs + 1),
+ indent,
+ indent,
+ )
+ output.append("%s<li>%s%s</li>" % (indent, escaper(item), sublist))
+ return "\n".join(output)
return mark_safe(list_formatter(value))
@@ -701,6 +716,7 @@ def unordered_list(value, autoescape=True):
# INTEGERS #
###################
+
@register.filter(is_safe=False)
def add(value, arg):
"""Add the arg to the value."""
@@ -710,7 +726,7 @@ def add(value, arg):
try:
return value + arg
except Exception:
- return ''
+ return ""
@register.filter(is_safe=False)
@@ -738,62 +754,64 @@ def get_digit(value, arg):
# DATES #
###################
+
@register.filter(expects_localtime=True, is_safe=False)
def date(value, arg=None):
"""Format a date according to the given format."""
- if value in (None, ''):
- return ''
+ if value in (None, ""):
+ return ""
try:
return formats.date_format(value, arg)
except AttributeError:
try:
return format(value, arg)
except AttributeError:
- return ''
+ return ""
@register.filter(expects_localtime=True, is_safe=False)
def time(value, arg=None):
"""Format a time according to the given format."""
- if value in (None, ''):
- return ''
+ if value in (None, ""):
+ return ""
try:
return formats.time_format(value, arg)
except (AttributeError, TypeError):
try:
return time_format(value, arg)
except (AttributeError, TypeError):
- return ''
+ return ""
@register.filter("timesince", is_safe=False)
def timesince_filter(value, arg=None):
"""Format a date as the time since that date (i.e. "4 days, 6 hours")."""
if not value:
- return ''
+ return ""
try:
if arg:
return timesince(value, arg)
return timesince(value)
except (ValueError, TypeError):
- return ''
+ return ""
@register.filter("timeuntil", is_safe=False)
def timeuntil_filter(value, arg=None):
"""Format a date as the time until that date (i.e. "4 days, 6 hours")."""
if not value:
- return ''
+ return ""
try:
return timeuntil(value, arg)
except (ValueError, TypeError):
- return ''
+ return ""
###################
# LOGIC #
###################
+
@register.filter(is_safe=False)
def default(value, arg):
"""If value is unavailable, use given default."""
@@ -832,8 +850,8 @@ def yesno(value, arg=None):
"""
if arg is None:
# Translators: Please do not add spaces around commas.
- arg = gettext('yes,no,maybe')
- bits = arg.split(',')
+ arg = gettext("yes,no,maybe")
+ bits = arg.split(",")
if len(bits) < 2:
return value # Invalid arg.
try:
@@ -852,6 +870,7 @@ def yesno(value, arg=None):
# MISC #
###################
+
@register.filter(is_safe=True)
def filesizeformat(bytes_):
"""
@@ -861,7 +880,7 @@ def filesizeformat(bytes_):
try:
bytes_ = int(bytes_)
except (TypeError, ValueError, UnicodeDecodeError):
- value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {'size': 0}
+ value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {"size": 0}
return avoid_wrapping(value)
def filesize_number_format(value):
@@ -878,7 +897,7 @@ def filesizeformat(bytes_):
bytes_ = -bytes_ # Allow formatting of negative numbers.
if bytes_ < KB:
- value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {'size': bytes_}
+ value = ngettext("%(size)d byte", "%(size)d bytes", bytes_) % {"size": bytes_}
elif bytes_ < MB:
value = gettext("%s KB") % filesize_number_format(bytes_ / KB)
elif bytes_ < GB:
@@ -896,7 +915,7 @@ def filesizeformat(bytes_):
@register.filter(is_safe=False)
-def pluralize(value, arg='s'):
+def pluralize(value, arg="s"):
"""
Return a plural suffix if the value is not 1, '1', or an object of
length 1. By default, use 's' as the suffix:
@@ -918,11 +937,11 @@ def pluralize(value, arg='s'):
* If value is 1, cand{{ value|pluralize:"y,ies" }} display "candy".
* If value is 2, cand{{ value|pluralize:"y,ies" }} display "candies".
"""
- if ',' not in arg:
- arg = ',' + arg
- bits = arg.split(',')
+ if "," not in arg:
+ arg = "," + arg
+ bits = arg.split(",")
if len(bits) > 2:
- return ''
+ return ""
singular_suffix, plural_suffix = bits[:2]
try:
@@ -934,7 +953,7 @@ def pluralize(value, arg='s'):
return singular_suffix if len(value) == 1 else plural_suffix
except TypeError: # len() of unsized object.
pass
- return ''
+ return ""
@register.filter("phone2numeric", is_safe=True)
diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py
index 9090b14e40..7762b94723 100644
--- a/django/template/defaulttags.py
+++ b/django/template/defaulttags.py
@@ -4,7 +4,8 @@ import sys
import warnings
from collections import namedtuple
from datetime import datetime
-from itertools import cycle as itertools_cycle, groupby
+from itertools import cycle as itertools_cycle
+from itertools import groupby
from django.conf import settings
from django.utils import timezone
@@ -13,11 +14,23 @@ from django.utils.lorem_ipsum import paragraphs, words
from django.utils.safestring import mark_safe
from .base import (
- BLOCK_TAG_END, BLOCK_TAG_START, COMMENT_TAG_END, COMMENT_TAG_START,
- FILTER_SEPARATOR, SINGLE_BRACE_END, SINGLE_BRACE_START,
- VARIABLE_ATTRIBUTE_SEPARATOR, VARIABLE_TAG_END, VARIABLE_TAG_START, Node,
- NodeList, TemplateSyntaxError, VariableDoesNotExist, kwarg_re,
- render_value_in_context, token_kwargs,
+ BLOCK_TAG_END,
+ BLOCK_TAG_START,
+ COMMENT_TAG_END,
+ COMMENT_TAG_START,
+ FILTER_SEPARATOR,
+ SINGLE_BRACE_END,
+ SINGLE_BRACE_START,
+ VARIABLE_ATTRIBUTE_SEPARATOR,
+ VARIABLE_TAG_END,
+ VARIABLE_TAG_START,
+ Node,
+ NodeList,
+ TemplateSyntaxError,
+ VariableDoesNotExist,
+ kwarg_re,
+ render_value_in_context,
+ token_kwargs,
)
from .context import Context
from .defaultfilters import date
@@ -29,6 +42,7 @@ register = Library()
class AutoEscapeControlNode(Node):
"""Implement the actions of the autoescape tag."""
+
def __init__(self, setting, nodelist):
self.setting, self.nodelist = setting, nodelist
@@ -47,19 +61,22 @@ class CommentNode(Node):
child_nodelists = ()
def render(self, context):
- return ''
+ return ""
class CsrfTokenNode(Node):
child_nodelists = ()
def render(self, context):
- csrf_token = context.get('csrf_token')
+ csrf_token = context.get("csrf_token")
if csrf_token:
- if csrf_token == 'NOTPROVIDED':
+ if csrf_token == "NOTPROVIDED":
return format_html("")
else:
- return format_html('<input type="hidden" name="csrfmiddlewaretoken" value="{}">', csrf_token)
+ return format_html(
+ '<input type="hidden" name="csrfmiddlewaretoken" value="{}">',
+ csrf_token,
+ )
else:
# It's very probable that the token is missing because of
# misconfiguration, so we raise a warning
@@ -69,7 +86,7 @@ class CsrfTokenNode(Node):
"did not provide the value. This is usually caused by not "
"using RequestContext."
)
- return ''
+ return ""
class CycleNode(Node):
@@ -87,7 +104,7 @@ class CycleNode(Node):
if self.variable_name:
context.set_upward(self.variable_name, value)
if self.silent:
- return ''
+ return ""
return render_value_in_context(value, context)
def reset(self, context):
@@ -100,13 +117,14 @@ class CycleNode(Node):
class DebugNode(Node):
def render(self, context):
if not settings.DEBUG:
- return ''
+ return ""
from pprint import pformat
+
output = [escape(pformat(val)) for val in context]
- output.append('\n\n')
+ output.append("\n\n")
output.append(escape(pformat(sys.modules)))
- return ''.join(output)
+ return "".join(output)
class FilterNode(Node):
@@ -126,7 +144,7 @@ class FirstOfNode(Node):
self.asvar = asvar
def render(self, context):
- first = ''
+ first = ""
for var in self.vars:
value = var.resolve(context, ignore_failures=True)
if value:
@@ -134,14 +152,16 @@ class FirstOfNode(Node):
break
if self.asvar:
context[self.asvar] = first
- return ''
+ return ""
return first
class ForNode(Node):
- child_nodelists = ('nodelist_loop', 'nodelist_empty')
+ child_nodelists = ("nodelist_loop", "nodelist_empty")
- def __init__(self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None):
+ def __init__(
+ self, loopvars, sequence, is_reversed, nodelist_loop, nodelist_empty=None
+ ):
self.loopvars, self.sequence = loopvars, sequence
self.is_reversed = is_reversed
self.nodelist_loop = nodelist_loop
@@ -151,25 +171,25 @@ class ForNode(Node):
self.nodelist_empty = nodelist_empty
def __repr__(self):
- reversed_text = ' reversed' if self.is_reversed else ''
- return '<%s: for %s in %s, tail_len: %d%s>' % (
+ reversed_text = " reversed" if self.is_reversed else ""
+ return "<%s: for %s in %s, tail_len: %d%s>" % (
self.__class__.__name__,
- ', '.join(self.loopvars),
+ ", ".join(self.loopvars),
self.sequence,
len(self.nodelist_loop),
reversed_text,
)
def render(self, context):
- if 'forloop' in context:
- parentloop = context['forloop']
+ if "forloop" in context:
+ parentloop = context["forloop"]
else:
parentloop = {}
with context.push():
values = self.sequence.resolve(context, ignore_failures=True)
if values is None:
values = []
- if not hasattr(values, '__len__'):
+ if not hasattr(values, "__len__"):
values = list(values)
len_values = len(values)
if len_values < 1:
@@ -181,17 +201,17 @@ class ForNode(Node):
unpack = num_loopvars > 1
# Create a forloop value in the context. We'll update counters on each
# iteration just below.
- loop_dict = context['forloop'] = {'parentloop': parentloop}
+ loop_dict = context["forloop"] = {"parentloop": parentloop}
for i, item in enumerate(values):
# Shortcuts for current loop iteration number.
- loop_dict['counter0'] = i
- loop_dict['counter'] = i + 1
+ loop_dict["counter0"] = i
+ loop_dict["counter"] = i + 1
# Reverse counter iteration numbers.
- loop_dict['revcounter'] = len_values - i
- loop_dict['revcounter0'] = len_values - i - 1
+ loop_dict["revcounter"] = len_values - i
+ loop_dict["revcounter0"] = len_values - i - 1
# Boolean values designating first and last times through loop.
- loop_dict['first'] = (i == 0)
- loop_dict['last'] = (i == len_values - 1)
+ loop_dict["first"] = i == 0
+ loop_dict["last"] = i == len_values - 1
pop_context = False
if unpack:
@@ -204,8 +224,9 @@ class ForNode(Node):
# Check loop variable count before unpacking
if num_loopvars != len_item:
raise ValueError(
- "Need {} values to unpack in for loop; got {}. "
- .format(num_loopvars, len_item),
+ "Need {} values to unpack in for loop; got {}. ".format(
+ num_loopvars, len_item
+ ),
)
unpacked_vars = dict(zip(self.loopvars, item))
pop_context = True
@@ -221,11 +242,11 @@ class ForNode(Node):
# the context ending up in an inconsistent state when other
# tags (e.g., include and with) push data to context.
context.pop()
- return mark_safe(''.join(nodelist))
+ return mark_safe("".join(nodelist))
class IfChangedNode(Node):
- child_nodelists = ('nodelist_true', 'nodelist_false')
+ child_nodelists = ("nodelist_true", "nodelist_false")
def __init__(self, nodelist_true, nodelist_false, *varlist):
self.nodelist_true, self.nodelist_false = nodelist_true, nodelist_false
@@ -240,7 +261,9 @@ class IfChangedNode(Node):
if self._varlist:
# Consider multiple parameters. This behaves like an OR evaluation
# of the multiple variables.
- compare_to = [var.resolve(context, ignore_failures=True) for var in self._varlist]
+ compare_to = [
+ var.resolve(context, ignore_failures=True) for var in self._varlist
+ ]
else:
# The "{% ifchanged %}" syntax (without any variables) compares
# the rendered output.
@@ -252,28 +275,27 @@ class IfChangedNode(Node):
return nodelist_true_output or self.nodelist_true.render(context)
elif self.nodelist_false:
return self.nodelist_false.render(context)
- return ''
+ return ""
def _get_context_stack_frame(self, context):
# The Context object behaves like a stack where each template tag can create a new scope.
# Find the place where to store the state to detect changes.
- if 'forloop' in context:
+ if "forloop" in context:
# Ifchanged is bound to the local for loop.
# When there is a loop-in-loop, the state is bound to the inner loop,
# so it resets when the outer loop continues.
- return context['forloop']
+ return context["forloop"]
else:
# Using ifchanged outside loops. Effectively this is a no-op because the state is associated with 'self'.
return context.render_context
class IfNode(Node):
-
def __init__(self, conditions_nodelists):
self.conditions_nodelists = conditions_nodelists
def __repr__(self):
- return '<%s>' % self.__class__.__name__
+ return "<%s>" % self.__class__.__name__
def __iter__(self):
for _, nodelist in self.conditions_nodelists:
@@ -286,18 +308,18 @@ class IfNode(Node):
def render(self, context):
for condition, nodelist in self.conditions_nodelists:
- if condition is not None: # if / elif clause
+ if condition is not None: # if / elif clause
try:
match = condition.eval(context)
except VariableDoesNotExist:
match = None
- else: # else clause
+ else: # else clause
match = True
if match:
return nodelist.render(context)
- return ''
+ return ""
class LoremNode(Node):
@@ -309,16 +331,16 @@ class LoremNode(Node):
count = int(self.count.resolve(context))
except (ValueError, TypeError):
count = 1
- if self.method == 'w':
+ if self.method == "w":
return words(count, common=self.common)
else:
paras = paragraphs(count, common=self.common)
- if self.method == 'p':
- paras = ['<p>%s</p>' % p for p in paras]
- return '\n\n'.join(paras)
+ if self.method == "p":
+ paras = ["<p>%s</p>" % p for p in paras]
+ return "\n\n".join(paras)
-GroupedResult = namedtuple('GroupedResult', ['grouper', 'list'])
+GroupedResult = namedtuple("GroupedResult", ["grouper", "list"])
class RegroupNode(Node):
@@ -337,22 +359,23 @@ class RegroupNode(Node):
if obj_list is None:
# target variable wasn't found in context; fail silently.
context[self.var_name] = []
- return ''
+ return ""
# List of dictionaries in the format:
# {'grouper': 'key', 'list': [list of contents]}.
context[self.var_name] = [
GroupedResult(grouper=key, list=list(val))
- for key, val in
- groupby(obj_list, lambda obj: self.resolve_expression(obj, context))
+ for key, val in groupby(
+ obj_list, lambda obj: self.resolve_expression(obj, context)
+ )
]
- return ''
+ return ""
class LoadNode(Node):
child_nodelists = ()
def render(self, context):
- return ''
+ return ""
class NowNode(Node):
@@ -366,7 +389,7 @@ class NowNode(Node):
if self.asvar:
context[self.asvar] = formatted
- return ''
+ return ""
else:
return formatted
@@ -377,7 +400,7 @@ class ResetCycleNode(Node):
def render(self, context):
self.node.reset(context)
- return ''
+ return ""
class SpacelessNode(Node):
@@ -386,26 +409,27 @@ class SpacelessNode(Node):
def render(self, context):
from django.utils.html import strip_spaces_between_tags
+
return strip_spaces_between_tags(self.nodelist.render(context).strip())
class TemplateTagNode(Node):
mapping = {
- 'openblock': BLOCK_TAG_START,
- 'closeblock': BLOCK_TAG_END,
- 'openvariable': VARIABLE_TAG_START,
- 'closevariable': VARIABLE_TAG_END,
- 'openbrace': SINGLE_BRACE_START,
- 'closebrace': SINGLE_BRACE_END,
- 'opencomment': COMMENT_TAG_START,
- 'closecomment': COMMENT_TAG_END,
+ "openblock": BLOCK_TAG_START,
+ "closeblock": BLOCK_TAG_END,
+ "openvariable": VARIABLE_TAG_START,
+ "closevariable": VARIABLE_TAG_END,
+ "openbrace": SINGLE_BRACE_START,
+ "closebrace": SINGLE_BRACE_END,
+ "opencomment": COMMENT_TAG_START,
+ "closecomment": COMMENT_TAG_END,
}
def __init__(self, tagtype):
self.tagtype = tagtype
def render(self, context):
- return self.mapping.get(self.tagtype, '')
+ return self.mapping.get(self.tagtype, "")
class URLNode(Node):
@@ -428,6 +452,7 @@ class URLNode(Node):
def render(self, context):
from django.urls import NoReverseMatch, reverse
+
args = [arg.resolve(context) for arg in self.args]
kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()}
view_name = self.view_name.resolve(context)
@@ -440,7 +465,7 @@ class URLNode(Node):
current_app = None
# Try to look up the URL. If it fails, raise NoReverseMatch unless the
# {% url ... as var %} construct is used, in which case return nothing.
- url = ''
+ url = ""
try:
url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
except NoReverseMatch:
@@ -449,7 +474,7 @@ class URLNode(Node):
if self.asvar:
context[self.asvar] = url
- return ''
+ return ""
else:
if context.autoescape:
url = conditional_escape(url)
@@ -477,7 +502,7 @@ class WidthRatioNode(Node):
max_value = self.max_expr.resolve(context)
max_width = int(self.max_width.resolve(context))
except VariableDoesNotExist:
- return ''
+ return ""
except (ValueError, TypeError):
raise TemplateSyntaxError("widthratio final argument must be a number")
try:
@@ -486,13 +511,13 @@ class WidthRatioNode(Node):
ratio = (value / max_value) * max_width
result = str(round(ratio))
except ZeroDivisionError:
- result = '0'
+ result = "0"
except (ValueError, TypeError, OverflowError):
- result = ''
+ result = ""
if self.asvar:
context[self.asvar] = result
- return ''
+ return ""
else:
return result
@@ -507,7 +532,7 @@ class WithNode(Node):
self.extra_context[name] = var
def __repr__(self):
- return '<%s>' % self.__class__.__name__
+ return "<%s>" % self.__class__.__name__
def render(self, context):
values = {key: val.resolve(context) for key, val in self.extra_context.items()}
@@ -525,11 +550,11 @@ def autoescape(parser, token):
if len(args) != 2:
raise TemplateSyntaxError("'autoescape' tag requires exactly one argument.")
arg = args[1]
- if arg not in ('on', 'off'):
+ if arg not in ("on", "off"):
raise TemplateSyntaxError("'autoescape' argument should be 'on' or 'off'")
- nodelist = parser.parse(('endautoescape',))
+ nodelist = parser.parse(("endautoescape",))
parser.delete_first_token()
- return AutoEscapeControlNode((arg == 'on'), nodelist)
+ return AutoEscapeControlNode((arg == "on"), nodelist)
@register.tag
@@ -537,7 +562,7 @@ def comment(parser, token):
"""
Ignore everything between ``{% comment %}`` and ``{% endcomment %}``.
"""
- parser.skip_past('endcomment')
+ parser.skip_past("endcomment")
return CommentNode()
@@ -595,8 +620,10 @@ def cycle(parser, token):
if len(args) == 2:
# {% cycle foo %} case.
name = args[1]
- if not hasattr(parser, '_named_cycle_nodes'):
- raise TemplateSyntaxError("No named cycles in template. '%s' is not defined" % name)
+ if not hasattr(parser, "_named_cycle_nodes"):
+ raise TemplateSyntaxError(
+ "No named cycles in template. '%s' is not defined" % name
+ )
if name not in parser._named_cycle_nodes:
raise TemplateSyntaxError("Named cycle '%s' does not exist" % name)
return parser._named_cycle_nodes[name]
@@ -607,7 +634,10 @@ def cycle(parser, token):
# {% cycle ... as foo [silent] %} case.
if args[-3] == "as":
if args[-1] != "silent":
- raise TemplateSyntaxError("Only 'silent' flag is allowed after cycle's name, not '%s'." % args[-1])
+ raise TemplateSyntaxError(
+ "Only 'silent' flag is allowed after cycle's name, not '%s'."
+ % args[-1]
+ )
as_form = True
silent = True
args = args[:-1]
@@ -619,7 +649,7 @@ def cycle(parser, token):
name = args[-1]
values = [parser.compile_filter(arg) for arg in args[1:-2]]
node = CycleNode(values, name, silent=silent)
- if not hasattr(parser, '_named_cycle_nodes'):
+ if not hasattr(parser, "_named_cycle_nodes"):
parser._named_cycle_nodes = {}
parser._named_cycle_nodes[name] = node
else:
@@ -649,7 +679,7 @@ def debug(parser, token):
return DebugNode()
-@register.tag('filter')
+@register.tag("filter")
def do_filter(parser, token):
"""
Filter the contents of the block through variable filters.
@@ -671,10 +701,13 @@ def do_filter(parser, token):
_, rest = token.contents.split(None, 1)
filter_expr = parser.compile_filter("var|%s" % (rest))
for func, unused in filter_expr.filters:
- filter_name = getattr(func, '_filter_name', None)
- if filter_name in ('escape', 'safe'):
- raise TemplateSyntaxError('"filter %s" is not permitted. Use the "autoescape" tag instead.' % filter_name)
- nodelist = parser.parse(('endfilter',))
+ filter_name = getattr(func, "_filter_name", None)
+ if filter_name in ("escape", "safe"):
+ raise TemplateSyntaxError(
+ '"filter %s" is not permitted. Use the "autoescape" tag instead.'
+ % filter_name
+ )
+ nodelist = parser.parse(("endfilter",))
parser.delete_first_token()
return FilterNode(filter_expr, nodelist)
@@ -722,13 +755,13 @@ def firstof(parser, token):
if not bits:
raise TemplateSyntaxError("'firstof' statement requires at least one argument")
- if len(bits) >= 2 and bits[-2] == 'as':
+ if len(bits) >= 2 and bits[-2] == "as":
asvar = bits[-1]
bits = bits[:-2]
return FirstOfNode([parser.compile_filter(bit) for bit in bits], asvar)
-@register.tag('for')
+@register.tag("for")
def do_for(parser, token):
"""
Loop over each item in an array.
@@ -797,14 +830,16 @@ def do_for(parser, token):
"'for' statements should have at least four words: %s" % token.contents
)
- is_reversed = bits[-1] == 'reversed'
+ is_reversed = bits[-1] == "reversed"
in_index = -3 if is_reversed else -2
- if bits[in_index] != 'in':
- raise TemplateSyntaxError("'for' statements should use the format"
- " 'for x in y': %s" % token.contents)
+ if bits[in_index] != "in":
+ raise TemplateSyntaxError(
+ "'for' statements should use the format"
+ " 'for x in y': %s" % token.contents
+ )
- invalid_chars = frozenset((' ', '"', "'", FILTER_SEPARATOR))
- loopvars = re.split(r' *, *', ' '.join(bits[1:in_index]))
+ invalid_chars = frozenset((" ", '"', "'", FILTER_SEPARATOR))
+ loopvars = re.split(r" *, *", " ".join(bits[1:in_index]))
for var in loopvars:
if not var or not invalid_chars.isdisjoint(var):
raise TemplateSyntaxError(
@@ -812,10 +847,15 @@ def do_for(parser, token):
)
sequence = parser.compile_filter(bits[in_index + 1])
- nodelist_loop = parser.parse(('empty', 'endfor',))
+ nodelist_loop = parser.parse(
+ (
+ "empty",
+ "endfor",
+ )
+ )
token = parser.next_token()
- if token.contents == 'empty':
- nodelist_empty = parser.parse(('endfor',))
+ if token.contents == "empty":
+ nodelist_empty = parser.parse(("endfor",))
parser.delete_first_token()
else:
nodelist_empty = None
@@ -845,7 +885,7 @@ class TemplateIfParser(IfParser):
return TemplateLiteral(self.template_parser.compile_filter(value), value)
-@register.tag('if')
+@register.tag("if")
def do_if(parser, token):
"""
Evaluate a variable, and if that variable is "true" (i.e., exists, is not
@@ -907,27 +947,31 @@ def do_if(parser, token):
# {% if ... %}
bits = token.split_contents()[1:]
condition = TemplateIfParser(parser, bits).parse()
- nodelist = parser.parse(('elif', 'else', 'endif'))
+ nodelist = parser.parse(("elif", "else", "endif"))
conditions_nodelists = [(condition, nodelist)]
token = parser.next_token()
# {% elif ... %} (repeatable)
- while token.contents.startswith('elif'):
+ while token.contents.startswith("elif"):
bits = token.split_contents()[1:]
condition = TemplateIfParser(parser, bits).parse()
- nodelist = parser.parse(('elif', 'else', 'endif'))
+ nodelist = parser.parse(("elif", "else", "endif"))
conditions_nodelists.append((condition, nodelist))
token = parser.next_token()
# {% else %} (optional)
- if token.contents == 'else':
- nodelist = parser.parse(('endif',))
+ if token.contents == "else":
+ nodelist = parser.parse(("endif",))
conditions_nodelists.append((None, nodelist))
token = parser.next_token()
# {% endif %}
- if token.contents != 'endif':
- raise TemplateSyntaxError('Malformed template tag at line {}: "{}"'.format(token.lineno, token.contents))
+ if token.contents != "endif":
+ raise TemplateSyntaxError(
+ 'Malformed template tag at line {}: "{}"'.format(
+ token.lineno, token.contents
+ )
+ )
return IfNode(conditions_nodelists)
@@ -963,10 +1007,10 @@ def ifchanged(parser, token):
{% endfor %}
"""
bits = token.split_contents()
- nodelist_true = parser.parse(('else', 'endifchanged'))
+ nodelist_true = parser.parse(("else", "endifchanged"))
token = parser.next_token()
- if token.contents == 'else':
- nodelist_false = parser.parse(('endifchanged',))
+ if token.contents == "else":
+ nodelist_false = parser.parse(("endifchanged",))
parser.delete_first_token()
else:
nodelist_false = NodeList()
@@ -979,8 +1023,10 @@ def find_library(parser, name):
return parser.libraries[name]
except KeyError:
raise TemplateSyntaxError(
- "'%s' is not a registered tag library. Must be one of:\n%s" % (
- name, "\n".join(sorted(parser.libraries)),
+ "'%s' is not a registered tag library. Must be one of:\n%s"
+ % (
+ name,
+ "\n".join(sorted(parser.libraries)),
),
)
@@ -1000,8 +1046,10 @@ def load_from_library(library, label, names):
subset.filters[name] = library.filters[name]
if found is False:
raise TemplateSyntaxError(
- "'%s' is not a valid tag or filter in tag library '%s'" % (
- name, label,
+ "'%s' is not a valid tag or filter in tag library '%s'"
+ % (
+ name,
+ label,
),
)
return subset
@@ -1066,19 +1114,19 @@ def lorem(parser, token):
bits = list(token.split_contents())
tagname = bits[0]
# Random bit
- common = bits[-1] != 'random'
+ common = bits[-1] != "random"
if not common:
bits.pop()
# Method bit
- if bits[-1] in ('w', 'p', 'b'):
+ if bits[-1] in ("w", "p", "b"):
method = bits.pop()
else:
- method = 'b'
+ method = "b"
# Count bit
if len(bits) > 1:
count = bits.pop()
else:
- count = '1'
+ count = "1"
count = parser.compile_filter(count)
if len(bits) != 1:
raise TemplateSyntaxError("Incorrect format for %r tag" % tagname)
@@ -1099,7 +1147,7 @@ def now(parser, token):
"""
bits = token.split_contents()
asvar = None
- if len(bits) == 4 and bits[-2] == 'as':
+ if len(bits) == 4 and bits[-2] == "as":
asvar = bits[-1]
bits = bits[:-2]
if len(bits) != 2:
@@ -1159,12 +1207,10 @@ def regroup(parser, token):
if len(bits) != 6:
raise TemplateSyntaxError("'regroup' tag takes five arguments")
target = parser.compile_filter(bits[1])
- if bits[2] != 'by':
+ if bits[2] != "by":
raise TemplateSyntaxError("second argument to 'regroup' tag must be 'by'")
- if bits[4] != 'as':
- raise TemplateSyntaxError(
- "next-to-last argument to 'regroup' tag must be 'as'"
- )
+ if bits[4] != "as":
+ raise TemplateSyntaxError("next-to-last argument to 'regroup' tag must be 'as'")
var_name = bits[5]
# RegroupNode will take each item in 'target', put it in the context under
# 'var_name', evaluate 'var_name'.'expression' in the current context, and
@@ -1172,9 +1218,9 @@ def regroup(parser, token):
# save the final result in the context under 'var_name', thus clearing the
# temporary values. This hack is necessary because the template engine
# doesn't provide a context-aware equivalent of Python's getattr.
- expression = parser.compile_filter(var_name +
- VARIABLE_ATTRIBUTE_SEPARATOR +
- bits[3])
+ expression = parser.compile_filter(
+ var_name + VARIABLE_ATTRIBUTE_SEPARATOR + bits[3]
+ )
return RegroupNode(target, expression, var_name)
@@ -1230,7 +1276,7 @@ def spaceless(parser, token):
</strong>
{% endspaceless %}
"""
- nodelist = parser.parse(('endspaceless',))
+ nodelist = parser.parse(("endspaceless",))
parser.delete_first_token()
return SpacelessNode(nodelist)
@@ -1264,9 +1310,10 @@ def templatetag(parser, token):
raise TemplateSyntaxError("'templatetag' statement takes one argument")
tag = bits[1]
if tag not in TemplateTagNode.mapping:
- raise TemplateSyntaxError("Invalid templatetag argument: '%s'."
- " Must be one of: %s" %
- (tag, list(TemplateTagNode.mapping)))
+ raise TemplateSyntaxError(
+ "Invalid templatetag argument: '%s'."
+ " Must be one of: %s" % (tag, list(TemplateTagNode.mapping))
+ )
return TemplateTagNode(tag)
@@ -1314,13 +1361,15 @@ def url(parser, token):
"""
bits = token.split_contents()
if len(bits) < 2:
- raise TemplateSyntaxError("'%s' takes at least one argument, a URL pattern name." % bits[0])
+ raise TemplateSyntaxError(
+ "'%s' takes at least one argument, a URL pattern name." % bits[0]
+ )
viewname = parser.compile_filter(bits[1])
args = []
kwargs = {}
asvar = None
bits = bits[2:]
- if len(bits) >= 2 and bits[-2] == 'as':
+ if len(bits) >= 2 and bits[-2] == "as":
asvar = bits[-1]
bits = bits[:-2]
@@ -1355,7 +1404,7 @@ def verbatim(parser, token):
...
{% endverbatim myblock %}
"""
- nodelist = parser.parse(('endverbatim',))
+ nodelist = parser.parse(("endverbatim",))
parser.delete_first_token()
return VerbatimNode(nodelist.render(Context()))
@@ -1387,18 +1436,22 @@ def widthratio(parser, token):
asvar = None
elif len(bits) == 6:
tag, this_value_expr, max_value_expr, max_width, as_, asvar = bits
- if as_ != 'as':
- raise TemplateSyntaxError("Invalid syntax in widthratio tag. Expecting 'as' keyword")
+ if as_ != "as":
+ raise TemplateSyntaxError(
+ "Invalid syntax in widthratio tag. Expecting 'as' keyword"
+ )
else:
raise TemplateSyntaxError("widthratio takes at least three arguments")
- return WidthRatioNode(parser.compile_filter(this_value_expr),
- parser.compile_filter(max_value_expr),
- parser.compile_filter(max_width),
- asvar=asvar)
+ return WidthRatioNode(
+ parser.compile_filter(this_value_expr),
+ parser.compile_filter(max_value_expr),
+ parser.compile_filter(max_width),
+ asvar=asvar,
+ )
-@register.tag('with')
+@register.tag("with")
def do_with(parser, token):
"""
Add one or more values to the context (inside of this block) for caching
@@ -1427,8 +1480,9 @@ def do_with(parser, token):
"%r expected at least one variable assignment" % bits[0]
)
if remaining_bits:
- raise TemplateSyntaxError("%r received an invalid token: %r" %
- (bits[0], remaining_bits[0]))
- nodelist = parser.parse(('endwith',))
+ raise TemplateSyntaxError(
+ "%r received an invalid token: %r" % (bits[0], remaining_bits[0])
+ )
+ nodelist = parser.parse(("endwith",))
parser.delete_first_token()
return WithNode(None, None, nodelist, extra_context=extra_context)
diff --git a/django/template/engine.py b/django/template/engine.py
index 91e503f709..9e6f1e97da 100644
--- a/django/template/engine.py
+++ b/django/template/engine.py
@@ -12,28 +12,39 @@ from .library import import_library
class Engine:
default_builtins = [
- 'django.template.defaulttags',
- 'django.template.defaultfilters',
- 'django.template.loader_tags',
+ "django.template.defaulttags",
+ "django.template.defaultfilters",
+ "django.template.loader_tags",
]
- def __init__(self, dirs=None, app_dirs=False, context_processors=None,
- debug=False, loaders=None, string_if_invalid='',
- file_charset='utf-8', libraries=None, builtins=None, autoescape=True):
+ def __init__(
+ self,
+ dirs=None,
+ app_dirs=False,
+ context_processors=None,
+ debug=False,
+ loaders=None,
+ string_if_invalid="",
+ file_charset="utf-8",
+ libraries=None,
+ builtins=None,
+ autoescape=True,
+ ):
if dirs is None:
dirs = []
if context_processors is None:
context_processors = []
if loaders is None:
- loaders = ['django.template.loaders.filesystem.Loader']
+ loaders = ["django.template.loaders.filesystem.Loader"]
if app_dirs:
- loaders += ['django.template.loaders.app_directories.Loader']
+ loaders += ["django.template.loaders.app_directories.Loader"]
if not debug:
- loaders = [('django.template.loaders.cached.Loader', loaders)]
+ loaders = [("django.template.loaders.cached.Loader", loaders)]
else:
if app_dirs:
raise ImproperlyConfigured(
- "app_dirs must not be set when loaders is defined.")
+ "app_dirs must not be set when loaders is defined."
+ )
if libraries is None:
libraries = {}
if builtins is None:
@@ -54,21 +65,21 @@ class Engine:
def __repr__(self):
return (
- '<%s:%s app_dirs=%s%s debug=%s loaders=%s string_if_invalid=%s '
- 'file_charset=%s%s%s autoescape=%s>'
+ "<%s:%s app_dirs=%s%s debug=%s loaders=%s string_if_invalid=%s "
+ "file_charset=%s%s%s autoescape=%s>"
) % (
self.__class__.__qualname__,
- '' if not self.dirs else ' dirs=%s' % repr(self.dirs),
+ "" if not self.dirs else " dirs=%s" % repr(self.dirs),
self.app_dirs,
- ''
+ ""
if not self.context_processors
- else ' context_processors=%s' % repr(self.context_processors),
+ else " context_processors=%s" % repr(self.context_processors),
self.debug,
repr(self.loaders),
repr(self.string_if_invalid),
repr(self.file_charset),
- '' if not self.libraries else ' libraries=%s' % repr(self.libraries),
- '' if not self.builtins else ' builtins=%s' % repr(self.builtins),
+ "" if not self.libraries else " libraries=%s" % repr(self.libraries),
+ "" if not self.builtins else " builtins=%s" % repr(self.builtins),
repr(self.autoescape),
)
@@ -93,10 +104,11 @@ class Engine:
# local imports are required to avoid import loops.
from django.template import engines
from django.template.backends.django import DjangoTemplates
+
for engine in engines.all():
if isinstance(engine, DjangoTemplates):
return engine.engine
- raise ImproperlyConfigured('No DjangoTemplates backend is configured.')
+ raise ImproperlyConfigured("No DjangoTemplates backend is configured.")
@cached_property
def template_context_processors(self):
@@ -136,7 +148,8 @@ class Engine:
return loader_class(self, *args)
else:
raise ImproperlyConfigured(
- "Invalid value in template loaders configuration: %r" % loader)
+ "Invalid value in template loaders configuration: %r" % loader
+ )
def find_template(self, name, dirs=None, skip=None):
tried = []
@@ -161,7 +174,7 @@ class Engine:
handling template inheritance recursively.
"""
template, origin = self.find_template(template_name)
- if not hasattr(template, 'render'):
+ if not hasattr(template, "render"):
# template needs to be compiled
template = Template(template, origin, template_name, engine=self)
return template
@@ -197,4 +210,4 @@ class Engine:
not_found.append(exc.args[0])
continue
# If we get here, none of the templates could be loaded
- raise TemplateDoesNotExist(', '.join(not_found))
+ raise TemplateDoesNotExist(", ".join(not_found))
diff --git a/django/template/exceptions.py b/django/template/exceptions.py
index 97edc9eba4..2a9c92f779 100644
--- a/django/template/exceptions.py
+++ b/django/template/exceptions.py
@@ -24,6 +24,7 @@ class TemplateDoesNotExist(Exception):
encapsulate multiple exceptions when loading templates from multiple
engines.
"""
+
def __init__(self, msg, tried=None, backend=None, chain=None):
self.backend = backend
if tried is None:
@@ -39,4 +40,5 @@ class TemplateSyntaxError(Exception):
"""
The exception used for syntax errors during parsing or rendering.
"""
+
pass
diff --git a/django/template/library.py b/django/template/library.py
index 06ea5f1ad8..fbec9484a1 100644
--- a/django/template/library.py
+++ b/django/template/library.py
@@ -20,6 +20,7 @@ class Library:
The filter, simple_tag, and inclusion_tag methods provide a convenient
way to register callables as tags.
"""
+
def __init__(self):
self.filters = {}
self.tags = {}
@@ -36,6 +37,7 @@ class Library:
# @register.tag('somename') or @register.tag(name='somename')
def dec(func):
return self.tag(name, func)
+
return dec
elif name is not None and compile_function is not None:
# register.tag('somename', somefunc)
@@ -43,8 +45,8 @@ class Library:
return compile_function
else:
raise ValueError(
- "Unsupported arguments to Library.tag: (%r, %r)" %
- (name, compile_function),
+ "Unsupported arguments to Library.tag: (%r, %r)"
+ % (name, compile_function),
)
def tag_function(self, func):
@@ -63,6 +65,7 @@ class Library:
# @register.filter()
def dec(func):
return self.filter_function(func, **flags)
+
return dec
elif name is not None and filter_func is None:
if callable(name):
@@ -72,11 +75,12 @@ class Library:
# @register.filter('somename') or @register.filter(name='somename')
def dec(func):
return self.filter(name, func, **flags)
+
return dec
elif name is not None and filter_func is not None:
# register.filter('somename', somefunc)
self.filters[name] = filter_func
- for attr in ('expects_localtime', 'is_safe', 'needs_autoescape'):
+ for attr in ("expects_localtime", "is_safe", "needs_autoescape"):
if attr in flags:
value = flags[attr]
# set the flag on the filter for FilterExpression.resolve
@@ -88,8 +92,8 @@ class Library:
return filter_func
else:
raise ValueError(
- "Unsupported arguments to Library.filter: (%r, %r)" %
- (name, filter_func),
+ "Unsupported arguments to Library.filter: (%r, %r)"
+ % (name, filter_func),
)
def filter_function(self, func, **flags):
@@ -103,22 +107,40 @@ class Library:
def hello(*args, **kwargs):
return 'world'
"""
+
def dec(func):
- params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func))
- function_name = (name or func.__name__)
+ (
+ params,
+ varargs,
+ varkw,
+ defaults,
+ kwonly,
+ kwonly_defaults,
+ _,
+ ) = getfullargspec(unwrap(func))
+ function_name = name or func.__name__
@functools.wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
target_var = None
- if len(bits) >= 2 and bits[-2] == 'as':
+ if len(bits) >= 2 and bits[-2] == "as":
target_var = bits[-1]
bits = bits[:-2]
args, kwargs = parse_bits(
- parser, bits, params, varargs, varkw, defaults,
- kwonly, kwonly_defaults, takes_context, function_name,
+ parser,
+ bits,
+ params,
+ varargs,
+ varkw,
+ defaults,
+ kwonly,
+ kwonly_defaults,
+ takes_context,
+ function_name,
)
return SimpleNode(func, takes_context, args, kwargs, target_var)
+
self.tag(function_name, compile_func)
return func
@@ -140,22 +162,45 @@ class Library:
choices = poll.choice_set.all()
return {'choices': choices}
"""
+
def dec(func):
- params, varargs, varkw, defaults, kwonly, kwonly_defaults, _ = getfullargspec(unwrap(func))
+ (
+ params,
+ varargs,
+ varkw,
+ defaults,
+ kwonly,
+ kwonly_defaults,
+ _,
+ ) = getfullargspec(unwrap(func))
function_name = name or func.__name__
@functools.wraps(func)
def compile_func(parser, token):
bits = token.split_contents()[1:]
args, kwargs = parse_bits(
- parser, bits, params, varargs, varkw, defaults,
- kwonly, kwonly_defaults, takes_context, function_name,
+ parser,
+ bits,
+ params,
+ varargs,
+ varkw,
+ defaults,
+ kwonly,
+ kwonly_defaults,
+ takes_context,
+ function_name,
)
return InclusionNode(
- func, takes_context, args, kwargs, filename,
+ func,
+ takes_context,
+ args,
+ kwargs,
+ filename,
)
+
self.tag(function_name, compile_func)
return func
+
return dec
@@ -165,6 +210,7 @@ class TagHelperNode(Node):
Manages the positional and keyword arguments to be passed to the decorated
function.
"""
+
def __init__(self, func, takes_context, args, kwargs):
self.func = func
self.takes_context = takes_context
@@ -191,14 +237,13 @@ class SimpleNode(TagHelperNode):
output = self.func(*resolved_args, **resolved_kwargs)
if self.target_var is not None:
context[self.target_var] = output
- return ''
+ return ""
if context.autoescape:
output = conditional_escape(output)
return output
class InclusionNode(TagHelperNode):
-
def __init__(self, func, takes_context, args, kwargs, filename):
super().__init__(func, takes_context, args, kwargs)
self.filename = filename
@@ -216,7 +261,7 @@ class InclusionNode(TagHelperNode):
if t is None:
if isinstance(self.filename, Template):
t = self.filename
- elif isinstance(getattr(self.filename, 'template', None), Template):
+ elif isinstance(getattr(self.filename, "template", None), Template):
t = self.filename.template
elif not isinstance(self.filename, str) and is_iterable(self.filename):
t = context.template.engine.select_template(self.filename)
@@ -227,32 +272,42 @@ class InclusionNode(TagHelperNode):
# Copy across the CSRF token, if present, because inclusion tags are
# often used for forms, and we need instructions for using CSRF
# protection to be as simple as possible.
- csrf_token = context.get('csrf_token')
+ csrf_token = context.get("csrf_token")
if csrf_token is not None:
- new_context['csrf_token'] = csrf_token
+ new_context["csrf_token"] = csrf_token
return t.render(new_context)
-def parse_bits(parser, bits, params, varargs, varkw, defaults,
- kwonly, kwonly_defaults, takes_context, name):
+def parse_bits(
+ parser,
+ bits,
+ params,
+ varargs,
+ varkw,
+ defaults,
+ kwonly,
+ kwonly_defaults,
+ takes_context,
+ name,
+):
"""
Parse bits for template tag helpers simple_tag and inclusion_tag, in
particular by detecting syntax errors and by extracting positional and
keyword arguments.
"""
if takes_context:
- if params and params[0] == 'context':
+ if params and params[0] == "context":
params = params[1:]
else:
raise TemplateSyntaxError(
"'%s' is decorated with takes_context=True so it must "
- "have a first argument of 'context'" % name)
+ "have a first argument of 'context'" % name
+ )
args = []
kwargs = {}
unhandled_params = list(params)
unhandled_kwargs = [
- kwarg for kwarg in kwonly
- if not kwonly_defaults or kwarg not in kwonly_defaults
+ kwarg for kwarg in kwonly if not kwonly_defaults or kwarg not in kwonly_defaults
]
for bit in bits:
# First we try to extract a potential kwarg from the bit
@@ -263,13 +318,14 @@ def parse_bits(parser, bits, params, varargs, varkw, defaults,
if param not in params and param not in kwonly and varkw is None:
# An unexpected keyword argument was supplied
raise TemplateSyntaxError(
- "'%s' received unexpected keyword argument '%s'" %
- (name, param))
+ "'%s' received unexpected keyword argument '%s'" % (name, param)
+ )
elif param in kwargs:
# The keyword argument has already been supplied once
raise TemplateSyntaxError(
- "'%s' received multiple values for keyword argument '%s'" %
- (name, param))
+ "'%s' received multiple values for keyword argument '%s'"
+ % (name, param)
+ )
else:
# All good, record the keyword argument
kwargs[str(param)] = value
@@ -284,7 +340,8 @@ def parse_bits(parser, bits, params, varargs, varkw, defaults,
if kwargs:
raise TemplateSyntaxError(
"'%s' received some positional argument(s) after some "
- "keyword argument(s)" % name)
+ "keyword argument(s)" % name
+ )
else:
# Record the positional argument
args.append(parser.compile_filter(bit))
@@ -294,17 +351,18 @@ def parse_bits(parser, bits, params, varargs, varkw, defaults,
except IndexError:
if varargs is None:
raise TemplateSyntaxError(
- "'%s' received too many positional arguments" %
- name)
+ "'%s' received too many positional arguments" % name
+ )
if defaults is not None:
# Consider the last n params handled, where n is the
# number of defaults.
- unhandled_params = unhandled_params[:-len(defaults)]
+ unhandled_params = unhandled_params[: -len(defaults)]
if unhandled_params or unhandled_kwargs:
# Some positional arguments were not supplied
raise TemplateSyntaxError(
- "'%s' did not receive value(s) for the argument(s): %s" %
- (name, ", ".join("'%s'" % p for p in unhandled_params + unhandled_kwargs)))
+ "'%s' did not receive value(s) for the argument(s): %s"
+ % (name, ", ".join("'%s'" % p for p in unhandled_params + unhandled_kwargs))
+ )
return args, kwargs
diff --git a/django/template/loader.py b/django/template/loader.py
index 2492aee760..9b108f3456 100644
--- a/django/template/loader.py
+++ b/django/template/loader.py
@@ -29,9 +29,9 @@ def select_template(template_name_list, using=None):
"""
if isinstance(template_name_list, str):
raise TypeError(
- 'select_template() takes an iterable of template names but got a '
- 'string: %r. Use get_template() if you want to load a single '
- 'template by name.' % template_name_list
+ "select_template() takes an iterable of template names but got a "
+ "string: %r. Use get_template() if you want to load a single "
+ "template by name." % template_name_list
)
chain = []
@@ -44,7 +44,7 @@ def select_template(template_name_list, using=None):
chain.append(e)
if template_name_list:
- raise TemplateDoesNotExist(', '.join(template_name_list), chain=chain)
+ raise TemplateDoesNotExist(", ".join(template_name_list), chain=chain)
else:
raise TemplateDoesNotExist("No template names provided")
diff --git a/django/template/loader_tags.py b/django/template/loader_tags.py
index 37cefaf9c7..bf4d4f0d74 100644
--- a/django/template/loader_tags.py
+++ b/django/template/loader_tags.py
@@ -3,14 +3,12 @@ from collections import defaultdict
from django.utils.safestring import mark_safe
-from .base import (
- Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs,
-)
+from .base import Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs
from .library import Library
register = Library()
-BLOCK_CONTEXT_KEY = 'block_context'
+BLOCK_CONTEXT_KEY = "block_context"
class BlockContext:
@@ -19,7 +17,7 @@ class BlockContext:
self.blocks = defaultdict(list)
def __repr__(self):
- return f'<{self.__class__.__qualname__}: blocks={self.blocks!r}>'
+ return f"<{self.__class__.__qualname__}: blocks={self.blocks!r}>"
def add_blocks(self, blocks):
for name, block in blocks.items():
@@ -52,7 +50,7 @@ class BlockNode(Node):
block_context = context.render_context.get(BLOCK_CONTEXT_KEY)
with context.push():
if block_context is None:
- context['block'] = self
+ context["block"] = self
result = self.nodelist.render(context)
else:
push = block = block_context.pop(self.name)
@@ -61,28 +59,30 @@ class BlockNode(Node):
# Create new block so we can store context without thread-safety issues.
block = type(self)(block.name, block.nodelist)
block.context = context
- context['block'] = block
+ context["block"] = block
result = block.nodelist.render(context)
if push is not None:
block_context.push(self.name, push)
return result
def super(self):
- if not hasattr(self, 'context'):
+ if not hasattr(self, "context"):
raise TemplateSyntaxError(
"'%s' object has no attribute 'context'. Did you use "
"{{ block.super }} in a base template?" % self.__class__.__name__
)
render_context = self.context.render_context
- if (BLOCK_CONTEXT_KEY in render_context and
- render_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None):
+ if (
+ BLOCK_CONTEXT_KEY in render_context
+ and render_context[BLOCK_CONTEXT_KEY].get_block(self.name) is not None
+ ):
return mark_safe(self.render(self.context))
- return ''
+ return ""
class ExtendsNode(Node):
must_be_first = True
- context_key = 'extends_context'
+ context_key = "extends_context"
def __init__(self, nodelist, parent_name, template_dirs=None):
self.nodelist = nodelist
@@ -91,7 +91,7 @@ class ExtendsNode(Node):
self.blocks = {n.name: n for n in nodelist.get_nodes_by_type(BlockNode)}
def __repr__(self):
- return '<%s: extends %s>' % (self.__class__.__name__, self.parent_name.token)
+ return "<%s: extends %s>" % (self.__class__.__name__, self.parent_name.token)
def find_template(self, template_name, context):
"""
@@ -101,10 +101,12 @@ class ExtendsNode(Node):
without extending the same template twice.
"""
history = context.render_context.setdefault(
- self.context_key, [self.origin],
+ self.context_key,
+ [self.origin],
)
template, origin = context.template.engine.find_template(
- template_name, skip=history,
+ template_name,
+ skip=history,
)
history.append(origin)
return template
@@ -113,15 +115,15 @@ class ExtendsNode(Node):
parent = self.parent_name.resolve(context)
if not parent:
error_msg = "Invalid template name in 'extends' tag: %r." % parent
- if self.parent_name.filters or\
- isinstance(self.parent_name.var, Variable):
- error_msg += " Got this from the '%s' variable." %\
- self.parent_name.token
+ if self.parent_name.filters or isinstance(self.parent_name.var, Variable):
+ error_msg += (
+ " Got this from the '%s' variable." % self.parent_name.token
+ )
raise TemplateSyntaxError(error_msg)
if isinstance(parent, Template):
# parent is a django.template.Template
return parent
- if isinstance(getattr(parent, 'template', None), Template):
+ if isinstance(getattr(parent, "template", None), Template):
# parent is a django.template.backends.django.Template
return parent.template
return self.find_template(parent, context)
@@ -142,8 +144,10 @@ class ExtendsNode(Node):
# The ExtendsNode has to be the first non-text node.
if not isinstance(node, TextNode):
if not isinstance(node, ExtendsNode):
- blocks = {n.name: n for n in
- compiled_parent.nodelist.get_nodes_by_type(BlockNode)}
+ blocks = {
+ n.name: n
+ for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)
+ }
block_context.add_blocks(blocks)
break
@@ -154,16 +158,18 @@ class ExtendsNode(Node):
class IncludeNode(Node):
- context_key = '__include_context'
+ context_key = "__include_context"
- def __init__(self, template, *args, extra_context=None, isolated_context=False, **kwargs):
+ def __init__(
+ self, template, *args, extra_context=None, isolated_context=False, **kwargs
+ ):
self.template = template
self.extra_context = extra_context or {}
self.isolated_context = isolated_context
super().__init__(*args, **kwargs)
def __repr__(self):
- return f'<{self.__class__.__qualname__}: template={self.template!r}>'
+ return f"<{self.__class__.__qualname__}: template={self.template!r}>"
def render(self, context):
"""
@@ -173,14 +179,16 @@ class IncludeNode(Node):
"""
template = self.template.resolve(context)
# Does this quack like a Template?
- if not callable(getattr(template, 'render', None)):
+ if not callable(getattr(template, "render", None)):
# If not, try the cache and select_template().
template_name = template or ()
if isinstance(template_name, str):
- template_name = (construct_relative_path(
- self.origin.template_name,
- template_name,
- ),)
+ template_name = (
+ construct_relative_path(
+ self.origin.template_name,
+ template_name,
+ ),
+ )
else:
template_name = tuple(template_name)
cache = context.render_context.dicts[0].setdefault(self, {})
@@ -189,11 +197,10 @@ class IncludeNode(Node):
template = context.template.engine.select_template(template_name)
cache[template_name] = template
# Use the base.Template of a backends.django.Template.
- elif hasattr(template, 'template'):
+ elif hasattr(template, "template"):
template = template.template
values = {
- name: var.resolve(context)
- for name, var in self.extra_context.items()
+ name: var.resolve(context) for name, var in self.extra_context.items()
}
if self.isolated_context:
return template.render(context.new(values))
@@ -201,7 +208,7 @@ class IncludeNode(Node):
return template.render(context)
-@register.tag('block')
+@register.tag("block")
def do_block(parser, token):
"""
Define a block that can be overridden by child templates.
@@ -215,17 +222,19 @@ def do_block(parser, token):
# check for duplication.
try:
if block_name in parser.__loaded_blocks:
- raise TemplateSyntaxError("'%s' tag with name '%s' appears more than once" % (bits[0], block_name))
+ raise TemplateSyntaxError(
+ "'%s' tag with name '%s' appears more than once" % (bits[0], block_name)
+ )
parser.__loaded_blocks.append(block_name)
except AttributeError: # parser.__loaded_blocks isn't a list yet
parser.__loaded_blocks = [block_name]
- nodelist = parser.parse(('endblock',))
+ nodelist = parser.parse(("endblock",))
# This check is kept for backwards-compatibility. See #3100.
endblock = parser.next_token()
- acceptable_endblocks = ('endblock', 'endblock %s' % block_name)
+ acceptable_endblocks = ("endblock", "endblock %s" % block_name)
if endblock.contents not in acceptable_endblocks:
- parser.invalid_block_tag(endblock, 'endblock', acceptable_endblocks)
+ parser.invalid_block_tag(endblock, "endblock", acceptable_endblocks)
return BlockNode(block_name, nodelist)
@@ -235,37 +244,36 @@ def construct_relative_path(current_template_name, relative_name):
Convert a relative path (starting with './' or '../') to the full template
name based on the current_template_name.
"""
- new_name = relative_name.strip('\'"')
- if not new_name.startswith(('./', '../')):
+ new_name = relative_name.strip("'\"")
+ if not new_name.startswith(("./", "../")):
# relative_name is a variable or a literal that doesn't contain a
# relative path.
return relative_name
new_name = posixpath.normpath(
posixpath.join(
- posixpath.dirname(current_template_name.lstrip('/')),
+ posixpath.dirname(current_template_name.lstrip("/")),
new_name,
)
)
- if new_name.startswith('../'):
+ if new_name.startswith("../"):
raise TemplateSyntaxError(
"The relative path '%s' points outside the file hierarchy that "
"template '%s' is in." % (relative_name, current_template_name)
)
- if current_template_name.lstrip('/') == new_name:
+ if current_template_name.lstrip("/") == new_name:
raise TemplateSyntaxError(
"The relative path '%s' was translated to template name '%s', the "
"same template in which the tag appears."
% (relative_name, current_template_name)
)
has_quotes = (
- relative_name.startswith(('"', "'")) and
- relative_name[0] == relative_name[-1]
+ relative_name.startswith(('"', "'")) and relative_name[0] == relative_name[-1]
)
return f'"{new_name}"' if has_quotes else new_name
-@register.tag('extends')
+@register.tag("extends")
def do_extends(parser, token):
"""
Signal that this template extends a parent template.
@@ -283,11 +291,13 @@ def do_extends(parser, token):
parent_name = parser.compile_filter(bits[1])
nodelist = parser.parse()
if nodelist.get_nodes_by_type(ExtendsNode):
- raise TemplateSyntaxError("'%s' cannot appear more than once in the same template" % bits[0])
+ raise TemplateSyntaxError(
+ "'%s' cannot appear more than once in the same template" % bits[0]
+ )
return ExtendsNode(nodelist, parent_name)
-@register.tag('include')
+@register.tag("include")
def do_include(parser, token):
"""
Load a template and render it with the current context. You can pass
@@ -316,22 +326,26 @@ def do_include(parser, token):
option = remaining_bits.pop(0)
if option in options:
raise TemplateSyntaxError(
- 'The %r option was specified more than once.' % option
+ "The %r option was specified more than once." % option
)
- if option == 'with':
+ if option == "with":
value = token_kwargs(remaining_bits, parser, support_legacy=False)
if not value:
raise TemplateSyntaxError(
'"with" in %r tag needs at least one keyword argument.' % bits[0]
)
- elif option == 'only':
+ elif option == "only":
value = True
else:
- raise TemplateSyntaxError('Unknown argument for %r tag: %r.' %
- (bits[0], option))
+ raise TemplateSyntaxError(
+ "Unknown argument for %r tag: %r." % (bits[0], option)
+ )
options[option] = value
- isolated_context = options.get('only', False)
- namemap = options.get('with', {})
+ isolated_context = options.get("only", False)
+ namemap = options.get("with", {})
bits[1] = construct_relative_path(parser.origin.template_name, bits[1])
- return IncludeNode(parser.compile_filter(bits[1]), extra_context=namemap,
- isolated_context=isolated_context)
+ return IncludeNode(
+ parser.compile_filter(bits[1]),
+ extra_context=namemap,
+ isolated_context=isolated_context,
+ )
diff --git a/django/template/loaders/app_directories.py b/django/template/loaders/app_directories.py
index c9a8adf49c..0bd7dc8e25 100644
--- a/django/template/loaders/app_directories.py
+++ b/django/template/loaders/app_directories.py
@@ -9,6 +9,5 @@ from .filesystem import Loader as FilesystemLoader
class Loader(FilesystemLoader):
-
def get_dirs(self):
- return get_app_template_dirs('templates')
+ return get_app_template_dirs("templates")
diff --git a/django/template/loaders/base.py b/django/template/loaders/base.py
index b77ea9eca2..9168c26c54 100644
--- a/django/template/loaders/base.py
+++ b/django/template/loaders/base.py
@@ -2,7 +2,6 @@ from django.template import Template, TemplateDoesNotExist
class Loader:
-
def __init__(self, engine):
self.engine = engine
@@ -17,17 +16,20 @@ class Loader:
for origin in self.get_template_sources(template_name):
if skip is not None and origin in skip:
- tried.append((origin, 'Skipped to avoid recursion'))
+ tried.append((origin, "Skipped to avoid recursion"))
continue
try:
contents = self.get_contents(origin)
except TemplateDoesNotExist:
- tried.append((origin, 'Source does not exist'))
+ tried.append((origin, "Source does not exist"))
continue
else:
return Template(
- contents, origin, origin.template_name, self.engine,
+ contents,
+ origin,
+ origin.template_name,
+ self.engine,
)
raise TemplateDoesNotExist(template_name, tried=tried)
@@ -38,7 +40,7 @@ class Loader:
template name.
"""
raise NotImplementedError(
- 'subclasses of Loader must provide a get_template_sources() method'
+ "subclasses of Loader must provide a get_template_sources() method"
)
def reset(self):
diff --git a/django/template/loaders/cached.py b/django/template/loaders/cached.py
index bb47682d7f..4f40953831 100644
--- a/django/template/loaders/cached.py
+++ b/django/template/loaders/cached.py
@@ -12,7 +12,6 @@ from .base import Loader as BaseLoader
class Loader(BaseLoader):
-
def __init__(self, engine, loaders):
self.get_template_cache = {}
self.loaders = engine.get_template_loaders(loaders)
@@ -57,7 +56,9 @@ class Loader(BaseLoader):
try:
template = super().get_template(template_name, skip)
except TemplateDoesNotExist as e:
- self.get_template_cache[key] = copy_exception(e) if self.engine.debug else TemplateDoesNotExist
+ self.get_template_cache[key] = (
+ copy_exception(e) if self.engine.debug else TemplateDoesNotExist
+ )
raise
else:
self.get_template_cache[key] = template
@@ -80,17 +81,19 @@ class Loader(BaseLoader):
y -> a -> a
z -> a -> a
"""
- skip_prefix = ''
+ skip_prefix = ""
if skip:
- matching = [origin.name for origin in skip if origin.template_name == template_name]
+ matching = [
+ origin.name for origin in skip if origin.template_name == template_name
+ ]
if matching:
skip_prefix = self.generate_hash(matching)
- return '-'.join(s for s in (str(template_name), skip_prefix) if s)
+ return "-".join(s for s in (str(template_name), skip_prefix) if s)
def generate_hash(self, values):
- return hashlib.sha1('|'.join(values).encode()).hexdigest()
+ return hashlib.sha1("|".join(values).encode()).hexdigest()
def reset(self):
"Empty the template cache."
diff --git a/django/template/loaders/filesystem.py b/django/template/loaders/filesystem.py
index 2e49e3d6b3..a2474a3fad 100644
--- a/django/template/loaders/filesystem.py
+++ b/django/template/loaders/filesystem.py
@@ -10,7 +10,6 @@ from .base import Loader as BaseLoader
class Loader(BaseLoader):
-
def __init__(self, engine, dirs=None):
super().__init__(engine)
self.dirs = dirs
diff --git a/django/template/loaders/locmem.py b/django/template/loaders/locmem.py
index 25d7672719..432de62b6c 100644
--- a/django/template/loaders/locmem.py
+++ b/django/template/loaders/locmem.py
@@ -8,7 +8,6 @@ from .base import Loader as BaseLoader
class Loader(BaseLoader):
-
def __init__(self, engine, templates_dict):
self.templates_dict = templates_dict
super().__init__(engine)
diff --git a/django/template/response.py b/django/template/response.py
index 63d2f4a577..c38b95e9de 100644
--- a/django/template/response.py
+++ b/django/template/response.py
@@ -8,10 +8,18 @@ class ContentNotRenderedError(Exception):
class SimpleTemplateResponse(HttpResponse):
- rendering_attrs = ['template_name', 'context_data', '_post_render_callbacks']
+ rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"]
- def __init__(self, template, context=None, content_type=None, status=None,
- charset=None, using=None, headers=None):
+ def __init__(
+ self,
+ template,
+ context=None,
+ content_type=None,
+ status=None,
+ charset=None,
+ using=None,
+ headers=None,
+ ):
# It would seem obvious to call these next two members 'template' and
# 'context', but those names are reserved as part of the test Client
# API. To avoid the name collision, we use different names.
@@ -33,7 +41,7 @@ class SimpleTemplateResponse(HttpResponse):
# content argument doesn't make sense here because it will be replaced
# with rendered template so we always pass empty string in order to
# prevent errors and provide shorter signature.
- super().__init__('', content_type, status, charset=charset, headers=headers)
+ super().__init__("", content_type, status, charset=charset, headers=headers)
# _is_rendered tracks whether the template and context has been baked
# into a final response.
@@ -50,7 +58,7 @@ class SimpleTemplateResponse(HttpResponse):
obj_dict = self.__dict__.copy()
if not self._is_rendered:
raise ContentNotRenderedError(
- 'The response content must be rendered before it can be pickled.'
+ "The response content must be rendered before it can be pickled."
)
for attr in self.rendering_attrs:
if attr in obj_dict:
@@ -117,7 +125,7 @@ class SimpleTemplateResponse(HttpResponse):
def __iter__(self):
if not self._is_rendered:
raise ContentNotRenderedError(
- 'The response content must be rendered before it can be iterated over.'
+ "The response content must be rendered before it can be iterated over."
)
return super().__iter__()
@@ -125,7 +133,7 @@ class SimpleTemplateResponse(HttpResponse):
def content(self):
if not self._is_rendered:
raise ContentNotRenderedError(
- 'The response content must be rendered before it can be accessed.'
+ "The response content must be rendered before it can be accessed."
)
return super().content
@@ -137,9 +145,20 @@ class SimpleTemplateResponse(HttpResponse):
class TemplateResponse(SimpleTemplateResponse):
- rendering_attrs = SimpleTemplateResponse.rendering_attrs + ['_request']
+ rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"]
- def __init__(self, request, template, context=None, content_type=None,
- status=None, charset=None, using=None, headers=None):
- super().__init__(template, context, content_type, status, charset, using, headers=headers)
+ def __init__(
+ self,
+ request,
+ template,
+ context=None,
+ content_type=None,
+ status=None,
+ charset=None,
+ using=None,
+ headers=None,
+ ):
+ super().__init__(
+ template, context, content_type, status, charset, using, headers=headers
+ )
self._request = request
diff --git a/django/template/smartif.py b/django/template/smartif.py
index 96a1af8db0..5b15a5a476 100644
--- a/django/template/smartif.py
+++ b/django/template/smartif.py
@@ -13,6 +13,7 @@ class TokenBase:
Base class for operators and literals, mainly for debugging and for throwing
syntax errors.
"""
+
id = None # node/token type name
value = None # used by literals
first = second = None # used by tree nodes
@@ -45,6 +46,7 @@ def infix(bp, func):
Create an infix operator, given a binding power and a function that
evaluates the node.
"""
+
class Operator(TokenBase):
lbp = bp
@@ -70,6 +72,7 @@ def prefix(bp, func):
Create a prefix operator, given a binding power and a function that
evaluates the node.
"""
+
class Operator(TokenBase):
lbp = bp
@@ -91,19 +94,19 @@ def prefix(bp, func):
# We defer variable evaluation to the lambda to ensure that terms are
# lazily evaluated using Python's boolean parsing logic.
OPERATORS = {
- 'or': infix(6, lambda context, x, y: x.eval(context) or y.eval(context)),
- 'and': infix(7, lambda context, x, y: x.eval(context) and y.eval(context)),
- 'not': prefix(8, lambda context, x: not x.eval(context)),
- 'in': infix(9, lambda context, x, y: x.eval(context) in y.eval(context)),
- 'not in': infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)),
- 'is': infix(10, lambda context, x, y: x.eval(context) is y.eval(context)),
- 'is not': infix(10, lambda context, x, y: x.eval(context) is not y.eval(context)),
- '==': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)),
- '!=': infix(10, lambda context, x, y: x.eval(context) != y.eval(context)),
- '>': infix(10, lambda context, x, y: x.eval(context) > y.eval(context)),
- '>=': infix(10, lambda context, x, y: x.eval(context) >= y.eval(context)),
- '<': infix(10, lambda context, x, y: x.eval(context) < y.eval(context)),
- '<=': infix(10, lambda context, x, y: x.eval(context) <= y.eval(context)),
+ "or": infix(6, lambda context, x, y: x.eval(context) or y.eval(context)),
+ "and": infix(7, lambda context, x, y: x.eval(context) and y.eval(context)),
+ "not": prefix(8, lambda context, x: not x.eval(context)),
+ "in": infix(9, lambda context, x, y: x.eval(context) in y.eval(context)),
+ "not in": infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)),
+ "is": infix(10, lambda context, x, y: x.eval(context) is y.eval(context)),
+ "is not": infix(10, lambda context, x, y: x.eval(context) is not y.eval(context)),
+ "==": infix(10, lambda context, x, y: x.eval(context) == y.eval(context)),
+ "!=": infix(10, lambda context, x, y: x.eval(context) != y.eval(context)),
+ ">": infix(10, lambda context, x, y: x.eval(context) > y.eval(context)),
+ ">=": infix(10, lambda context, x, y: x.eval(context) >= y.eval(context)),
+ "<": infix(10, lambda context, x, y: x.eval(context) < y.eval(context)),
+ "<=": infix(10, lambda context, x, y: x.eval(context) <= y.eval(context)),
}
# Assign 'id' to each:
@@ -115,6 +118,7 @@ class Literal(TokenBase):
"""
A basic self-resolvable object similar to a Django template variable.
"""
+
# IfParser uses Literal in create_var, but TemplateIfParser overrides
# create_var so that a proper implementation that actually resolves
# variables, filters etc. is used.
@@ -190,8 +194,9 @@ class IfParser:
retval = self.expression()
# Check that we have exhausted all the tokens
if self.current_token is not EndToken:
- raise self.error_class("Unused '%s' at end of if expression." %
- self.current_token.display())
+ raise self.error_class(
+ "Unused '%s' at end of if expression." % self.current_token.display()
+ )
return retval
def expression(self, rbp=0):
diff --git a/django/template/utils.py b/django/template/utils.py
index ad7baba2f3..2b118f900e 100644
--- a/django/template/utils.py
+++ b/django/template/utils.py
@@ -33,31 +33,34 @@ class EngineHandler:
try:
# This will raise an exception if 'BACKEND' doesn't exist or
# isn't a string containing at least one dot.
- default_name = tpl['BACKEND'].rsplit('.', 2)[-2]
+ default_name = tpl["BACKEND"].rsplit(".", 2)[-2]
except Exception:
- invalid_backend = tpl.get('BACKEND', '<not defined>')
+ invalid_backend = tpl.get("BACKEND", "<not defined>")
raise ImproperlyConfigured(
"Invalid BACKEND for a template engine: {}. Check "
- "your TEMPLATES setting.".format(invalid_backend))
+ "your TEMPLATES setting.".format(invalid_backend)
+ )
tpl = {
- 'NAME': default_name,
- 'DIRS': [],
- 'APP_DIRS': False,
- 'OPTIONS': {},
+ "NAME": default_name,
+ "DIRS": [],
+ "APP_DIRS": False,
+ "OPTIONS": {},
**tpl,
}
- templates[tpl['NAME']] = tpl
- backend_names.append(tpl['NAME'])
+ templates[tpl["NAME"]] = tpl
+ backend_names.append(tpl["NAME"])
counts = Counter(backend_names)
duplicates = [alias for alias, count in counts.most_common() if count > 1]
if duplicates:
raise ImproperlyConfigured(
"Template engine aliases aren't unique, duplicates: {}. "
- "Set a unique NAME for each engine in settings.TEMPLATES."
- .format(", ".join(duplicates)))
+ "Set a unique NAME for each engine in settings.TEMPLATES.".format(
+ ", ".join(duplicates)
+ )
+ )
return templates
@@ -70,13 +73,14 @@ class EngineHandler:
except KeyError:
raise InvalidTemplateEngineError(
"Could not find config for '{}' "
- "in settings.TEMPLATES".format(alias))
+ "in settings.TEMPLATES".format(alias)
+ )
# If importing or initializing the backend raises an exception,
# self._engines[alias] isn't set and this code may get executed
# again, so we must preserve the original params. See #24265.
params = params.copy()
- backend = params.pop('BACKEND')
+ backend = params.pop("BACKEND")
engine_cls = import_string(backend)
engine = engine_cls(params)