From d563e3be68369694a3bac1efd7779d8e03bb6a51 Mon Sep 17 00:00:00 2001
From: Ola Sitarska <ola@sitarska.com>
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(