diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2985f31bacb47a..6d751ab04b49dd 100644 --- a/Doc/library/pprint.rst +++ b/Doc/library/pprint.rst @@ -36,7 +36,8 @@ Functions --------- .. function:: pp(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=False, underscore_numbers=False) + block_style=False, compact=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 @@ -85,6 +86,12 @@ Functions integers will be formatted with the ``_`` character for a thousands separator, otherwise underscores are not displayed (the default). + :param bool block_style: + If ``True``, + opening parentheses and brackets will be followed by a newline and the + following content will be indented by one level, similar to block style + JSON formatting. This option is not compatible with *compact*. + >>> import pprint >>> stuff = ['spam', 'eggs', 'lumberjack', 'knights', 'ni'] >>> stuff.insert(0, stuff) @@ -100,7 +107,8 @@ Functions .. function:: pprint(object, stream=None, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + block_style=False, compact=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, @@ -108,10 +116,11 @@ Functions .. function:: pformat(object, indent=1, width=80, depth=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + block_style=False, compact=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*, *sort_dicts*, *underscore_numbers* and *block_style* are passed to the :class:`PrettyPrinter` constructor as formatting parameters and their meanings are as described in the documentation above. @@ -155,7 +164,8 @@ PrettyPrinter Objects .. index:: single: ...; placeholder .. class:: PrettyPrinter(indent=1, width=80, depth=None, stream=None, *, \ - compact=False, sort_dicts=True, underscore_numbers=False) + block_style=False, compact=False, sort_dicts=True, \ + underscore_numbers=False) Construct a :class:`PrettyPrinter` instance. @@ -179,6 +189,22 @@ PrettyPrinter Objects 'knights', 'ni'], 'spam', 'eggs', 'lumberjack', 'knights', 'ni'] + >>> pp = pprint.PrettyPrinter(width=41, block_style=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) @@ -198,6 +224,9 @@ PrettyPrinter Objects .. versionchanged:: 3.11 No longer attempts to write to :data:`!sys.stdout` if it is ``None``. + .. versionchanged:: next + Added the *block_style* parameter. + :class:`PrettyPrinter` instances have the following methods: @@ -420,3 +449,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 achieve block style formatting with the *block_style* parameter. +Best results are achieved with a higher *indent* value:: + + >>> pprint.pp(project_info, indent=4, block_style=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 + } diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 4d4fb77ad4f030..881c5827b78164 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -408,6 +408,16 @@ platform (Contributed by Alexey Makridenko in :gh:`133604`.) +pprint +------ + +* Add a *block_style* keyword argument for :func:`pprint.pprint`, + :func:`pprint.pformat`, :func:`pprint.pp`. If true, the output will be + formatted in a block style similar to pretty-printed :func:`json.dumps` when + *indent* is supplied. + (Contributed by Stefan Todoran and Semyon Moroz in :gh:`112632`.) + + sre_* ----- diff --git a/Lib/pprint.py b/Lib/pprint.py index 92a2c543ac279c..79711f601b0514 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -44,21 +44,24 @@ def pprint(object, stream=None, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + block_style=False, compact=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, - underscore_numbers=underscore_numbers) + underscore_numbers=underscore_numbers, block_style=block_style) printer.pprint(object) def pformat(object, indent=1, width=80, depth=None, *, - compact=False, sort_dicts=True, underscore_numbers=False): + block_style=False, compact=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, - underscore_numbers=underscore_numbers).pformat(object) + underscore_numbers=underscore_numbers, + block_style=block_style).pformat(object) def pp(object, *args, sort_dicts=False, **kwargs): @@ -111,7 +114,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): + block_style=False, compact=False, sort_dicts=True, + underscore_numbers=False): """Handle pretty printing operations onto a stream using a set of configured parameters. @@ -137,6 +141,11 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, underscore_numbers If true, digit groups are separated with underscores. + block_style + If true, the output will be formatted in a block style similar to + pretty-printed json.dumps() when ``indent`` is supplied. + Incompatible with compact mode. + """ indent = int(indent) width = int(width) @@ -146,6 +155,8 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, raise ValueError('depth must be > 0') if not width: raise ValueError('width must be != 0') + if compact and block_style: + raise ValueError('compact and block_style are incompatible') self._depth = depth self._indent_per_level = indent self._width = width @@ -156,6 +167,7 @@ def __init__(self, indent=1, width=80, depth=None, stream=None, *, self._compact = bool(compact) self._sort_dicts = sort_dicts self._underscore_numbers = underscore_numbers + self._block_style = bool(block_style) def pprint(self, object): if self._stream is not None: @@ -205,24 +217,39 @@ def _format(self, object, stream, indent, allowance, context, level): return stream.write(rep) + def _format_block_start(self, start_str, indent): + if self._block_style: + return f"{start_str}\n{' ' * indent}" + return start_str + + def _format_block_end(self, end_str, indent): + if self._block_style: + return f"\n{' ' * indent}{end_str}" + return end_str + 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._block_style: + 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._format_block_start('{', indent)) + if self._indent_per_level > 1 and not self._block_style: write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + write(self._indent_per_level * ' ') length = len(object) if length: if self._sort_dicts: @@ -231,7 +258,7 @@ def _pprint_dict(self, object, stream, indent, allowance, context, level): 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 @@ -241,9 +268,12 @@ def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level return cls = object.__class__ stream.write(cls.__name__ + '(') - self._format(list(object.items()), stream, - indent + len(cls.__name__) + 1, allowance + 1, - context, level) + if self._block_style: + recursive_indent = indent + else: + recursive_indent = indent + len(cls.__name__) + 1 + self._format(list(object.items()), stream, recursive_indent, + allowance + 1, context, level) stream.write(')') _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict @@ -292,19 +322,19 @@ def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, l _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('(') + stream.write(self._format_block_start('(', indent)) endchar = ',)' if len(object) == 1 else ')' 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 @@ -314,16 +344,17 @@ def _pprint_set(self, object, stream, indent, allowance, context, level): 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._block_style: + 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 @@ -389,9 +420,12 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): return parens = level == 1 if parens: - indent += 1 + if self._block_style: + 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) @@ -399,22 +433,31 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): 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._block_style: + 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, + if self._block_style: + recursive_indent = indent + else: + recursive_indent = indent + 13 + self._format(object.copy(), stream, recursive_indent, allowance + 1, context, level) stream.write(')') @@ -427,11 +470,15 @@ def _pprint_simplenamespace(self, object, stream, indent, allowance, context, le cls_name = 'namespace' else: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + if self._block_style: + 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 @@ -446,7 +493,11 @@ def _format_dict_items(self, items, stream, indent, allowance, context, rep = self._repr(key, context, level) write(rep) write(': ') - self._format(ent, stream, indent + len(rep) + 2, + if self._block_style: + recursive_indent = indent + else: + recursive_indent = indent + len(rep) + 2 + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -465,7 +516,11 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev # recursive dataclass repr. write("...") else: - self._format(ent, stream, indent + len(key) + 1, + if self._block_style: + recursive_indent = indent + else: + recursive_indent = indent + len(key) + 1 + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -474,8 +529,10 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev def _format_items(self, items, stream, indent, allowance, context, level): write = stream.write indent += self._indent_per_level - if self._indent_per_level > 1: + if self._indent_per_level > 1 and not self._block_style: write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + write(self._indent_per_level * ' ') delimnl = ',\n' + ' ' * indent delim = '' width = max_width = self._width - indent + 1 @@ -534,9 +591,13 @@ def _pprint_default_dict(self, object, stream, indent, allowance, context, level 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._block_style: + 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 @@ -546,14 +607,19 @@ def _pprint_counter(self, object, stream, indent, allowance, context, level): stream.write(repr(object)) return cls = object.__class__ - stream.write(cls.__name__ + '({') - if self._indent_per_level > 1: + stream.write(self._format_block_start(cls.__name__ + '({', indent)) + if self._indent_per_level > 1 and not self._block_style: stream.write((self._indent_per_level - 1) * ' ') + if self._indent_per_level > 0 and self._block_style: + stream.write(self._indent_per_level * ' ') items = object.most_common() - self._format_dict_items(items, stream, - indent + len(cls.__name__) + 1, allowance + 2, + if self._block_style: + recursive_indent = indent + else: + recursive_indent = indent + len(cls.__name__) + 1 + self._format_dict_items(items, stream, recursive_indent, allowance + 2, context, level) - stream.write('})') + stream.write(self._format_block_end('})', indent)) _dispatch[_collections.Counter.__repr__] = _pprint_counter @@ -562,12 +628,16 @@ def _pprint_chain_map(self, object, stream, indent, allowance, context, level): 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._block_style: + 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(')') + stream.write(self._format_block_end(')', indent - self._indent_per_level)) else: self._format(m, stream, indent, 1, context, level) stream.write(',\n' + ' ' * indent) @@ -579,18 +649,21 @@ def _pprint_deque(self, object, stream, indent, allowance, context, level): 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._block_style: + 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._block_style: + stream.write('%s], maxlen=%s)' % ('\n' + ' ' * indent, rml)) + else: + stream.write('],\n%smaxlen=%s)' % (' ' * indent, rml)) _dispatch[_collections.deque.__repr__] = _pprint_deque diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 41c337ade7eca1..24914e9c35c498 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -157,6 +157,7 @@ def test_init(self): 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, block_style=True) def test_basic(self): # Verify .isrecursive() and .isreadable() w/o recursion @@ -1262,6 +1263,12 @@ def test_counter(self): '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() @@ -1472,6 +1479,379 @@ def test_user_string(self): 'jumped over a ' 'lazy dog'}""") + def test_block_style_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, + block_style=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_block_style_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, + block_style=True, sort_dicts=False), +"""\ +{ + 'foo': 'bar', + 'baz': 123, + 'qux': {'foo': 'bar', 'baz': 123}, + 'quux': ['foo', 'bar', 'baz'], + 'corge': 7 +}""") + + def test_block_style_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, + block_style=True), +"""\ +OrderedDict([ + ('foo', 1), + ('bar', 12), + ('baz', 123) +])""") + + def test_block_style_list(self): + dummy_list = [ + "foo", + "bar", + "baz", + "qux", + ] + self.assertEqual(pprint.pformat(dummy_list, width=20, indent=4, + block_style=True), +"""\ +[ + 'foo', + 'bar', + 'baz', + 'qux' +]""") + + def test_block_style_tuple(self): + dummy_tuple = ( + "foo", + "bar", + "baz", + 4, + 5, + 6, + ) + self.assertEqual(pprint.pformat(dummy_tuple, width=20, indent=4, + block_style=True), +"""\ +( + 'foo', + 'bar', + 'baz', + 4, + 5, + 6 +)""") + + def test_block_style_set(self): + dummy_set = { + "foo", + "bar", + "baz", + "qux", + (1, 2, 3), + } + self.assertEqual(pprint.pformat(dummy_set, width=20, indent=4, + block_style=True), +"""\ +{ + 'bar', + 'baz', + 'foo', + 'qux', + (1, 2, 3) +}""") + + def test_block_style_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, + block_style=True), +"""\ +frozenset({ + frozenset({(1, 2, 3)}), + 'bar', + 'baz', + 'foo', + (1, 2, 3) +})""") + + def test_block_style_bytes(self): + dummy_bytes = b"Hello world! foo bar baz 123 456 789" + self.assertEqual(pprint.pformat(dummy_bytes, width=20, indent=4, + block_style=True), +"""\ +( + b'Hello world!' + b' foo bar baz' + b' 123 456 789' +)""") + + def test_block_style_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, + block_style=True), +"""\ +bytearray( + b'Hello world! foo bar baz 123 456' + b' 789' +)""") + + def test_block_style_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, + block_style=True), +"""\ +mappingproxy({ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'} +})""") + + def test_block_style_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, + block_style=True), +"""\ +namespace( + foo='bar', + bar=42, + baz=namespace( + x=321, + y='string', + d={'bar': 'baz', 'foo': True} + ) +)""") + + def test_block_style_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, + block_style=True), +"""\ +defaultdict(, { + 'bar': {'baz': None, 'foo': 'bar'}, + 'foo': ['bar', 'baz', 'qux'] +})""") + + def test_block_style_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, + block_style=True), expected) + + expected2 = """\ +Counter({ + 'a': 5, + 'b': 4, + 'c': 3, + 'd': 2, + 'e': 1 +})""" + self.assertEqual(pprint.pformat(dummy_counter, width=20, indent=2, + block_style=True), expected2) + + def test_block_style_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, + block_style=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_block_style_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, + block_style=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_block_style_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, + block_style=True), +"""\ +{ + 'baz': 123, + 'corge': 7, + 'foo': 'bar', + 'quux': ['foo', 'bar', 'baz'], + 'qux': {'baz': 123, 'foo': 'bar'} +}""") + + def test_block_style_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, + block_style=True), +"""\ +[ + 'first', + 2, + {'key': 'value'}, + [4, 5, 6] +]""") + class DottedPrettyPrinter(pprint.PrettyPrinter): diff --git a/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst new file mode 100644 index 00000000000000..ba3fe9de81225a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-02-07-00-48-07.gh-issue-112632.95MM0C.rst @@ -0,0 +1,3 @@ +Add a *block_style* 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. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy