0
0
mirror of https://github.com/django/django.git synced 2024-11-24 20:07:01 +01:00

feat: switched to fully custom repr implementation

This commit is contained in:
Keerthi Vasan 2024-06-24 12:26:44 +05:30
parent d68430349e
commit c55d4a1d39
2 changed files with 132 additions and 32 deletions

2
.gitignore vendored
View File

@ -6,7 +6,7 @@
*.pot
*.py[co]
.tox/
venv/
venv*/
__pycache__
MANIFEST
dist/

View File

@ -1,16 +1,15 @@
import builtins
import gzip
import re
import reprlib
import secrets
import unicodedata
from collections import deque
from collections.abc import Sized
from gzip import GzipFile
from gzip import compress as gzip_compress
from html import escape
from html.parser import HTMLParser
from io import BytesIO
from itertools import islice
from django.core.exceptions import SuspiciousFileOperation
from django.utils.functional import (
@ -490,47 +489,148 @@ def _format_lazy(format_string, *args, **kwargs):
format_lazy = lazy(_format_lazy, str)
class DebugRepr(reprlib.Repr):
class DebugRepr:
"""
Modified Reprlib.Repr from Python 3.12 that includes fillvalue customization.
"""
def __init__(self, limit):
"""Sets maximum print length for all data structures using the given value"""
self.maxlevel = limit
self.maxtuple = limit
self.maxlist = limit
self.maxarray = limit
self.maxdict = limit
self.maxset = limit
self.maxfrozenset = limit
self.maxdeque = limit
self.maxstring = limit
self.maxlong = limit
self.maxother = limit
self.indent = 0
self.maxlevel = 2
self.limit = limit
self.fillvalue = "..."
self.indent = 2
def repr(self, x):
return self.repr1(x, self.maxlevel)
def repr_str(self, x, level):
return "'%s'" % (x[: self.maxstring] + self.gen_trim_msg(len(x)))
def repr1(self, x, level):
typename = type(x).__name__
if " " in typename:
parts = typename.split()
typename = "_".join(parts)
if hasattr(self, "repr_" + typename):
return getattr(self, "repr_" + typename)(x, level)
else:
return self.repr_instance(x, level)
def repr_instance(self, x, level):
def _join(self, pieces, level):
if self.indent is None:
return ", ".join(pieces)
if not pieces:
return ""
indent = self.indent
if isinstance(indent, int):
if indent < 0:
raise ValueError(f"Repr.indent cannot be negative int (was {indent!r})")
indent *= " "
try:
sep = ", " + (self.maxlevel - level + 1) * indent
except TypeError as error:
raise TypeError(
f"Repr.indent must be a str, int or None, not {type(indent)}"
) from error
return sep.join(("", *pieces))[1 : -len(indent) or None]
def _repr_iterable(self, x, level, left, right, maxiter, trail=""):
n = len(x)
fillvalue = self.gen_trim_msg(n)
if level <= 0 and n:
s = fillvalue
else:
newlevel = level - 1
repr1 = self.repr1
pieces = [repr1(elem, newlevel) for elem in islice(x, maxiter)]
if n > maxiter:
pieces.append(fillvalue)
s = self._join(pieces, level)
if n == 1 and trail and self.indent is None:
right = trail + right
return "%s%s%s" % (left, s, right)
def repr_tuple(self, x, level):
return self._repr_iterable(x, level, "(", ")", self.limit, ",")
def repr_list(self, x, level):
return self._repr_iterable(x, level, "[", "]", self.limit)
def repr_array(self, x, level):
if not x:
return "array('%s')" % x.typecode
header = "array('%s', [" % x.typecode
return self._repr_iterable(x, level, header, "])", self.limit)
def repr_set(self, x, level):
if not x:
return "set()"
x = _possibly_sorted(x)
return self._repr_iterable(x, level, "{", "}", self.limit)
def repr_frozenset(self, x, level):
if not x:
return "frozenset()"
x = _possibly_sorted(x)
return self._repr_iterable(x, level, "frozenset({", "})", self.limit)
def repr_deque(self, x, level):
return self._repr_iterable(x, level, "deque([", "])", self.limit)
def repr_dict(self, x, level):
n = len(x)
if n == 0:
return "{}"
fillvalue = self.gen_trim_msg(n)
if level <= 0:
return "{" + fillvalue + "}"
newlevel = level - 1
repr1 = self.repr1
pieces = []
for key in islice(_possibly_sorted(x), self.limit):
keyrepr = repr1(key, newlevel)
valrepr = repr1(x[key], newlevel)
pieces.append("%s: %s" % (keyrepr, valrepr))
if n > self.limit:
pieces.append(fillvalue)
s = self._join(pieces, level)
return "{%s}" % (s,)
def repr_int(self, x, level):
s = builtins.repr(x)
if len(s) > self.maxother:
return s[: self.maxother] + self.gen_trim_msg(len(s))
if len(s) > self.limit:
i = max(0, (self.limit - 3) // 2)
j = max(0, self.limit - 3 - i)
s = s[:i] + self.limit + s[len(s) - j :]
return s
def repr_instance(self, x, level):
try:
s = builtins.repr(x)
# Bugs in x.__repr__() can cause arbitrary
# exceptions -- then make up something
except Exception:
return "<%s instance at %#x>" % (x.__class__.__name__, id(x))
if len(s) > self.limit:
i = max(0, (self.limit - 3) // 2)
j = max(0, self.limit - 3 - i)
fillvalue = self.gen_trim_msg(len(s))
s = s[:i] + fillvalue + s[len(s) - j :]
return s
def repr_str(self, x, level):
return "'%s'" % (x[: self.limit] + self.gen_trim_msg(len(x)))
def print(self, value):
if isinstance(value, Sized):
length = len(value)
if length > self.limit:
self.fillvalue = self.gen_trim_msg(length)
else:
self.fillvalue = ""
else:
self.fillvalue = "..."
return self.repr(value)
def gen_trim_msg(self, length):
if length <= self.limit:
return ""
return "...<trimmed %d bytes string>" % (length - self.limit)
return "...<trimmed %d bytes string> " % (length - self.limit)
def _possibly_sorted(x):
# Since not all sequences of items can be sorted and comparison
# functions may raise arbitrary exceptions, return an unsorted
# sequence in that case.
try:
return sorted(x)
except Exception:
return list(x)