diff --git a/django/utils/safestring.py b/django/utils/safestring.py index b7d1adff62..c3eab58440 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -7,6 +7,8 @@ be interpreted by the HTML engine (e.g. '<') into the appropriate entities. from functools import wraps +from django.utils.functional import keep_lazy + class SafeData: __slots__ = () @@ -53,6 +55,7 @@ def _safety_decorator(safety_marker, func): return wrapped +@keep_lazy(SafeString) def mark_safe(s): """ Explicitly mark a string as safe for (HTML) output purposes. The returned diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 936350979f..08e1103314 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -312,6 +312,8 @@ Utilities * ``SimpleLazyObject`` now supports addition operations. +* :func:`~django.utils.safestring.mark_safe` now preserves lazy objects. + Validators ~~~~~~~~~~ diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py index d7805662f1..1a79afbf48 100644 --- a/tests/utils_tests/test_safestring.py +++ b/tests/utils_tests/test_safestring.py @@ -1,8 +1,9 @@ from django.template import Context, Template from django.test import SimpleTestCase -from django.utils import html -from django.utils.functional import lazy, lazystr +from django.utils import html, translation +from django.utils.functional import Promise, lazy, lazystr from django.utils.safestring import SafeData, SafeString, mark_safe +from django.utils.translation import gettext_lazy class customescape(str): @@ -40,10 +41,17 @@ class SafeStringTest(SimpleTestCase): self.assertRenderEqual("{{ s|force_escape }}", "<a&b>", s=s) def test_mark_safe_lazy(self): - s = lazystr("a&b") + safe_s = mark_safe(lazystr("a&b")) - self.assertIsInstance(mark_safe(s), SafeData) - self.assertRenderEqual("{{ s }}", "a&b", s=mark_safe(s)) + self.assertIsInstance(safe_s, Promise) + self.assertRenderEqual("{{ s }}", "a&b", s=safe_s) + self.assertIsInstance(str(safe_s), SafeData) + + def test_mark_safe_lazy_i18n(self): + s = mark_safe(gettext_lazy("name")) + tpl = Template("{{ s }}") + with translation.override("fr"): + self.assertEqual(tpl.render(Context({"s": s})), "nom") def test_mark_safe_object_implementing_dunder_str(self): class Obj: