From 8e4b531111ddd3256c45eee601947e475651e8e7 Mon Sep 17 00:00:00 2001 From: Sean Date: Fri, 19 Dec 2025 00:37:07 +0100 Subject: [PATCH] Fixed #36810 -- Avoided infinite recursion in SimpleLazyObject.__repr__(). Detect when `SimpleLazyObject._setupfunc` is a bound method of the same instance to use a safe representation and avoid infinite recursion. --- django/utils/functional.py | 6 +++++- tests/utils_tests/test_lazyobject.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/django/utils/functional.py b/django/utils/functional.py index e26cbdfaa8..6c725e1221 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -407,7 +407,11 @@ class SimpleLazyObject(LazyObject): # without evaluating the wrapped object. def __repr__(self): if self._wrapped is empty: - repr_attr = self._setupfunc + # Avoid recursion if setupfunc is a bound method of this instance. + if getattr(self._setupfunc, "__self__", None) is self: + repr_attr = f"" + else: + repr_attr = self._setupfunc else: repr_attr = self._wrapped return "<%s: %r>" % (type(self).__name__, repr_attr) diff --git a/tests/utils_tests/test_lazyobject.py b/tests/utils_tests/test_lazyobject.py index a95335413e..e538c66377 100644 --- a/tests/utils_tests/test_lazyobject.py +++ b/tests/utils_tests/test_lazyobject.py @@ -348,6 +348,22 @@ class SimpleLazyObjectTestCase(LazyObjectTestCase): self.assertIsInstance(obj._wrapped, int) self.assertEqual(repr(obj), "") + def test_repr_bound_method(self): + + class MyLazyGenerator(SimpleLazyObject): + def __init__(self): + super().__init__(self._generate) + + def _generate(self): + return "test-generated-value" + + obj = MyLazyGenerator() + self.assertEqual(repr(obj), "'>") + self.assertIs(obj._wrapped, empty) # The evaluation hasn't happened. + + self.assertEqual(str(obj), "test-generated-value") # Evaluate. + self.assertEqual(repr(obj), "") + def test_add(self): obj1 = self.lazy_wrap(1) self.assertEqual(obj1 + 1, 2)