| 19 | | def render(self, context): |
|---|
| 20 | | context.push() |
|---|
| 21 | | # Save context in case of block.super(). |
|---|
| 22 | | self.context = context |
|---|
| 23 | | context['block'] = self |
|---|
| 24 | | result = self.nodelist.render(context) |
|---|
| 25 | | context.pop() |
|---|
| | 19 | def render(self, context, force_self=False): |
|---|
| | 20 | """ |
|---|
| | 21 | Find the correct BlockNode and render its nodelist. |
|---|
| | 22 | case 1: no extends tags encountered => render self |
|---|
| | 23 | case 2: in block.super => render self |
|---|
| | 24 | case 3: extends tags but not in block.super => render "youngest" version of this block |
|---|
| | 25 | """ |
|---|
| | 26 | block_context = context.get(BlockContext.KEY, None) |
|---|
| | 27 | if block_context is None: |
|---|
| | 28 | # no extends tag encountered; block tags have no effect |
|---|
| | 29 | return self.nodelist.render(context) |
|---|
| | 30 | elif force_self: |
|---|
| | 31 | block = self |
|---|
| | 32 | else: |
|---|
| | 33 | block = block_context[self.name] |
|---|
| | 34 | |
|---|
| | 35 | block_context.enter(block) |
|---|
| | 36 | result = block.nodelist.render(context) |
|---|
| | 37 | block_context.exit(block) |
|---|
| 33 | | def add_parent(self, nodelist): |
|---|
| 34 | | if self.parent: |
|---|
| 35 | | self.parent.add_parent(nodelist) |
|---|
| | 55 | def __repr__(self): |
|---|
| | 56 | return 'BlockContext(%r, %r)' % (self.block_stack, self.dicts) |
|---|
| | 57 | |
|---|
| | 58 | def __getitem__(self, key): |
|---|
| | 59 | "Get a block by name" |
|---|
| | 60 | for d in self.dicts: |
|---|
| | 61 | if key in d: |
|---|
| | 62 | return d[key] |
|---|
| | 63 | raise KeyError(key) |
|---|
| | 64 | |
|---|
| | 65 | def add_blocks(self, blocks_dict): |
|---|
| | 66 | "Like Context.update() but puts blocks_dict last." |
|---|
| | 67 | if not hasattr(blocks_dict, '__getitem__'): |
|---|
| | 68 | raise TypeError('blocks_dict must be a mapping (dictionary-like) object.') |
|---|
| | 69 | self.dicts.append(blocks_dict) |
|---|
| | 70 | |
|---|
| | 71 | def enter(self, block): |
|---|
| | 72 | # TODO: check for loops |
|---|
| | 73 | self.block_stack.append(block) |
|---|
| | 74 | |
|---|
| | 75 | def exit(self, block): |
|---|
| | 76 | last = self.block_stack.pop() |
|---|
| | 77 | if last is not block: |
|---|
| | 78 | raise ValueError('Invalid block stack: %r' % (self.block_stack + [last])) |
|---|
| | 79 | |
|---|
| | 80 | def get_parent(self, block): |
|---|
| | 81 | "Find the parent version of a given block, if any" |
|---|
| | 82 | name = block.name |
|---|
| | 83 | found = False |
|---|
| | 84 | for d in self.dicts: |
|---|
| | 85 | b = d.get(name, None) |
|---|
| | 86 | if b: |
|---|
| | 87 | if found: |
|---|
| | 88 | return b |
|---|
| | 89 | elif b is block: |
|---|
| | 90 | found = True |
|---|
| | 91 | return None |
|---|
| | 92 | |
|---|
| | 93 | def super(self): |
|---|
| | 94 | "Implement block.super" |
|---|
| | 95 | parent = self.get_parent(self.block_stack[-1]) |
|---|
| | 96 | if parent: |
|---|
| | 97 | return mark_safe(parent.render(self.context, force_self=True)) |
|---|
| 71 | | compiled_parent = self.get_parent(context) |
|---|
| 72 | | parent_blocks = dict([(n.name, n) for n in compiled_parent.nodelist.get_nodes_by_type(BlockNode)]) |
|---|
| 73 | | for block_node in self.nodelist.get_nodes_by_type(BlockNode): |
|---|
| 74 | | # Check for a BlockNode with this node's name, and replace it if found. |
|---|
| 75 | | try: |
|---|
| 76 | | parent_block = parent_blocks[block_node.name] |
|---|
| 77 | | except KeyError: |
|---|
| 78 | | # This BlockNode wasn't found in the parent template, but the |
|---|
| 79 | | # parent block might be defined in the parent's *parent*, so we |
|---|
| 80 | | # add this BlockNode to the parent's ExtendsNode nodelist, so |
|---|
| 81 | | # it'll be checked when the parent node's render() is called. |
|---|
| 82 | | |
|---|
| 83 | | # Find out if the parent template has a parent itself |
|---|
| 84 | | for node in compiled_parent.nodelist: |
|---|
| 85 | | if not isinstance(node, TextNode): |
|---|
| 86 | | # If the first non-text node is an extends, handle it. |
|---|
| 87 | | if isinstance(node, ExtendsNode): |
|---|
| 88 | | node.nodelist.append(block_node) |
|---|
| 89 | | # Extends must be the first non-text node, so once you find |
|---|
| 90 | | # the first non-text node you can stop looking. |
|---|
| 91 | | break |
|---|
| 92 | | else: |
|---|
| 93 | | # Keep any existing parents and add a new one. Used by BlockNode. |
|---|
| 94 | | parent_block.parent = block_node.parent |
|---|
| 95 | | parent_block.add_parent(parent_block.nodelist) |
|---|
| 96 | | parent_block.nodelist = block_node.nodelist |
|---|
| 97 | | return compiled_parent.render(context) |
|---|
| | 134 | block_context = context.get(BlockContext.KEY, None) |
|---|
| | 135 | if block_context is None: |
|---|
| | 136 | block_context = BlockContext(context) |
|---|
| | 137 | context[BlockContext.KEY] = block_context |
|---|
| | 138 | block_context.add_blocks(self.block_nodes) |
|---|
| | 139 | |
|---|
| | 140 | parent = self.get_parent(context) |
|---|
| | 141 | |
|---|
| | 142 | # if parent is the base template, add all its blocks here |
|---|
| | 143 | # we only need to check the first two nodes of the parent to find ExtendsNode |
|---|
| | 144 | #if not parent.nodelist.get_nodes_by_type(ExtendsNode): |
|---|
| | 145 | if not filter(lambda n : isinstance(n, ExtendsNode), parent.nodelist[:2]): |
|---|
| | 146 | if not hasattr(parent, '_block_nodes'): |
|---|
| | 147 | parent._block_nodes = dict([(n.name, n) for n in parent.nodelist.get_nodes_by_type(BlockNode)]) |
|---|
| | 148 | block_context.add_blocks(parent._block_nodes) |
|---|
| | 149 | |
|---|
| | 150 | return parent.render(context) |
|---|
| | 151 | |
|---|
| | 152 | |
|---|
| | 153 | def _render_include(template, context): |
|---|
| | 154 | """ |
|---|
| | 155 | Don't pass block context into included templates. |
|---|
| | 156 | """ |
|---|
| | 157 | block_context = context.get(BlockContext.KEY, None) |
|---|
| | 158 | if block_context is None: |
|---|
| | 159 | return template.render(context) |
|---|
| | 160 | else: |
|---|
| | 161 | context[BlockContext.KEY] = None |
|---|
| | 162 | result = template.render(context) |
|---|
| | 163 | # this may result in multiple references to block_context in the context stack |
|---|
| | 164 | # but that is OK since they all point to the same object |
|---|
| | 165 | context[BlockContext.KEY] = block_context |
|---|
| | 166 | return result |
|---|