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 |