def load_widgets(context, **kwargs): ''' Load a series of widget libraries. ''' _soft = kwargs.pop('_soft', False) try: widgets = context.render_context[WIDGET_CONTEXT_KEY] except KeyError: widgets = context.render_context[WIDGET_CONTEXT_KEY] = {} for alias, template_name in kwargs.items(): if _soft and alias in widgets: continue context.render_context.push() context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() try: blocks = resolve_blocks(template_name, context) widgets[alias] = blocks finally: context.render_context.pop() return ''
def handle_extendsnode(extendsnode, block_context=None, original=None): """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 is None: block_context = BlockContext() blocks = dict( (n.name, n) for n in extendsnode.nodelist.get_nodes_by_type(BlockNode)) block_context.add_blocks(blocks) context = Context(settings.COMPRESS_OFFLINE_CONTEXT) if original is not None: context.template = original 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, block_context, original) 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 resolve_blocks(template, context): ''' Return a BlockContext instance of all the {% block %} tags in the template. If template is a string, it will be resolved through get_template ''' try: blocks = context.render_context[BLOCK_CONTEXT_KEY] except KeyError: blocks = context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() # If it's just the name, resolve into template if isinstance(template, six.string_types): template = get_template(template) # For Django 1.8 compatibility template = getattr(template, 'template', template) # Add this templates blocks as the first local_blocks = dict( (block.name, block) for block in template.nodelist.get_nodes_by_type(BlockNode)) blocks.add_blocks(local_blocks) # Do we extend a parent template? extends = template.nodelist.get_nodes_by_type(ExtendsNode) if extends: # Can only have one extends in a template extends_node = extends[0] # Get the parent, and recurse parent_template = extends_node.get_parent(context) resolve_blocks(parent_template, context) return blocks
def reuse(context, block_list, **kwargs): ''' Allow reuse of a block within a template. {% reuse '_myblock' foo=bar %} If passed a list of block names, will use the first that matches: {% reuse list_of_block_names .... %} ''' try: block_context = context.render_context[BLOCK_CONTEXT_KEY] except KeyError: block_context = BlockContext() if not isinstance(block_list, (list, tuple)): block_list = [block_list] for block in block_list: block = block_context.get_block(block) if block: break else: return '' context.update(kwargs) try: return block.render(context) finally: context.pop()
def resolve_blocks(template, context, blocks=None): '''Get all the blocks from this template, accounting for 'extends' tags''' if blocks is None: blocks = BlockContext() # If it's just the name, resolve into template if isinstance(template, str): template = get_template(template) # Add this templates blocks as the first local_blocks = dict( (block.name, block) for block in template.nodelist.get_nodes_by_type(BlockNode)) blocks.add_blocks(local_blocks) # Do we extend a parent template? extends = template.nodelist.get_nodes_by_type(ExtendsNode) if extends: # Can only have one extends in a template extends_node = extends[0] # Get the parent, and recurse parent_template = extends_node.get_parent(context) resolve_blocks(parent_template, context, blocks) return blocks
def resolve_blocks(template, context): '''Get all the blocks from this template, accounting for 'extends' tags''' try: blocks = context.render_context[BLOCK_CONTEXT_KEY] except KeyError: blocks = context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() # If it's just the name, resolve into template if isinstance(template, six.string_types): template = get_template(template) # Add this templates blocks as the first local_blocks = dict( (block.name, block) for block in template.nodelist.get_nodes_by_type(BlockNode)) blocks.add_blocks(local_blocks) # Do we extend a parent template? extends = template.nodelist.get_nodes_by_type(ExtendsNode) if extends: # Can only have one extends in a template extends_node = extends[0] # Get the parent, and recurse parent_template = extends_node.get_parent(context) resolve_blocks(parent_template, context) return blocks
def _build_block_context(template, context): """Populate the block context with BlockNodes from parent templates.""" # Ensure there's a BlockContext before rendering. This allows blocks in # ExtendsNodes to be found by sub-templates (allowing {{ block.super }} and # overriding sub-blocks to work). if BLOCK_CONTEXT_KEY not in context.render_context: context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() block_context = context.render_context[BLOCK_CONTEXT_KEY] for node in template.nodelist: if isinstance(node, ExtendsNode): compiled_parent = node.get_parent(context) # Add the parent node's blocks to the context. (This ends up being # similar logic to ExtendsNode.render(), where we're adding the # parent's blocks to the context so a child can find them.) block_context.add_blocks({ n.name: n for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode) }) _build_block_context(compiled_parent, context) return compiled_parent # The ExtendsNode has to be the first non-text node. if not isinstance(node, TextNode): break
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): nodelist = compiled_parent.nodelist blocks = { n.name: n for n in nodelist.get_nodes_by_type(BlockNode) } block_context.add_blocks(blocks) break # Call Template._render explicitly so the parser context stays # the same. return compiled_parent._render(context)
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 test_repr(self): block_context = BlockContext() block_context.add_blocks({"content": BlockNode("content", [])}) self.assertEqual( repr(block_context), "<BlockContext: blocks=defaultdict(<class 'list'>, " "{'content': [<Block Node: content. Contents: []>]})>", )
def _render_template_block_nodelist(nodelist, block_name, context): """Recursively iterate over a node to find the wanted block.""" # The result, if found via an ExtendsNode. rendered_extends_block = None # Attempt to find the wanted block in the current template. for node in nodelist: # ExtendsNode must be first, so this check is gratuitous after the first # iteration. if isinstance(node, ExtendsNode): # Attempt to render this block in the parent, save it in case the # only instance of this block is from the super-template. (We don't # know this until we traverse the rest of the nodelist, # unfortunately.) try: rendered_extends_block = _render_template_block( node.get_parent(context), block_name, context) except BlockNotFound: pass # If the wanted block was found, return it. if isinstance(node, BlockNode) and node.name == block_name: # Ensure there's a BlockContext before rendinering. This allows # blocks in ExtendsNodes to be found by sub-templates (essentially, # allowing {{ block.super }} to work. if BLOCK_CONTEXT_KEY not in context.render_context: context.render_context[BLOCK_CONTEXT_KEY] = BlockContext() context.render_context[BLOCK_CONTEXT_KEY].push(block_name, node) return node.render(context) # If a node has children, recurse into them. Based on # django.template.base.Node.get_nodes_by_type. for attr in node.child_nodelists: try: new_nodelist = getattr(node, attr) except AttributeError: continue # Try to find the block recursively. try: return _render_template_block_nodelist(new_nodelist, block_name, context) except BlockNotFound: continue # No better node was found besides the one from an ExtendsNode. Return it! if rendered_extends_block: return rendered_extends_block # The wanted block_name was not found. raise BlockNotFound("block with name '%s' does not exist" % block_name)
def get_block_context(self, form_template, blocks): block_context = BlockContext() # Add the block nodes from this node to the block context. block_context.add_blocks(blocks) # Add the template's nodes too if it is the root template. for node in form_template.nodelist: # The ExtendsNode has to be the first non-text node. if not isinstance(node, template.TextNode): if not isinstance(node, ExtendsNode): blocks = dict([(n.name, n) for n in form_template.nodelist\ .get_nodes_by_type(BlockNode)]) block_context.add_blocks(blocks) break return block_context
def render_extends_node(self, context): compiled_parent = self.get_parent(context) embeds = get_embed_dict(self.embed_list, 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] if EMBED_CONTEXT_KEY not in context.render_context: context.render_context[EMBED_CONTEXT_KEY] = EmbedContext() embed_context = context.render_context[EMBED_CONTEXT_KEY] # Add the block nodes from this node to the block context # Do the equivalent for embed nodes block_context.add_blocks(self.blocks) embed_context.add_embeds(embeds) # 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 = dict([ (n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type( BlockNode) ]) block_context.add_blocks(blocks) embeds = get_embed_dict( compiled_parent.nodelist.get_nodes_by_type( ConstantEmbedNode), context) embed_context.add_embeds(embeds) break # Explicitly render all direct embed children of this node. if self.embed_list: for node in self.nodelist: if isinstance(node, ConstantEmbedNode): node.render(context) # Call Template._render explicitly so the parser context stays # the same. return compiled_parent._render(context)
def render(self, context): # Resolve our arguments tmpl_name = self.tmpl_name.resolve(context) form = self.form if form is not None: form = form.resolve(context) safe_context = copy(context) # Since render_context is derived from BaseContext, a simple copy will # wind up with the same stack of dicts. safe_context.render_context = safe_context.render_context.new({ BLOCK_CONTEXT_KEY: BlockContext(), }) extra = { 'formulation': resolve_blocks(tmpl_name, safe_context), 'formulation-form': form, } # Render our children with extra_context(safe_context, extra): return self.nodelist.render(safe_context)
def reuse(context, block_list, **kwargs): ''' Allow reuse of a block within a template. {% reuse '_myblock' foo=bar %} If passed a list of block names, will use the first that matches: {% reuse list_of_block_names .... %} ''' try: block_context = context.render_context[BLOCK_CONTEXT_KEY] except KeyError: block_context = BlockContext() blocks = { n.name: n for n in context.template.nodelist.get_nodes_by_type(BlockNode) } block_context.add_blocks(blocks) if not isinstance(block_list, (list, tuple)): block_list = [block_list] for name in block_list: block = block_context.get_block(name) if block is not None: break if block is None: return '' context.update(kwargs) try: return block.render(context) finally: context.pop()
def handle_extendsnode(extendsnode, block_context=None, original=None): """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 is None: block_context = BlockContext() blocks = dict( (n.name, n) for n in extendsnode.nodelist.get_nodes_by_type(BlockNode)) block_context.add_blocks(blocks) # Note: we pass an empty context when we find the parent, this breaks # inheritance using variables ({% extends template_var %}) but a refactor # will be needed to support that use-case with multiple offline contexts. context = Context() if original is not None: context.template = original 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, block_context, original) 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 load_widgets(context, **kwargs): ''' Load a series of widget libraries. ''' _soft = kwargs.pop('_soft', False) try: widgets = context.render_context[WIDGET_CONTEXT_KEY] except KeyError: widgets = context.render_context[WIDGET_CONTEXT_KEY] = {} for alias, template_name in kwargs.items(): if _soft and alias in widgets: continue # Build an isolated render context each time safe_context = copy(context) safe_context.render_context = safe_context.render_context.new({ BLOCK_CONTEXT_KEY: BlockContext(), }) blocks = resolve_blocks(template_name, safe_context) widgets[alias] = blocks return ''