diff --git a/CHANGES b/CHANGES
index 7ed12dc82..700627a31 100644
--- a/CHANGES
+++ b/CHANGES
@@ -35,6 +35,7 @@ Version 2.9
tests in one expression without extra parentheses. In particular you can
now write ``foo is divisibleby 2 or foo is divisibleby 3``
as you would expect.
+- Added filter support to the block ``set`` tag.
Version 2.8.2
-------------
diff --git a/docs/templates.rst b/docs/templates.rst
index 9c3e7d5fa..b99b4c519 100644
--- a/docs/templates.rst
+++ b/docs/templates.rst
@@ -865,6 +865,17 @@ Example::
The `navigation` variable then contains the navigation HTML source.
+.. versionchanged:: 2.9
+
+Starting with Jinja 2.9, the block assignment supports filters.
+
+Example::
+
+ {% set reply | wordwrap %}
+ You wrote:
+ {{ message }}
+ {% endset %}
+
.. _extends:
diff --git a/jinja2/compiler.py b/jinja2/compiler.py
index 6595d6321..594d7cf4a 100644
--- a/jinja2/compiler.py
+++ b/jinja2/compiler.py
@@ -1314,7 +1314,12 @@ def visit_AssignBlock(self, node, frame):
self.newline(node)
self.visit(node.target, frame)
self.write(' = (Markup if context.eval_ctx.autoescape '
- 'else identity)(concat(%s))' % block_frame.buffer)
+ 'else identity)(')
+ if node.filter is not None:
+ self.visit_Filter(node.filter, block_frame)
+ else:
+ self.write('concat(%s)' % block_frame.buffer)
+ self.write(')')
self.pop_assign_tracking(frame)
self.leave_frame(block_frame)
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
index 5e0726a36..3e784ad9c 100644
--- a/jinja2/nodes.py
+++ b/jinja2/nodes.py
@@ -378,7 +378,7 @@ class Assign(Stmt):
class AssignBlock(Stmt):
"""Assigns a block to a target."""
- fields = ('target', 'body')
+ fields = ('target', 'filter', 'body')
class Expr(Node):
diff --git a/jinja2/parser.py b/jinja2/parser.py
index 00e055807..fba39caf3 100644
--- a/jinja2/parser.py
+++ b/jinja2/parser.py
@@ -180,9 +180,10 @@ def parse_set(self):
if self.stream.skip_if('assign'):
expr = self.parse_tuple()
return nodes.Assign(target, expr, lineno=lineno)
+ filter_node = self.parse_filter(None)
body = self.parse_statements(('name:endset',),
drop_needle=True)
- return nodes.AssignBlock(target, body, lineno=lineno)
+ return nodes.AssignBlock(target, filter_node, body, lineno=lineno)
def parse_for(self):
"""Parse a for loop."""
diff --git a/tests/test_core_tags.py b/tests/test_core_tags.py
index 0a865f53e..17f59bd87 100644
--- a/tests/test_core_tags.py
+++ b/tests/test_core_tags.py
@@ -354,3 +354,16 @@ def test_block_escaping(self):
tmpl = env.from_string('{% set foo %}{{ test }}'
'{% endset %}foo: {{ foo }}')
assert tmpl.render(test='') == 'foo: <unsafe>'
+
+ def test_block_escaping_filtered(self):
+ env = Environment(autoescape=True)
+ tmpl = env.from_string('{% set foo | trim %}{{ test }} '
+ '{% endset %}foo: {{ foo }}')
+ assert tmpl.render(test='') == 'foo: <unsafe>'
+
+ def test_block_filtered(self, env_trim):
+ tmpl = env_trim.from_string(
+ '{% set foo | trim | length | string %} 42 {% endset %}'
+ '{{ foo }}')
+ assert tmpl.render() == '2'
+ assert tmpl.module.foo == u'2'