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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
from copy import copy
from django import template
from django.template import Context
from django.template.base import Node, VariableNode, TextNode, NodeList
from django.template.defaulttags import IfNode
from django.template.loader import get_template
from django.template.loader_tags import (
BLOCK_CONTEXT_KEY,
ExtendsNode,
BlockNode,
BlockContext,
)
from compressor.exceptions import TemplateSyntaxError, TemplateDoesNotExist
from compressor.templatetags.compress import CompressorNode
def handle_extendsnode(extendsnode, context):
"""Create a copy of Node tree of a derived template replacing
all blocks tags with the nodes of appropriate blocks.
Also handles {{ block.super }} tags.
"""
if BLOCK_CONTEXT_KEY not in context.render_context:
context.render_context[BLOCK_CONTEXT_KEY] = BlockContext()
block_context = context.render_context[BLOCK_CONTEXT_KEY]
blocks = dict(
(n.name, n) for n in extendsnode.nodelist.get_nodes_by_type(BlockNode)
)
block_context.add_blocks(blocks)
compiled_parent = extendsnode.get_parent(context)
parent_nodelist = compiled_parent.nodelist
# If the parent template has an ExtendsNode it is not the root.
for node in parent_nodelist:
# The ExtendsNode has to be the first non-text node.
if not isinstance(node, TextNode):
if isinstance(node, ExtendsNode):
return handle_extendsnode(node, context)
break
# Add blocks of the root template to block context.
blocks = dict((n.name, n) for n in parent_nodelist.get_nodes_by_type(BlockNode))
block_context.add_blocks(blocks)
block_stack = []
new_nodelist = remove_block_nodes(parent_nodelist, block_stack, block_context)
return new_nodelist
def remove_block_nodes(nodelist, block_stack, block_context):
new_nodelist = NodeList()
for node in nodelist:
if isinstance(node, VariableNode):
var_name = node.filter_expression.token.strip()
if var_name == "block.super":
if not block_stack:
continue
node = block_context.get_block(block_stack[-1].name)
if not node:
continue
if isinstance(node, BlockNode):
expanded_block = expand_blocknode(node, block_stack, block_context)
new_nodelist.extend(expanded_block)
else:
# IfNode has nodelist as a @property so we can not modify it
if isinstance(node, IfNode):
node = copy(node)
for i, (condition, sub_nodelist) in enumerate(
node.conditions_nodelists
):
sub_nodelist = remove_block_nodes(
sub_nodelist, block_stack, block_context
)
node.conditions_nodelists[i] = (condition, sub_nodelist)
else:
for attr in node.child_nodelists:
sub_nodelist = getattr(node, attr, None)
if sub_nodelist:
sub_nodelist = remove_block_nodes(
sub_nodelist, block_stack, block_context
)
node = copy(node)
setattr(node, attr, sub_nodelist)
new_nodelist.append(node)
return new_nodelist
def expand_blocknode(node, block_stack, block_context):
popped_block = block = block_context.pop(node.name)
if block is None:
block = node
block_stack.append(block)
expanded_nodelist = remove_block_nodes(block.nodelist, block_stack, block_context)
block_stack.pop()
if popped_block is not None:
block_context.push(node.name, popped_block)
return expanded_nodelist
class DjangoParser:
def __init__(self, charset):
self.charset = charset
def parse(self, template_name):
try:
return get_template(template_name).template
except template.TemplateSyntaxError as e:
raise TemplateSyntaxError(str(e))
except template.TemplateDoesNotExist as e:
raise TemplateDoesNotExist(str(e))
def process_template(self, template, context):
return True
def get_init_context(self, offline_context):
return offline_context
def process_node(self, template, context, node):
pass
def render_nodelist(self, template, context, node):
context.template = template
return node.nodelist.render(context)
def render_node(self, template, context, node):
return node.render(context, forced=True)
def get_nodelist(self, node, original, context=None):
if isinstance(node, ExtendsNode):
try:
if context is None:
context = Context()
context.template = original
return handle_extendsnode(node, context)
except template.TemplateSyntaxError as e:
raise TemplateSyntaxError(str(e))
except template.TemplateDoesNotExist as e:
raise TemplateDoesNotExist(str(e))
# Check if node is an ```if``` switch with true and false branches
nodelist = []
if isinstance(node, Node):
for attr in node.child_nodelists:
# see https://github.com/django-compressor/django-compressor/pull/825
# and linked issues/PRs for a discussion on the `None) or []` part
nodelist += getattr(node, attr, None) or []
else:
nodelist = getattr(node, "nodelist", [])
return nodelist
def walk_nodes(self, node, original=None, context=None):
if original is None:
original = node
for node in self.get_nodelist(node, original, context):
if isinstance(node, CompressorNode) and node.is_offline_compression_enabled(
forced=True
):
yield node
else:
for node in self.walk_nodes(node, original, context):
yield node
|