Fixed #33465 -- Added empty __slots__ to SafeString and SafeData.

Despite inheriting from the str type, every SafeString instance gains
an empty __dict__ due to the normal, expected behaviour of type
subclassing in Python.

Adding __slots__ to SafeData is necessary, because otherwise inheriting
from that (as SafeString does) will give it a __dict__ and negate the
benefit added by modifying SafeString.
This commit is contained in:
Keryn Knight 2022-01-25 09:53:03 +00:00 committed by Mariusz Felisiak
parent 67db54a5a7
commit 55022f75c1
2 changed files with 18 additions and 1 deletions

View File

@ -9,6 +9,8 @@ from functools import wraps
class SafeData: class SafeData:
__slots__ = ()
def __html__(self): def __html__(self):
""" """
Return the html representation of a string for interoperability. Return the html representation of a string for interoperability.
@ -23,6 +25,9 @@ class SafeString(str, SafeData):
A str subclass that has been specifically marked as "safe" for HTML output A str subclass that has been specifically marked as "safe" for HTML output
purposes. purposes.
""" """
__slots__ = ()
def __add__(self, rhs): def __add__(self, rhs):
""" """
Concatenating a safe string with another safe bytestring or Concatenating a safe string with another safe bytestring or

View File

@ -2,7 +2,7 @@ from django.template import Context, Template
from django.test import SimpleTestCase from django.test import SimpleTestCase
from django.utils import html from django.utils import html
from django.utils.functional import lazy, lazystr from django.utils.functional import lazy, lazystr
from django.utils.safestring import SafeData, mark_safe from django.utils.safestring import SafeData, SafeString, mark_safe
class customescape(str): class customescape(str):
@ -98,3 +98,15 @@ class SafeStringTest(SimpleTestCase):
lazy_str = lazy(html_str, str)() lazy_str = lazy(html_str, str)()
self.assertEqual(mark_safe(lazy_str), html_str()) self.assertEqual(mark_safe(lazy_str), html_str())
def test_default_additional_attrs(self):
s = SafeString('a&b')
msg = "object has no attribute 'dynamic_attr'"
with self.assertRaisesMessage(AttributeError, msg):
s.dynamic_attr = True
def test_default_safe_data_additional_attrs(self):
s = SafeData()
msg = "object has no attribute 'dynamic_attr'"
with self.assertRaisesMessage(AttributeError, msg):
s.dynamic_attr = True