Skip to content

Commit

Permalink
Merge pull request #734 from davidism/dont-inline-random
Browse files Browse the repository at this point in the history
Prevent random filter from being folded
  • Loading branch information
davidism committed Jul 5, 2017
2 parents 47ef6a3 + 2e15e52 commit ecba702
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGES
Expand Up @@ -22,6 +22,10 @@ Version 2.10
- Added a `trimmed` modifier to `{% trans %}` to strip linebreaks and
surrounding whitespace. Also added a new policy to enable this for all
`trans` blocks.
- The ``random`` filter is no longer incorrectly constant folded and will
produce a new random choice each time the template is rendered. (`#478`_)

.. _#478: https://github.com/pallets/jinja/pull/478

Version 2.9.6
-------------
Expand Down
10 changes: 5 additions & 5 deletions jinja2/filters.py
Expand Up @@ -10,8 +10,8 @@
"""
import re
import math
import random

from random import choice
from itertools import groupby
from collections import namedtuple
from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \
Expand Down Expand Up @@ -359,13 +359,13 @@ def do_last(environment, seq):
return environment.undefined('No last item, sequence was empty.')


@environmentfilter
def do_random(environment, seq):
@contextfilter
def do_random(context, seq):
"""Return a random item from the sequence."""
try:
return choice(seq)
return random.choice(seq)
except IndexError:
return environment.undefined('No random item, sequence was empty.')
return context.environment.undefined('No random item, sequence was empty.')


def do_filesizeformat(value, binary=False):
Expand Down
21 changes: 16 additions & 5 deletions tests/test_filters.py
Expand Up @@ -8,6 +8,7 @@
:copyright: (c) 2017 by the Jinja Team.
:license: BSD, see LICENSE for more details.
"""
import random
import pytest
from jinja2 import Markup, Environment
from jinja2._compat import text_type, implements_to_string
Expand Down Expand Up @@ -182,11 +183,21 @@ def test_pprint(self, env):
data = list(range(1000))
assert tmpl.render(data=data) == pformat(data)

def test_random(self, env):
tmpl = env.from_string('''{{ seq|random }}''')
seq = list(range(100))
for _ in range(10):
assert int(tmpl.render(seq=seq)) in seq
def test_random(self, env, request):
# restore the random state when the test ends
state = random.getstate()
request.addfinalizer(lambda: random.setstate(state))
# generate the random values from a known seed
random.seed('jinja')
expected = [random.choice('1234567890') for _ in range(10)]

# check that the random sequence is generated again by a template
# ensures that filter result is not constant folded
random.seed('jinja')
t = env.from_string('{{ "1234567890"|random }}')

for value in expected:
assert t.render() == value

def test_reverse(self, env):
tmpl = env.from_string('{{ "foobar"|reverse|join }}|'
Expand Down

0 comments on commit ecba702

Please sign in to comment.