From 4c60c3edff4312303e1021fca47ed52c2152d285 Mon Sep 17 00:00:00 2001
From: David <smithdc@gmail.com>
Date: Fri, 7 Jan 2022 07:46:55 +0000
Subject: [PATCH] Fixed #33419 -- Restored marking forms.Field.help_text as
 HTML safe.

Regression in 456466d932830b096d39806e291fe23ec5ed38d5.

Thanks Matt Westcott for the report.
---
 django/forms/jinja2/django/forms/p.html       |  2 +-
 django/forms/jinja2/django/forms/table.html   |  2 +-
 django/forms/jinja2/django/forms/ul.html      |  2 +-
 django/forms/templates/django/forms/p.html    |  2 +-
 .../forms/templates/django/forms/table.html   |  2 +-
 django/forms/templates/django/forms/ul.html   |  2 +-
 docs/releases/4.0.2.txt                       |  3 ++
 tests/forms_tests/tests/test_forms.py         | 35 +++++++++++++++++++
 8 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/django/forms/jinja2/django/forms/p.html b/django/forms/jinja2/django/forms/p.html
index 999c4d963a..a2872d993e 100644
--- a/django/forms/jinja2/django/forms/p.html
+++ b/django/forms/jinja2/django/forms/p.html
@@ -8,7 +8,7 @@
     {% if field.label %}{{ field.label_tag() }}{% endif %}
     {{ field }}
     {% if field.help_text %}
-      <span class="helptext">{{ field.help_text }}</span>
+      <span class="helptext">{{ field.help_text|safe }}</span>
     {% endif %}
     {% if loop.last %}
       {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/django/forms/jinja2/django/forms/table.html b/django/forms/jinja2/django/forms/table.html
index 92cd746a49..d1d51f2b12 100644
--- a/django/forms/jinja2/django/forms/table.html
+++ b/django/forms/jinja2/django/forms/table.html
@@ -16,7 +16,7 @@
       {{ field }}
       {% if field.help_text %}
         <br>
-        <span class="helptext">{{ field.help_text }}</span>
+        <span class="helptext">{{ field.help_text|safe }}</span>
       {% endif %}
       {% if loop.last %}
         {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/django/forms/jinja2/django/forms/ul.html b/django/forms/jinja2/django/forms/ul.html
index 116a9b0808..cc4d893c0e 100644
--- a/django/forms/jinja2/django/forms/ul.html
+++ b/django/forms/jinja2/django/forms/ul.html
@@ -12,7 +12,7 @@
     {% if field.label %}{{ field.label_tag() }}{% endif %}
     {{ field }}
     {% if field.help_text %}
-      <span class="helptext">{{ field.help_text }}</span>
+      <span class="helptext">{{ field.help_text|safe }}</span>
     {% endif %}
     {% if loop.last %}
       {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/django/forms/templates/django/forms/p.html b/django/forms/templates/django/forms/p.html
index 1835b7a461..1346937a34 100644
--- a/django/forms/templates/django/forms/p.html
+++ b/django/forms/templates/django/forms/p.html
@@ -8,7 +8,7 @@
     {% if field.label %}{{ field.label_tag }}{% endif %}
     {{ field }}
     {% if field.help_text %}
-      <span class="helptext">{{ field.help_text }}</span>
+      <span class="helptext">{{ field.help_text|safe }}</span>
     {% endif %}
     {% if forloop.last %}
       {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/django/forms/templates/django/forms/table.html b/django/forms/templates/django/forms/table.html
index a553776f2f..d4aaafcf53 100644
--- a/django/forms/templates/django/forms/table.html
+++ b/django/forms/templates/django/forms/table.html
@@ -16,7 +16,7 @@
       {{ field }}
       {% if field.help_text %}
         <br>
-        <span class="helptext">{{ field.help_text }}</span>
+        <span class="helptext">{{ field.help_text|safe }}</span>
       {% endif %}
       {% if forloop.last %}
         {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/django/forms/templates/django/forms/ul.html b/django/forms/templates/django/forms/ul.html
index 9ce6a49f07..ae38af6527 100644
--- a/django/forms/templates/django/forms/ul.html
+++ b/django/forms/templates/django/forms/ul.html
@@ -12,7 +12,7 @@
     {% if field.label %}{{ field.label_tag }}{% endif %}
     {{ field }}
     {% if field.help_text %}
-      <span class="helptext">{{ field.help_text }}</span>
+      <span class="helptext">{{ field.help_text|safe }}</span>
     {% endif %}
     {% if forloop.last %}
       {% for field in hidden_fields %}{{ field }}{% endfor %}
diff --git a/docs/releases/4.0.2.txt b/docs/releases/4.0.2.txt
index b1f1fb9c76..21b1a56884 100644
--- a/docs/releases/4.0.2.txt
+++ b/docs/releases/4.0.2.txt
@@ -11,3 +11,6 @@ Bugfixes
 
 * Fixed a bug in Django 4.0 where ``TestCase.captureOnCommitCallbacks()`` could
   execute callbacks multiple times (:ticket:`33410`).
+
+* Fixed a regression in Django 4.0 where ``help_text`` was HTML-escaped in
+  automatically-generated forms (:ticket:`33419`).
diff --git a/tests/forms_tests/tests/test_forms.py b/tests/forms_tests/tests/test_forms.py
index 5c653fb492..aa47481998 100644
--- a/tests/forms_tests/tests/test_forms.py
+++ b/tests/forms_tests/tests/test_forms.py
@@ -2326,6 +2326,41 @@ Password: <input type="password" name="password" required>
 <input type="hidden" name="next" value="/"></li>"""
         )
 
+    def test_help_text_html_safe(self):
+        """help_text should not be escaped."""
+        class UserRegistration(Form):
+            username = CharField(max_length=10, help_text='e.g., user@example.com')
+            password = CharField(
+                widget=PasswordInput,
+                help_text='Help text is <strong>escaped</strong>.',
+            )
+
+        p = UserRegistration(auto_id=False)
+        self.assertHTMLEqual(
+            p.as_ul(),
+            '<li>Username: <input type="text" name="username" maxlength="10" required>'
+            '<span class="helptext">e.g., user@example.com</span></li>'
+            '<li>Password: <input type="password" name="password" required>'
+            '<span class="helptext">Help text is <strong>escaped</strong>.</span></li>'
+        )
+        self.assertHTMLEqual(
+            p.as_p(),
+            '<p>Username: <input type="text" name="username" maxlength="10" required>'
+            '<span class="helptext">e.g., user@example.com</span></p>'
+            '<p>Password: <input type="password" name="password" required>'
+            '<span class="helptext">Help text is <strong>escaped</strong>.</span></p>'
+        )
+        self.assertHTMLEqual(
+            p.as_table(),
+            '<tr><th>Username:</th><td>'
+            '<input type="text" name="username" maxlength="10" required><br>'
+            '<span class="helptext">e.g., user@example.com</span></td></tr>'
+            '<tr><th>Password:</th><td>'
+            '<input type="password" name="password" required><br>'
+            '<span class="helptext">Help text is <strong>escaped</strong>.</span>'
+            '</td></tr>'
+        )
+
     def test_subclassing_forms(self):
         # You can subclass a Form to add fields. The resulting form subclass will have
         # all of the fields of the parent Form, plus whichever fields you define in the