Skip to content

Commit

Permalink
Support filters in set block
Browse files Browse the repository at this point in the history
- e.g {% set foo | trim %}...{% endset %}
- closes #486
  • Loading branch information
ThiefMaster committed Oct 31, 2017
1 parent d17c7db commit 072cdf9
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGES
Expand Up @@ -33,10 +33,12 @@ Version 2.10
- Add ``reverse`` argument for ``dictsort`` filter. (`#692`_)
- Add a ``NativeEnvironment`` that renders templates to native Python types
instead of strings. (`#708`_)
- Added filter support to the block ``set`` tag. (`#489`_)

.. _#469: https://github.com/pallets/jinja/pull/469
.. _#475: https://github.com/pallets/jinja/pull/475
.. _#478: https://github.com/pallets/jinja/pull/478
.. _#489: https://github.com/pallets/jinja/pull/489
.. _#617: https://github.com/pallets/jinja/pull/617
.. _#618: https://github.com/pallets/jinja/pull/618
.. _#665: https://github.com/pallets/jinja/pull/665
Expand Down
11 changes: 11 additions & 0 deletions docs/templates.rst
Expand Up @@ -947,6 +947,17 @@ Example::

The `navigation` variable then contains the navigation HTML source.

.. versionchanged:: 2.10

Starting with Jinja 2.10, the block assignment supports filters.

Example::

{% set reply | wordwrap %}
You wrote:
{{ message }}
{% endset %}


.. _extends:

Expand Down
7 changes: 6 additions & 1 deletion jinja2/compiler.py
Expand Up @@ -1385,7 +1385,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)

Expand Down
2 changes: 1 addition & 1 deletion jinja2/nodes.py
Expand Up @@ -387,7 +387,7 @@ class Assign(Stmt):

class AssignBlock(Stmt):
"""Assigns a block to a target."""
fields = ('target', 'body')
fields = ('target', 'filter', 'body')


class Expr(Node):
Expand Down
3 changes: 2 additions & 1 deletion jinja2/parser.py
Expand Up @@ -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."""
Expand Down
27 changes: 27 additions & 0 deletions tests/test_core_tags.py
Expand Up @@ -453,6 +453,33 @@ def test_namespace_macro(self, env_trim):
'{{ ns.a }}|{{ ns.b }}')
assert tmpl.render() == '13|37'

def test_block_escaping_filtered(self):
env = Environment(autoescape=True)
tmpl = env.from_string('{% set foo | trim %}<em>{{ test }}</em> '
'{% endset %}foo: {{ foo }}')
assert tmpl.render(test='<unsafe>') == 'foo: <em>&lt;unsafe&gt;</em>'

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'

def test_block_filtered_set(self, env_trim):
def _myfilter(val, arg):
assert arg == ' xxx '
return val
env_trim.filters['myfilter'] = _myfilter
tmpl = env_trim.from_string(
'{% set a = " xxx " %}'
'{% set foo | myfilter(a) | trim | length | string %}'
' {% set b = " yy " %} 42 {{ a }}{{ b }} '
'{% endset %}'
'{{ foo }}')
assert tmpl.render() == '11'
assert tmpl.module.foo == u'11'


@pytest.mark.core_tags
@pytest.mark.with_
Expand Down

0 comments on commit 072cdf9

Please sign in to comment.