From 55022f75c1e76e92206e023a127532d97cedd5b7 Mon Sep 17 00:00:00 2001 From: Keryn Knight Date: Tue, 25 Jan 2022 09:53:03 +0000 Subject: [PATCH] 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. --- django/utils/safestring.py | 5 +++++ tests/utils_tests/test_safestring.py | 14 +++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/django/utils/safestring.py b/django/utils/safestring.py index a484f678e2..c061717889 100644 --- a/django/utils/safestring.py +++ b/django/utils/safestring.py @@ -9,6 +9,8 @@ from functools import wraps class SafeData: + __slots__ = () + def __html__(self): """ 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 purposes. """ + + __slots__ = () + def __add__(self, rhs): """ Concatenating a safe string with another safe bytestring or diff --git a/tests/utils_tests/test_safestring.py b/tests/utils_tests/test_safestring.py index 2eeddb0571..80f31d42ac 100644 --- a/tests/utils_tests/test_safestring.py +++ b/tests/utils_tests/test_safestring.py @@ -2,7 +2,7 @@ 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.safestring import SafeData, mark_safe +from django.utils.safestring import SafeData, SafeString, mark_safe class customescape(str): @@ -98,3 +98,15 @@ class SafeStringTest(SimpleTestCase): lazy_str = lazy(html_str, 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