Added support for optional `scoped` modifier to blocks.
--HG--
branch : trunk
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 20ac03b..6b9c786 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -783,8 +783,12 @@
self.writeline('if parent_template is None:')
self.indent()
level += 1
- self.writeline('for event in context.blocks[%r][0](context):' %
- node.name, node)
+ if node.scoped:
+ context = 'context.derived(locals())'
+ else:
+ context = 'context'
+ self.writeline('for event in context.blocks[%r][0](%s):' % (
+ node.name, context), node)
self.indent()
self.simple_write('event', frame)
self.outdent(level)
diff --git a/jinja2/environment.py b/jinja2/environment.py
index 3c53fbd..fcc11d2 100644
--- a/jinja2/environment.py
+++ b/jinja2/environment.py
@@ -15,7 +15,7 @@
from jinja2.parser import Parser
from jinja2.optimizer import optimize
from jinja2.compiler import generate
-from jinja2.runtime import Undefined, Context
+from jinja2.runtime import Undefined, new_context
from jinja2.exceptions import TemplateSyntaxError
from jinja2.utils import import_string, LRUCache, Markup, missing, \
concat, consume
@@ -646,21 +646,8 @@
`locals` can be a dict of local variables for internal usage.
"""
- if vars is None:
- vars = {}
- if shared:
- parent = vars
- else:
- parent = dict(self.globals, **vars)
- if locals:
- # if the parent is shared a copy should be created because
- # we don't want to modify the dict passed
- if shared:
- parent = dict(parent)
- for key, value in locals.iteritems():
- if key[:2] == 'l_' and value is not missing:
- parent[key[2:]] = value
- return Context(self.environment, parent, self.name, self.blocks)
+ return new_context(self.environment, self.name, self.blocks,
+ vars, shared, self.globals, locals)
def make_module(self, vars=None, shared=False, locals=None):
"""This method works like the :attr:`module` attribute when called
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 6383372..c7858b6 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -269,7 +269,7 @@
class Block(Stmt):
"""A node that represents a block."""
- fields = ('name', 'body')
+ fields = ('name', 'body', 'scoped')
class Include(Stmt):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index d3eb8c4..f3de6e7 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -149,6 +149,7 @@
def parse_block(self):
node = nodes.Block(lineno=self.stream.next().lineno)
node.name = self.stream.expect('name').value
+ node.scoped = self.stream.skip_if('name:scoped')
node.body = self.parse_statements(('name:endblock',), drop_needle=True)
self.stream.skip_if('name:' + node.name)
return node
diff --git a/jinja2/runtime.py b/jinja2/runtime.py
index 60a9035..013d987 100644
--- a/jinja2/runtime.py
+++ b/jinja2/runtime.py
@@ -17,7 +17,7 @@
# these variables are exported to the template runtime
-__all__ = ['LoopContext', 'Context', 'TemplateReference', 'Macro', 'Markup',
+__all__ = ['LoopContext', 'TemplateReference', 'Macro', 'Markup',
'TemplateRuntimeError', 'missing', 'concat', 'escape',
'markup_join', 'unicode_join', 'TemplateNotFound']
@@ -42,6 +42,45 @@
return concat(imap(unicode, seq))
+def new_context(environment, template_name, blocks, vars=None,
+ shared=None, globals=None, locals=None):
+ """Internal helper to for context creation."""
+ if vars is None:
+ vars = {}
+ if shared:
+ parent = vars
+ else:
+ parent = dict(globals or (), **vars)
+ if locals:
+ # if the parent is shared a copy should be created because
+ # we don't want to modify the dict passed
+ if shared:
+ parent = dict(parent)
+ for key, value in locals.iteritems():
+ if key[:2] == 'l_' and value is not missing:
+ parent[key[2:]] = value
+ return Context(environment, parent, template_name, blocks)
+
+
+class TemplateReference(object):
+ """The `self` in templates."""
+
+ def __init__(self, context):
+ self.__context = context
+
+ def __getitem__(self, name):
+ blocks = self.__context.blocks[name]
+ wrap = self.__context.environment.autoescape and \
+ Markup or (lambda x: x)
+ return BlockReference(name, self.__context, blocks, 0)
+
+ def __repr__(self):
+ return '<%s %r>' % (
+ self.__class__.__name__,
+ self.__context.name
+ )
+
+
class Context(object):
"""The template context holds the variables of a template. It stores the
values passed to the template and also the names the template exports.
@@ -132,6 +171,11 @@
args = (__self.environment,) + args
return __obj(*args, **kwargs)
+ def derived(self, locals=None):
+ """Internal helper function to create a derived context."""
+ return new_context(self.environment, self.name, self.blocks,
+ self.parent, True, None, locals)
+
def _all(meth):
proxy = lambda self: getattr(self.get_all(), meth)()
proxy.__doc__ = getattr(dict, meth).__doc__
@@ -174,25 +218,6 @@
pass
-class TemplateReference(object):
- """The `self` in templates."""
-
- def __init__(self, context):
- self.__context = context
-
- def __getitem__(self, name):
- blocks = self.__context.blocks[name]
- wrap = self.__context.environment.autoescape and \
- Markup or (lambda x: x)
- return BlockReference(name, self.__context, blocks, 0)
-
- def __repr__(self):
- return '<%s %r>' % (
- self.__class__.__name__,
- self.__context.name
- )
-
-
class BlockReference(object):
"""One block on a template reference."""