1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

completely switch to repr, added tests for repr exception reports

This commit is contained in:
Keerthi Vasan 2024-02-02 16:15:12 +05:30
parent 03c7c2d095
commit 2a977f6705
3 changed files with 65 additions and 11 deletions

View File

@ -6,12 +6,12 @@ import reprlib
import sys import sys
import types import types
import warnings import warnings
from collections.abc import Sized
from pathlib import Path from pathlib import Path
from django.conf import settings from django.conf import settings
from django.http import Http404, HttpResponse, HttpResponseNotFound from django.http import Http404, HttpResponse, HttpResponseNotFound
from django.template import Context, Engine, TemplateDoesNotExist from django.template import Context, Engine, TemplateDoesNotExist
from django.template.defaultfilters import pprint
from django.urls import resolve from django.urls import resolve
from django.utils import timezone from django.utils import timezone
from django.utils.datastructures import MultiValueDict from django.utils.datastructures import MultiValueDict
@ -311,8 +311,16 @@ class SafeExceptionReporterFilter:
class ExceptionReporter: class ExceptionReporter:
"""Organize and coordinate reporting on exceptions.""" """Organize and coordinate reporting on exceptions."""
repr_instance = reprlib.Repr(maxstring=4096, maxlist=1000) repr_instance = reprlib.Repr()
MAX_VAR_SIZE_PRETTY_PRINT = 512 * 1024 # 512KB repr_instance.indent = 2
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):
@ -352,18 +360,13 @@ class ExceptionReporter:
self.postmortem = self.exc_value.chain or [self.exc_value] self.postmortem = self.exc_value.chain or [self.exc_value]
frames = self.get_traceback_frames() frames = self.get_traceback_frames()
for i, frame in enumerate(frames): for i, frame in enumerate(frames):
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 sys.getsizeof(v) > self.MAX_VAR_SIZE_PRETTY_PRINT: if isinstance(v, Sized) and len(v) > 4096:
v = self.repr_instance.repr(v) self.repr_instance.fillvalue = "...%d more" % (len(v) - 4096)
else: v = self.repr_instance.repr(v)
v = pprint(v)
# Trim large blobs of data
if len(v) > 4096:
v = "%s… <trimmed %d bytes string>" % (v[0:4096], len(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

View File

@ -0,0 +1,51 @@
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