mirror of
https://github.com/python/cpython.git
synced 2026-05-06 12:49:07 -04:00
gh-112632: Add optional keyword-only argument expand to pprint (#136964)
Co-authored-by: stodoran <stefan.todoran@uipath.com> Co-authored-by: StefanTodoran <stefan.alex4@gmail.com> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
+104
-5
@@ -31,7 +31,8 @@ Functions
|
||||
---------
|
||||
|
||||
.. function:: pp(object, stream=None, indent=1, width=80, depth=None, *, \
|
||||
compact=False, sort_dicts=False, underscore_numbers=False)
|
||||
compact=False, expand=False, sort_dicts=False, \
|
||||
underscore_numbers=False)
|
||||
|
||||
Prints the formatted representation of *object*, followed by a newline.
|
||||
This function may be used in the interactive interpreter
|
||||
@@ -69,6 +70,13 @@ Functions
|
||||
each item of a sequence will be formatted on a separate line,
|
||||
otherwise as many items as will fit within the *width*
|
||||
will be formatted on each output line.
|
||||
Incompatible with *expand*.
|
||||
|
||||
:param bool expand:
|
||||
If ``True``,
|
||||
opening parentheses and brackets will be followed by a newline and the
|
||||
following content will be indented by one level, similar to
|
||||
pretty-printed JSON. Incompatible with *compact*.
|
||||
|
||||
:param bool sort_dicts:
|
||||
If ``True``, dictionaries will be formatted with
|
||||
@@ -95,7 +103,8 @@ Functions
|
||||
|
||||
|
||||
.. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \
|
||||
compact=False, sort_dicts=True, underscore_numbers=False)
|
||||
compact=False, expand=False, sort_dicts=True, \
|
||||
underscore_numbers=False)
|
||||
|
||||
Alias for :func:`~pprint.pp` with *sort_dicts* set to ``True`` by default,
|
||||
which would automatically sort the dictionaries' keys,
|
||||
@@ -103,10 +112,11 @@ Functions
|
||||
|
||||
|
||||
.. function:: pformat(object, indent=1, width=80, depth=None, *, \
|
||||
compact=False, sort_dicts=True, underscore_numbers=False)
|
||||
compact=False, expand=False, sort_dicts=True, \
|
||||
underscore_numbers=False)
|
||||
|
||||
Return the formatted representation of *object* as a string. *indent*,
|
||||
*width*, *depth*, *compact*, *sort_dicts* and *underscore_numbers* are
|
||||
*width*, *depth*, *compact*, *expand*, *sort_dicts* and *underscore_numbers* are
|
||||
passed to the :class:`PrettyPrinter` constructor as formatting parameters
|
||||
and their meanings are as described in the documentation above.
|
||||
|
||||
@@ -150,7 +160,8 @@ PrettyPrinter Objects
|
||||
.. index:: single: ...; placeholder
|
||||
|
||||
.. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \
|
||||
compact=False, sort_dicts=True, underscore_numbers=False)
|
||||
compact=False, expand=False, sort_dicts=True, \
|
||||
underscore_numbers=False)
|
||||
|
||||
Construct a :class:`PrettyPrinter` instance.
|
||||
|
||||
@@ -174,6 +185,22 @@ PrettyPrinter Objects
|
||||
'knights', 'ni'],
|
||||
'spam', 'eggs', 'lumberjack', 'knights',
|
||||
'ni']
|
||||
>>> pp = pprint.PrettyPrinter(width=41, expand=True, indent=3)
|
||||
>>> pp.pprint(stuff)
|
||||
[
|
||||
[
|
||||
'spam',
|
||||
'eggs',
|
||||
'lumberjack',
|
||||
'knights',
|
||||
'ni',
|
||||
],
|
||||
'spam',
|
||||
'eggs',
|
||||
'lumberjack',
|
||||
'knights',
|
||||
'ni',
|
||||
]
|
||||
>>> tup = ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead',
|
||||
... ('parrot', ('fresh fruit',))))))))
|
||||
>>> pp = pprint.PrettyPrinter(depth=6)
|
||||
@@ -193,6 +220,9 @@ PrettyPrinter Objects
|
||||
.. versionchanged:: 3.11
|
||||
No longer attempts to write to :data:`!sys.stdout` if it is ``None``.
|
||||
|
||||
.. versionchanged:: next
|
||||
Added the *expand* parameter.
|
||||
|
||||
|
||||
:class:`PrettyPrinter` instances have the following methods:
|
||||
|
||||
@@ -415,3 +445,72 @@ cannot be split, the specified width will be exceeded::
|
||||
'requires_python': None,
|
||||
'summary': 'A sample Python project',
|
||||
'version': '1.2.0'}
|
||||
|
||||
Lastly, we can format like pretty-printed JSON with the *expand* parameter.
|
||||
Best results are achieved with a higher *indent* value::
|
||||
|
||||
>>> pprint.pp(project_info, indent=4, expand=True)
|
||||
{
|
||||
'author': 'The Python Packaging Authority',
|
||||
'author_email': 'pypa-dev@googlegroups.com',
|
||||
'bugtrack_url': None,
|
||||
'classifiers': [
|
||||
'Development Status :: 3 - Alpha',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.2',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Topic :: Software Development :: Build Tools',
|
||||
],
|
||||
'description': 'A sample Python project\n'
|
||||
'=======================\n'
|
||||
'\n'
|
||||
'This is the description file for the project.\n'
|
||||
'\n'
|
||||
'The file should use UTF-8 encoding and be written using ReStructured '
|
||||
'Text. It\n'
|
||||
'will be used to generate the project webpage on PyPI, and should be '
|
||||
'written for\n'
|
||||
'that purpose.\n'
|
||||
'\n'
|
||||
'Typical contents for this file would include an overview of the project, '
|
||||
'basic\n'
|
||||
'usage examples, etc. Generally, including the project changelog in here '
|
||||
'is not\n'
|
||||
'a good idea, although a simple "What\'s New" section for the most recent '
|
||||
'version\n'
|
||||
'may be appropriate.',
|
||||
'description_content_type': None,
|
||||
'docs_url': None,
|
||||
'download_url': 'UNKNOWN',
|
||||
'downloads': {'last_day': -1, 'last_month': -1, 'last_week': -1},
|
||||
'dynamic': None,
|
||||
'home_page': 'https://github.com/pypa/sampleproject',
|
||||
'keywords': 'sample setuptools development',
|
||||
'license': 'MIT',
|
||||
'license_expression': None,
|
||||
'license_files': None,
|
||||
'maintainer': None,
|
||||
'maintainer_email': None,
|
||||
'name': 'sampleproject',
|
||||
'package_url': 'https://pypi.org/project/sampleproject/',
|
||||
'platform': 'UNKNOWN',
|
||||
'project_url': 'https://pypi.org/project/sampleproject/',
|
||||
'project_urls': {
|
||||
'Download': 'UNKNOWN',
|
||||
'Homepage': 'https://github.com/pypa/sampleproject',
|
||||
},
|
||||
'provides_extra': None,
|
||||
'release_url': 'https://pypi.org/project/sampleproject/1.2.0/',
|
||||
'requires_dist': None,
|
||||
'requires_python': None,
|
||||
'summary': 'A sample Python project',
|
||||
'version': '1.2.0',
|
||||
'yanked': False,
|
||||
'yanked_reason': None,
|
||||
}
|
||||
|
||||
@@ -1507,6 +1507,16 @@ platform
|
||||
(Contributed by Alexey Makridenko in :gh:`133604`.)
|
||||
|
||||
|
||||
pprint
|
||||
------
|
||||
|
||||
* Add an *expand* keyword argument for :func:`pprint.pprint`,
|
||||
:func:`pprint.pformat`, :func:`pprint.pp`. If true, the output will be
|
||||
formatted similar to pretty-printed :func:`json.dumps` when
|
||||
*indent* is supplied.
|
||||
(Contributed by Stefan Todoran and Semyon Moroz in :gh:`112632`.)
|
||||
|
||||
|
||||
sre_*
|
||||
-----
|
||||
|
||||
|
||||
+193
-81
@@ -44,20 +44,22 @@ __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr",
|
||||
|
||||
|
||||
def pprint(object, stream=None, indent=1, width=80, depth=None, *,
|
||||
compact=False, sort_dicts=True, underscore_numbers=False):
|
||||
compact=False, expand=False, sort_dicts=True,
|
||||
underscore_numbers=False):
|
||||
"""Pretty-print a Python object to a stream [default is sys.stdout]."""
|
||||
printer = PrettyPrinter(
|
||||
stream=stream, indent=indent, width=width, depth=depth,
|
||||
compact=compact, sort_dicts=sort_dicts,
|
||||
compact=compact, expand=expand, sort_dicts=sort_dicts,
|
||||
underscore_numbers=underscore_numbers)
|
||||
printer.pprint(object)
|
||||
|
||||
|
||||
def pformat(object, indent=1, width=80, depth=None, *,
|
||||
compact=False, sort_dicts=True, underscore_numbers=False):
|
||||
compact=False, expand=False, sort_dicts=True,
|
||||
underscore_numbers=False):
|
||||
"""Format a Python object into a pretty-printed representation."""
|
||||
return PrettyPrinter(indent=indent, width=width, depth=depth,
|
||||
compact=compact, sort_dicts=sort_dicts,
|
||||
compact=compact, expand=expand, sort_dicts=sort_dicts,
|
||||
underscore_numbers=underscore_numbers).pformat(object)
|
||||
|
||||
|
||||
@@ -111,7 +113,8 @@ def _safe_tuple(t):
|
||||
|
||||
class PrettyPrinter:
|
||||
def __init__(self, indent=1, width=80, depth=None, stream=None, *,
|
||||
compact=False, sort_dicts=True, underscore_numbers=False):
|
||||
compact=False, expand=False, sort_dicts=True,
|
||||
underscore_numbers=False):
|
||||
"""Handle pretty printing operations onto a stream using a set of
|
||||
configured parameters.
|
||||
|
||||
@@ -130,6 +133,12 @@ class PrettyPrinter:
|
||||
|
||||
compact
|
||||
If true, several items will be combined in one line.
|
||||
Incompatible with expand mode.
|
||||
|
||||
expand
|
||||
If true, the output will be formatted similar to
|
||||
pretty-printed json.dumps() when ``indent`` is supplied.
|
||||
Incompatible with compact mode.
|
||||
|
||||
sort_dicts
|
||||
If true, dict keys are sorted.
|
||||
@@ -146,6 +155,8 @@ class PrettyPrinter:
|
||||
raise ValueError('depth must be > 0')
|
||||
if not width:
|
||||
raise ValueError('width must be != 0')
|
||||
if compact and expand:
|
||||
raise ValueError('compact and expand are incompatible')
|
||||
self._depth = depth
|
||||
self._indent_per_level = indent
|
||||
self._width = width
|
||||
@@ -154,6 +165,7 @@ class PrettyPrinter:
|
||||
else:
|
||||
self._stream = _sys.stdout
|
||||
self._compact = bool(compact)
|
||||
self._expand = bool(expand)
|
||||
self._sort_dicts = sort_dicts
|
||||
self._underscore_numbers = underscore_numbers
|
||||
|
||||
@@ -205,24 +217,48 @@ class PrettyPrinter:
|
||||
return
|
||||
stream.write(rep)
|
||||
|
||||
def _format_block_start(self, start_str, indent):
|
||||
if self._expand:
|
||||
return f"{start_str}\n{' ' * indent}"
|
||||
return start_str
|
||||
|
||||
def _format_block_end(self, end_str, indent):
|
||||
if self._expand:
|
||||
return f"\n{' ' * indent}{end_str}"
|
||||
return end_str
|
||||
|
||||
def _child_indent(self, indent, prefix_len):
|
||||
if self._expand:
|
||||
return indent
|
||||
return indent + prefix_len
|
||||
|
||||
def _write_indent_padding(self, write):
|
||||
if self._expand:
|
||||
if self._indent_per_level > 0:
|
||||
write(self._indent_per_level * " ")
|
||||
elif self._indent_per_level > 1:
|
||||
write((self._indent_per_level - 1) * " ")
|
||||
|
||||
def _pprint_dataclass(self, object, stream, indent, allowance, context, level):
|
||||
# Lazy import to improve module import time
|
||||
from dataclasses import fields as dataclass_fields
|
||||
|
||||
cls_name = object.__class__.__name__
|
||||
indent += len(cls_name) + 1
|
||||
if self._expand:
|
||||
indent += self._indent_per_level
|
||||
else:
|
||||
indent += len(cls_name) + 1
|
||||
items = [(f.name, getattr(object, f.name)) for f in dataclass_fields(object) if f.repr]
|
||||
stream.write(cls_name + '(')
|
||||
stream.write(self._format_block_start(cls_name + '(', indent))
|
||||
self._format_namespace_items(items, stream, indent, allowance, context, level)
|
||||
stream.write(')')
|
||||
stream.write(self._format_block_end(')', indent - self._indent_per_level))
|
||||
|
||||
_dispatch = {}
|
||||
|
||||
def _pprint_dict(self, object, stream, indent, allowance, context, level):
|
||||
write = stream.write
|
||||
write('{')
|
||||
if self._indent_per_level > 1:
|
||||
write((self._indent_per_level - 1) * ' ')
|
||||
write(self._format_block_start('{', indent))
|
||||
self._write_indent_padding(write)
|
||||
length = len(object)
|
||||
if length:
|
||||
if self._sort_dicts:
|
||||
@@ -231,21 +267,33 @@ class PrettyPrinter:
|
||||
items = object.items()
|
||||
self._format_dict_items(items, stream, indent, allowance + 1,
|
||||
context, level)
|
||||
write('}')
|
||||
write(self._format_block_end('}', indent))
|
||||
|
||||
_dispatch[dict.__repr__] = _pprint_dict
|
||||
|
||||
def _pprint_frozendict(self, object, stream, indent, allowance, context, level):
|
||||
write = stream.write
|
||||
cls = object.__class__
|
||||
write(cls.__name__ + '(')
|
||||
length = len(object)
|
||||
if length:
|
||||
self._pprint_dict(object, stream,
|
||||
indent + len(cls.__name__) + 1,
|
||||
allowance + 1,
|
||||
context, level)
|
||||
write(')')
|
||||
if not len(object):
|
||||
write(repr(object))
|
||||
return
|
||||
|
||||
write(self._format_block_start(cls.__name__ + "({", indent))
|
||||
self._write_indent_padding(write)
|
||||
|
||||
if self._sort_dicts:
|
||||
items = sorted(object.items(), key=_safe_tuple)
|
||||
else:
|
||||
items = object.items()
|
||||
self._format_dict_items(
|
||||
items,
|
||||
stream,
|
||||
self._child_indent(indent, len(cls.__name__) + 1),
|
||||
allowance + 2,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
write(self._format_block_end("})", indent))
|
||||
|
||||
_dispatch[frozendict.__repr__] = _pprint_frozendict
|
||||
|
||||
@@ -255,9 +303,14 @@ class PrettyPrinter:
|
||||
return
|
||||
cls = object.__class__
|
||||
stream.write(cls.__name__ + '(')
|
||||
self._format(list(object.items()), stream,
|
||||
indent + len(cls.__name__) + 1, allowance + 1,
|
||||
context, level)
|
||||
self._format(
|
||||
list(object.items()),
|
||||
stream,
|
||||
self._child_indent(indent, len(cls.__name__) + 1),
|
||||
allowance + 1,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
stream.write(')')
|
||||
|
||||
_dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict
|
||||
@@ -268,19 +321,21 @@ class PrettyPrinter:
|
||||
key = _safe_tuple
|
||||
else:
|
||||
key = _safe_key
|
||||
|
||||
write = stream.write
|
||||
write(object.__class__.__name__ + '([')
|
||||
if self._indent_per_level > 1:
|
||||
write((self._indent_per_level - 1) * ' ')
|
||||
length = len(object)
|
||||
if length:
|
||||
write(
|
||||
self._format_block_start(object.__class__.__name__ + "([", indent)
|
||||
)
|
||||
|
||||
if len(object):
|
||||
if self._sort_dicts:
|
||||
entries = sorted(object, key=key)
|
||||
else:
|
||||
entries = object
|
||||
self._format_items(entries, stream, indent, allowance + 1,
|
||||
context, level)
|
||||
write('])')
|
||||
self._format_items(
|
||||
entries, stream, indent, allowance + 2, context, level
|
||||
)
|
||||
write(self._format_block_end("])", indent))
|
||||
|
||||
def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, level):
|
||||
"""Pretty print mapping views from collections.abc."""
|
||||
@@ -306,19 +361,22 @@ class PrettyPrinter:
|
||||
_collections.abc.MappingView)}
|
||||
|
||||
def _pprint_list(self, object, stream, indent, allowance, context, level):
|
||||
stream.write('[')
|
||||
stream.write(self._format_block_start('[', indent))
|
||||
self._format_items(object, stream, indent, allowance + 1,
|
||||
context, level)
|
||||
stream.write(']')
|
||||
stream.write(self._format_block_end(']', indent))
|
||||
|
||||
_dispatch[list.__repr__] = _pprint_list
|
||||
|
||||
def _pprint_tuple(self, object, stream, indent, allowance, context, level):
|
||||
stream.write('(')
|
||||
endchar = ',)' if len(object) == 1 else ')'
|
||||
stream.write(self._format_block_start('(', indent))
|
||||
if len(object) == 1 and not self._expand:
|
||||
endchar = ',)'
|
||||
else:
|
||||
endchar = ')'
|
||||
self._format_items(object, stream, indent, allowance + len(endchar),
|
||||
context, level)
|
||||
stream.write(endchar)
|
||||
stream.write(self._format_block_end(endchar, indent))
|
||||
|
||||
_dispatch[tuple.__repr__] = _pprint_tuple
|
||||
|
||||
@@ -328,16 +386,17 @@ class PrettyPrinter:
|
||||
return
|
||||
typ = object.__class__
|
||||
if typ is set:
|
||||
stream.write('{')
|
||||
stream.write(self._format_block_start('{', indent))
|
||||
endchar = '}'
|
||||
else:
|
||||
stream.write(typ.__name__ + '({')
|
||||
stream.write(self._format_block_start(typ.__name__ + '({', indent))
|
||||
endchar = '})'
|
||||
indent += len(typ.__name__) + 1
|
||||
if not self._expand:
|
||||
indent += len(typ.__name__) + 1
|
||||
object = sorted(object, key=_safe_key)
|
||||
self._format_items(object, stream, indent, allowance + len(endchar),
|
||||
context, level)
|
||||
stream.write(endchar)
|
||||
stream.write(self._format_block_end(endchar, indent))
|
||||
|
||||
_dispatch[set.__repr__] = _pprint_set
|
||||
_dispatch[frozenset.__repr__] = _pprint_set
|
||||
@@ -350,7 +409,10 @@ class PrettyPrinter:
|
||||
chunks = []
|
||||
lines = object.splitlines(True)
|
||||
if level == 1:
|
||||
indent += 1
|
||||
if self._expand:
|
||||
indent += self._indent_per_level
|
||||
else:
|
||||
indent += 1
|
||||
allowance += 1
|
||||
max_width1 = max_width = self._width - indent
|
||||
for i, line in enumerate(lines):
|
||||
@@ -386,13 +448,13 @@ class PrettyPrinter:
|
||||
write(rep)
|
||||
return
|
||||
if level == 1:
|
||||
write('(')
|
||||
write(self._format_block_start("(", indent))
|
||||
for i, rep in enumerate(chunks):
|
||||
if i > 0:
|
||||
write('\n' + ' '*indent)
|
||||
write(rep)
|
||||
if level == 1:
|
||||
write(')')
|
||||
write(self._format_block_end(")", indent - self._indent_per_level))
|
||||
|
||||
_dispatch[str.__repr__] = _pprint_str
|
||||
|
||||
@@ -403,9 +465,12 @@ class PrettyPrinter:
|
||||
return
|
||||
parens = level == 1
|
||||
if parens:
|
||||
indent += 1
|
||||
if self._expand:
|
||||
indent += self._indent_per_level
|
||||
else:
|
||||
indent += 1
|
||||
allowance += 1
|
||||
write('(')
|
||||
write(self._format_block_start('(', indent))
|
||||
delim = ''
|
||||
for rep in _wrap_bytes_repr(object, self._width - indent, allowance):
|
||||
write(delim)
|
||||
@@ -413,23 +478,34 @@ class PrettyPrinter:
|
||||
if not delim:
|
||||
delim = '\n' + ' '*indent
|
||||
if parens:
|
||||
write(')')
|
||||
write(self._format_block_end(')', indent - self._indent_per_level))
|
||||
|
||||
_dispatch[bytes.__repr__] = _pprint_bytes
|
||||
|
||||
def _pprint_bytearray(self, object, stream, indent, allowance, context, level):
|
||||
write = stream.write
|
||||
write('bytearray(')
|
||||
self._pprint_bytes(bytes(object), stream, indent + 10,
|
||||
write(self._format_block_start('bytearray(', indent))
|
||||
if self._expand:
|
||||
write(' ' * self._indent_per_level)
|
||||
recursive_indent = indent + self._indent_per_level
|
||||
else:
|
||||
recursive_indent = indent + 10
|
||||
self._pprint_bytes(bytes(object), stream, recursive_indent,
|
||||
allowance + 1, context, level + 1)
|
||||
write(')')
|
||||
write(self._format_block_end(')', indent))
|
||||
|
||||
_dispatch[bytearray.__repr__] = _pprint_bytearray
|
||||
|
||||
def _pprint_mappingproxy(self, object, stream, indent, allowance, context, level):
|
||||
stream.write('mappingproxy(')
|
||||
self._format(object.copy(), stream, indent + 13, allowance + 1,
|
||||
context, level)
|
||||
self._format(
|
||||
object.copy(),
|
||||
stream,
|
||||
self._child_indent(indent, 13),
|
||||
allowance + 1,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
stream.write(')')
|
||||
|
||||
_dispatch[_types.MappingProxyType.__repr__] = _pprint_mappingproxy
|
||||
@@ -441,11 +517,15 @@ class PrettyPrinter:
|
||||
cls_name = 'namespace'
|
||||
else:
|
||||
cls_name = object.__class__.__name__
|
||||
indent += len(cls_name) + 1
|
||||
if self._expand:
|
||||
indent += self._indent_per_level
|
||||
else:
|
||||
indent += len(cls_name) + 1
|
||||
items = object.__dict__.items()
|
||||
stream.write(cls_name + '(')
|
||||
self._format_namespace_items(items, stream, indent, allowance, context, level)
|
||||
stream.write(')')
|
||||
stream.write(self._format_block_start(cls_name + '(', indent))
|
||||
self._format_namespace_items(items, stream, indent, allowance, context,
|
||||
level)
|
||||
stream.write(self._format_block_end(')', indent - self._indent_per_level))
|
||||
|
||||
_dispatch[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace
|
||||
|
||||
@@ -460,11 +540,18 @@ class PrettyPrinter:
|
||||
rep = self._repr(key, context, level)
|
||||
write(rep)
|
||||
write(': ')
|
||||
self._format(ent, stream, indent + len(rep) + 2,
|
||||
allowance if last else 1,
|
||||
context, level)
|
||||
self._format(
|
||||
ent,
|
||||
stream,
|
||||
self._child_indent(indent, len(rep) + 2),
|
||||
allowance if last else 1,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
if not last:
|
||||
write(delimnl)
|
||||
elif self._expand:
|
||||
write(',')
|
||||
|
||||
def _format_namespace_items(self, items, stream, indent, allowance, context, level):
|
||||
write = stream.write
|
||||
@@ -479,17 +566,23 @@ class PrettyPrinter:
|
||||
# recursive dataclass repr.
|
||||
write("...")
|
||||
else:
|
||||
self._format(ent, stream, indent + len(key) + 1,
|
||||
allowance if last else 1,
|
||||
context, level)
|
||||
self._format(
|
||||
ent,
|
||||
stream,
|
||||
self._child_indent(indent, len(key) + 1),
|
||||
allowance if last else 1,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
if not last:
|
||||
write(delimnl)
|
||||
elif self._expand:
|
||||
write(',')
|
||||
|
||||
def _format_items(self, items, stream, indent, allowance, context, level):
|
||||
write = stream.write
|
||||
indent += self._indent_per_level
|
||||
if self._indent_per_level > 1:
|
||||
write((self._indent_per_level - 1) * ' ')
|
||||
self._write_indent_padding(write)
|
||||
delimnl = ',\n' + ' ' * indent
|
||||
delim = ''
|
||||
width = max_width = self._width - indent + 1
|
||||
@@ -525,6 +618,8 @@ class PrettyPrinter:
|
||||
self._format(ent, stream, indent,
|
||||
allowance if last else 1,
|
||||
context, level)
|
||||
if last and self._expand:
|
||||
write(',')
|
||||
|
||||
def _repr(self, object, context, level):
|
||||
repr, readable, recursive = self.format(object, context.copy(),
|
||||
@@ -548,9 +643,13 @@ class PrettyPrinter:
|
||||
return
|
||||
rdf = self._repr(object.default_factory, context, level)
|
||||
cls = object.__class__
|
||||
indent += len(cls.__name__) + 1
|
||||
stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
|
||||
self._pprint_dict(object, stream, indent, allowance + 1, context, level)
|
||||
if self._expand:
|
||||
stream.write('%s(%s, ' % (cls.__name__, rdf))
|
||||
else:
|
||||
indent += len(cls.__name__) + 1
|
||||
stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent))
|
||||
self._pprint_dict(object, stream, indent, allowance + 1, context,
|
||||
level)
|
||||
stream.write(')')
|
||||
|
||||
_dispatch[_collections.defaultdict.__repr__] = _pprint_default_dict
|
||||
@@ -560,14 +659,18 @@ class PrettyPrinter:
|
||||
stream.write(repr(object))
|
||||
return
|
||||
cls = object.__class__
|
||||
stream.write(cls.__name__ + '({')
|
||||
if self._indent_per_level > 1:
|
||||
stream.write((self._indent_per_level - 1) * ' ')
|
||||
stream.write(self._format_block_start(cls.__name__ + '({', indent))
|
||||
self._write_indent_padding(stream.write)
|
||||
items = object.most_common()
|
||||
self._format_dict_items(items, stream,
|
||||
indent + len(cls.__name__) + 1, allowance + 2,
|
||||
context, level)
|
||||
stream.write('})')
|
||||
self._format_dict_items(
|
||||
items,
|
||||
stream,
|
||||
self._child_indent(indent, len(cls.__name__) + 1),
|
||||
allowance + 2,
|
||||
context,
|
||||
level,
|
||||
)
|
||||
stream.write(self._format_block_end('})', indent))
|
||||
|
||||
_dispatch[_collections.Counter.__repr__] = _pprint_counter
|
||||
|
||||
@@ -576,12 +679,18 @@ class PrettyPrinter:
|
||||
stream.write(repr(object))
|
||||
return
|
||||
cls = object.__class__
|
||||
stream.write(cls.__name__ + '(')
|
||||
indent += len(cls.__name__) + 1
|
||||
stream.write(self._format_block_start(cls.__name__ + '(',
|
||||
indent + self._indent_per_level))
|
||||
if self._expand:
|
||||
indent += self._indent_per_level
|
||||
else:
|
||||
indent += len(cls.__name__) + 1
|
||||
for i, m in enumerate(object.maps):
|
||||
if i == len(object.maps) - 1:
|
||||
self._format(m, stream, indent, allowance + 1, context, level)
|
||||
stream.write(')')
|
||||
if self._expand:
|
||||
stream.write(',')
|
||||
stream.write(self._format_block_end(')', indent - self._indent_per_level))
|
||||
else:
|
||||
self._format(m, stream, indent, 1, context, level)
|
||||
stream.write(',\n' + ' ' * indent)
|
||||
@@ -593,18 +702,21 @@ class PrettyPrinter:
|
||||
stream.write(repr(object))
|
||||
return
|
||||
cls = object.__class__
|
||||
stream.write(cls.__name__ + '(')
|
||||
indent += len(cls.__name__) + 1
|
||||
stream.write('[')
|
||||
stream.write(self._format_block_start(cls.__name__ + '([', indent))
|
||||
if not self._expand:
|
||||
indent += len(cls.__name__) + 1
|
||||
if object.maxlen is None:
|
||||
self._format_items(object, stream, indent, allowance + 2,
|
||||
context, level)
|
||||
stream.write('])')
|
||||
stream.write(self._format_block_end('])', indent))
|
||||
else:
|
||||
self._format_items(object, stream, indent, 2,
|
||||
context, level)
|
||||
rml = self._repr(object.maxlen, context, level)
|
||||
stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
|
||||
if self._expand:
|
||||
stream.write('%s], maxlen=%s)' % ('\n' + ' ' * indent, rml))
|
||||
else:
|
||||
stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml))
|
||||
|
||||
_dispatch[_collections.deque.__repr__] = _pprint_deque
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ class QueryTestCase(unittest.TestCase):
|
||||
self.assertRaises(ValueError, pprint.PrettyPrinter, depth=0)
|
||||
self.assertRaises(ValueError, pprint.PrettyPrinter, depth=-1)
|
||||
self.assertRaises(ValueError, pprint.PrettyPrinter, width=0)
|
||||
self.assertRaises(ValueError, pprint.PrettyPrinter, compact=True, expand=True)
|
||||
|
||||
def test_basic(self):
|
||||
# Verify .isrecursive() and .isreadable() w/o recursion
|
||||
@@ -1298,6 +1299,12 @@ Counter({'s': 6,
|
||||
'e': 4,
|
||||
'n': 2,
|
||||
'l': 1})""")
|
||||
self.assertEqual(pprint.pformat(d, indent=2, width=1),
|
||||
"""\
|
||||
Counter({ 's': 6,
|
||||
'e': 4,
|
||||
'n': 2,
|
||||
'l': 1})""")
|
||||
|
||||
def test_chainmap(self):
|
||||
d = collections.ChainMap()
|
||||
@@ -1508,6 +1515,457 @@ ValuesView({'a': 6,
|
||||
'jumped over a '
|
||||
'lazy dog'}""")
|
||||
|
||||
def test_expand_dataclass(self):
|
||||
@dataclasses.dataclass
|
||||
class DummyDataclass:
|
||||
foo: str
|
||||
bar: float
|
||||
baz: bool
|
||||
qux: dict = dataclasses.field(default_factory=dict)
|
||||
quux: list = dataclasses.field(default_factory=list)
|
||||
corge: int = 1
|
||||
garply: tuple = (1, 2, 3, 4)
|
||||
dummy_dataclass = DummyDataclass(
|
||||
foo="foo",
|
||||
bar=1.2,
|
||||
baz=False,
|
||||
qux={"foo": "bar", "baz": 123},
|
||||
quux=["foo", "bar", "baz"],
|
||||
corge=7,
|
||||
garply=(1, 2, 3, 4),
|
||||
)
|
||||
self.assertEqual(pprint.pformat(dummy_dataclass, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
DummyDataclass(
|
||||
foo='foo',
|
||||
bar=1.2,
|
||||
baz=False,
|
||||
qux={'baz': 123, 'foo': 'bar'},
|
||||
quux=['foo', 'bar', 'baz'],
|
||||
corge=7,
|
||||
garply=(1, 2, 3, 4),
|
||||
)""")
|
||||
|
||||
def test_expand_dict(self):
|
||||
dummy_dict = {
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"qux": {"foo": "bar", "baz": 123},
|
||||
"quux": ["foo", "bar", "baz"],
|
||||
"corge": 7,
|
||||
}
|
||||
self.assertEqual(pprint.pformat(dummy_dict, width=40, indent=4,
|
||||
expand=True, sort_dicts=False),
|
||||
"""\
|
||||
{
|
||||
'foo': 'bar',
|
||||
'baz': 123,
|
||||
'qux': {'foo': 'bar', 'baz': 123},
|
||||
'quux': ['foo', 'bar', 'baz'],
|
||||
'corge': 7,
|
||||
}""")
|
||||
|
||||
def test_expand_ordered_dict(self):
|
||||
dummy_ordered_dict = collections.OrderedDict(
|
||||
[
|
||||
("foo", 1),
|
||||
("bar", 12),
|
||||
("baz", 123),
|
||||
]
|
||||
)
|
||||
self.assertEqual(pprint.pformat(dummy_ordered_dict, width=20, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
OrderedDict([
|
||||
('foo', 1),
|
||||
('bar', 12),
|
||||
('baz', 123),
|
||||
])""")
|
||||
|
||||
def test_expand_list(self):
|
||||
dummy_list = [
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
"qux",
|
||||
]
|
||||
self.assertEqual(pprint.pformat(dummy_list, width=20, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
[
|
||||
'foo',
|
||||
'bar',
|
||||
'baz',
|
||||
'qux',
|
||||
]""")
|
||||
|
||||
def test_expand_tuple(self):
|
||||
dummy_tuple = (
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
)
|
||||
self.assertEqual(pprint.pformat(dummy_tuple, width=20, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
(
|
||||
'foo',
|
||||
'bar',
|
||||
'baz',
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
)""")
|
||||
|
||||
def test_expand_single_element_tuple(self):
|
||||
self.assertEqual(
|
||||
pprint.pformat((1,), width=1, indent=4, expand=True),
|
||||
"""\
|
||||
(
|
||||
1,
|
||||
)""")
|
||||
|
||||
def test_expand_set(self):
|
||||
dummy_set = {
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
"qux",
|
||||
(1, 2, 3),
|
||||
}
|
||||
self.assertEqual(pprint.pformat(dummy_set, width=20, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
{
|
||||
'bar',
|
||||
'baz',
|
||||
'foo',
|
||||
'qux',
|
||||
(1, 2, 3),
|
||||
}""")
|
||||
|
||||
def test_expand_frozenset(self):
|
||||
dummy_set = {
|
||||
(1, 2, 3),
|
||||
}
|
||||
dummy_frozenset = frozenset(
|
||||
{
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
(1, 2, 3),
|
||||
frozenset(dummy_set),
|
||||
}
|
||||
)
|
||||
self.assertEqual(pprint.pformat(dummy_frozenset, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
frozenset({
|
||||
frozenset({(1, 2, 3)}),
|
||||
'bar',
|
||||
'baz',
|
||||
'foo',
|
||||
(1, 2, 3),
|
||||
})""")
|
||||
|
||||
def test_expand_frozendict(self):
|
||||
dummy_frozendict = frozendict(
|
||||
{"foo": "bar", "baz": 123, "qux": [1, 2]}
|
||||
)
|
||||
self.assertEqual(
|
||||
pprint.pformat(dummy_frozendict, width=20, indent=4, expand=True),
|
||||
"""\
|
||||
frozendict({
|
||||
'baz': 123,
|
||||
'foo': 'bar',
|
||||
'qux': [1, 2],
|
||||
})""",
|
||||
)
|
||||
|
||||
def test_expand_bytes(self):
|
||||
dummy_bytes = b"Hello world! foo bar baz 123 456 789"
|
||||
self.assertEqual(pprint.pformat(dummy_bytes, width=20, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
(
|
||||
b'Hello world!'
|
||||
b' foo bar baz'
|
||||
b' 123 456 789'
|
||||
)""")
|
||||
|
||||
def test_expand_bytearray(self):
|
||||
dummy_bytes = b"Hello world! foo bar baz 123 456 789"
|
||||
dummy_byte_array = bytearray(dummy_bytes)
|
||||
self.assertEqual(pprint.pformat(dummy_byte_array, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
bytearray(
|
||||
b'Hello world! foo bar baz 123 456'
|
||||
b' 789'
|
||||
)""")
|
||||
|
||||
def test_expand_mappingproxy(self):
|
||||
dummy_dict = {
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"qux": {"foo": "bar", "baz": 123},
|
||||
"quux": ["foo", "bar", "baz"],
|
||||
"corge": 7,
|
||||
}
|
||||
dummy_mappingproxy = types.MappingProxyType(dummy_dict)
|
||||
self.assertEqual(pprint.pformat(dummy_mappingproxy, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
mappingproxy({
|
||||
'baz': 123,
|
||||
'corge': 7,
|
||||
'foo': 'bar',
|
||||
'quux': ['foo', 'bar', 'baz'],
|
||||
'qux': {'baz': 123, 'foo': 'bar'},
|
||||
})""")
|
||||
|
||||
def test_expand_namespace(self):
|
||||
dummy_namespace = types.SimpleNamespace(
|
||||
foo="bar",
|
||||
bar=42,
|
||||
baz=types.SimpleNamespace(
|
||||
x=321,
|
||||
y="string",
|
||||
d={"foo": True, "bar": "baz"},
|
||||
),
|
||||
)
|
||||
|
||||
self.assertEqual(pprint.pformat(dummy_namespace, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
namespace(
|
||||
foo='bar',
|
||||
bar=42,
|
||||
baz=namespace(
|
||||
x=321,
|
||||
y='string',
|
||||
d={'bar': 'baz', 'foo': True},
|
||||
),
|
||||
)""")
|
||||
|
||||
def test_expand_defaultdict(self):
|
||||
dummy_defaultdict = collections.defaultdict(list)
|
||||
dummy_defaultdict["foo"].append("bar")
|
||||
dummy_defaultdict["foo"].append("baz")
|
||||
dummy_defaultdict["foo"].append("qux")
|
||||
dummy_defaultdict["bar"] = {"foo": "bar", "baz": None}
|
||||
self.assertEqual(pprint.pformat(dummy_defaultdict, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
defaultdict(<class 'list'>, {
|
||||
'bar': {'baz': None, 'foo': 'bar'},
|
||||
'foo': ['bar', 'baz', 'qux'],
|
||||
})""")
|
||||
|
||||
def test_expand_counter(self):
|
||||
dummy_counter = collections.Counter("abcdeabcdabcaba")
|
||||
expected = """\
|
||||
Counter({
|
||||
'a': 5,
|
||||
'b': 4,
|
||||
'c': 3,
|
||||
'd': 2,
|
||||
'e': 1,
|
||||
})"""
|
||||
self.assertEqual(pprint.pformat(dummy_counter, width=40, indent=4,
|
||||
expand=True), expected)
|
||||
|
||||
expected2 = """\
|
||||
Counter({
|
||||
'a': 5,
|
||||
'b': 4,
|
||||
'c': 3,
|
||||
'd': 2,
|
||||
'e': 1,
|
||||
})"""
|
||||
self.assertEqual(pprint.pformat(dummy_counter, width=20, indent=2,
|
||||
expand=True), expected2)
|
||||
|
||||
def test_expand_chainmap(self):
|
||||
dummy_dict = {
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"qux": {"foo": "bar", "baz": 123},
|
||||
"quux": ["foo", "bar", "baz"],
|
||||
"corge": 7,
|
||||
}
|
||||
dummy_chainmap = collections.ChainMap(
|
||||
{"foo": "bar"},
|
||||
{"baz": "qux"},
|
||||
{"corge": dummy_dict},
|
||||
)
|
||||
dummy_chainmap.maps.append({"garply": "waldo"})
|
||||
self.assertEqual(pprint.pformat(dummy_chainmap, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
ChainMap(
|
||||
{'foo': 'bar'},
|
||||
{'baz': 'qux'},
|
||||
{
|
||||
'corge': {
|
||||
'baz': 123,
|
||||
'corge': 7,
|
||||
'foo': 'bar',
|
||||
'quux': ['foo', 'bar', 'baz'],
|
||||
'qux': {
|
||||
'baz': 123,
|
||||
'foo': 'bar',
|
||||
},
|
||||
},
|
||||
},
|
||||
{'garply': 'waldo'},
|
||||
)""")
|
||||
|
||||
def test_expand_deque(self):
|
||||
dummy_dict = {
|
||||
"foo": "bar",
|
||||
"baz": 123,
|
||||
"qux": {"foo": "bar", "baz": 123},
|
||||
"quux": ["foo", "bar", "baz"],
|
||||
"corge": 7,
|
||||
}
|
||||
dummy_list = [
|
||||
"foo",
|
||||
"bar",
|
||||
"baz",
|
||||
]
|
||||
dummy_set = {
|
||||
(1, 2, 3),
|
||||
}
|
||||
dummy_deque = collections.deque(maxlen=10)
|
||||
dummy_deque.append("foo")
|
||||
dummy_deque.append(123)
|
||||
dummy_deque.append(dummy_dict)
|
||||
dummy_deque.extend(dummy_list)
|
||||
dummy_deque.appendleft(dummy_set)
|
||||
self.assertEqual(pprint.pformat(dummy_deque, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
deque([
|
||||
{(1, 2, 3)},
|
||||
'foo',
|
||||
123,
|
||||
{
|
||||
'baz': 123,
|
||||
'corge': 7,
|
||||
'foo': 'bar',
|
||||
'quux': ['foo', 'bar', 'baz'],
|
||||
'qux': {'baz': 123, 'foo': 'bar'},
|
||||
},
|
||||
'foo',
|
||||
'bar',
|
||||
'baz',
|
||||
], maxlen=10)""")
|
||||
|
||||
def test_expand_userdict(self):
|
||||
class DummyUserDict(collections.UserDict):
|
||||
"""A custom UserDict with some extra attributes"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.access_count = 0
|
||||
dummy_userdict = DummyUserDict({ "foo": "bar", "baz": 123,
|
||||
"qux": {"foo": "bar", "baz": 123},
|
||||
"quux": ["foo", "bar", "baz"],
|
||||
"corge": 7 })
|
||||
dummy_userdict.access_count = 5
|
||||
|
||||
self.assertEqual(pprint.pformat(dummy_userdict, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
{
|
||||
'baz': 123,
|
||||
'corge': 7,
|
||||
'foo': 'bar',
|
||||
'quux': ['foo', 'bar', 'baz'],
|
||||
'qux': {'baz': 123, 'foo': 'bar'},
|
||||
}""")
|
||||
|
||||
def test_expand_userlist(self):
|
||||
class DummyUserList(collections.UserList):
|
||||
"""A custom UserList with some extra attributes"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.description = "foo"
|
||||
dummy_userlist = DummyUserList(["first", 2, {"key": "value"},
|
||||
[4, 5, 6]])
|
||||
|
||||
self.assertEqual(pprint.pformat(dummy_userlist, width=40, indent=4,
|
||||
expand=True),
|
||||
"""\
|
||||
[
|
||||
'first',
|
||||
2,
|
||||
{'key': 'value'},
|
||||
[4, 5, 6],
|
||||
]""")
|
||||
|
||||
def test_expand_dict_keys(self):
|
||||
d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5}
|
||||
self.assertEqual(
|
||||
pprint.pformat(d.keys(), width=20, indent=4, expand=True),
|
||||
"""\
|
||||
dict_keys([
|
||||
'bar',
|
||||
'baz',
|
||||
'foo',
|
||||
'quux',
|
||||
'qux',
|
||||
])""",
|
||||
)
|
||||
|
||||
def test_expand_dict_values(self):
|
||||
d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5}
|
||||
self.assertEqual(
|
||||
pprint.pformat(d.values(), width=20, indent=4, expand=True),
|
||||
"""\
|
||||
dict_values([
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
])""",
|
||||
)
|
||||
|
||||
def test_expand_dict_items(self):
|
||||
d = {"foo": 1, "bar": 2, "baz": 3, "qux": 4, "quux": 5}
|
||||
self.assertEqual(
|
||||
pprint.pformat(d.items(), width=20, indent=4, expand=True),
|
||||
"""\
|
||||
dict_items([
|
||||
('bar', 2),
|
||||
('baz', 3),
|
||||
('foo', 1),
|
||||
('quux', 5),
|
||||
('qux', 4),
|
||||
])""",
|
||||
)
|
||||
|
||||
def test_expand_str(self):
|
||||
s = "The quick brown fox jumped over the lazy dog " * 3
|
||||
self.assertEqual(
|
||||
pprint.pformat(s, width=40, indent=4, expand=True),
|
||||
"""\
|
||||
(
|
||||
'The quick brown fox jumped over '
|
||||
'the lazy dog The quick brown fox '
|
||||
'jumped over the lazy dog The '
|
||||
'quick brown fox jumped over the '
|
||||
'lazy dog '
|
||||
)""",
|
||||
)
|
||||
|
||||
|
||||
class DottedPrettyPrinter(pprint.PrettyPrinter):
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Add an *expand* keyword argument for :func:`pprint.pprint`,
|
||||
:func:`pprint.pformat`, :func:`pprint.pp` by passing on all *kwargs* and
|
||||
:class:`pprint.PrettyPrinter`. Contributed by Stefan Todoran and Semyon Moroz.
|
||||
Reference in New Issue
Block a user