Refs #27753 -- Deprecated django.utils.text.unescape_entities().
diff --git a/django/http/multipartparser.py b/django/http/multipartparser.py
index 96e22f69b9301..fd8fce8b4d46a 100644
--- a/django/http/multipartparser.py
+++ b/django/http/multipartparser.py
@@ -8,6 +8,7 @@
 import binascii
 import cgi
 import collections
+import html
 from urllib.parse import unquote
 
 from django.conf import settings
@@ -19,7 +20,6 @@
 )
 from django.utils.datastructures import MultiValueDict
 from django.utils.encoding import force_str
-from django.utils.text import unescape_entities
 
 __all__ = ('MultiPartParser', 'MultiPartParserError', 'InputStreamExhausted')
 
@@ -209,7 +209,7 @@ def parse(self):
                     file_name = disposition.get('filename')
                     if file_name:
                         file_name = force_str(file_name, encoding, errors='replace')
-                        file_name = self.IE_sanitize(unescape_entities(file_name))
+                        file_name = self.IE_sanitize(html.unescape(file_name))
                     if not file_name:
                         continue
 
diff --git a/django/utils/text.py b/django/utils/text.py
index 44007beb0f1f7..e9b7dcc72bc10 100644
--- a/django/utils/text.py
+++ b/django/utils/text.py
@@ -1,9 +1,11 @@
 import html.entities
 import re
 import unicodedata
+import warnings
 from gzip import GzipFile
 from io import BytesIO
 
+from django.utils.deprecation import RemovedInDjango40Warning
 from django.utils.functional import SimpleLazyObject, keep_lazy_text, lazy
 from django.utils.translation import gettext as _, gettext_lazy, pgettext
 
@@ -358,6 +360,11 @@ def _replace_entity(match):
 
 @keep_lazy_text
 def unescape_entities(text):
+    warnings.warn(
+        'django.utils.text.unescape_entities() is deprecated in favor of '
+        'html.unescape().',
+        RemovedInDjango40Warning, stacklevel=2,
+    )
     return _entity_re.sub(_replace_entity, str(text))
 
 
diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt
index 93b6a04594a0f..7020a3341aa12 100644
--- a/docs/internals/deprecation.txt
+++ b/docs/internals/deprecation.txt
@@ -30,6 +30,8 @@ details on these changes.
 * ``alias=None`` will be required in the signature of
   ``django.db.models.Expression.get_group_by_cols()`` subclasses.
 
+* ``django.utils.text.unescape_entities()`` will be removed.
+
 .. _deprecation-removed-in-3.1:
 
 3.1
diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt
index 1c222cfe7af37..f43a11938a9a3 100644
--- a/docs/releases/3.0.txt
+++ b/docs/releases/3.0.txt
@@ -411,6 +411,10 @@ Miscellaneous
 * ``alias=None`` is added to the signature of
   :meth:`.Expression.get_group_by_cols`.
 
+* ``django.utils.text.unescape_entities()`` is deprecated in favor of
+  :func:`html.unescape`. Note that unlike ``unescape_entities()``,
+  ``html.unescape()`` evaluates lazy strings immediately.
+
 .. _removed-features-3.0:
 
 Features removed in 3.0
diff --git a/tests/utils_tests/test_text.py b/tests/utils_tests/test_text.py
index 2d584c1cdd3a4..ee04f3062c313 100644
--- a/tests/utils_tests/test_text.py
+++ b/tests/utils_tests/test_text.py
@@ -1,8 +1,9 @@
 import json
 import sys
 
-from django.test import SimpleTestCase
+from django.test import SimpleTestCase, ignore_warnings
 from django.utils import text
+from django.utils.deprecation import RemovedInDjango40Warning
 from django.utils.functional import lazystr
 from django.utils.text import format_lazy
 from django.utils.translation import gettext_lazy, override
@@ -184,6 +185,7 @@ def test_slugify(self):
         # interning the result may be useful, e.g. when fed to Path.
         self.assertEqual(sys.intern(text.slugify('a')), 'a')
 
+    @ignore_warnings(category=RemovedInDjango40Warning)
     def test_unescape_entities(self):
         items = [
             ('', ''),
@@ -200,6 +202,14 @@ def test_unescape_entities(self):
             self.assertEqual(text.unescape_entities(value), output)
             self.assertEqual(text.unescape_entities(lazystr(value)), output)
 
+    def test_unescape_entities_deprecated(self):
+        msg = (
+            'django.utils.text.unescape_entities() is deprecated in favor of '
+            'html.unescape().'
+        )
+        with self.assertWarnsMessage(RemovedInDjango40Warning, msg):
+            text.unescape_entities('foo')
+
     def test_unescape_string_literal(self):
         items = [
             ('"abc"', 'abc'),

The function was undocumented and only required for compatibility with Python 2.

Use Python's html.unescape() instead -- available since Python 3.4.

https://docs.python.org/3/library/html.html#html.unescape

https://code.djangoproject.com/ticket/27753