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:
Semyon Moroz
2026-04-07 08:42:54 +04:00
committed by GitHub
parent 3df0c4da4c
commit 132abfd135
5 changed files with 768 additions and 86 deletions
+104 -5
View File
@@ -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,
}
+10
View File
@@ -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
View File
@@ -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
+458
View File
@@ -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.