Permalink
Browse files

improved thread safety of the LRUCache and fixed a bug in for loops

--HG--
branch : trunk
  • Loading branch information...
1 parent 8a1d27f commit e2244881cd7f57c4436095e54675fb1da9446d8d @mitsuhiko mitsuhiko committed May 19, 2008
Showing with 42 additions and 26 deletions.
  1. +4 −0 docs/switching.rst
  2. +19 −0 docs/tricks.rst
  3. +17 −24 jinja2/compiler.py
  4. +2 −2 jinja2/utils.py
View
@@ -79,6 +79,10 @@ instead. Includes no longer import macros and variable assignments, for
that the new `import` tag is used. This concept is explained in the
:ref:`import` documentation.
+Another small change happened in the `for`-tag. The special loop variable
+doesn't have a `parent` attribute, instead you have to alias the loop
+yourself. See :ref:`accessing-the-parent-loop` for more details.
+
Django
------
View
@@ -79,3 +79,22 @@ sense to defined a default for that variable::
{% endfor %}
</ul>
...
+
+.. _accessing-the-parent-loop:
+
+Accessing the parent Loop
+-------------------------
+
+The special `loop` variable always points to the innermost loop. If it's
+desired to have access to an outer loop it's possible to alias it::
+
+ <table>
+ {% for row in table %}
+ <tr>
+ {% set rowloop = loop %}
+ {% for cell in row %}
+ <td id="cell-{{ rowloop.index }}-{{ loop.index }}>{{ cell }}</td>
+ {% endfor %}
+ </tr>
+ {% endfor %}
+ </table>
View
@@ -352,16 +352,19 @@ def __init__(self, environment, name, filename, stream=None):
# the current indentation
self._indentation = 0
+ def fail(self, msg, lineno):
+ """Fail with a `TemplateAssertionError`."""
+ raise TemplateAssertionError(msg, lineno, self.name, self.filename)
+
def temporary_identifier(self):
"""Get a new unique identifier."""
self._last_identifier += 1
return 't_%d' % self._last_identifier
def buffer(self, frame):
"""Enable buffering for the frame from that point onwards."""
- frame.buffer = buf = self.temporary_identifier()
- self.writeline('%s = []' % buf)
- return buf
+ frame.buffer = self.temporary_identifier()
+ self.writeline('%s = []' % frame.buffer)
def return_buffer_contents(self, frame):
"""Return the buffer contents of the frame."""
@@ -543,12 +546,9 @@ def function_scoping(self, node, frame, children=None,
func_frame.identifiers.declared_parameter)
)
if overriden_closure_vars:
- vars = ', '.join(sorted(overriden_closure_vars))
- raise TemplateAssertionError('It\'s not possible to set and '
- 'access variables derived from '
- 'an outer scope! (affects: %s' %
- vars, node.lineno, self.name,
- self.filename)
+ self.fail('It\'s not possible to set and access variables '
+ 'derived from an outer scope! (affects: %s' %
+ ', '.join(sorted(overriden_closure_vars)), node.lineno)
# remove variables from a closure from the frame's undeclared
# identifiers.
@@ -597,9 +597,7 @@ def visit_Template(self, node, frame=None):
# find all blocks
for block in node.find_all(nodes.Block):
if block.name in self.blocks:
- raise TemplateAssertionError('block %r defined twice' %
- block.name, block.lineno,
- self.name, self.filename)
+ self.fail('block %r defined twice' % block.name, block.lineno)
self.blocks[block.name] = block
# find all imports and import them
@@ -700,9 +698,8 @@ def visit_Block(self, node, frame):
def visit_Extends(self, node, frame):
"""Calls the extender."""
if not frame.toplevel:
- raise TemplateAssertionError('cannot use extend from a non '
- 'top-level scope', node.lineno,
- self.name, self.filename)
+ self.fail('cannot use extend from a non top-level scope',
+ node.lineno)
# if the number of extends statements in general is zero so
# far, we don't have to add a check if something extended
@@ -831,7 +828,7 @@ def visit_FromImport(self, node, frame):
def visit_For(self, node, frame):
# when calculating the nodes for the inner frame we have to exclude
# the iterator contents from it
- children = node.iter_child_nodes(exclude=('iter',))
+ children = list(node.iter_child_nodes(exclude=('iter',)))
if node.recursive:
loop_frame = self.function_scoping(node, frame, children,
@@ -840,8 +837,8 @@ def visit_For(self, node, frame):
loop_frame = frame.inner()
loop_frame.inspect(children)
- extended_loop = node.recursive or node.else_ or \
- 'loop' in loop_frame.identifiers.undeclared
+ undeclared = find_undeclared(children, ('loop',))
+ extended_loop = node.recursive or node.else_ or 'loop' in undeclared
if extended_loop:
loop_frame.identifiers.add_special('loop')
@@ -1294,9 +1291,7 @@ def visit_Filter(self, node, frame, initial=None):
self.write(self.filters[node.name] + '(')
func = self.environment.filters.get(node.name)
if func is None:
- raise TemplateAssertionError('no filter named %r' % node.name,
- node.lineno, self.name,
- self.filename)
+ self.fail('no filter named %r' % node.name, node.lineno)
if getattr(func, 'contextfilter', False):
self.write('context, ')
elif getattr(func, 'environmentfilter', False):
@@ -1313,9 +1308,7 @@ def visit_Filter(self, node, frame, initial=None):
def visit_Test(self, node, frame):
self.write(self.tests[node.name] + '(')
if node.name not in self.environment.tests:
- raise TemplateAssertionError('no test named %r' % node.name,
- node.lineno, self.name,
- self.filename)
+ self.fail('no test named %r' % node.name, node.lineno)
self.visit(node.node, frame)
self.signature(node, frame)
self.write(')')
View
@@ -469,13 +469,13 @@ def __iter__(self):
"""Iterate over all values in the cache dict, ordered by
the most recent usage.
"""
- return reversed(self._queue)
+ return reversed(tuple(self._queue))
def __reversed__(self):
"""Iterate over the values in the cache dict, oldest items
coming first.
"""
- return iter(self._queue)
+ return iter(tuple(self._queue))
__copy__ = copy

0 comments on commit e224488

Please sign in to comment.