1
0
mirror of https://github.com/django/django.git synced 2025-01-03 06:55:47 +00:00

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.
This commit is contained in:
Matthias Kestenholz 2024-08-09 17:18:42 +02:00 committed by nessita
parent b5c048f5ec
commit d84200e4eb
3 changed files with 65 additions and 5 deletions

View File

@ -35,11 +35,17 @@ class SafeString(str, SafeData):
Concatenating a safe string with another safe bytestring or Concatenating a safe string with another safe bytestring or
safe string is safe. Otherwise, the result is no longer safe. safe string is safe. Otherwise, the result is no longer safe.
""" """
if isinstance(rhs, str):
t = super().__add__(rhs) t = super().__add__(rhs)
if isinstance(rhs, SafeData): if isinstance(rhs, SafeData):
return SafeString(t) t = SafeString(t)
return 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): def __str__(self):
return self return self

View File

@ -262,7 +262,10 @@ URLs
Utilities 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 Validators
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -132,3 +132,54 @@ class SafeStringTest(SimpleTestCase):
for case, expected in cases: for case, expected in cases:
with self.subTest(case=case): with self.subTest(case=case):
self.assertRenderEqual("{{ s }}", expected, s=s + 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