mirror of
https://github.com/django/django.git
synced 2024-12-22 17:16:24 +00:00
feat: switched to fully custom repr implementation
This commit is contained in:
parent
d68430349e
commit
c55d4a1d39
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,7 +6,7 @@
|
|||||||
*.pot
|
*.pot
|
||||||
*.py[co]
|
*.py[co]
|
||||||
.tox/
|
.tox/
|
||||||
venv/
|
venv*/
|
||||||
__pycache__
|
__pycache__
|
||||||
MANIFEST
|
MANIFEST
|
||||||
dist/
|
dist/
|
||||||
|
@ -1,16 +1,15 @@
|
|||||||
import builtins
|
import builtins
|
||||||
import gzip
|
import gzip
|
||||||
import re
|
import re
|
||||||
import reprlib
|
|
||||||
import secrets
|
import secrets
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from collections.abc import Sized
|
|
||||||
from gzip import GzipFile
|
from gzip import GzipFile
|
||||||
from gzip import compress as gzip_compress
|
from gzip import compress as gzip_compress
|
||||||
from html import escape
|
from html import escape
|
||||||
from html.parser import HTMLParser
|
from html.parser import HTMLParser
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
from django.core.exceptions import SuspiciousFileOperation
|
from django.core.exceptions import SuspiciousFileOperation
|
||||||
from django.utils.functional import (
|
from django.utils.functional import (
|
||||||
@ -490,47 +489,148 @@ def _format_lazy(format_string, *args, **kwargs):
|
|||||||
format_lazy = lazy(_format_lazy, str)
|
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):
|
def __init__(self, limit):
|
||||||
"""Sets maximum print length for all data structures using the given value"""
|
self.indent = 0
|
||||||
self.maxlevel = limit
|
self.maxlevel = 2
|
||||||
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.limit = limit
|
self.limit = limit
|
||||||
|
|
||||||
self.fillvalue = "..."
|
def repr(self, x):
|
||||||
self.indent = 2
|
return self.repr1(x, self.maxlevel)
|
||||||
|
|
||||||
def repr_str(self, x, level):
|
def repr1(self, x, level):
|
||||||
return "'%s'" % (x[: self.maxstring] + self.gen_trim_msg(len(x)))
|
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)
|
s = builtins.repr(x)
|
||||||
if len(s) > self.maxother:
|
if len(s) > self.limit:
|
||||||
return s[: self.maxother] + self.gen_trim_msg(len(s))
|
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
|
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):
|
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)
|
return self.repr(value)
|
||||||
|
|
||||||
def gen_trim_msg(self, length):
|
def gen_trim_msg(self, length):
|
||||||
if length <= self.limit:
|
if length <= self.limit:
|
||||||
return ""
|
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)
|
||||||
|
Loading…
Reference in New Issue
Block a user