diff --git a/django/utils/repr.py b/django/utils/repr.py index 5b0c467c4e..510a7d990b 100644 --- a/django/utils/repr.py +++ b/django/utils/repr.py @@ -1,3 +1,4 @@ +import builtins import reprlib @@ -5,9 +6,23 @@ class DjangoRepr(reprlib.Repr): def config(self, limit): """Sets maximum print length for all data structures using the given value""" + self.limit = limit for attr in dir(self): if attr.startswith("max") and attr != "maxlevel": setattr(self, attr, limit) def repr_str(self, x, level): + if len(x) > self.maxstring: + return x[: self.maxstring] + self.gen_trim_msg(len(x)) return x[: self.maxstring] + + def repr_instance(self, x, level): + s = builtins.repr(x) + if len(s) > self.maxother: + return s[: self.maxother] + self.gen_trim_msg(len(s)) + return s + + def gen_trim_msg(self, length): + if length <= self.limit: + return "" + return "..." % (length - self.limit) diff --git a/django/views/debug.py b/django/views/debug.py index 80e162aa2c..f533a93657 100644 --- a/django/views/debug.py +++ b/django/views/debug.py @@ -359,21 +359,17 @@ class ExceptionReporter: frame_vars = [] for k, v in frame["vars"]: try: - # Check if there are any exceptions in __repr__ fn of the object - v = repr(v) - - trim_msg = "" - - if isinstance(v, Sized) and len(v) > 4096: - diff = len(v) - 4096 - trim_msg = "..." % diff + if isinstance(v, Sized) and len(v) > self.PRINT_LIMIT: + diff = len(v) - self.PRINT_LIMIT + self.repr_instance.fillvalue = ( + "..." % diff + ) v = self.repr_instance.repr(v) - v += trim_msg except Exception as e: v = "Error in formatting: %s: %s" % (e.__class__.__name__, e) - print(v) + frame_vars.append((k, v)) frame["vars"] = frame_vars frames[i] = frame diff --git a/tests/exception_report/__init__.py b/tests/exception_report/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/exception_report/test_large_exception.py b/tests/exception_report/test_large_exception.py deleted file mode 100644 index 4c4204e347..0000000000 --- a/tests/exception_report/test_large_exception.py +++ /dev/null @@ -1,51 +0,0 @@ -import sys - -from django.test import TestCase -from django.test.client import RequestFactory -from django.views.debug import ExceptionReporter - - -class ExceptionReport(TestCase): - factory = RequestFactory() - - def test_large_sizable_object(self): - lg = list(range(50 * 1024 * 1024)) - try: - request = self.factory.get("/") - lg["a"] - except TypeError: - exc_type, exc_value, tb = sys.exc_info() - - reporter = ExceptionReporter(request, exc_type, exc_value, tb) - d = reporter.get_traceback_data() - vars = d["lastframe"]["vars"] - - for k, v in vars: - if k == "lg": - i = v.index("...") - # Check if it has been trimmed - self.assertGreater(i, -1) - - # Construct list with elements before trimming - ls = eval(v[:i] + "]") - - # Check if length of trimmed list is our limit - self.assertEqual(len(ls), 4096) - break - - def test_non_sizable_object(self): - num = 10000000 - try: - request = self.factory.get("/") - num["a"] - except TypeError: - exc_type, exc_value, tb = sys.exc_info() - - reporter = ExceptionReporter(request, exc_type, exc_value, tb) - d = reporter.get_traceback_data() - vars = d["lastframe"]["vars"] - - for k, v in vars: - if k == "a": - self.assertEqual(v, str(num)) - break diff --git a/tests/view_tests/tests/test_debug.py b/tests/view_tests/tests/test_debug.py index e0e0eafe4e..cb87778f62 100644 --- a/tests/view_tests/tests/test_debug.py +++ b/tests/view_tests/tests/test_debug.py @@ -1036,19 +1036,16 @@ class ExceptionReporterTests(SimpleTestCase): def test_local_variable_escaping(self): """Safe strings in local variables are escaped.""" - # <p>Local variable</p>\ - # '<p>Local variable</p>' + try: local = mark_safe("

Local variable

") - print("safe string:", repr(local)) raise ValueError(local) + except Exception: exc_type, exc_value, tb = sys.exc_info() html = ExceptionReporter(None, exc_type, exc_value, tb).get_traceback_html() self.assertIn( - #
<p>Local variable</p>
- #
<p>Local variable</p>
'
'<p>Local variable</p>'
', html, ) @@ -1071,7 +1068,7 @@ class ExceptionReporterTests(SimpleTestCase): def test_too_large_values_handling(self): "Large values should not create a large HTML." - large = 5000 + large = 256 * 1024 repr_of_str_adds = len(repr("")) try: @@ -1086,10 +1083,50 @@ class ExceptionReporterTests(SimpleTestCase): reporter = ExceptionReporter(None, exc_type, exc_value, tb) html = reporter.get_traceback_html() self.assertEqual(len(html) // 1024 // 128, 0) # still fit in 128Kb - msg = "<trimmed %d bytes string>" % ( + trim_msg = "<trimmed %d bytes string>" % ( large - ExceptionReporter.PRINT_LIMIT + repr_of_str_adds, ) - self.assertIn(msg, html) + self.assertIn(trim_msg, html) + + def test_large_sizable_object(self): + """Large objects should not be rendered entirely""" + lg = list(range(1000 * 1000)) + try: + lg["a"] + except TypeError: + exc_type, exc_value, tb = sys.exc_info() + + reporter = ExceptionReporter(None, exc_type, exc_value, tb) + d = reporter.get_traceback_data() + vars = d["lastframe"]["vars"] + + for k, v in vars: + if k == "lg": + i = v.index("...") + + # Construct list with elements before trimming + ls = eval(v[:i] + "]") + + # Check if length of trimmed list is our limit + self.assertEqual(len(ls), ExceptionReporter.PRINT_LIMIT) + break + + def test_non_sizable_object(self): + """Non-sizable variables be handled with builtin repr""" + num = 10000000 + try: + num["a"] + except TypeError: + exc_type, exc_value, tb = sys.exc_info() + + reporter = ExceptionReporter(None, exc_type, exc_value, tb) + d = reporter.get_traceback_data() + vars = d["lastframe"]["vars"] + + for k, v in vars: + if k == "a": + self.assertEqual(v, str(num)) + break def test_encoding_error(self): """