From d563e3be68369694a3bac1efd7779d8e03bb6a51 Mon Sep 17 00:00:00 2001 From: Ola Sitarska Date: Mon, 1 Dec 2014 21:20:37 +0000 Subject: [PATCH] Fixed #23913 -- Deprecated the `=` comparison in `if` template tag. --- django/template/defaulttags.py | 2 +- django/template/smartif.py | 9 ++++ docs/internals/deprecation.txt | 3 ++ docs/releases/1.8.txt | 6 +++ tests/template_tests/syntax_tests/test_if.py | 44 +++++++++++++++++++- tests/template_tests/utils.py | 7 +++- 6 files changed, 68 insertions(+), 3 deletions(-) diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index 7085e04cdb..b65bed7bf8 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -999,7 +999,7 @@ def do_if(parser, token): ``{% if 1>2 %}`` is not a valid if tag. All supported operators are: ``or``, ``and``, ``in``, ``not in`` - ``==`` (or ``=``), ``!=``, ``>``, ``>=``, ``<`` and ``<=``. + ``==``, ``!=``, ``>``, ``>=``, ``<`` and ``<=``. Operator precedence follows Python. """ diff --git a/django/template/smartif.py b/django/template/smartif.py index 554a214cb6..7b5a0083da 100644 --- a/django/template/smartif.py +++ b/django/template/smartif.py @@ -1,6 +1,9 @@ """ Parser and utilities for the smart 'if' tag """ +import warnings + +from django.utils.deprecation import RemovedInDjango20Warning # Using a simple top down parser, as described here: @@ -99,6 +102,7 @@ OPERATORS = { 'not': prefix(8, lambda context, x: not x.eval(context)), 'in': infix(9, lambda context, x, y: x.eval(context) in y.eval(context)), 'not in': infix(9, lambda context, x, y: x.eval(context) not in y.eval(context)), + # This should be removed in Django 2.0: '=': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), '==': infix(10, lambda context, x, y: x.eval(context) == y.eval(context)), '!=': infix(10, lambda context, x, y: x.eval(context) != y.eval(context)), @@ -174,6 +178,11 @@ class IfParser(object): except (KeyError, TypeError): return self.create_var(token) else: + if token == '=': + warnings.warn( + "Operator '=' is deprecated and will be removed in Django 2.0. Use '==' instead.", + RemovedInDjango20Warning, stacklevel=2 + ) return op() def next_token(self): diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index e2c89d360b..345202545e 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -140,6 +140,9 @@ details on these changes. * The ``ssi`` template tag will be removed. +* Support for the ``=`` comparison operator in the ``if`` template tag will be + removed. + .. _deprecation-removed-in-1.9: 1.9 diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index dde70c9f84..dc0ce839bb 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -1492,6 +1492,12 @@ absolute path. This is of limited use in most deployment situations, and the :ttag:`include` tag often makes more sense. This tag is now deprecated and will be removed in Django 2.0. +``=`` as comparison operator in ``if`` template tag +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Using a single equals sign with the ``{% if %}`` template tag for equality +testing was undocumented and untested. It's now deprecated in favor of ``==``. + .. removed-features-1.8: Features removed in 1.8 diff --git a/tests/template_tests/syntax_tests/test_if.py b/tests/template_tests/syntax_tests/test_if.py index 4ca11fb79d..357841444a 100644 --- a/tests/template_tests/syntax_tests/test_if.py +++ b/tests/template_tests/syntax_tests/test_if.py @@ -1,5 +1,8 @@ +import warnings + from django.template import TemplateSyntaxError -from django.test import SimpleTestCase +from django.test import ignore_warnings, SimpleTestCase +from django.utils.deprecation import RemovedInDjango20Warning from ..utils import setup, TestObj @@ -521,3 +524,42 @@ class IfTagTests(SimpleTestCase): def test_if_tag_badarg04(self): output = self.engine.render_to_string('if-tag-badarg04') self.assertEqual(output, 'no') + + @setup({'if-tag-eq-deprecated': '{% if foo = bar %}yes{% else %}no{% endif %}'}, + test_once=True) + def test_if_tag_eq_deprecated(self): + with warnings.catch_warnings(record=True) as warns: + warnings.simplefilter('always') + output = self.engine.render_to_string('if-tag-eq-deprecated') + self.assertEqual(output, 'yes') + self.assertEqual(len(warns), 1) + self.assertEqual( + str(warns[0].message), + "Operator '=' is deprecated and will be removed in Django 2.0. " + "Use '==' instead." + ) + + +@ignore_warnings(category=RemovedInDjango20Warning) +class TestEqualitySingleEqualsSign(SimpleTestCase): + # The following tests should be changed to template.TemplateSyntaxError + # (or simply removed) when the deprecation path ends in Django 2.0. + @setup({'if-tag-eq01': '{% if foo = bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq01(self): + output = self.engine.render_to_string('if-tag-eq01', {'foo': 1}) + self.assertEqual(output, 'no') + + @setup({'if-tag-eq02': '{% if foo = bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq02(self): + output = self.engine.render_to_string('if-tag-eq02', {'foo': 1, 'bar': 1}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-eq03': '{% if foo = bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq03(self): + output = self.engine.render_to_string('if-tag-eq03', {'foo': 1, 'bar': 2}) + self.assertEqual(output, 'no') + + @setup({'if-tag-eq04': '{% if foo == \'\' %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq04(self): + output = self.engine.render_to_string('if-tag-eq04') + self.assertEqual(output, 'no') diff --git a/tests/template_tests/utils.py b/tests/template_tests/utils.py index 3c37be18e4..588c1a4096 100644 --- a/tests/template_tests/utils.py +++ b/tests/template_tests/utils.py @@ -18,7 +18,7 @@ from django.utils.safestring import mark_safe ROOT = os.path.dirname(os.path.abspath(upath(__file__))) -def setup(templates, *args): +def setup(templates, *args, **kwargs): """ Runs test method multiple times in the following order: @@ -31,6 +31,9 @@ def setup(templates, *args): True False True True """ + # when testing deprecation warnings, it's useful to run just one test since + # the message won't be displayed multiple times + test_once = kwargs.get('test_once', False) for arg in args: templates.update(arg) @@ -57,6 +60,8 @@ def setup(templates, *args): loaders=loaders, ) func(self) + if test_once: + return func(self) self.engine = Engine(