From 9c7a2400a84e03639e3e6019278cb9cbe532a502 Mon Sep 17 00:00:00 2001 From: Sebastian Noack Date: Mon, 10 Aug 2015 10:34:13 +0200 Subject: [PATCH 1/3] Prevent random filter from being inlined --- jinja2/filters.py | 10 +++++----- tests/test_filters.py | 9 ++++++++- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/jinja2/filters.py b/jinja2/filters.py index e5c7a1ab4..eb8f54ba9 100644 --- a/jinja2/filters.py +++ b/jinja2/filters.py @@ -10,8 +10,8 @@ """ import re import math +import random -from random import choice from operator import itemgetter from itertools import groupby from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, \ @@ -360,13 +360,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): diff --git a/tests/test_filters.py b/tests/test_filters.py index 741ef341b..ceaba509a 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -8,6 +8,7 @@ :copyright: (c) 2010 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 @@ -177,12 +178,18 @@ def test_pprint(self, env): data = list(range(1000)) assert tmpl.render(data=data) == pformat(data) - def test_random(self, env): + def test_random1(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_random2(self, env, monkeypatch): + monkeypatch.setattr(random, 'choice', lambda seq: seq[0]) + tmpl = env.from_string('''{{ range(100)|random }}''') + monkeypatch.setattr(random, 'choice', lambda seq: seq[2]) + assert tmpl.render() == '2' + def test_reverse(self, env): tmpl = env.from_string('{{ "foobar"|reverse|join }}|' '{{ [1, 2, 3]|reverse|list }}') From c8636fed17f08fd052ec25c3607f3743812eb049 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 5 Jul 2017 11:09:24 -0700 Subject: [PATCH 2/3] test compares random filter to random call with same seed --- tests/test_filters.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/test_filters.py b/tests/test_filters.py index 30e7ca555..894ec29c4 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -183,17 +183,21 @@ def test_pprint(self, env): data = list(range(1000)) assert tmpl.render(data=data) == pformat(data) - def test_random1(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_random2(self, env, monkeypatch): - monkeypatch.setattr(random, 'choice', lambda seq: seq[0]) - tmpl = env.from_string('''{{ range(100)|random }}''') - monkeypatch.setattr(random, 'choice', lambda seq: seq[2]) - assert tmpl.render() == '2' + 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', version=2) + 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', version=2) + 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 }}|' From 2e15e52adf0c07028501f64cb8d15f70bf64bce8 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 5 Jul 2017 11:17:09 -0700 Subject: [PATCH 3/3] fix random test for py2 add changelog --- CHANGES | 4 ++++ tests/test_filters.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index b12dd4f50..2441849b0 100644 --- a/CHANGES +++ b/CHANGES @@ -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 ------------- diff --git a/tests/test_filters.py b/tests/test_filters.py index 894ec29c4..54aa16b81 100644 --- a/tests/test_filters.py +++ b/tests/test_filters.py @@ -188,12 +188,12 @@ def test_random(self, env, request): state = random.getstate() request.addfinalizer(lambda: random.setstate(state)) # generate the random values from a known seed - random.seed('jinja', version=2) + 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', version=2) + random.seed('jinja') t = env.from_string('{{ "1234567890"|random }}') for value in expected: