first comit
This commit is contained in:
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -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
|
||||
@@ -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]
|
||||
@@ -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)
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user