summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Kestenholz <mk@feinheit.ch>2024-08-09 17:18:42 +0200
committernessita <124304+nessita@users.noreply.github.com>2024-08-12 14:25:05 -0300
commitd84200e4eb4d20116080130c612ba157a4718977 (patch)
tree34f59ee7d1860b2ccbd83ef913f0ca2fe449f076
parentb5c048f5ecbf1c718102f19c3018b1fb62d66b3d (diff)
Fixed #35648 -- Raised NotImplementedError in SafeString.__add__ for non-string RHS.
This change ensures SafeString addition operations handle non-string RHS properly, allowing them to implement __radd__ for better compatibility.
-rw-r--r--django/utils/safestring.py14
-rw-r--r--docs/releases/5.2.txt5
-rw-r--r--tests/utils_tests/test_safestring.py51
3 files changed, 65 insertions, 5 deletions
diff --git a/django/utils/safestring.py b/django/utils/safestring.py
index 4eb0207a66..1ac9b877e9 100644
--- a/django/utils/safestring.py
+++ b/django/utils/safestring.py
@@ -35,10 +35,16 @@ class SafeString(str, SafeData):
Concatenating a safe string with another safe bytestring or
safe string is safe. Otherwise, the result is no longer safe.
"""
- t = super().__add__(rhs)
- if isinstance(rhs, SafeData):
- return SafeString(t)
- return t
+ if isinstance(rhs, str):
+ t = super().__add__(rhs)
+ if isinstance(rhs, SafeData):
+ t = SafeString(t)
+ return t
+
+ # Give the rhs object a chance to handle the addition, for example if
+ # the rhs object's class implements `__radd__`. More details:
+ # https://docs.python.org/3/reference/datamodel.html#object.__radd__
+ return NotImplemented
def __str__(self):
return self
diff --git a/docs/releases/5.2.txt b/docs/releases/5.2.txt
index a15e669205..9b81117095 100644
--- a/docs/releases/5.2.txt
+++ b/docs/releases/5.2.txt
@@ -262,7 +262,10 @@ URLs
Utilities
~~~~~~~~~
-* ...
+* :class:`~django.utils.safestring.SafeString` now raises
+ :exc:`NotImplementedError` in ``__add__`` for non-string right-hand side
+ values. This aligns with the :py:class:`str` addition behavior and allows
+ ``__radd__`` to be used if available.
Validators
~~~~~~~~~~
diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py
index eca32ff8f6..2ae8e57b19 100644
--- a/tests/utils_tests/test_safestring.py
+++ b/tests/utils_tests/test_safestring.py
@@ -132,3 +132,54 @@ class SafeStringTest(SimpleTestCase):
for case, expected in cases:
with self.subTest(case=case):
self.assertRenderEqual("{{ s }}", expected, s=s + case)
+
+ def test_add_obj(self):
+
+ base_str = "<strong>strange</strong>"
+ add_str = "hello</br>"
+
+ class Add:
+ def __add__(self, other):
+ return base_str + other
+
+ class AddSafe:
+ def __add__(self, other):
+ return mark_safe(base_str) + other
+
+ class Radd:
+ def __radd__(self, other):
+ return other + base_str
+
+ class RaddSafe:
+ def __radd__(self, other):
+ return other + mark_safe(base_str)
+
+ left_add_expected = f"{base_str}{add_str}"
+ right_add_expected = f"{add_str}{base_str}"
+ cases = [
+ # Left-add test cases.
+ (Add(), add_str, left_add_expected, str),
+ (Add(), mark_safe(add_str), left_add_expected, str),
+ (AddSafe(), add_str, left_add_expected, str),
+ (AddSafe(), mark_safe(add_str), left_add_expected, SafeString),
+ # Right-add test cases.
+ (add_str, Radd(), right_add_expected, str),
+ (mark_safe(add_str), Radd(), right_add_expected, str),
+ (add_str, Radd(), right_add_expected, str),
+ (mark_safe(add_str), RaddSafe(), right_add_expected, SafeString),
+ ]
+ for lhs, rhs, expected, expected_type in cases:
+ with self.subTest(lhs=lhs, rhs=rhs):
+ result = lhs + rhs
+ self.assertEqual(result, expected)
+ self.assertEqual(type(result), expected_type)
+
+ cases = [
+ ("hello", Add()),
+ ("hello", AddSafe()),
+ (Radd(), "hello"),
+ (RaddSafe(), "hello"),
+ ]
+ for lhs, rhs in cases:
+ with self.subTest(lhs=lhs, rhs=rhs), self.assertRaises(TypeError):
+ lhs + rhs