1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
import io
import jinja2
import jinja2.ext
from jinja2 import nodes
from jinja2.ext import Extension
from jinja2.nodes import CallBlock, Call, ExtensionAttribute
from compressor.exceptions import TemplateSyntaxError, TemplateDoesNotExist
def flatten_context(context):
if hasattr(context, "dicts"):
context_dict = {}
for d in context.dicts:
context_dict.update(d)
return context_dict
return context
class SpacelessExtension(Extension):
"""
Functional "spaceless" extension equivalent to Django's.
See: https://github.com/django/django/blob/master/django/template/defaulttags.py
"""
tags = set(["spaceless"])
def parse(self, parser):
lineno = next(parser.stream).lineno
body = parser.parse_statements(["name:endspaceless"], drop_needle=True)
return nodes.CallBlock(
self.call_method("_spaceless", []), [], [], body
).set_lineno(lineno)
def _spaceless(self, caller):
from django.utils.html import strip_spaces_between_tags
return strip_spaces_between_tags(caller().strip())
def url_for(mod, filename):
"""
Incomplete emulation of Flask's url_for.
"""
try:
from django.contrib.staticfiles.templatetags import staticfiles
except ImportError:
# Django 3.0+
import django.templatetags.static as staticfiles
if mod == "static":
return staticfiles.static(filename)
return ""
class Jinja2Parser:
COMPRESSOR_ID = "compressor.contrib.jinja2ext.CompressorExtension"
def __init__(self, charset, env):
self.charset = charset
self.env = env
def parse(self, template_name):
with io.open(template_name, mode="rb") as file:
try:
template = self.env.parse(file.read().decode(self.charset))
except jinja2.TemplateSyntaxError as e:
raise TemplateSyntaxError(str(e))
except jinja2.TemplateNotFound as e:
raise TemplateDoesNotExist(str(e))
return template
def process_template(self, template, context):
return True
def get_init_context(self, offline_context):
# Don't need to add filters and tests to the context, as Jinja2 will
# automatically look for them in self.env.filters and self.env.tests.
# This is tested by test_complex and test_templatetag.
# Allow offline context to override the globals.
context = self.env.globals.copy()
context.update(flatten_context(offline_context))
return context
def process_node(self, template, context, node):
pass
def _render_nodes(self, template, context, nodes):
compiled_node = self.env.compile(jinja2.nodes.Template(nodes))
template = jinja2.Template.from_code(self.env, compiled_node, {})
flat_context = flatten_context(context)
return template.render(flat_context)
def render_nodelist(self, template, context, node):
return self._render_nodes(template, context, node.body)
def render_node(self, template, context, node):
return self._render_nodes(template, context, [node])
def get_nodelist(self, node):
body = getattr(node, "body", getattr(node, "nodes", []))
if isinstance(node, jinja2.nodes.If):
return body + node.else_
return body
def walk_nodes(self, node, block_name=None, context=None):
for node in self.get_nodelist(node):
if (
isinstance(node, CallBlock)
and isinstance(node.call, Call)
and isinstance(node.call.node, ExtensionAttribute)
and node.call.node.identifier == self.COMPRESSOR_ID
):
node.call.node.name = "_compress_forced"
yield node
else:
for node in self.walk_nodes(node, block_name=block_name):
yield node
|