first comit
This commit is contained in:
75
venv/lib/python3.10/site-packages/django/template/__init__.py
Executable file
75
venv/lib/python3.10/site-packages/django/template/__init__.py
Executable file
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
Django's support for templates.
|
||||
|
||||
The django.template namespace contains two independent subsystems:
|
||||
|
||||
1. Multiple Template Engines: support for pluggable template backends,
|
||||
built-in backends and backend-independent APIs
|
||||
2. Django Template Language: Django's own template engine, including its
|
||||
built-in loaders, context processors, tags and filters.
|
||||
|
||||
Ideally these subsystems would be implemented in distinct packages. However
|
||||
keeping them together made the implementation of Multiple Template Engines
|
||||
less disruptive .
|
||||
|
||||
Here's a breakdown of which modules belong to which subsystem.
|
||||
|
||||
Multiple Template Engines:
|
||||
|
||||
- django.template.backends.*
|
||||
- django.template.loader
|
||||
- django.template.response
|
||||
|
||||
Django Template Language:
|
||||
|
||||
- django.template.base
|
||||
- django.template.context
|
||||
- django.template.context_processors
|
||||
- django.template.loaders.*
|
||||
- django.template.debug
|
||||
- django.template.defaultfilters
|
||||
- django.template.defaulttags
|
||||
- django.template.engine
|
||||
- django.template.loader_tags
|
||||
- django.template.smartif
|
||||
|
||||
Shared:
|
||||
|
||||
- django.template.utils
|
||||
|
||||
"""
|
||||
|
||||
# Multiple Template Engines
|
||||
|
||||
from .engine import Engine
|
||||
from .utils import EngineHandler
|
||||
|
||||
engines = EngineHandler()
|
||||
|
||||
__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
|
||||
|
||||
# Template parts
|
||||
from .base import ( # NOQA isort:skip
|
||||
Node,
|
||||
NodeList,
|
||||
Origin,
|
||||
Template,
|
||||
Variable,
|
||||
)
|
||||
|
||||
# Library management
|
||||
from .library import Library # NOQA isort:skip
|
||||
|
||||
# Import the .autoreload module to trigger the registrations of signals.
|
||||
from . import autoreload # NOQA isort:skip
|
||||
|
||||
|
||||
__all__ += ("Template", "Context", "RequestContext")
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
61
venv/lib/python3.10/site-packages/django/template/autoreload.py
Executable file
61
venv/lib/python3.10/site-packages/django/template/autoreload.py
Executable file
@@ -0,0 +1,61 @@
|
||||
from pathlib import Path
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_template_directories():
|
||||
# Iterate through each template backend and find
|
||||
# any template_loader that has a 'get_dirs' method.
|
||||
# Collect the directories, filtering out Django templates.
|
||||
cwd = Path.cwd()
|
||||
items = set()
|
||||
for backend in engines.all():
|
||||
if not isinstance(backend, DjangoTemplates):
|
||||
continue
|
||||
|
||||
items.update(cwd / to_path(dir) for dir in backend.engine.dirs if dir)
|
||||
|
||||
for loader in backend.engine.template_loaders:
|
||||
if not hasattr(loader, "get_dirs"):
|
||||
continue
|
||||
items.update(
|
||||
cwd / to_path(directory)
|
||||
for directory in loader.get_dirs()
|
||||
if directory and not is_django_path(directory)
|
||||
)
|
||||
return items
|
||||
|
||||
|
||||
def reset_loaders():
|
||||
from django.forms.renderers import get_default_renderer
|
||||
|
||||
for backend in engines.all():
|
||||
if not isinstance(backend, DjangoTemplates):
|
||||
continue
|
||||
for loader in backend.engine.template_loaders:
|
||||
loader.reset()
|
||||
|
||||
backend = getattr(get_default_renderer(), "engine", None)
|
||||
if isinstance(backend, DjangoTemplates):
|
||||
for loader in backend.engine.template_loaders:
|
||||
loader.reset()
|
||||
|
||||
|
||||
@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, "**/*")
|
||||
|
||||
|
||||
@receiver(file_changed, dispatch_uid="template_loaders_file_changed")
|
||||
def template_changed(sender, file_path, **kwargs):
|
||||
if file_path.suffix == ".py":
|
||||
return
|
||||
for template_dir in get_template_directories():
|
||||
if template_dir in file_path.parents:
|
||||
reset_loaders()
|
||||
return True
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
80
venv/lib/python3.10/site-packages/django/template/backends/base.py
Executable file
80
venv/lib/python3.10/site-packages/django/template/backends/base.py
Executable file
@@ -0,0 +1,80 @@
|
||||
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
|
||||
|
||||
|
||||
class BaseEngine:
|
||||
# Core methods: engines have to provide their own implementation
|
||||
# (except for from_string which is optional).
|
||||
|
||||
def __init__(self, params):
|
||||
"""
|
||||
Initialize the template engine.
|
||||
|
||||
`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")
|
||||
if params:
|
||||
raise ImproperlyConfigured(
|
||||
"Unknown parameters: {}".format(", ".join(params))
|
||||
)
|
||||
|
||||
@property
|
||||
def app_dirname(self):
|
||||
raise ImproperlyConfigured(
|
||||
"{} doesn't support loading templates from installed "
|
||||
"applications.".format(self.__class__.__name__)
|
||||
)
|
||||
|
||||
def from_string(self, template_code):
|
||||
"""
|
||||
Create and return a template for the given source code.
|
||||
|
||||
This method is optional.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseEngine should provide a from_string() method"
|
||||
)
|
||||
|
||||
def get_template(self, template_name):
|
||||
"""
|
||||
Load and return a template for the given name.
|
||||
|
||||
Raise TemplateDoesNotExist if no such template exists.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of BaseEngine must provide a get_template() method"
|
||||
)
|
||||
|
||||
# Utility methods: they are provided to minimize code duplication and
|
||||
# security issues in third-party backends.
|
||||
|
||||
@cached_property
|
||||
def template_dirs(self):
|
||||
"""
|
||||
Return a list of directories to search for templates.
|
||||
"""
|
||||
# Immutable return value because it's cached and shared by callers.
|
||||
template_dirs = tuple(self.dirs)
|
||||
if self.app_dirs:
|
||||
template_dirs += get_app_template_dirs(self.app_dirname)
|
||||
return template_dirs
|
||||
|
||||
def iter_template_filenames(self, template_name):
|
||||
"""
|
||||
Iterate over candidate files for template_name.
|
||||
|
||||
Ignore files that don't lie inside configured template dirs to avoid
|
||||
directory traversal attacks.
|
||||
"""
|
||||
for template_dir in self.template_dirs:
|
||||
try:
|
||||
yield safe_join(template_dir, template_name)
|
||||
except SuspiciousFileOperation:
|
||||
# The joined path was located outside of this template_dir
|
||||
# (it might be inside another one, so this isn't fatal).
|
||||
pass
|
||||
136
venv/lib/python3.10/site-packages/django/template/backends/django.py
Executable file
136
venv/lib/python3.10/site-packages/django/template/backends/django.py
Executable file
@@ -0,0 +1,136 @@
|
||||
from importlib import import_module
|
||||
from pkgutil import walk_packages
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template.context import make_context
|
||||
from django.template.engine import Engine
|
||||
from django.template.library import InvalidTemplateLibrary
|
||||
|
||||
from .base import BaseEngine
|
||||
|
||||
|
||||
class DjangoTemplates(BaseEngine):
|
||||
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)
|
||||
super().__init__(params)
|
||||
self.engine = Engine(self.dirs, self.app_dirs, **options)
|
||||
|
||||
def from_string(self, template_code):
|
||||
return Template(self.engine.from_string(template_code), self)
|
||||
|
||||
def get_template(self, template_name):
|
||||
try:
|
||||
return Template(self.engine.get_template(template_name), self)
|
||||
except TemplateDoesNotExist as exc:
|
||||
reraise(exc, self)
|
||||
|
||||
def get_templatetag_libraries(self, custom_libraries):
|
||||
"""
|
||||
Return a collation of template tag libraries from installed
|
||||
applications and the supplied custom_libraries argument.
|
||||
"""
|
||||
libraries = get_installed_libraries()
|
||||
libraries.update(custom_libraries)
|
||||
return libraries
|
||||
|
||||
|
||||
class Template:
|
||||
def __init__(self, template, backend):
|
||||
self.template = template
|
||||
self.backend = backend
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self.template.origin
|
||||
|
||||
def render(self, context=None, request=None):
|
||||
context = make_context(
|
||||
context, request, autoescape=self.backend.engine.autoescape
|
||||
)
|
||||
try:
|
||||
return self.template.render(context)
|
||||
except TemplateDoesNotExist as exc:
|
||||
reraise(exc, self.backend)
|
||||
|
||||
|
||||
def copy_exception(exc, backend=None):
|
||||
"""
|
||||
Create a new TemplateDoesNotExist. Preserve its declared attributes and
|
||||
template debug data but discard __traceback__, __context__, and __cause__
|
||||
to make this object suitable for keeping around (in a cache, for example).
|
||||
"""
|
||||
backend = backend or exc.backend
|
||||
new = exc.__class__(*exc.args, tried=exc.tried, backend=backend, chain=exc.chain)
|
||||
if hasattr(exc, "template_debug"):
|
||||
new.template_debug = exc.template_debug
|
||||
return new
|
||||
|
||||
|
||||
def reraise(exc, backend):
|
||||
"""
|
||||
Reraise TemplateDoesNotExist while maintaining template debug information.
|
||||
"""
|
||||
new = copy_exception(exc, backend)
|
||||
raise new from exc
|
||||
|
||||
|
||||
def get_template_tag_modules():
|
||||
"""
|
||||
Yield (module_name, module_path) pairs for all installed template tag
|
||||
libraries.
|
||||
"""
|
||||
candidates = ["django.templatetags"]
|
||||
candidates.extend(
|
||||
f"{app_config.name}.templatetags" for app_config in apps.get_app_configs()
|
||||
)
|
||||
|
||||
for candidate in candidates:
|
||||
try:
|
||||
pkg = import_module(candidate)
|
||||
except ImportError:
|
||||
# No templatetags package defined. This is safe to ignore.
|
||||
continue
|
||||
|
||||
if hasattr(pkg, "__path__"):
|
||||
for name in get_package_libraries(pkg):
|
||||
yield name.removeprefix(candidate).lstrip("."), name
|
||||
|
||||
|
||||
def get_installed_libraries():
|
||||
"""
|
||||
Return the built-in template tag libraries and those from installed
|
||||
applications. Libraries are stored in a dictionary where keys are the
|
||||
individual module names, not the full module paths. Example:
|
||||
django.templatetags.i18n is stored as i18n.
|
||||
"""
|
||||
return {
|
||||
module_name: full_name for module_name, full_name in get_template_tag_modules()
|
||||
}
|
||||
|
||||
|
||||
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__ + "."):
|
||||
try:
|
||||
module = import_module(entry[1])
|
||||
except ImportError as e:
|
||||
raise InvalidTemplateLibrary(
|
||||
"Invalid template library specified. ImportError raised when "
|
||||
"trying to load '%s': %s" % (entry[1], e)
|
||||
) from e
|
||||
|
||||
if hasattr(module, "register"):
|
||||
yield entry[1]
|
||||
51
venv/lib/python3.10/site-packages/django/template/backends/dummy.py
Executable file
51
venv/lib/python3.10/site-packages/django/template/backends/dummy.py
Executable file
@@ -0,0 +1,51 @@
|
||||
import string
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.template import Origin, TemplateDoesNotExist
|
||||
from django.utils.html import conditional_escape
|
||||
|
||||
from .base import BaseEngine
|
||||
from .utils import csrf_input_lazy, csrf_token_lazy
|
||||
|
||||
|
||||
class TemplateStrings(BaseEngine):
|
||||
app_dirname = "template_strings"
|
||||
|
||||
def __init__(self, params):
|
||||
params = params.copy()
|
||||
options = params.pop("OPTIONS").copy()
|
||||
if options:
|
||||
raise ImproperlyConfigured("Unknown options: {}".format(", ".join(options)))
|
||||
super().__init__(params)
|
||||
|
||||
def from_string(self, template_code):
|
||||
return Template(template_code)
|
||||
|
||||
def get_template(self, template_name):
|
||||
tried = []
|
||||
for template_file in self.iter_template_filenames(template_name):
|
||||
try:
|
||||
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",
|
||||
)
|
||||
)
|
||||
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)
|
||||
return self.safe_substitute(context)
|
||||
125
venv/lib/python3.10/site-packages/django/template/backends/jinja2.py
Executable file
125
venv/lib/python3.10/site-packages/django/template/backends/jinja2.py
Executable file
@@ -0,0 +1,125 @@
|
||||
from pathlib import Path
|
||||
|
||||
import jinja2
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import TemplateDoesNotExist, TemplateSyntaxError
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
from .base import BaseEngine
|
||||
from .utils import csrf_input_lazy, csrf_token_lazy
|
||||
|
||||
|
||||
class Jinja2(BaseEngine):
|
||||
app_dirname = "jinja2"
|
||||
|
||||
def __init__(self, params):
|
||||
params = params.copy()
|
||||
options = params.pop("OPTIONS").copy()
|
||||
super().__init__(params)
|
||||
|
||||
self.context_processors = options.pop("context_processors", [])
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
self.env = environment_cls(**options)
|
||||
|
||||
def from_string(self, template_code):
|
||||
return Template(self.env.from_string(template_code), self)
|
||||
|
||||
def get_template(self, template_name):
|
||||
try:
|
||||
return Template(self.env.get_template(template_name), self)
|
||||
except jinja2.TemplateNotFound as exc:
|
||||
raise TemplateDoesNotExist(exc.name, backend=self) from exc
|
||||
except jinja2.TemplateSyntaxError as exc:
|
||||
new = TemplateSyntaxError(exc.args)
|
||||
new.template_debug = get_exception_info(exc)
|
||||
raise new from exc
|
||||
|
||||
@cached_property
|
||||
def template_context_processors(self):
|
||||
return [import_string(path) for path in self.context_processors]
|
||||
|
||||
|
||||
class Template:
|
||||
def __init__(self, template, backend):
|
||||
self.template = template
|
||||
self.backend = backend
|
||||
self.origin = Origin(
|
||||
name=template.filename,
|
||||
template_name=template.name,
|
||||
)
|
||||
|
||||
def render(self, context=None, request=None):
|
||||
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)
|
||||
for context_processor in self.backend.template_context_processors:
|
||||
context.update(context_processor(request))
|
||||
try:
|
||||
return self.template.render(context)
|
||||
except jinja2.TemplateSyntaxError as exc:
|
||||
new = TemplateSyntaxError(exc.args)
|
||||
new.template_debug = get_exception_info(exc)
|
||||
raise new from exc
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def get_exception_info(exception):
|
||||
"""
|
||||
Format exception information for display on the debug page using the
|
||||
structure described in the template API documentation.
|
||||
"""
|
||||
context_lines = 10
|
||||
lineno = exception.lineno
|
||||
source = exception.source
|
||||
if source is None:
|
||||
exception_file = Path(exception.filename)
|
||||
if exception_file.exists():
|
||||
source = exception_file.read_text()
|
||||
if source is not None:
|
||||
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 = ""
|
||||
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,
|
||||
}
|
||||
15
venv/lib/python3.10/site-packages/django/template/backends/utils.py
Executable file
15
venv/lib/python3.10/site-packages/django/template/backends/utils.py
Executable file
@@ -0,0 +1,15 @@
|
||||
from django.middleware.csrf import get_token
|
||||
from django.utils.functional import lazy
|
||||
from django.utils.html import format_html
|
||||
from django.utils.safestring import SafeString
|
||||
|
||||
|
||||
def csrf_input(request):
|
||||
return format_html(
|
||||
'<input type="hidden" name="csrfmiddlewaretoken" value="{}">',
|
||||
get_token(request),
|
||||
)
|
||||
|
||||
|
||||
csrf_input_lazy = lazy(csrf_input, SafeString, str)
|
||||
csrf_token_lazy = lazy(get_token, str)
|
||||
1116
venv/lib/python3.10/site-packages/django/template/base.py
Executable file
1116
venv/lib/python3.10/site-packages/django/template/base.py
Executable file
File diff suppressed because it is too large
Load Diff
298
venv/lib/python3.10/site-packages/django/template/context.py
Executable file
298
venv/lib/python3.10/site-packages/django/template/context.py
Executable file
@@ -0,0 +1,298 @@
|
||||
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",)
|
||||
|
||||
|
||||
class ContextPopException(Exception):
|
||||
"pop() has been called more times than push()"
|
||||
pass
|
||||
|
||||
|
||||
class ContextDict(dict):
|
||||
def __init__(self, context, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
context.dicts.append(self)
|
||||
self.context = context
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
self.context.pop()
|
||||
|
||||
|
||||
class BaseContext:
|
||||
def __init__(self, dict_=None):
|
||||
self._reset_dicts(dict_)
|
||||
|
||||
def _reset_dicts(self, value=None):
|
||||
builtins = {"True": True, "False": False, "None": None}
|
||||
self.dicts = [builtins]
|
||||
if value is not None:
|
||||
self.dicts.append(value)
|
||||
|
||||
def __copy__(self):
|
||||
duplicate = copy(super())
|
||||
duplicate.dicts = self.dicts[:]
|
||||
return duplicate
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.dicts)
|
||||
|
||||
def __iter__(self):
|
||||
return reversed(self.dicts)
|
||||
|
||||
def push(self, *args, **kwargs):
|
||||
dicts = []
|
||||
for d in args:
|
||||
if isinstance(d, BaseContext):
|
||||
dicts += d.dicts[1:]
|
||||
else:
|
||||
dicts.append(d)
|
||||
return ContextDict(self, *dicts, **kwargs)
|
||||
|
||||
def pop(self):
|
||||
if len(self.dicts) == 1:
|
||||
raise ContextPopException
|
||||
return self.dicts.pop()
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"Set a variable in the current context"
|
||||
self.dicts[-1][key] = value
|
||||
|
||||
def set_upward(self, key, value):
|
||||
"""
|
||||
Set a variable in one of the higher contexts if it exists there,
|
||||
otherwise in the current context.
|
||||
"""
|
||||
context = self.dicts[-1]
|
||||
for d in reversed(self.dicts):
|
||||
if key in d:
|
||||
context = d
|
||||
break
|
||||
context[key] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
"Get a variable's value, starting at the current context and going upward"
|
||||
for d in reversed(self.dicts):
|
||||
if key in d:
|
||||
return d[key]
|
||||
raise KeyError(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
"Delete a variable from the current context"
|
||||
del self.dicts[-1][key]
|
||||
|
||||
def __contains__(self, key):
|
||||
return any(key in d for d in self.dicts)
|
||||
|
||||
def get(self, key, otherwise=None):
|
||||
for d in reversed(self.dicts):
|
||||
if key in d:
|
||||
return d[key]
|
||||
return otherwise
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
try:
|
||||
return self[key]
|
||||
except KeyError:
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def new(self, values=None):
|
||||
"""
|
||||
Return a new context with the same properties, but with only the
|
||||
values given in 'values' stored.
|
||||
"""
|
||||
new_context = copy(self)
|
||||
new_context._reset_dicts(values)
|
||||
return new_context
|
||||
|
||||
def flatten(self):
|
||||
"""
|
||||
Return self.dicts as one dictionary.
|
||||
"""
|
||||
flat = {}
|
||||
for d in self.dicts:
|
||||
flat.update(d)
|
||||
return flat
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Compare two contexts by comparing theirs 'dicts' attributes.
|
||||
"""
|
||||
if not isinstance(other, BaseContext):
|
||||
return NotImplemented
|
||||
# flatten dictionaries because they can be put in a different order.
|
||||
return self.flatten() == other.flatten()
|
||||
|
||||
|
||||
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
|
||||
self.use_tz = use_tz
|
||||
self.template_name = "unknown"
|
||||
self.render_context = RenderContext()
|
||||
# Set to the original template -- as opposed to extended or included
|
||||
# templates -- during rendering, see bind_template.
|
||||
self.template = None
|
||||
super().__init__(dict_)
|
||||
|
||||
@contextmanager
|
||||
def bind_template(self, template):
|
||||
if self.template is not None:
|
||||
raise RuntimeError("Context is already bound to a template")
|
||||
self.template = template
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.template = None
|
||||
|
||||
def __copy__(self):
|
||||
duplicate = super().__copy__()
|
||||
duplicate.render_context = copy(self.render_context)
|
||||
return duplicate
|
||||
|
||||
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 isinstance(other_dict, BaseContext):
|
||||
other_dict = other_dict.dicts[1:].pop()
|
||||
return ContextDict(self, other_dict)
|
||||
|
||||
|
||||
class RenderContext(BaseContext):
|
||||
"""
|
||||
A stack container for storing Template state.
|
||||
|
||||
RenderContext simplifies the implementation of template Nodes by providing a
|
||||
safe place to store state between invocations of a node's `render` method.
|
||||
|
||||
The RenderContext also provides scoping rules that are more sensible for
|
||||
'template local' variables. The render context stack is pushed before each
|
||||
template is rendered, creating a fresh scope with nothing in it. Name
|
||||
resolution fails if a variable is not found at the top of the RequestContext
|
||||
stack. Thus, variables are local to a specific template and don't affect the
|
||||
rendering of other templates as they would if they were stored in the normal
|
||||
template context.
|
||||
"""
|
||||
|
||||
template = None
|
||||
|
||||
def __iter__(self):
|
||||
yield from self.dicts[-1]
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self.dicts[-1]
|
||||
|
||||
def get(self, key, otherwise=None):
|
||||
return self.dicts[-1].get(key, otherwise)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.dicts[-1][key]
|
||||
|
||||
@contextmanager
|
||||
def push_state(self, template, isolated_context=True):
|
||||
initial = self.template
|
||||
self.template = template
|
||||
if isolated_context:
|
||||
self.push()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.template = initial
|
||||
if isolated_context:
|
||||
self.pop()
|
||||
|
||||
|
||||
class RequestContext(Context):
|
||||
"""
|
||||
This subclass of template.Context automatically populates itself using
|
||||
the processors defined in the engine's configuration.
|
||||
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,
|
||||
):
|
||||
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)
|
||||
self._processors_index = len(self.dicts)
|
||||
|
||||
# placeholder for context processors output
|
||||
self.update({})
|
||||
|
||||
# empty dict for any new modifications
|
||||
# (so that context processors don't overwrite them)
|
||||
self.update({})
|
||||
|
||||
@contextmanager
|
||||
def bind_template(self, template):
|
||||
if self.template is not None:
|
||||
raise RuntimeError("Context is already bound to a template")
|
||||
|
||||
self.template = template
|
||||
# Set context processors according to the template engine's settings.
|
||||
processors = template.engine.template_context_processors + self._processors
|
||||
updates = {}
|
||||
for processor in processors:
|
||||
context = processor(self.request)
|
||||
try:
|
||||
updates.update(context)
|
||||
except TypeError as e:
|
||||
raise TypeError(
|
||||
f"Context processor {processor.__qualname__} didn't return a "
|
||||
"dictionary."
|
||||
) from e
|
||||
|
||||
self.dicts[self._processors_index] = updates
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.template = None
|
||||
# Unset context processors.
|
||||
self.dicts[self._processors_index] = {}
|
||||
|
||||
def new(self, values=None):
|
||||
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"):
|
||||
del new_context._processors_index
|
||||
return new_context
|
||||
|
||||
|
||||
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__
|
||||
)
|
||||
if request is None:
|
||||
context = Context(context, **kwargs)
|
||||
else:
|
||||
# The following pattern is required to ensure values from
|
||||
# context override those from template context processors.
|
||||
original_context = context
|
||||
context = RequestContext(request, **kwargs)
|
||||
if original_context:
|
||||
context.push(original_context)
|
||||
return context
|
||||
89
venv/lib/python3.10/site-packages/django/template/context_processors.py
Executable file
89
venv/lib/python3.10/site-packages/django/template/context_processors.py
Executable file
@@ -0,0 +1,89 @@
|
||||
"""
|
||||
A set of request processors that return dictionaries to be merged into a
|
||||
template context. Each function takes the request object as its only parameter
|
||||
and returns a dictionary to add to the context.
|
||||
|
||||
These are referenced from the 'context_processors' option of the configuration
|
||||
of a DjangoTemplates backend and used by RequestContext.
|
||||
"""
|
||||
|
||||
import itertools
|
||||
|
||||
from django.conf import settings
|
||||
from django.middleware.csrf import get_token
|
||||
from django.utils.functional import SimpleLazyObject, lazy
|
||||
|
||||
|
||||
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"
|
||||
else:
|
||||
return token
|
||||
|
||||
return {"csrf_token": SimpleLazyObject(_get_val)}
|
||||
|
||||
|
||||
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
|
||||
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,
|
||||
)
|
||||
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(),
|
||||
}
|
||||
|
||||
|
||||
def tz(request):
|
||||
from django.utils import timezone
|
||||
|
||||
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}
|
||||
|
||||
|
||||
def media(request):
|
||||
"""
|
||||
Add media-related context variables to the context.
|
||||
"""
|
||||
return {"MEDIA_URL": settings.MEDIA_URL}
|
||||
|
||||
|
||||
def request(request):
|
||||
return {"request": request}
|
||||
994
venv/lib/python3.10/site-packages/django/template/defaultfilters.py
Executable file
994
venv/lib/python3.10/site-packages/django/template/defaultfilters.py
Executable file
@@ -0,0 +1,994 @@
|
||||
"""Default variable filters."""
|
||||
|
||||
import random as random_module
|
||||
import re
|
||||
import types
|
||||
import warnings
|
||||
from decimal import ROUND_HALF_UP, Context, Decimal, InvalidOperation, getcontext
|
||||
from functools import wraps
|
||||
from inspect import unwrap
|
||||
from operator import itemgetter
|
||||
from pprint import pformat
|
||||
from urllib.parse import quote
|
||||
|
||||
from django.utils import formats
|
||||
from django.utils.dateformat import format, time_format
|
||||
from django.utils.deprecation import RemovedInDjango51Warning
|
||||
from django.utils.encoding import iri_to_uri
|
||||
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
|
||||
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
|
||||
|
||||
from .base import VARIABLE_ATTRIBUTE_SEPARATOR
|
||||
from .library import Library
|
||||
|
||||
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):
|
||||
result = mark_safe(result)
|
||||
return result
|
||||
|
||||
return _dec
|
||||
|
||||
|
||||
###################
|
||||
# STRINGS #
|
||||
###################
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def addslashes(value):
|
||||
"""
|
||||
Add slashes before quotes. Useful for escaping strings in CSV, for
|
||||
example. Less useful for escaping JavaScript; use the ``escapejs``
|
||||
filter instead.
|
||||
"""
|
||||
return value.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'")
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def capfirst(value):
|
||||
"""Capitalize the first character of the value."""
|
||||
return value and value[0].upper() + value[1:]
|
||||
|
||||
|
||||
@register.filter("escapejs")
|
||||
@stringfilter
|
||||
def escapejs_filter(value):
|
||||
"""Hex encode characters for use in JavaScript strings."""
|
||||
return escapejs(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def json_script(value, element_id=None):
|
||||
"""
|
||||
Output value JSON-encoded, wrapped in a <script type="application/json">
|
||||
tag (with an optional id).
|
||||
"""
|
||||
return _json_script(value, element_id)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def floatformat(text, arg=-1):
|
||||
"""
|
||||
Display a float to a specified number of decimal places.
|
||||
|
||||
If called without an argument, display the floating point number with one
|
||||
decimal place -- but only if there's a decimal place to be displayed:
|
||||
|
||||
* num1 = 34.23234
|
||||
* num2 = 34.00000
|
||||
* num3 = 34.26000
|
||||
* {{ num1|floatformat }} displays "34.2"
|
||||
* {{ num2|floatformat }} displays "34"
|
||||
* {{ num3|floatformat }} displays "34.3"
|
||||
|
||||
If arg is positive, always display exactly arg number of decimal places:
|
||||
|
||||
* {{ num1|floatformat:3 }} displays "34.232"
|
||||
* {{ num2|floatformat:3 }} displays "34.000"
|
||||
* {{ num3|floatformat:3 }} displays "34.260"
|
||||
|
||||
If arg is negative, display arg number of decimal places -- but only if
|
||||
there are places to be displayed:
|
||||
|
||||
* {{ num1|floatformat:"-3" }} displays "34.232"
|
||||
* {{ num2|floatformat:"-3" }} displays "34"
|
||||
* {{ num3|floatformat:"-3" }} displays "34.260"
|
||||
|
||||
If arg has the 'g' suffix, force the result to be grouped by the
|
||||
THOUSAND_SEPARATOR for the active locale. When the active locale is
|
||||
en (English):
|
||||
|
||||
* {{ 6666.6666|floatformat:"2g" }} displays "6,666.67"
|
||||
* {{ 10000|floatformat:"g" }} displays "10,000"
|
||||
|
||||
If arg has the 'u' suffix, force the result to be unlocalized. When the
|
||||
active locale is pl (Polish):
|
||||
|
||||
* {{ 66666.6666|floatformat:"2" }} displays "66666,67"
|
||||
* {{ 66666.6666|floatformat:"2u" }} displays "66666.67"
|
||||
|
||||
If the input float is infinity or NaN, display the string representation
|
||||
of that value.
|
||||
"""
|
||||
force_grouping = False
|
||||
use_l10n = True
|
||||
if isinstance(arg, str):
|
||||
last_char = arg[-1]
|
||||
if arg[-2:] in {"gu", "ug"}:
|
||||
force_grouping = True
|
||||
use_l10n = False
|
||||
arg = arg[:-2] or -1
|
||||
elif last_char == "g":
|
||||
force_grouping = True
|
||||
arg = arg[:-1] or -1
|
||||
elif last_char == "u":
|
||||
use_l10n = False
|
||||
arg = arg[:-1] or -1
|
||||
try:
|
||||
input_val = str(text)
|
||||
d = Decimal(input_val)
|
||||
except InvalidOperation:
|
||||
try:
|
||||
d = Decimal(str(float(text)))
|
||||
except (ValueError, InvalidOperation, TypeError):
|
||||
return ""
|
||||
try:
|
||||
p = int(arg)
|
||||
except ValueError:
|
||||
return input_val
|
||||
|
||||
try:
|
||||
m = int(d) - d
|
||||
except (ValueError, OverflowError, InvalidOperation):
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
exp = Decimal(1).scaleb(-abs(p))
|
||||
# Set the precision high enough to avoid an exception (#15789).
|
||||
tupl = d.as_tuple()
|
||||
units = len(tupl[1])
|
||||
units += -tupl[2] if m else tupl[2]
|
||||
prec = abs(p) + units + 1
|
||||
prec = max(getcontext().prec, prec)
|
||||
|
||||
# Avoid conversion to scientific notation by accessing `sign`, `digits`,
|
||||
# and `exponent` from Decimal.as_tuple() directly.
|
||||
rounded_d = d.quantize(exp, ROUND_HALF_UP, Context(prec=prec))
|
||||
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, ".")
|
||||
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,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def iriencode(value):
|
||||
"""Escape an IRI value for use in a URL."""
|
||||
return iri_to_uri(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
@stringfilter
|
||||
def linenumbers(value, autoescape=True):
|
||||
"""Display text with line numbers."""
|
||||
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))))
|
||||
if not autoescape or isinstance(value, SafeData):
|
||||
for i, line in enumerate(lines):
|
||||
lines[i] = ("%0" + width + "d. %s") % (i + 1, line)
|
||||
else:
|
||||
for i, line in enumerate(lines):
|
||||
lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
|
||||
return mark_safe("\n".join(lines))
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def lower(value):
|
||||
"""Convert a string into all lowercase."""
|
||||
return value.lower()
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def make_list(value):
|
||||
"""
|
||||
Return the value turned into a list.
|
||||
|
||||
For an integer, it's a list of digits.
|
||||
For a string, it's a list of characters.
|
||||
"""
|
||||
return list(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def slugify(value):
|
||||
"""
|
||||
Convert to ASCII. Convert spaces to hyphens. Remove characters that aren't
|
||||
alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip
|
||||
leading and trailing whitespace.
|
||||
"""
|
||||
return _slugify(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def stringformat(value, arg):
|
||||
"""
|
||||
Format the variable according to the arg, a string formatting specifier.
|
||||
|
||||
This specifier uses Python string formatting syntax, with the exception
|
||||
that the leading "%" is dropped.
|
||||
|
||||
See https://docs.python.org/library/stdtypes.html#printf-style-string-formatting
|
||||
for documentation of Python string formatting.
|
||||
"""
|
||||
if isinstance(value, tuple):
|
||||
value = str(value)
|
||||
try:
|
||||
return ("%" + str(arg)) % value
|
||||
except (ValueError, TypeError):
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
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)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def truncatechars(value, arg):
|
||||
"""Truncate a string after `arg` number of characters."""
|
||||
try:
|
||||
length = int(arg)
|
||||
except ValueError: # Invalid literal for int().
|
||||
return value # Fail silently.
|
||||
return Truncator(value).chars(length)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def truncatechars_html(value, arg):
|
||||
"""
|
||||
Truncate HTML after `arg` number of chars.
|
||||
Preserve newlines in the HTML.
|
||||
"""
|
||||
try:
|
||||
length = int(arg)
|
||||
except ValueError: # invalid literal for int()
|
||||
return value # Fail silently.
|
||||
return Truncator(value).chars(length, html=True)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def truncatewords(value, arg):
|
||||
"""
|
||||
Truncate a string after `arg` number of words.
|
||||
Remove newlines within the string.
|
||||
"""
|
||||
try:
|
||||
length = int(arg)
|
||||
except ValueError: # Invalid literal for int().
|
||||
return value # Fail silently.
|
||||
return Truncator(value).words(length, truncate=" …")
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def truncatewords_html(value, arg):
|
||||
"""
|
||||
Truncate HTML after `arg` number of words.
|
||||
Preserve newlines in the HTML.
|
||||
"""
|
||||
try:
|
||||
length = int(arg)
|
||||
except ValueError: # invalid literal for int()
|
||||
return value # Fail silently.
|
||||
return Truncator(value).words(length, html=True, truncate=" …")
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def upper(value):
|
||||
"""Convert a string into all uppercase."""
|
||||
return value.upper()
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def urlencode(value, safe=None):
|
||||
"""
|
||||
Escape a value for use in a URL.
|
||||
|
||||
The ``safe`` parameter determines the characters which should not be
|
||||
escaped by Python's quote() function. If not provided, use the default safe
|
||||
characters (but an empty string can be provided when *all* characters
|
||||
should be escaped).
|
||||
"""
|
||||
kwargs = {}
|
||||
if safe is not None:
|
||||
kwargs["safe"] = safe
|
||||
return quote(value, **kwargs)
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
@stringfilter
|
||||
def urlize(value, autoescape=True):
|
||||
"""Convert URLs in plain text into clickable links."""
|
||||
return mark_safe(_urlize(value, nofollow=True, autoescape=autoescape))
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
@stringfilter
|
||||
def urlizetrunc(value, limit, autoescape=True):
|
||||
"""
|
||||
Convert URLs into clickable links, truncating URLs to the given character
|
||||
limit, and adding 'rel=nofollow' attribute to discourage spamming.
|
||||
|
||||
Argument: Length to truncate URLs to.
|
||||
"""
|
||||
return mark_safe(
|
||||
_urlize(value, trim_url_limit=int(limit), nofollow=True, autoescape=autoescape)
|
||||
)
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
@stringfilter
|
||||
def wordcount(value):
|
||||
"""Return the number of words."""
|
||||
return len(value.split())
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def wordwrap(value, arg):
|
||||
"""Wrap words at `arg` line length."""
|
||||
return wrap(value, int(arg))
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def ljust(value, arg):
|
||||
"""Left-align the value in a field of a given width."""
|
||||
return value.ljust(int(arg))
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def rjust(value, arg):
|
||||
"""Right-align the value in a field of a given width."""
|
||||
return value.rjust(int(arg))
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def center(value, arg):
|
||||
"""Center the value in a field of a given width."""
|
||||
return value.center(int(arg))
|
||||
|
||||
|
||||
@register.filter
|
||||
@stringfilter
|
||||
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 != ";":
|
||||
return mark_safe(value)
|
||||
return value
|
||||
|
||||
|
||||
###################
|
||||
# HTML STRINGS #
|
||||
###################
|
||||
|
||||
|
||||
@register.filter("escape", is_safe=True)
|
||||
@stringfilter
|
||||
def escape_filter(value):
|
||||
"""Mark the value as a string that should be auto-escaped."""
|
||||
return conditional_escape(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def escapeseq(value):
|
||||
"""
|
||||
An "escape" filter for sequences. Mark each element in the sequence,
|
||||
individually, as a string that should be auto-escaped. Return a list with
|
||||
the results.
|
||||
"""
|
||||
return [conditional_escape(obj) for obj in value]
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def force_escape(value):
|
||||
"""
|
||||
Escape a string's HTML. Return a new string containing the escaped
|
||||
characters (as opposed to "escape", which marks the content for later
|
||||
possible escaping).
|
||||
"""
|
||||
return escape(value)
|
||||
|
||||
|
||||
@register.filter("linebreaks", is_safe=True, needs_autoescape=True)
|
||||
@stringfilter
|
||||
def linebreaks_filter(value, autoescape=True):
|
||||
"""
|
||||
Replace line breaks in plain text with appropriate HTML; a single
|
||||
newline becomes an HTML line break (``<br>``) and a new line
|
||||
followed by a blank line becomes a paragraph break (``</p>``).
|
||||
"""
|
||||
autoescape = autoescape and not isinstance(value, SafeData)
|
||||
return mark_safe(linebreaks(value, autoescape))
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
@stringfilter
|
||||
def linebreaksbr(value, autoescape=True):
|
||||
"""
|
||||
Convert all newlines in a piece of plain text to HTML line breaks
|
||||
(``<br>``).
|
||||
"""
|
||||
autoescape = autoescape and not isinstance(value, SafeData)
|
||||
value = normalize_newlines(value)
|
||||
if autoescape:
|
||||
value = escape(value)
|
||||
return mark_safe(value.replace("\n", "<br>"))
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def safe(value):
|
||||
"""Mark the value as a string that should not be auto-escaped."""
|
||||
return mark_safe(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def safeseq(value):
|
||||
"""
|
||||
A "safe" filter for sequences. Mark each element in the sequence,
|
||||
individually, as safe, after converting them to strings. Return a list
|
||||
with the results.
|
||||
"""
|
||||
return [mark_safe(obj) for obj in value]
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
@stringfilter
|
||||
def striptags(value):
|
||||
"""Strip all [X]HTML tags."""
|
||||
return strip_tags(value)
|
||||
|
||||
|
||||
###################
|
||||
# LISTS #
|
||||
###################
|
||||
|
||||
|
||||
def _property_resolver(arg):
|
||||
"""
|
||||
When arg is convertible to float, behave like operator.itemgetter(arg)
|
||||
Otherwise, chain __getitem__() and getattr().
|
||||
|
||||
>>> _property_resolver(1)('abc')
|
||||
'b'
|
||||
>>> _property_resolver('1')('abc')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: string indices must be integers
|
||||
>>> class Foo:
|
||||
... a = 42
|
||||
... b = 3.14
|
||||
... c = 'Hey!'
|
||||
>>> _property_resolver('b')(Foo())
|
||||
3.14
|
||||
"""
|
||||
try:
|
||||
float(arg)
|
||||
except ValueError:
|
||||
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):
|
||||
for part in parts:
|
||||
try:
|
||||
value = value[part]
|
||||
except (AttributeError, IndexError, KeyError, TypeError, ValueError):
|
||||
value = getattr(value, part)
|
||||
return value
|
||||
|
||||
return resolve
|
||||
else:
|
||||
return itemgetter(arg)
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def dictsort(value, arg):
|
||||
"""
|
||||
Given a list of dicts, return that list sorted by the property given in
|
||||
the argument.
|
||||
"""
|
||||
try:
|
||||
return sorted(value, key=_property_resolver(arg))
|
||||
except (AttributeError, TypeError):
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def dictsortreversed(value, arg):
|
||||
"""
|
||||
Given a list of dicts, return that list sorted in reverse order by the
|
||||
property given in the argument.
|
||||
"""
|
||||
try:
|
||||
return sorted(value, key=_property_resolver(arg), reverse=True)
|
||||
except (AttributeError, TypeError):
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def first(value):
|
||||
"""Return the first item in a list."""
|
||||
try:
|
||||
return value[0]
|
||||
except IndexError:
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
def join(value, arg, autoescape=True):
|
||||
"""Join a list with a string, like Python's ``str.join(list)``."""
|
||||
try:
|
||||
if autoescape:
|
||||
data = conditional_escape(arg).join([conditional_escape(v) for v in value])
|
||||
else:
|
||||
data = arg.join(value)
|
||||
except TypeError: # Fail silently if arg isn't iterable.
|
||||
return value
|
||||
return mark_safe(data)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def last(value):
|
||||
"""Return the last item in a list."""
|
||||
try:
|
||||
return value[-1]
|
||||
except IndexError:
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def length(value):
|
||||
"""Return the length of the value - useful for lists."""
|
||||
try:
|
||||
return len(value)
|
||||
except (ValueError, TypeError):
|
||||
return 0
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def length_is(value, arg):
|
||||
"""Return a boolean of whether the value's length is the argument."""
|
||||
warnings.warn(
|
||||
"The length_is template filter is deprecated in favor of the length template "
|
||||
"filter and the == operator within an {% if %} tag.",
|
||||
RemovedInDjango51Warning,
|
||||
)
|
||||
try:
|
||||
return len(value) == int(arg)
|
||||
except (ValueError, TypeError):
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def random(value):
|
||||
"""Return a random item from the list."""
|
||||
try:
|
||||
return random_module.choice(value)
|
||||
except IndexError:
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter("slice", is_safe=True)
|
||||
def slice_filter(value, arg):
|
||||
"""
|
||||
Return a slice of the list using the same syntax as Python's list slicing.
|
||||
"""
|
||||
try:
|
||||
bits = []
|
||||
for x in str(arg).split(":"):
|
||||
if not x:
|
||||
bits.append(None)
|
||||
else:
|
||||
bits.append(int(x))
|
||||
return value[slice(*bits)]
|
||||
|
||||
except (ValueError, TypeError):
|
||||
return value # Fail silently.
|
||||
|
||||
|
||||
@register.filter(is_safe=True, needs_autoescape=True)
|
||||
def unordered_list(value, autoescape=True):
|
||||
"""
|
||||
Recursively take a self-nested list and return an HTML unordered list --
|
||||
WITHOUT opening and closing <ul> tags.
|
||||
|
||||
Assume the list is in the proper format. For example, if ``var`` contains:
|
||||
``['States', ['Kansas', ['Lawrence', 'Topeka'], 'Illinois']]``, then
|
||||
``{{ var|unordered_list }}`` returns::
|
||||
|
||||
<li>States
|
||||
<ul>
|
||||
<li>Kansas
|
||||
<ul>
|
||||
<li>Lawrence</li>
|
||||
<li>Topeka</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Illinois</li>
|
||||
</ul>
|
||||
</li>
|
||||
"""
|
||||
if autoescape:
|
||||
escaper = conditional_escape
|
||||
else:
|
||||
|
||||
def escaper(x):
|
||||
return x
|
||||
|
||||
def walk_items(item_list):
|
||||
item_iterator = iter(item_list)
|
||||
try:
|
||||
item = next(item_iterator)
|
||||
while True:
|
||||
try:
|
||||
next_item = next(item_iterator)
|
||||
except StopIteration:
|
||||
yield item, None
|
||||
break
|
||||
if isinstance(next_item, (list, tuple, types.GeneratorType)):
|
||||
try:
|
||||
iter(next_item)
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
yield item, next_item
|
||||
item = next(item_iterator)
|
||||
continue
|
||||
yield item, None
|
||||
item = next_item
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
def list_formatter(item_list, tabs=1):
|
||||
indent = "\t" * tabs
|
||||
output = []
|
||||
for item, children in walk_items(item_list):
|
||||
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)
|
||||
|
||||
return mark_safe(list_formatter(value))
|
||||
|
||||
|
||||
###################
|
||||
# INTEGERS #
|
||||
###################
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def add(value, arg):
|
||||
"""Add the arg to the value."""
|
||||
try:
|
||||
return int(value) + int(arg)
|
||||
except (ValueError, TypeError):
|
||||
try:
|
||||
return value + arg
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def get_digit(value, arg):
|
||||
"""
|
||||
Given a whole number, return the requested digit of it, where 1 is the
|
||||
right-most digit, 2 is the second-right-most digit, etc. Return the
|
||||
original value for invalid input (if input or argument is not an integer,
|
||||
or if argument is less than 1). Otherwise, output is always an integer.
|
||||
"""
|
||||
try:
|
||||
arg = int(arg)
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
return value # Fail silently for an invalid argument
|
||||
if arg < 1:
|
||||
return value
|
||||
try:
|
||||
return int(str(value)[-arg])
|
||||
except IndexError:
|
||||
return 0
|
||||
|
||||
|
||||
###################
|
||||
# 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 ""
|
||||
try:
|
||||
return formats.date_format(value, arg)
|
||||
except AttributeError:
|
||||
try:
|
||||
return format(value, arg)
|
||||
except AttributeError:
|
||||
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 ""
|
||||
try:
|
||||
return formats.time_format(value, arg)
|
||||
except (AttributeError, TypeError):
|
||||
try:
|
||||
return time_format(value, arg)
|
||||
except (AttributeError, TypeError):
|
||||
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 ""
|
||||
try:
|
||||
if arg:
|
||||
return timesince(value, arg)
|
||||
return timesince(value)
|
||||
except (ValueError, TypeError):
|
||||
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 ""
|
||||
try:
|
||||
return timeuntil(value, arg)
|
||||
except (ValueError, TypeError):
|
||||
return ""
|
||||
|
||||
|
||||
###################
|
||||
# LOGIC #
|
||||
###################
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def default(value, arg):
|
||||
"""If value is unavailable, use given default."""
|
||||
return value or arg
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def default_if_none(value, arg):
|
||||
"""If value is None, use given default."""
|
||||
if value is None:
|
||||
return arg
|
||||
return value
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def divisibleby(value, arg):
|
||||
"""Return True if the value is divisible by the argument."""
|
||||
return int(value) % int(arg) == 0
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
def yesno(value, arg=None):
|
||||
"""
|
||||
Given a string mapping values for true, false, and (optionally) None,
|
||||
return one of those strings according to the value:
|
||||
|
||||
========== ====================== ==================================
|
||||
Value Argument Outputs
|
||||
========== ====================== ==================================
|
||||
``True`` ``"yeah,no,maybe"`` ``yeah``
|
||||
``False`` ``"yeah,no,maybe"`` ``no``
|
||||
``None`` ``"yeah,no,maybe"`` ``maybe``
|
||||
``None`` ``"yeah,no"`` ``"no"`` (converts None to False
|
||||
if no mapping for None is given.
|
||||
========== ====================== ==================================
|
||||
"""
|
||||
if arg is None:
|
||||
# Translators: Please do not add spaces around commas.
|
||||
arg = gettext("yes,no,maybe")
|
||||
bits = arg.split(",")
|
||||
if len(bits) < 2:
|
||||
return value # Invalid arg.
|
||||
try:
|
||||
yes, no, maybe = bits
|
||||
except ValueError:
|
||||
# Unpack list of wrong size (no "maybe" value provided).
|
||||
yes, no, maybe = bits[0], bits[1], bits[1]
|
||||
if value is None:
|
||||
return maybe
|
||||
if value:
|
||||
return yes
|
||||
return no
|
||||
|
||||
|
||||
###################
|
||||
# MISC #
|
||||
###################
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def filesizeformat(bytes_):
|
||||
"""
|
||||
Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB,
|
||||
102 bytes, etc.).
|
||||
"""
|
||||
try:
|
||||
bytes_ = int(bytes_)
|
||||
except (TypeError, ValueError, UnicodeDecodeError):
|
||||
value = ngettext("%(size)d byte", "%(size)d bytes", 0) % {"size": 0}
|
||||
return avoid_wrapping(value)
|
||||
|
||||
def filesize_number_format(value):
|
||||
return formats.number_format(round(value, 1), 1)
|
||||
|
||||
KB = 1 << 10
|
||||
MB = 1 << 20
|
||||
GB = 1 << 30
|
||||
TB = 1 << 40
|
||||
PB = 1 << 50
|
||||
|
||||
negative = bytes_ < 0
|
||||
if negative:
|
||||
bytes_ = -bytes_ # Allow formatting of negative numbers.
|
||||
|
||||
if bytes_ < KB:
|
||||
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:
|
||||
value = gettext("%s MB") % filesize_number_format(bytes_ / MB)
|
||||
elif bytes_ < TB:
|
||||
value = gettext("%s GB") % filesize_number_format(bytes_ / GB)
|
||||
elif bytes_ < PB:
|
||||
value = gettext("%s TB") % filesize_number_format(bytes_ / TB)
|
||||
else:
|
||||
value = gettext("%s PB") % filesize_number_format(bytes_ / PB)
|
||||
|
||||
if negative:
|
||||
value = "-%s" % value
|
||||
return avoid_wrapping(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=False)
|
||||
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:
|
||||
|
||||
* If value is 0, vote{{ value|pluralize }} display "votes".
|
||||
* If value is 1, vote{{ value|pluralize }} display "vote".
|
||||
* If value is 2, vote{{ value|pluralize }} display "votes".
|
||||
|
||||
If an argument is provided, use that string instead:
|
||||
|
||||
* If value is 0, class{{ value|pluralize:"es" }} display "classes".
|
||||
* If value is 1, class{{ value|pluralize:"es" }} display "class".
|
||||
* If value is 2, class{{ value|pluralize:"es" }} display "classes".
|
||||
|
||||
If the provided argument contains a comma, use the text before the comma
|
||||
for the singular case and the text after the comma for the plural case:
|
||||
|
||||
* If value is 0, cand{{ value|pluralize:"y,ies" }} display "candies".
|
||||
* 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 len(bits) > 2:
|
||||
return ""
|
||||
singular_suffix, plural_suffix = bits[:2]
|
||||
|
||||
try:
|
||||
return singular_suffix if float(value) == 1 else plural_suffix
|
||||
except ValueError: # Invalid string that's not a number.
|
||||
pass
|
||||
except TypeError: # Value isn't a string or a number; maybe it's a list?
|
||||
try:
|
||||
return singular_suffix if len(value) == 1 else plural_suffix
|
||||
except TypeError: # len() of unsized object.
|
||||
pass
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter("phone2numeric", is_safe=True)
|
||||
def phone2numeric_filter(value):
|
||||
"""Take a phone number and converts it in to its numerical equivalent."""
|
||||
return phone2numeric(value)
|
||||
|
||||
|
||||
@register.filter(is_safe=True)
|
||||
def pprint(value):
|
||||
"""A wrapper around pprint.pprint -- for debugging, really."""
|
||||
try:
|
||||
return pformat(value)
|
||||
except Exception as e:
|
||||
return "Error in formatting: %s: %s" % (e.__class__.__name__, e)
|
||||
1501
venv/lib/python3.10/site-packages/django/template/defaulttags.py
Executable file
1501
venv/lib/python3.10/site-packages/django/template/defaulttags.py
Executable file
File diff suppressed because it is too large
Load Diff
214
venv/lib/python3.10/site-packages/django/template/engine.py
Executable file
214
venv/lib/python3.10/site-packages/django/template/engine.py
Executable file
@@ -0,0 +1,214 @@
|
||||
import functools
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
from .base import Template
|
||||
from .context import Context, _builtin_context_processors
|
||||
from .exceptions import TemplateDoesNotExist
|
||||
from .library import import_library
|
||||
|
||||
|
||||
class Engine:
|
||||
default_builtins = [
|
||||
"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,
|
||||
):
|
||||
if dirs is None:
|
||||
dirs = []
|
||||
if context_processors is None:
|
||||
context_processors = []
|
||||
if loaders is None:
|
||||
loaders = ["django.template.loaders.filesystem.Loader"]
|
||||
if app_dirs:
|
||||
loaders += ["django.template.loaders.app_directories.Loader"]
|
||||
loaders = [("django.template.loaders.cached.Loader", loaders)]
|
||||
else:
|
||||
if app_dirs:
|
||||
raise ImproperlyConfigured(
|
||||
"app_dirs must not be set when loaders is defined."
|
||||
)
|
||||
if libraries is None:
|
||||
libraries = {}
|
||||
if builtins is None:
|
||||
builtins = []
|
||||
|
||||
self.dirs = dirs
|
||||
self.app_dirs = app_dirs
|
||||
self.autoescape = autoescape
|
||||
self.context_processors = context_processors
|
||||
self.debug = debug
|
||||
self.loaders = loaders
|
||||
self.string_if_invalid = string_if_invalid
|
||||
self.file_charset = file_charset
|
||||
self.libraries = libraries
|
||||
self.template_libraries = self.get_template_libraries(libraries)
|
||||
self.builtins = self.default_builtins + builtins
|
||||
self.template_builtins = self.get_template_builtins(self.builtins)
|
||||
|
||||
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>"
|
||||
) % (
|
||||
self.__class__.__qualname__,
|
||||
"" 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)
|
||||
),
|
||||
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),
|
||||
repr(self.autoescape),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@functools.lru_cache
|
||||
def get_default():
|
||||
"""
|
||||
Return the first DjangoTemplates backend that's configured, or raise
|
||||
ImproperlyConfigured if none are configured.
|
||||
|
||||
This is required for preserving historical APIs that rely on a
|
||||
globally available, implicitly configured engine such as:
|
||||
|
||||
>>> from django.template import Context, Template
|
||||
>>> template = Template("Hello {{ name }}!")
|
||||
>>> context = Context({'name': "world"})
|
||||
>>> template.render(context)
|
||||
'Hello world!'
|
||||
"""
|
||||
# Since Engine is imported in django.template and since
|
||||
# DjangoTemplates is a wrapper around this Engine class,
|
||||
# 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.")
|
||||
|
||||
@cached_property
|
||||
def template_context_processors(self):
|
||||
context_processors = _builtin_context_processors
|
||||
context_processors += tuple(self.context_processors)
|
||||
return tuple(import_string(path) for path in context_processors)
|
||||
|
||||
def get_template_builtins(self, builtins):
|
||||
return [import_library(x) for x in builtins]
|
||||
|
||||
def get_template_libraries(self, libraries):
|
||||
loaded = {}
|
||||
for name, path in libraries.items():
|
||||
loaded[name] = import_library(path)
|
||||
return loaded
|
||||
|
||||
@cached_property
|
||||
def template_loaders(self):
|
||||
return self.get_template_loaders(self.loaders)
|
||||
|
||||
def get_template_loaders(self, template_loaders):
|
||||
loaders = []
|
||||
for template_loader in template_loaders:
|
||||
loader = self.find_template_loader(template_loader)
|
||||
if loader is not None:
|
||||
loaders.append(loader)
|
||||
return loaders
|
||||
|
||||
def find_template_loader(self, loader):
|
||||
if isinstance(loader, (tuple, list)):
|
||||
loader, *args = loader
|
||||
else:
|
||||
args = []
|
||||
|
||||
if isinstance(loader, str):
|
||||
loader_class = import_string(loader)
|
||||
return loader_class(self, *args)
|
||||
else:
|
||||
raise ImproperlyConfigured(
|
||||
"Invalid value in template loaders configuration: %r" % loader
|
||||
)
|
||||
|
||||
def find_template(self, name, dirs=None, skip=None):
|
||||
tried = []
|
||||
for loader in self.template_loaders:
|
||||
try:
|
||||
template = loader.get_template(name, skip=skip)
|
||||
return template, template.origin
|
||||
except TemplateDoesNotExist as e:
|
||||
tried.extend(e.tried)
|
||||
raise TemplateDoesNotExist(name, tried=tried)
|
||||
|
||||
def from_string(self, template_code):
|
||||
"""
|
||||
Return a compiled Template object for the given template code,
|
||||
handling template inheritance recursively.
|
||||
"""
|
||||
return Template(template_code, engine=self)
|
||||
|
||||
def get_template(self, template_name):
|
||||
"""
|
||||
Return a compiled Template object for the given template name,
|
||||
handling template inheritance recursively.
|
||||
"""
|
||||
template, origin = self.find_template(template_name)
|
||||
if not hasattr(template, "render"):
|
||||
# template needs to be compiled
|
||||
template = Template(template, origin, template_name, engine=self)
|
||||
return template
|
||||
|
||||
def render_to_string(self, template_name, context=None):
|
||||
"""
|
||||
Render the template specified by template_name with the given context.
|
||||
For use in Django's test suite.
|
||||
"""
|
||||
if isinstance(template_name, (list, tuple)):
|
||||
t = self.select_template(template_name)
|
||||
else:
|
||||
t = self.get_template(template_name)
|
||||
# Django < 1.8 accepted a Context in `context` even though that's
|
||||
# unintended. Preserve this ability but don't rewrap `context`.
|
||||
if isinstance(context, Context):
|
||||
return t.render(context)
|
||||
else:
|
||||
return t.render(Context(context, autoescape=self.autoescape))
|
||||
|
||||
def select_template(self, template_name_list):
|
||||
"""
|
||||
Given a list of template names, return the first that can be loaded.
|
||||
"""
|
||||
if not template_name_list:
|
||||
raise TemplateDoesNotExist("No template names provided")
|
||||
not_found = []
|
||||
for template_name in template_name_list:
|
||||
try:
|
||||
return self.get_template(template_name)
|
||||
except TemplateDoesNotExist as exc:
|
||||
if exc.args[0] not in not_found:
|
||||
not_found.append(exc.args[0])
|
||||
continue
|
||||
# If we get here, none of the templates could be loaded
|
||||
raise TemplateDoesNotExist(", ".join(not_found))
|
||||
44
venv/lib/python3.10/site-packages/django/template/exceptions.py
Executable file
44
venv/lib/python3.10/site-packages/django/template/exceptions.py
Executable file
@@ -0,0 +1,44 @@
|
||||
"""
|
||||
This module contains generic exceptions used by template backends. Although,
|
||||
due to historical reasons, the Django template language also internally uses
|
||||
these exceptions, other exceptions specific to the DTL should not be added
|
||||
here.
|
||||
"""
|
||||
|
||||
|
||||
class TemplateDoesNotExist(Exception):
|
||||
"""
|
||||
The exception used when a template does not exist. Optional arguments:
|
||||
|
||||
backend
|
||||
The template backend class used when raising this exception.
|
||||
|
||||
tried
|
||||
A list of sources that were tried when finding the template. This
|
||||
is formatted as a list of tuples containing (origin, status), where
|
||||
origin is an Origin object or duck type and status is a string with the
|
||||
reason the template wasn't found.
|
||||
|
||||
chain
|
||||
A list of intermediate TemplateDoesNotExist exceptions. This is used to
|
||||
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:
|
||||
tried = []
|
||||
self.tried = tried
|
||||
if chain is None:
|
||||
chain = []
|
||||
self.chain = chain
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class TemplateSyntaxError(Exception):
|
||||
"""
|
||||
The exception used for syntax errors during parsing or rendering.
|
||||
"""
|
||||
|
||||
pass
|
||||
385
venv/lib/python3.10/site-packages/django/template/library.py
Executable file
385
venv/lib/python3.10/site-packages/django/template/library.py
Executable file
@@ -0,0 +1,385 @@
|
||||
from functools import wraps
|
||||
from importlib import import_module
|
||||
from inspect import getfullargspec, unwrap
|
||||
|
||||
from django.utils.html import conditional_escape
|
||||
from django.utils.itercompat import is_iterable
|
||||
|
||||
from .base import Node, Template, token_kwargs
|
||||
from .exceptions import TemplateSyntaxError
|
||||
|
||||
|
||||
class InvalidTemplateLibrary(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Library:
|
||||
"""
|
||||
A class for registering template tags and filters. Compiled filter and
|
||||
template tag functions are stored in the filters and tags attributes.
|
||||
The filter, simple_tag, and inclusion_tag methods provide a convenient
|
||||
way to register callables as tags.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.filters = {}
|
||||
self.tags = {}
|
||||
|
||||
def tag(self, name=None, compile_function=None):
|
||||
if name is None and compile_function is None:
|
||||
# @register.tag()
|
||||
return self.tag_function
|
||||
elif name is not None and compile_function is None:
|
||||
if callable(name):
|
||||
# @register.tag
|
||||
return self.tag_function(name)
|
||||
else:
|
||||
# @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)
|
||||
self.tags[name] = compile_function
|
||||
return compile_function
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported arguments to Library.tag: (%r, %r)"
|
||||
% (name, compile_function),
|
||||
)
|
||||
|
||||
def tag_function(self, func):
|
||||
self.tags[func.__name__] = func
|
||||
return func
|
||||
|
||||
def filter(self, name=None, filter_func=None, **flags):
|
||||
"""
|
||||
Register a callable as a template filter. Example:
|
||||
|
||||
@register.filter
|
||||
def lower(value):
|
||||
return value.lower()
|
||||
"""
|
||||
if name is None and filter_func is None:
|
||||
# @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):
|
||||
# @register.filter
|
||||
return self.filter_function(name, **flags)
|
||||
else:
|
||||
# @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"):
|
||||
if attr in flags:
|
||||
value = flags[attr]
|
||||
# set the flag on the filter for FilterExpression.resolve
|
||||
setattr(filter_func, attr, value)
|
||||
# set the flag on the innermost decorated function
|
||||
# for decorators that need it, e.g. stringfilter
|
||||
setattr(unwrap(filter_func), attr, value)
|
||||
filter_func._filter_name = name
|
||||
return filter_func
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported arguments to Library.filter: (%r, %r)"
|
||||
% (name, filter_func),
|
||||
)
|
||||
|
||||
def filter_function(self, func, **flags):
|
||||
return self.filter(func.__name__, func, **flags)
|
||||
|
||||
def simple_tag(self, func=None, takes_context=None, name=None):
|
||||
"""
|
||||
Register a callable as a compiled template tag. Example:
|
||||
|
||||
@register.simple_tag
|
||||
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__
|
||||
|
||||
@wraps(func)
|
||||
def compile_func(parser, token):
|
||||
bits = token.split_contents()[1:]
|
||||
target_var = None
|
||||
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,
|
||||
)
|
||||
return SimpleNode(func, takes_context, args, kwargs, target_var)
|
||||
|
||||
self.tag(function_name, compile_func)
|
||||
return func
|
||||
|
||||
if func is None:
|
||||
# @register.simple_tag(...)
|
||||
return dec
|
||||
elif callable(func):
|
||||
# @register.simple_tag
|
||||
return dec(func)
|
||||
else:
|
||||
raise ValueError("Invalid arguments provided to simple_tag")
|
||||
|
||||
def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
|
||||
"""
|
||||
Register a callable as an inclusion tag:
|
||||
|
||||
@register.inclusion_tag('results.html')
|
||||
def show_results(poll):
|
||||
choices = poll.choice_set.all()
|
||||
return {'choices': choices}
|
||||
"""
|
||||
|
||||
def dec(func):
|
||||
(
|
||||
params,
|
||||
varargs,
|
||||
varkw,
|
||||
defaults,
|
||||
kwonly,
|
||||
kwonly_defaults,
|
||||
_,
|
||||
) = getfullargspec(unwrap(func))
|
||||
function_name = name or func.__name__
|
||||
|
||||
@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,
|
||||
)
|
||||
return InclusionNode(
|
||||
func,
|
||||
takes_context,
|
||||
args,
|
||||
kwargs,
|
||||
filename,
|
||||
)
|
||||
|
||||
self.tag(function_name, compile_func)
|
||||
return func
|
||||
|
||||
return dec
|
||||
|
||||
|
||||
class TagHelperNode(Node):
|
||||
"""
|
||||
Base class for tag helper nodes such as SimpleNode and InclusionNode.
|
||||
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
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def get_resolved_arguments(self, context):
|
||||
resolved_args = [var.resolve(context) for var in self.args]
|
||||
if self.takes_context:
|
||||
resolved_args = [context] + resolved_args
|
||||
resolved_kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()}
|
||||
return resolved_args, resolved_kwargs
|
||||
|
||||
|
||||
class SimpleNode(TagHelperNode):
|
||||
child_nodelists = ()
|
||||
|
||||
def __init__(self, func, takes_context, args, kwargs, target_var):
|
||||
super().__init__(func, takes_context, args, kwargs)
|
||||
self.target_var = target_var
|
||||
|
||||
def render(self, context):
|
||||
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
|
||||
output = self.func(*resolved_args, **resolved_kwargs)
|
||||
if self.target_var is not None:
|
||||
context[self.target_var] = output
|
||||
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
|
||||
|
||||
def render(self, context):
|
||||
"""
|
||||
Render the specified template and context. Cache the template object
|
||||
in render_context to avoid reparsing and loading when used in a for
|
||||
loop.
|
||||
"""
|
||||
resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
|
||||
_dict = self.func(*resolved_args, **resolved_kwargs)
|
||||
|
||||
t = context.render_context.get(self)
|
||||
if t is None:
|
||||
if isinstance(self.filename, Template):
|
||||
t = self.filename
|
||||
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)
|
||||
else:
|
||||
t = context.template.engine.get_template(self.filename)
|
||||
context.render_context[self] = t
|
||||
new_context = context.new(_dict)
|
||||
# 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")
|
||||
if csrf_token is not None:
|
||||
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,
|
||||
):
|
||||
"""
|
||||
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":
|
||||
params = params[1:]
|
||||
else:
|
||||
raise TemplateSyntaxError(
|
||||
"'%s' is decorated with takes_context=True so it must "
|
||||
"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
|
||||
]
|
||||
for bit in bits:
|
||||
# First we try to extract a potential kwarg from the bit
|
||||
kwarg = token_kwargs([bit], parser)
|
||||
if kwarg:
|
||||
# The kwarg was successfully extracted
|
||||
param, value = kwarg.popitem()
|
||||
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)
|
||||
)
|
||||
elif param in kwargs:
|
||||
# The keyword argument has already been supplied once
|
||||
raise TemplateSyntaxError(
|
||||
"'%s' received multiple values for keyword argument '%s'"
|
||||
% (name, param)
|
||||
)
|
||||
else:
|
||||
# All good, record the keyword argument
|
||||
kwargs[str(param)] = value
|
||||
if param in unhandled_params:
|
||||
# If using the keyword syntax for a positional arg, then
|
||||
# consume it.
|
||||
unhandled_params.remove(param)
|
||||
elif param in unhandled_kwargs:
|
||||
# Same for keyword-only arguments
|
||||
unhandled_kwargs.remove(param)
|
||||
else:
|
||||
if kwargs:
|
||||
raise TemplateSyntaxError(
|
||||
"'%s' received some positional argument(s) after some "
|
||||
"keyword argument(s)" % name
|
||||
)
|
||||
else:
|
||||
# Record the positional argument
|
||||
args.append(parser.compile_filter(bit))
|
||||
try:
|
||||
# Consume from the list of expected positional arguments
|
||||
unhandled_params.pop(0)
|
||||
except IndexError:
|
||||
if varargs is None:
|
||||
raise TemplateSyntaxError(
|
||||
"'%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)]
|
||||
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))
|
||||
)
|
||||
return args, kwargs
|
||||
|
||||
|
||||
def import_library(name):
|
||||
"""
|
||||
Load a Library object from a template tag module.
|
||||
"""
|
||||
try:
|
||||
module = import_module(name)
|
||||
except ImportError as e:
|
||||
raise InvalidTemplateLibrary(
|
||||
"Invalid template library specified. ImportError raised when "
|
||||
"trying to load '%s': %s" % (name, e)
|
||||
)
|
||||
try:
|
||||
return module.register
|
||||
except AttributeError:
|
||||
raise InvalidTemplateLibrary(
|
||||
"Module %s does not have a variable named 'register'" % name,
|
||||
)
|
||||
66
venv/lib/python3.10/site-packages/django/template/loader.py
Executable file
66
venv/lib/python3.10/site-packages/django/template/loader.py
Executable file
@@ -0,0 +1,66 @@
|
||||
from . import engines
|
||||
from .exceptions import TemplateDoesNotExist
|
||||
|
||||
|
||||
def get_template(template_name, using=None):
|
||||
"""
|
||||
Load and return a template for the given name.
|
||||
|
||||
Raise TemplateDoesNotExist if no such template exists.
|
||||
"""
|
||||
chain = []
|
||||
engines = _engine_list(using)
|
||||
for engine in engines:
|
||||
try:
|
||||
return engine.get_template(template_name)
|
||||
except TemplateDoesNotExist as e:
|
||||
chain.append(e)
|
||||
|
||||
raise TemplateDoesNotExist(template_name, chain=chain)
|
||||
|
||||
|
||||
def select_template(template_name_list, using=None):
|
||||
"""
|
||||
Load and return a template for one of the given names.
|
||||
|
||||
Try names in order and return the first template found.
|
||||
|
||||
Raise TemplateDoesNotExist if no such template exists.
|
||||
"""
|
||||
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
|
||||
)
|
||||
|
||||
chain = []
|
||||
engines = _engine_list(using)
|
||||
for template_name in template_name_list:
|
||||
for engine in engines:
|
||||
try:
|
||||
return engine.get_template(template_name)
|
||||
except TemplateDoesNotExist as e:
|
||||
chain.append(e)
|
||||
|
||||
if template_name_list:
|
||||
raise TemplateDoesNotExist(", ".join(template_name_list), chain=chain)
|
||||
else:
|
||||
raise TemplateDoesNotExist("No template names provided")
|
||||
|
||||
|
||||
def render_to_string(template_name, context=None, request=None, using=None):
|
||||
"""
|
||||
Load a template and render it with a context. Return a string.
|
||||
|
||||
template_name may be a string or a list of strings.
|
||||
"""
|
||||
if isinstance(template_name, (list, tuple)):
|
||||
template = select_template(template_name, using=using)
|
||||
else:
|
||||
template = get_template(template_name, using=using)
|
||||
return template.render(context, request)
|
||||
|
||||
|
||||
def _engine_list(using=None):
|
||||
return engines.all() if using is None else [engines[using]]
|
||||
354
venv/lib/python3.10/site-packages/django/template/loader_tags.py
Executable file
354
venv/lib/python3.10/site-packages/django/template/loader_tags.py
Executable file
@@ -0,0 +1,354 @@
|
||||
import posixpath
|
||||
from collections import defaultdict
|
||||
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .base import Node, Template, TemplateSyntaxError, TextNode, Variable, token_kwargs
|
||||
from .library import Library
|
||||
|
||||
register = Library()
|
||||
|
||||
BLOCK_CONTEXT_KEY = "block_context"
|
||||
|
||||
|
||||
class BlockContext:
|
||||
def __init__(self):
|
||||
# Dictionary of FIFO queues.
|
||||
self.blocks = defaultdict(list)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<{self.__class__.__qualname__}: blocks={self.blocks!r}>"
|
||||
|
||||
def add_blocks(self, blocks):
|
||||
for name, block in blocks.items():
|
||||
self.blocks[name].insert(0, block)
|
||||
|
||||
def pop(self, name):
|
||||
try:
|
||||
return self.blocks[name].pop()
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def push(self, name, block):
|
||||
self.blocks[name].append(block)
|
||||
|
||||
def get_block(self, name):
|
||||
try:
|
||||
return self.blocks[name][-1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
class BlockNode(Node):
|
||||
def __init__(self, name, nodelist, parent=None):
|
||||
self.name = name
|
||||
self.nodelist = nodelist
|
||||
self.parent = parent
|
||||
|
||||
def __repr__(self):
|
||||
return "<Block Node: %s. Contents: %r>" % (self.name, self.nodelist)
|
||||
|
||||
def render(self, context):
|
||||
block_context = context.render_context.get(BLOCK_CONTEXT_KEY)
|
||||
with context.push():
|
||||
if block_context is None:
|
||||
context["block"] = self
|
||||
result = self.nodelist.render(context)
|
||||
else:
|
||||
push = block = block_context.pop(self.name)
|
||||
if block is None:
|
||||
block = self
|
||||
# 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
|
||||
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"):
|
||||
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
|
||||
):
|
||||
return mark_safe(self.render(self.context))
|
||||
return ""
|
||||
|
||||
|
||||
class ExtendsNode(Node):
|
||||
must_be_first = True
|
||||
context_key = "extends_context"
|
||||
|
||||
def __init__(self, nodelist, parent_name, template_dirs=None):
|
||||
self.nodelist = nodelist
|
||||
self.parent_name = parent_name
|
||||
self.template_dirs = template_dirs
|
||||
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)
|
||||
|
||||
def find_template(self, template_name, context):
|
||||
"""
|
||||
This is a wrapper around engine.find_template(). A history is kept in
|
||||
the render_context attribute between successive extends calls and
|
||||
passed as the skip argument. This enables extends to work recursively
|
||||
without extending the same template twice.
|
||||
"""
|
||||
history = context.render_context.setdefault(
|
||||
self.context_key,
|
||||
[self.origin],
|
||||
)
|
||||
template, origin = context.template.engine.find_template(
|
||||
template_name,
|
||||
skip=history,
|
||||
)
|
||||
history.append(origin)
|
||||
return template
|
||||
|
||||
def get_parent(self, context):
|
||||
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
|
||||
)
|
||||
raise TemplateSyntaxError(error_msg)
|
||||
if isinstance(parent, Template):
|
||||
# parent is a django.template.Template
|
||||
return parent
|
||||
if isinstance(getattr(parent, "template", None), Template):
|
||||
# parent is a django.template.backends.django.Template
|
||||
return parent.template
|
||||
return self.find_template(parent, context)
|
||||
|
||||
def render(self, context):
|
||||
compiled_parent = self.get_parent(context)
|
||||
|
||||
if BLOCK_CONTEXT_KEY not in context.render_context:
|
||||
context.render_context[BLOCK_CONTEXT_KEY] = BlockContext()
|
||||
block_context = context.render_context[BLOCK_CONTEXT_KEY]
|
||||
|
||||
# Add the block nodes from this node to the block context
|
||||
block_context.add_blocks(self.blocks)
|
||||
|
||||
# If this block's parent doesn't have an extends node it is the root,
|
||||
# and its block nodes also need to be added to the block context.
|
||||
for node in compiled_parent.nodelist:
|
||||
# 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)
|
||||
}
|
||||
block_context.add_blocks(blocks)
|
||||
break
|
||||
|
||||
# Call Template._render explicitly so the parser context stays
|
||||
# the same.
|
||||
with context.render_context.push_state(compiled_parent, isolated_context=False):
|
||||
return compiled_parent._render(context)
|
||||
|
||||
|
||||
class IncludeNode(Node):
|
||||
context_key = "__include_context"
|
||||
|
||||
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}>"
|
||||
|
||||
def render(self, context):
|
||||
"""
|
||||
Render the specified template and context. Cache the template object
|
||||
in render_context to avoid reparsing and loading when used in a for
|
||||
loop.
|
||||
"""
|
||||
template = self.template.resolve(context)
|
||||
# Does this quack like a Template?
|
||||
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,
|
||||
),
|
||||
)
|
||||
else:
|
||||
template_name = tuple(template_name)
|
||||
cache = context.render_context.dicts[0].setdefault(self, {})
|
||||
template = cache.get(template_name)
|
||||
if template is None:
|
||||
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"):
|
||||
template = template.template
|
||||
values = {
|
||||
name: var.resolve(context) for name, var in self.extra_context.items()
|
||||
}
|
||||
if self.isolated_context:
|
||||
return template.render(context.new(values))
|
||||
with context.push(**values):
|
||||
return template.render(context)
|
||||
|
||||
|
||||
@register.tag("block")
|
||||
def do_block(parser, token):
|
||||
"""
|
||||
Define a block that can be overridden by child templates.
|
||||
"""
|
||||
# token.split_contents() isn't useful here because this tag doesn't accept
|
||||
# variable as arguments.
|
||||
bits = token.contents.split()
|
||||
if len(bits) != 2:
|
||||
raise TemplateSyntaxError("'%s' tag takes only one argument" % bits[0])
|
||||
block_name = bits[1]
|
||||
# Keep track of the names of BlockNodes found in this template, so we can
|
||||
# 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)
|
||||
)
|
||||
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",))
|
||||
|
||||
# This check is kept for backwards-compatibility. See #3100.
|
||||
endblock = parser.next_token()
|
||||
acceptable_endblocks = ("endblock", "endblock %s" % block_name)
|
||||
if endblock.contents not in acceptable_endblocks:
|
||||
parser.invalid_block_tag(endblock, "endblock", acceptable_endblocks)
|
||||
|
||||
return BlockNode(block_name, nodelist)
|
||||
|
||||
|
||||
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(("./", "../")):
|
||||
# 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("/")),
|
||||
new_name,
|
||||
)
|
||||
)
|
||||
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:
|
||||
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]
|
||||
)
|
||||
return f'"{new_name}"' if has_quotes else new_name
|
||||
|
||||
|
||||
@register.tag("extends")
|
||||
def do_extends(parser, token):
|
||||
"""
|
||||
Signal that this template extends a parent template.
|
||||
|
||||
This tag may be used in two ways: ``{% extends "base" %}`` (with quotes)
|
||||
uses the literal value "base" as the name of the parent template to extend,
|
||||
or ``{% extends variable %}`` uses the value of ``variable`` as either the
|
||||
name of the parent template to extend (if it evaluates to a string) or as
|
||||
the parent template itself (if it evaluates to a Template object).
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
if len(bits) != 2:
|
||||
raise TemplateSyntaxError("'%s' takes one argument" % bits[0])
|
||||
bits[1] = construct_relative_path(parser.origin.template_name, bits[1])
|
||||
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]
|
||||
)
|
||||
return ExtendsNode(nodelist, parent_name)
|
||||
|
||||
|
||||
@register.tag("include")
|
||||
def do_include(parser, token):
|
||||
"""
|
||||
Load a template and render it with the current context. You can pass
|
||||
additional context using keyword arguments.
|
||||
|
||||
Example::
|
||||
|
||||
{% include "foo/some_include" %}
|
||||
{% include "foo/some_include" with bar="BAZZ!" baz="BING!" %}
|
||||
|
||||
Use the ``only`` argument to exclude the current context when rendering
|
||||
the included template::
|
||||
|
||||
{% include "foo/some_include" only %}
|
||||
{% include "foo/some_include" with bar="1" only %}
|
||||
"""
|
||||
bits = token.split_contents()
|
||||
if len(bits) < 2:
|
||||
raise TemplateSyntaxError(
|
||||
"%r tag takes at least one argument: the name of the template to "
|
||||
"be included." % bits[0]
|
||||
)
|
||||
options = {}
|
||||
remaining_bits = bits[2:]
|
||||
while remaining_bits:
|
||||
option = remaining_bits.pop(0)
|
||||
if option in options:
|
||||
raise TemplateSyntaxError(
|
||||
"The %r option was specified more than once." % option
|
||||
)
|
||||
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":
|
||||
value = True
|
||||
else:
|
||||
raise TemplateSyntaxError(
|
||||
"Unknown argument for %r tag: %r." % (bits[0], option)
|
||||
)
|
||||
options[option] = value
|
||||
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,
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
13
venv/lib/python3.10/site-packages/django/template/loaders/app_directories.py
Executable file
13
venv/lib/python3.10/site-packages/django/template/loaders/app_directories.py
Executable file
@@ -0,0 +1,13 @@
|
||||
"""
|
||||
Wrapper for loading templates from "templates" directories in INSTALLED_APPS
|
||||
packages.
|
||||
"""
|
||||
|
||||
from django.template.utils import get_app_template_dirs
|
||||
|
||||
from .filesystem import Loader as FilesystemLoader
|
||||
|
||||
|
||||
class Loader(FilesystemLoader):
|
||||
def get_dirs(self):
|
||||
return get_app_template_dirs("templates")
|
||||
51
venv/lib/python3.10/site-packages/django/template/loaders/base.py
Executable file
51
venv/lib/python3.10/site-packages/django/template/loaders/base.py
Executable file
@@ -0,0 +1,51 @@
|
||||
from django.template import Template, TemplateDoesNotExist
|
||||
|
||||
|
||||
class Loader:
|
||||
def __init__(self, engine):
|
||||
self.engine = engine
|
||||
|
||||
def get_template(self, template_name, skip=None):
|
||||
"""
|
||||
Call self.get_template_sources() and return a Template object for
|
||||
the first template matching template_name. If skip is provided, ignore
|
||||
template origins in skip. This is used to avoid recursion during
|
||||
template extending.
|
||||
"""
|
||||
tried = []
|
||||
|
||||
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"))
|
||||
continue
|
||||
|
||||
try:
|
||||
contents = self.get_contents(origin)
|
||||
except TemplateDoesNotExist:
|
||||
tried.append((origin, "Source does not exist"))
|
||||
continue
|
||||
else:
|
||||
return Template(
|
||||
contents,
|
||||
origin,
|
||||
origin.template_name,
|
||||
self.engine,
|
||||
)
|
||||
|
||||
raise TemplateDoesNotExist(template_name, tried=tried)
|
||||
|
||||
def get_template_sources(self, template_name):
|
||||
"""
|
||||
An iterator that yields possible matching template paths for a
|
||||
template name.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of Loader must provide a get_template_sources() method"
|
||||
)
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset any state maintained by the loader instance (e.g. cached
|
||||
templates or cached loader modules).
|
||||
"""
|
||||
pass
|
||||
100
venv/lib/python3.10/site-packages/django/template/loaders/cached.py
Executable file
100
venv/lib/python3.10/site-packages/django/template/loaders/cached.py
Executable file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Wrapper class that takes a list of template loaders as an argument and attempts
|
||||
to load templates from them in order, caching the result.
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
|
||||
from django.template import TemplateDoesNotExist
|
||||
from django.template.backends.django import copy_exception
|
||||
|
||||
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)
|
||||
super().__init__(engine)
|
||||
|
||||
def get_dirs(self):
|
||||
for loader in self.loaders:
|
||||
if hasattr(loader, "get_dirs"):
|
||||
yield from loader.get_dirs()
|
||||
|
||||
def get_contents(self, origin):
|
||||
return origin.loader.get_contents(origin)
|
||||
|
||||
def get_template(self, template_name, skip=None):
|
||||
"""
|
||||
Perform the caching that gives this loader its name. Often many of the
|
||||
templates attempted will be missing, so memory use is of concern here.
|
||||
To keep it in check, caching behavior is a little complicated when a
|
||||
template is not found. See ticket #26306 for more details.
|
||||
|
||||
With template debugging disabled, cache the TemplateDoesNotExist class
|
||||
for every missing template and raise a new instance of it after
|
||||
fetching it from the cache.
|
||||
|
||||
With template debugging enabled, a unique TemplateDoesNotExist object
|
||||
is cached for each missing template to preserve debug data. When
|
||||
raising an exception, Python sets __traceback__, __context__, and
|
||||
__cause__ attributes on it. Those attributes can contain references to
|
||||
all sorts of objects up the call chain and caching them creates a
|
||||
memory leak. Thus, unraised copies of the exceptions are cached and
|
||||
copies of those copies are raised after they're fetched from the cache.
|
||||
"""
|
||||
key = self.cache_key(template_name, skip)
|
||||
cached = self.get_template_cache.get(key)
|
||||
if cached:
|
||||
if isinstance(cached, type) and issubclass(cached, TemplateDoesNotExist):
|
||||
raise cached(template_name)
|
||||
elif isinstance(cached, TemplateDoesNotExist):
|
||||
raise copy_exception(cached)
|
||||
return cached
|
||||
|
||||
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
|
||||
)
|
||||
raise
|
||||
else:
|
||||
self.get_template_cache[key] = template
|
||||
|
||||
return template
|
||||
|
||||
def get_template_sources(self, template_name):
|
||||
for loader in self.loaders:
|
||||
yield from loader.get_template_sources(template_name)
|
||||
|
||||
def cache_key(self, template_name, skip=None):
|
||||
"""
|
||||
Generate a cache key for the template name and skip.
|
||||
|
||||
If skip is provided, only origins that match template_name are included
|
||||
in the cache key. This ensures each template is only parsed and cached
|
||||
once if contained in different extend chains like:
|
||||
|
||||
x -> a -> a
|
||||
y -> a -> a
|
||||
z -> a -> a
|
||||
"""
|
||||
skip_prefix = ""
|
||||
|
||||
if skip:
|
||||
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)
|
||||
|
||||
def generate_hash(self, values):
|
||||
return hashlib.sha1("|".join(values).encode()).hexdigest()
|
||||
|
||||
def reset(self):
|
||||
"Empty the template cache."
|
||||
self.get_template_cache.clear()
|
||||
45
venv/lib/python3.10/site-packages/django/template/loaders/filesystem.py
Executable file
45
venv/lib/python3.10/site-packages/django/template/loaders/filesystem.py
Executable file
@@ -0,0 +1,45 @@
|
||||
"""
|
||||
Wrapper for loading templates from the filesystem.
|
||||
"""
|
||||
|
||||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from django.template import Origin, TemplateDoesNotExist
|
||||
from django.utils._os import safe_join
|
||||
|
||||
from .base import Loader as BaseLoader
|
||||
|
||||
|
||||
class Loader(BaseLoader):
|
||||
def __init__(self, engine, dirs=None):
|
||||
super().__init__(engine)
|
||||
self.dirs = dirs
|
||||
|
||||
def get_dirs(self):
|
||||
return self.dirs if self.dirs is not None else self.engine.dirs
|
||||
|
||||
def get_contents(self, origin):
|
||||
try:
|
||||
with open(origin.name, encoding=self.engine.file_charset) as fp:
|
||||
return fp.read()
|
||||
except FileNotFoundError:
|
||||
raise TemplateDoesNotExist(origin)
|
||||
|
||||
def get_template_sources(self, template_name):
|
||||
"""
|
||||
Return an Origin object pointing to an absolute path in each directory
|
||||
in template_dirs. For security reasons, if a path doesn't lie inside
|
||||
one of the template_dirs it is excluded from the result set.
|
||||
"""
|
||||
for template_dir in self.get_dirs():
|
||||
try:
|
||||
name = safe_join(template_dir, template_name)
|
||||
except SuspiciousFileOperation:
|
||||
# The joined path was located outside of this template_dir
|
||||
# (it might be inside another one, so this isn't fatal).
|
||||
continue
|
||||
|
||||
yield Origin(
|
||||
name=name,
|
||||
template_name=template_name,
|
||||
loader=self,
|
||||
)
|
||||
26
venv/lib/python3.10/site-packages/django/template/loaders/locmem.py
Executable file
26
venv/lib/python3.10/site-packages/django/template/loaders/locmem.py
Executable file
@@ -0,0 +1,26 @@
|
||||
"""
|
||||
Wrapper for loading templates from a plain Python dict.
|
||||
"""
|
||||
|
||||
from django.template import Origin, TemplateDoesNotExist
|
||||
|
||||
from .base import Loader as BaseLoader
|
||||
|
||||
|
||||
class Loader(BaseLoader):
|
||||
def __init__(self, engine, templates_dict):
|
||||
self.templates_dict = templates_dict
|
||||
super().__init__(engine)
|
||||
|
||||
def get_contents(self, origin):
|
||||
try:
|
||||
return self.templates_dict[origin.name]
|
||||
except KeyError:
|
||||
raise TemplateDoesNotExist(origin)
|
||||
|
||||
def get_template_sources(self, template_name):
|
||||
yield Origin(
|
||||
name=template_name,
|
||||
template_name=template_name,
|
||||
loader=self,
|
||||
)
|
||||
164
venv/lib/python3.10/site-packages/django/template/response.py
Executable file
164
venv/lib/python3.10/site-packages/django/template/response.py
Executable file
@@ -0,0 +1,164 @@
|
||||
from django.http import HttpResponse
|
||||
|
||||
from .loader import get_template, select_template
|
||||
|
||||
|
||||
class ContentNotRenderedError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SimpleTemplateResponse(HttpResponse):
|
||||
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,
|
||||
):
|
||||
# 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.
|
||||
self.template_name = template
|
||||
self.context_data = context
|
||||
|
||||
self.using = using
|
||||
|
||||
self._post_render_callbacks = []
|
||||
|
||||
# _request stores the current request object in subclasses that know
|
||||
# about requests, like TemplateResponse. It's defined in the base class
|
||||
# to minimize code duplication.
|
||||
# It's called self._request because self.request gets overwritten by
|
||||
# django.test.client.Client. Unlike template_name and context_data,
|
||||
# _request should not be considered part of the public API.
|
||||
self._request = None
|
||||
|
||||
# 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)
|
||||
|
||||
# _is_rendered tracks whether the template and context has been baked
|
||||
# into a final response.
|
||||
# Super __init__ doesn't know any better than to set self.content to
|
||||
# the empty string we just gave it, which wrongly sets _is_rendered
|
||||
# True, so we initialize it to False after the call to super __init__.
|
||||
self._is_rendered = False
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Raise an exception if trying to pickle an unrendered response. Pickle
|
||||
only rendered data, not the data used to construct the response.
|
||||
"""
|
||||
obj_dict = self.__dict__.copy()
|
||||
if not self._is_rendered:
|
||||
raise ContentNotRenderedError(
|
||||
"The response content must be rendered before it can be pickled."
|
||||
)
|
||||
for attr in self.rendering_attrs:
|
||||
if attr in obj_dict:
|
||||
del obj_dict[attr]
|
||||
|
||||
return obj_dict
|
||||
|
||||
def resolve_template(self, template):
|
||||
"""Accept a template object, path-to-template, or list of paths."""
|
||||
if isinstance(template, (list, tuple)):
|
||||
return select_template(template, using=self.using)
|
||||
elif isinstance(template, str):
|
||||
return get_template(template, using=self.using)
|
||||
else:
|
||||
return template
|
||||
|
||||
def resolve_context(self, context):
|
||||
return context
|
||||
|
||||
@property
|
||||
def rendered_content(self):
|
||||
"""Return the freshly rendered content for the template and context
|
||||
described by the TemplateResponse.
|
||||
|
||||
This *does not* set the final content of the response. To set the
|
||||
response content, you must either call render(), or set the
|
||||
content explicitly using the value of this property.
|
||||
"""
|
||||
template = self.resolve_template(self.template_name)
|
||||
context = self.resolve_context(self.context_data)
|
||||
return template.render(context, self._request)
|
||||
|
||||
def add_post_render_callback(self, callback):
|
||||
"""Add a new post-rendering callback.
|
||||
|
||||
If the response has already been rendered,
|
||||
invoke the callback immediately.
|
||||
"""
|
||||
if self._is_rendered:
|
||||
callback(self)
|
||||
else:
|
||||
self._post_render_callbacks.append(callback)
|
||||
|
||||
def render(self):
|
||||
"""Render (thereby finalizing) the content of the response.
|
||||
|
||||
If the content has already been rendered, this is a no-op.
|
||||
|
||||
Return the baked response instance.
|
||||
"""
|
||||
retval = self
|
||||
if not self._is_rendered:
|
||||
self.content = self.rendered_content
|
||||
for post_callback in self._post_render_callbacks:
|
||||
newretval = post_callback(retval)
|
||||
if newretval is not None:
|
||||
retval = newretval
|
||||
return retval
|
||||
|
||||
@property
|
||||
def is_rendered(self):
|
||||
return self._is_rendered
|
||||
|
||||
def __iter__(self):
|
||||
if not self._is_rendered:
|
||||
raise ContentNotRenderedError(
|
||||
"The response content must be rendered before it can be iterated over."
|
||||
)
|
||||
return super().__iter__()
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
if not self._is_rendered:
|
||||
raise ContentNotRenderedError(
|
||||
"The response content must be rendered before it can be accessed."
|
||||
)
|
||||
return super().content
|
||||
|
||||
@content.setter
|
||||
def content(self, value):
|
||||
"""Set the content for the response."""
|
||||
HttpResponse.content.fset(self, value)
|
||||
self._is_rendered = True
|
||||
|
||||
|
||||
class TemplateResponse(SimpleTemplateResponse):
|
||||
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
|
||||
)
|
||||
self._request = request
|
||||
214
venv/lib/python3.10/site-packages/django/template/smartif.py
Executable file
214
venv/lib/python3.10/site-packages/django/template/smartif.py
Executable file
@@ -0,0 +1,214 @@
|
||||
"""
|
||||
Parser and utilities for the smart 'if' tag
|
||||
"""
|
||||
|
||||
# Using a simple top down parser, as described here:
|
||||
# http://effbot.org/zone/simple-top-down-parsing.htm.
|
||||
# 'led' = left denotation
|
||||
# 'nud' = null denotation
|
||||
# 'bp' = binding power (left = lbp, right = rbp)
|
||||
|
||||
|
||||
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
|
||||
|
||||
def nud(self, parser):
|
||||
# Null denotation - called in prefix context
|
||||
raise parser.error_class(
|
||||
"Not expecting '%s' in this position in if tag." % self.id
|
||||
)
|
||||
|
||||
def led(self, left, parser):
|
||||
# Left denotation - called in infix context
|
||||
raise parser.error_class(
|
||||
"Not expecting '%s' as infix operator in if tag." % self.id
|
||||
)
|
||||
|
||||
def display(self):
|
||||
"""
|
||||
Return what to display in error messages for this node
|
||||
"""
|
||||
return self.id
|
||||
|
||||
def __repr__(self):
|
||||
out = [str(x) for x in [self.id, self.first, self.second] if x is not None]
|
||||
return "(" + " ".join(out) + ")"
|
||||
|
||||
|
||||
def infix(bp, func):
|
||||
"""
|
||||
Create an infix operator, given a binding power and a function that
|
||||
evaluates the node.
|
||||
"""
|
||||
|
||||
class Operator(TokenBase):
|
||||
lbp = bp
|
||||
|
||||
def led(self, left, parser):
|
||||
self.first = left
|
||||
self.second = parser.expression(bp)
|
||||
return self
|
||||
|
||||
def eval(self, context):
|
||||
try:
|
||||
return func(context, self.first, self.second)
|
||||
except Exception:
|
||||
# Templates shouldn't throw exceptions when rendering. We are
|
||||
# most likely to get exceptions for things like {% if foo in bar
|
||||
# %} where 'bar' does not support 'in', so default to False
|
||||
return False
|
||||
|
||||
return Operator
|
||||
|
||||
|
||||
def prefix(bp, func):
|
||||
"""
|
||||
Create a prefix operator, given a binding power and a function that
|
||||
evaluates the node.
|
||||
"""
|
||||
|
||||
class Operator(TokenBase):
|
||||
lbp = bp
|
||||
|
||||
def nud(self, parser):
|
||||
self.first = parser.expression(bp)
|
||||
self.second = None
|
||||
return self
|
||||
|
||||
def eval(self, context):
|
||||
try:
|
||||
return func(context, self.first)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return Operator
|
||||
|
||||
|
||||
# Operator precedence follows Python.
|
||||
# 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)),
|
||||
}
|
||||
|
||||
# Assign 'id' to each:
|
||||
for key, op in OPERATORS.items():
|
||||
op.id = key
|
||||
|
||||
|
||||
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.
|
||||
id = "literal"
|
||||
lbp = 0
|
||||
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def display(self):
|
||||
return repr(self.value)
|
||||
|
||||
def nud(self, parser):
|
||||
return self
|
||||
|
||||
def eval(self, context):
|
||||
return self.value
|
||||
|
||||
def __repr__(self):
|
||||
return "(%s %r)" % (self.id, self.value)
|
||||
|
||||
|
||||
class EndToken(TokenBase):
|
||||
lbp = 0
|
||||
|
||||
def nud(self, parser):
|
||||
raise parser.error_class("Unexpected end of expression in if tag.")
|
||||
|
||||
|
||||
EndToken = EndToken()
|
||||
|
||||
|
||||
class IfParser:
|
||||
error_class = ValueError
|
||||
|
||||
def __init__(self, tokens):
|
||||
# Turn 'is','not' and 'not','in' into single tokens.
|
||||
num_tokens = len(tokens)
|
||||
mapped_tokens = []
|
||||
i = 0
|
||||
while i < num_tokens:
|
||||
token = tokens[i]
|
||||
if token == "is" and i + 1 < num_tokens and tokens[i + 1] == "not":
|
||||
token = "is not"
|
||||
i += 1 # skip 'not'
|
||||
elif token == "not" and i + 1 < num_tokens and tokens[i + 1] == "in":
|
||||
token = "not in"
|
||||
i += 1 # skip 'in'
|
||||
mapped_tokens.append(self.translate_token(token))
|
||||
i += 1
|
||||
|
||||
self.tokens = mapped_tokens
|
||||
self.pos = 0
|
||||
self.current_token = self.next_token()
|
||||
|
||||
def translate_token(self, token):
|
||||
try:
|
||||
op = OPERATORS[token]
|
||||
except (KeyError, TypeError):
|
||||
return self.create_var(token)
|
||||
else:
|
||||
return op()
|
||||
|
||||
def next_token(self):
|
||||
if self.pos >= len(self.tokens):
|
||||
return EndToken
|
||||
else:
|
||||
retval = self.tokens[self.pos]
|
||||
self.pos += 1
|
||||
return retval
|
||||
|
||||
def parse(self):
|
||||
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()
|
||||
)
|
||||
return retval
|
||||
|
||||
def expression(self, rbp=0):
|
||||
t = self.current_token
|
||||
self.current_token = self.next_token()
|
||||
left = t.nud(self)
|
||||
while rbp < self.current_token.lbp:
|
||||
t = self.current_token
|
||||
self.current_token = self.next_token()
|
||||
left = t.led(left, self)
|
||||
return left
|
||||
|
||||
def create_var(self, value):
|
||||
return Literal(value)
|
||||
111
venv/lib/python3.10/site-packages/django/template/utils.py
Executable file
111
venv/lib/python3.10/site-packages/django/template/utils.py
Executable file
@@ -0,0 +1,111 @@
|
||||
import functools
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
|
||||
class InvalidTemplateEngineError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
|
||||
class EngineHandler:
|
||||
def __init__(self, templates=None):
|
||||
"""
|
||||
templates is an optional list of template engine definitions
|
||||
(structured like settings.TEMPLATES).
|
||||
"""
|
||||
self._templates = templates
|
||||
self._engines = {}
|
||||
|
||||
@cached_property
|
||||
def templates(self):
|
||||
if self._templates is None:
|
||||
self._templates = settings.TEMPLATES
|
||||
|
||||
templates = {}
|
||||
backend_names = []
|
||||
for tpl in self._templates:
|
||||
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]
|
||||
except Exception:
|
||||
invalid_backend = tpl.get("BACKEND", "<not defined>")
|
||||
raise ImproperlyConfigured(
|
||||
"Invalid BACKEND for a template engine: {}. Check "
|
||||
"your TEMPLATES setting.".format(invalid_backend)
|
||||
)
|
||||
|
||||
tpl = {
|
||||
"NAME": default_name,
|
||||
"DIRS": [],
|
||||
"APP_DIRS": False,
|
||||
"OPTIONS": {},
|
||||
**tpl,
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
)
|
||||
|
||||
return templates
|
||||
|
||||
def __getitem__(self, alias):
|
||||
try:
|
||||
return self._engines[alias]
|
||||
except KeyError:
|
||||
try:
|
||||
params = self.templates[alias]
|
||||
except KeyError:
|
||||
raise InvalidTemplateEngineError(
|
||||
"Could not find config for '{}' "
|
||||
"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")
|
||||
engine_cls = import_string(backend)
|
||||
engine = engine_cls(params)
|
||||
|
||||
self._engines[alias] = engine
|
||||
return engine
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.templates)
|
||||
|
||||
def all(self):
|
||||
return [self[alias] for alias in self]
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
def get_app_template_dirs(dirname):
|
||||
"""
|
||||
Return an iterable of paths of directories to load app templates from.
|
||||
|
||||
dirname is the name of the subdirectory containing templates inside
|
||||
installed applications.
|
||||
"""
|
||||
template_dirs = [
|
||||
Path(app_config.path) / dirname
|
||||
for app_config in apps.get_app_configs()
|
||||
if app_config.path and (Path(app_config.path) / dirname).is_dir()
|
||||
]
|
||||
# Immutable return value because it will be cached and shared by callers.
|
||||
return tuple(template_dirs)
|
||||
Reference in New Issue
Block a user