
Test case for template inheritance with variable from context and super Test case for template inheritance with variable from context
150 lines
5.7 KiB
Python
150 lines
5.7 KiB
Python
from __future__ import absolute_import
|
|
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(object):
|
|
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):
|
|
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:
|
|
nodelist += getattr(node, attr, [])
|
|
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
|