1
0
mirror of https://github.com/django/django.git synced 2025-06-11 06:29:13 +00:00

fix: custom repr implementation for consistency with pprint

This commit is contained in:
Keerthi Vasan 2024-02-02 22:38:08 +05:30
parent 2a977f6705
commit 27dc608eb3
3 changed files with 45 additions and 19 deletions

13
django/utils/repr.py Normal file
View File

@ -0,0 +1,13 @@
import reprlib
class DjangoRepr(reprlib.Repr):
def config(self, limit):
"""Sets maximum print length for all data structures using the given value"""
for attr in dir(self):
if attr.startswith("max") and attr != "maxlevel":
setattr(self, attr, limit)
def repr_str(self, x, level):
return x[: self.maxstring]

View File

@ -2,7 +2,6 @@ import functools
import inspect import inspect
import itertools import itertools
import re import re
import reprlib
import sys import sys
import types import types
import warnings import warnings
@ -18,6 +17,7 @@ from django.utils.datastructures import MultiValueDict
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils.module_loading import import_string from django.utils.module_loading import import_string
from django.utils.regex_helper import _lazy_re_compile from django.utils.regex_helper import _lazy_re_compile
from django.utils.repr import DjangoRepr
from django.utils.version import PY311, get_docs_version from django.utils.version import PY311, get_docs_version
from django.views.decorators.debug import coroutine_functions_to_sensitive_variables from django.views.decorators.debug import coroutine_functions_to_sensitive_variables
@ -311,16 +311,8 @@ class SafeExceptionReporterFilter:
class ExceptionReporter: class ExceptionReporter:
"""Organize and coordinate reporting on exceptions.""" """Organize and coordinate reporting on exceptions."""
repr_instance = reprlib.Repr() repr_instance = DjangoRepr()
repr_instance.indent = 2 PRINT_LIMIT = 4096
repr_instance.maxdeque = 4096
repr_instance.maxstring = 4096
repr_instance.maxlist = 4096
repr_instance.maxset = 4096
repr_instance.maxdict = 4096
repr_instance.maxfrozenset = 4096
repr_instance.maxarray = 4096
repr_instance.maxother = 4096
@property @property
def html_template_path(self): def html_template_path(self):
@ -342,6 +334,8 @@ class ExceptionReporter:
self.template_does_not_exist = False self.template_does_not_exist = False
self.postmortem = None self.postmortem = None
self.repr_instance.config(limit=ExceptionReporter.PRINT_LIMIT)
def _get_raw_insecure_uri(self): def _get_raw_insecure_uri(self):
""" """
Return an absolute URI from variables available in this request. Skip Return an absolute URI from variables available in this request. Skip
@ -364,9 +358,22 @@ class ExceptionReporter:
if "vars" in frame: if "vars" in frame:
frame_vars = [] frame_vars = []
for k, v in frame["vars"]: for k, v in frame["vars"]:
if isinstance(v, Sized) and len(v) > 4096: try:
self.repr_instance.fillvalue = "...%d more" % (len(v) - 4096) # Check if there are any exceptions in __repr__ fn of the object
v = self.repr_instance.repr(v) v = repr(v)
trim_msg = ""
if isinstance(v, Sized) and len(v) > 4096:
diff = len(v) - 4096
trim_msg = "...<trimmed %d bytes string>" % 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.append((k, v))
frame["vars"] = frame_vars frame["vars"] = frame_vars
frames[i] = frame frames[i] = frame

View File

@ -1036,15 +1036,20 @@ class ExceptionReporterTests(SimpleTestCase):
def test_local_variable_escaping(self): def test_local_variable_escaping(self):
"""Safe strings in local variables are escaped.""" """Safe strings in local variables are escaped."""
# &lt;p&gt;Local variable&lt;/p&gt;\
# &#x27;&lt;p&gt;Local variable&lt;/p&gt;&#x27;
try: try:
local = mark_safe("<p>Local variable</p>") local = mark_safe("<p>Local variable</p>")
print("safe string:", repr(local))
raise ValueError(local) raise ValueError(local)
except Exception: except Exception:
exc_type, exc_value, tb = sys.exc_info() exc_type, exc_value, tb = sys.exc_info()
html = ExceptionReporter(None, exc_type, exc_value, tb).get_traceback_html() html = ExceptionReporter(None, exc_type, exc_value, tb).get_traceback_html()
self.assertIn( self.assertIn(
'<td class="code"><pre>&#x27;&lt;p&gt;Local variable&lt;/p&gt;&#x27;</pre>' # <td><pre>&lt;p&gt;Local variable&lt;/p&gt;</pre></td>
"</td>", # <td class="code"><pre>&lt;p&gt;Local variable&lt;/p&gt;</pre></td>
'<td class="code"><pre>&#x27;&lt;p&gt;Local variable&lt;/p&gt;&#x27;</pre>',
html, html,
) )
@ -1066,7 +1071,7 @@ class ExceptionReporterTests(SimpleTestCase):
def test_too_large_values_handling(self): def test_too_large_values_handling(self):
"Large values should not create a large HTML." "Large values should not create a large HTML."
large = 256 * 1024 large = 5000
repr_of_str_adds = len(repr("")) repr_of_str_adds = len(repr(""))
try: try:
@ -1081,9 +1086,10 @@ class ExceptionReporterTests(SimpleTestCase):
reporter = ExceptionReporter(None, exc_type, exc_value, tb) reporter = ExceptionReporter(None, exc_type, exc_value, tb)
html = reporter.get_traceback_html() html = reporter.get_traceback_html()
self.assertEqual(len(html) // 1024 // 128, 0) # still fit in 128Kb self.assertEqual(len(html) // 1024 // 128, 0) # still fit in 128Kb
self.assertIn( msg = "&lt;trimmed %d bytes string&gt;" % (
"&lt;trimmed %d bytes string&gt;" % (large + repr_of_str_adds,), html large - ExceptionReporter.PRINT_LIMIT + repr_of_str_adds,
) )
self.assertIn(msg, html)
def test_encoding_error(self): def test_encoding_error(self):
""" """