Merge pull request #11644 from BenjaminSchubert/bschubert/pprint-cleanup

Various cleanups of the vendored pprint module
This commit is contained in:
Benjamin Schubert 2023-11-27 22:54:46 +00:00 committed by GitHub
commit 172bf89ad1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 243 additions and 102 deletions

View File

@ -15,14 +15,17 @@
import collections as _collections import collections as _collections
import dataclasses as _dataclasses import dataclasses as _dataclasses
import re import re
import sys as _sys
import types as _types import types as _types
from io import StringIO as _StringIO from io import StringIO as _StringIO
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
from typing import IO from typing import IO
from typing import Iterator
from typing import List from typing import List
from typing import Optional
from typing import Set
from typing import Tuple
class _safe_key: class _safe_key:
@ -58,15 +61,13 @@ def _safe_tuple(t):
class PrettyPrinter: class PrettyPrinter:
def __init__( def __init__(
self, self,
indent=4, indent: int = 4,
width=80, width: int = 80,
depth=None, depth: Optional[int] = None,
stream=None,
*, *,
compact=False, sort_dicts: bool = True,
sort_dicts=True, underscore_numbers: bool = False,
underscore_numbers=False, ) -> None:
):
"""Handle pretty printing operations onto a stream using a set of """Handle pretty printing operations onto a stream using a set of
configured parameters. configured parameters.
@ -79,13 +80,6 @@ class PrettyPrinter:
depth depth
The maximum depth to print out nested structures. The maximum depth to print out nested structures.
stream
The desired output stream. If omitted (or false), the standard
output stream available at construction will be used.
compact
If true, several items will be combined in one line.
sort_dicts sort_dicts
If true, dict keys are sorted. If true, dict keys are sorted.
@ -101,32 +95,33 @@ class PrettyPrinter:
self._depth = depth self._depth = depth
self._indent_per_level = indent self._indent_per_level = indent
self._width = width self._width = width
if stream is not None:
self._stream = stream
else:
self._stream = _sys.stdout
self._compact = bool(compact)
self._sort_dicts = sort_dicts self._sort_dicts = sort_dicts
self._underscore_numbers = underscore_numbers self._underscore_numbers = underscore_numbers
def pformat(self, object: Any) -> str: def pformat(self, object: Any) -> str:
sio = _StringIO() sio = _StringIO()
self._format(object, sio, 0, 0, {}, 0) self._format(object, sio, 0, 0, set(), 0)
return sio.getvalue() return sio.getvalue()
def _format(self, object, stream, indent, allowance, context, level): def _format(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
objid = id(object) objid = id(object)
if objid in context: if objid in context:
stream.write(_recursion(object)) stream.write(_recursion(object))
self._recursive = True
self._readable = False
return return
p = self._dispatch.get(type(object).__repr__, None) p = self._dispatch.get(type(object).__repr__, None)
if p is not None: if p is not None:
context[objid] = 1 context.add(objid)
p(self, object, stream, indent, allowance, context, level + 1) p(self, object, stream, indent, allowance, context, level + 1)
del context[objid] context.remove(objid)
elif ( elif (
_dataclasses.is_dataclass(object) _dataclasses.is_dataclass(object)
and not isinstance(object, type) and not isinstance(object, type)
@ -136,15 +131,23 @@ class PrettyPrinter:
hasattr(object.__repr__, "__wrapped__") hasattr(object.__repr__, "__wrapped__")
and "__create_fn__" in object.__repr__.__wrapped__.__qualname__ and "__create_fn__" in object.__repr__.__wrapped__.__qualname__
): ):
context[objid] = 1 context.add(objid)
self._pprint_dataclass( self._pprint_dataclass(
object, stream, indent, allowance, context, level + 1 object, stream, indent, allowance, context, level + 1
) )
del context[objid] context.remove(objid)
else: else:
stream.write(self._repr(object, context, level)) stream.write(self._repr(object, context, level))
def _pprint_dataclass(self, object, stream, indent, allowance, context, level): def _pprint_dataclass(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
cls_name = object.__class__.__name__ cls_name = object.__class__.__name__
items = [ items = [
(f.name, getattr(object, f.name)) (f.name, getattr(object, f.name))
@ -157,10 +160,18 @@ class PrettyPrinter:
_dispatch: Dict[ _dispatch: Dict[
Callable[..., str], Callable[..., str],
Callable[["PrettyPrinter", Any, IO[str], int, int, Dict[int, int], int], str], Callable[["PrettyPrinter", Any, IO[str], int, int, Set[int], int], None],
] = {} ] = {}
def _pprint_dict(self, object, stream, indent, allowance, context, level): def _pprint_dict(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
write = stream.write write = stream.write
write("{") write("{")
if self._sort_dicts: if self._sort_dicts:
@ -172,7 +183,15 @@ class PrettyPrinter:
_dispatch[dict.__repr__] = _pprint_dict _dispatch[dict.__repr__] = _pprint_dict
def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level): def _pprint_ordered_dict(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not len(object): if not len(object):
stream.write(repr(object)) stream.write(repr(object))
return return
@ -183,21 +202,45 @@ class PrettyPrinter:
_dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
def _pprint_list(self, object, stream, indent, allowance, context, level): def _pprint_list(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
stream.write("[") stream.write("[")
self._format_items(object, stream, indent, allowance, context, level) self._format_items(object, stream, indent, allowance, context, level)
stream.write("]") stream.write("]")
_dispatch[list.__repr__] = _pprint_list _dispatch[list.__repr__] = _pprint_list
def _pprint_tuple(self, object, stream, indent, allowance, context, level): def _pprint_tuple(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
stream.write("(") stream.write("(")
self._format_items(object, stream, indent, allowance, context, level) self._format_items(object, stream, indent, allowance, context, level)
stream.write(")") stream.write(")")
_dispatch[tuple.__repr__] = _pprint_tuple _dispatch[tuple.__repr__] = _pprint_tuple
def _pprint_set(self, object, stream, indent, allowance, context, level): def _pprint_set(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not len(object): if not len(object):
stream.write(repr(object)) stream.write(repr(object))
return return
@ -215,7 +258,15 @@ class PrettyPrinter:
_dispatch[set.__repr__] = _pprint_set _dispatch[set.__repr__] = _pprint_set
_dispatch[frozenset.__repr__] = _pprint_set _dispatch[frozenset.__repr__] = _pprint_set
def _pprint_str(self, object, stream, indent, allowance, context, level): def _pprint_str(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
write = stream.write write = stream.write
if not len(object): if not len(object):
write(repr(object)) write(repr(object))
@ -266,7 +317,15 @@ class PrettyPrinter:
_dispatch[str.__repr__] = _pprint_str _dispatch[str.__repr__] = _pprint_str
def _pprint_bytes(self, object, stream, indent, allowance, context, level): def _pprint_bytes(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
write = stream.write write = stream.write
if len(object) <= 4: if len(object) <= 4:
write(repr(object)) write(repr(object))
@ -287,7 +346,15 @@ class PrettyPrinter:
_dispatch[bytes.__repr__] = _pprint_bytes _dispatch[bytes.__repr__] = _pprint_bytes
def _pprint_bytearray(self, object, stream, indent, allowance, context, level): def _pprint_bytearray(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
write = stream.write write = stream.write
write("bytearray(") write("bytearray(")
self._pprint_bytes( self._pprint_bytes(
@ -297,7 +364,15 @@ class PrettyPrinter:
_dispatch[bytearray.__repr__] = _pprint_bytearray _dispatch[bytearray.__repr__] = _pprint_bytearray
def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level): def _pprint_mappingproxy(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
stream.write("mappingproxy(") stream.write("mappingproxy(")
self._format(object.copy(), stream, indent, allowance, context, level) self._format(object.copy(), stream, indent, allowance, context, level)
stream.write(")") stream.write(")")
@ -305,8 +380,14 @@ class PrettyPrinter:
_dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy _dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
def _pprint_simplenamespace( def _pprint_simplenamespace(
self, object, stream, indent, allowance, context, level self,
): object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if type(object) is _types.SimpleNamespace: if type(object) is _types.SimpleNamespace:
# The SimpleNamespace repr is "namespace" instead of the class # The SimpleNamespace repr is "namespace" instead of the class
# name, so we do the same here. For subclasses; use the class name. # name, so we do the same here. For subclasses; use the class name.
@ -320,7 +401,15 @@ class PrettyPrinter:
_dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace _dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
def _format_dict_items(self, items, stream, indent, allowance, context, level): def _format_dict_items(
self,
items: List[Tuple[Any, Any]],
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not items: if not items:
return return
@ -336,7 +425,15 @@ class PrettyPrinter:
write("\n" + " " * indent) write("\n" + " " * indent)
def _format_namespace_items(self, items, stream, indent, allowance, context, level): def _format_namespace_items(
self,
items: List[Tuple[Any, Any]],
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not items: if not items:
return return
@ -365,7 +462,15 @@ class PrettyPrinter:
write("\n" + " " * indent) write("\n" + " " * indent)
def _format_items(self, items, stream, indent, allowance, context, level): def _format_items(
self,
items: List[Any],
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not items: if not items:
return return
@ -380,24 +485,23 @@ class PrettyPrinter:
write("\n" + " " * indent) write("\n" + " " * indent)
def _repr(self, object, context, level): def _repr(self, object: Any, context: Set[int], level: int) -> str:
repr, readable, recursive = self.format( return self.format(object, context.copy(), self._depth, level)
object, context.copy(), self._depth, level
)
if not readable:
self._readable = False
if recursive:
self._recursive = True
return repr
def format(self, object, context, maxlevels, level): def format(
"""Format object for a specific context, returning a string self, object: Any, context: Set[int], maxlevels: Optional[int], level: int
and flags indicating whether the representation is 'readable' ) -> str:
and whether the object represents a recursive construct.
"""
return self._safe_repr(object, context, maxlevels, level) return self._safe_repr(object, context, maxlevels, level)
def _pprint_default_dict(self, object, stream, indent, allowance, context, level): def _pprint_default_dict(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
rdf = self._repr(object.default_factory, context, level) rdf = self._repr(object.default_factory, context, level)
stream.write(f"{object.__class__.__name__}({rdf}, ") stream.write(f"{object.__class__.__name__}({rdf}, ")
self._pprint_dict(object, stream, indent, allowance, context, level) self._pprint_dict(object, stream, indent, allowance, context, level)
@ -405,7 +509,15 @@ class PrettyPrinter:
_dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict _dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
def _pprint_counter(self, object, stream, indent, allowance, context, level): def _pprint_counter(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
stream.write(object.__class__.__name__ + "(") stream.write(object.__class__.__name__ + "(")
if object: if object:
@ -418,7 +530,15 @@ class PrettyPrinter:
_dispatch[_collections.Counter.__repr__] = _pprint_counter _dispatch[_collections.Counter.__repr__] = _pprint_counter
def _pprint_chain_map(self, object, stream, indent, allowance, context, level): def _pprint_chain_map(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])): if not len(object.maps) or (len(object.maps) == 1 and not len(object.maps[0])):
stream.write(repr(object)) stream.write(repr(object))
return return
@ -429,7 +549,15 @@ class PrettyPrinter:
_dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map _dispatch[_collections.ChainMap.__repr__] = _pprint_chain_map
def _pprint_deque(self, object, stream, indent, allowance, context, level): def _pprint_deque(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
stream.write(object.__class__.__name__ + "(") stream.write(object.__class__.__name__ + "(")
if object.maxlen is not None: if object.maxlen is not None:
stream.write("maxlen=%d, " % object.maxlen) stream.write("maxlen=%d, " % object.maxlen)
@ -440,46 +568,69 @@ class PrettyPrinter:
_dispatch[_collections.deque.__repr__] = _pprint_deque _dispatch[_collections.deque.__repr__] = _pprint_deque
def _pprint_user_dict(self, object, stream, indent, allowance, context, level): def _pprint_user_dict(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
self._format(object.data, stream, indent, allowance, context, level - 1) self._format(object.data, stream, indent, allowance, context, level - 1)
_dispatch[_collections.UserDict.__repr__] = _pprint_user_dict _dispatch[_collections.UserDict.__repr__] = _pprint_user_dict
def _pprint_user_list(self, object, stream, indent, allowance, context, level): def _pprint_user_list(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
self._format(object.data, stream, indent, allowance, context, level - 1) self._format(object.data, stream, indent, allowance, context, level - 1)
_dispatch[_collections.UserList.__repr__] = _pprint_user_list _dispatch[_collections.UserList.__repr__] = _pprint_user_list
def _pprint_user_string(self, object, stream, indent, allowance, context, level): def _pprint_user_string(
self,
object: Any,
stream: IO[str],
indent: int,
allowance: int,
context: Set[int],
level: int,
) -> None:
self._format(object.data, stream, indent, allowance, context, level - 1) self._format(object.data, stream, indent, allowance, context, level - 1)
_dispatch[_collections.UserString.__repr__] = _pprint_user_string _dispatch[_collections.UserString.__repr__] = _pprint_user_string
def _safe_repr(self, object, context, maxlevels, level): def _safe_repr(
# Return triple (repr_string, isreadable, isrecursive). self, object: Any, context: Set[int], maxlevels: Optional[int], level: int
) -> str:
typ = type(object) typ = type(object)
if typ in _builtin_scalars: if typ in _builtin_scalars:
return repr(object), True, False return repr(object)
r = getattr(typ, "__repr__", None) r = getattr(typ, "__repr__", None)
if issubclass(typ, int) and r is int.__repr__: if issubclass(typ, int) and r is int.__repr__:
if self._underscore_numbers: if self._underscore_numbers:
return f"{object:_d}", True, False return f"{object:_d}"
else: else:
return repr(object), True, False return repr(object)
if issubclass(typ, dict) and r is dict.__repr__: if issubclass(typ, dict) and r is dict.__repr__:
if not object: if not object:
return "{}", True, False return "{}"
objid = id(object) objid = id(object)
if maxlevels and level >= maxlevels: if maxlevels and level >= maxlevels:
return "{...}", False, objid in context return "{...}"
if objid in context: if objid in context:
return _recursion(object), False, True return _recursion(object)
context[objid] = 1 context.add(objid)
readable = True
recursive = False
components: List[str] = [] components: List[str] = []
append = components.append append = components.append
level += 1 level += 1
@ -488,61 +639,51 @@ class PrettyPrinter:
else: else:
items = object.items() items = object.items()
for k, v in items: for k, v in items:
krepr, kreadable, krecur = self.format(k, context, maxlevels, level) krepr = self.format(k, context, maxlevels, level)
vrepr, vreadable, vrecur = self.format(v, context, maxlevels, level) vrepr = self.format(v, context, maxlevels, level)
append(f"{krepr}: {vrepr}") append(f"{krepr}: {vrepr}")
readable = readable and kreadable and vreadable context.remove(objid)
if krecur or vrecur: return "{%s}" % ", ".join(components)
recursive = True
del context[objid]
return "{%s}" % ", ".join(components), readable, recursive
if (issubclass(typ, list) and r is list.__repr__) or ( if (issubclass(typ, list) and r is list.__repr__) or (
issubclass(typ, tuple) and r is tuple.__repr__ issubclass(typ, tuple) and r is tuple.__repr__
): ):
if issubclass(typ, list): if issubclass(typ, list):
if not object: if not object:
return "[]", True, False return "[]"
format = "[%s]" format = "[%s]"
elif len(object) == 1: elif len(object) == 1:
format = "(%s,)" format = "(%s,)"
else: else:
if not object: if not object:
return "()", True, False return "()"
format = "(%s)" format = "(%s)"
objid = id(object) objid = id(object)
if maxlevels and level >= maxlevels: if maxlevels and level >= maxlevels:
return format % "...", False, objid in context return format % "..."
if objid in context: if objid in context:
return _recursion(object), False, True return _recursion(object)
context[objid] = 1 context.add(objid)
readable = True
recursive = False
components = [] components = []
append = components.append append = components.append
level += 1 level += 1
for o in object: for o in object:
orepr, oreadable, orecur = self.format(o, context, maxlevels, level) orepr = self.format(o, context, maxlevels, level)
append(orepr) append(orepr)
if not oreadable: context.remove(objid)
readable = False return format % ", ".join(components)
if orecur:
recursive = True
del context[objid]
return format % ", ".join(components), readable, recursive
rep = repr(object) return repr(object)
return rep, (rep and not rep.startswith("<")), False
_builtin_scalars = frozenset({str, bytes, bytearray, float, complex, bool, type(None)}) _builtin_scalars = frozenset({str, bytes, bytearray, float, complex, bool, type(None)})
def _recursion(object): def _recursion(object: Any) -> str:
return f"<Recursion on {type(object).__name__} with id={id(object)}>" return f"<Recursion on {type(object).__name__} with id={id(object)}>"
def _wrap_bytes_repr(object, width, allowance): def _wrap_bytes_repr(object: Any, width: int, allowance: int) -> Iterator[str]:
current = b"" current = b""
last = len(object) // 4 * 4 last = len(object) // 4 * 4
for i in range(0, len(object), 4): for i in range(0, len(object), 4):