diff --git a/Doc/library/pprint.rst b/Doc/library/pprint.rst index 2985f31bacb47a..918b9c01b4e3f1 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) + compact=False, sort_dicts=False, underscore_numbers=False, \ + block_style=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) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=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) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=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) + compact=False, sort_dicts=True, \ + underscore_numbers=False, block_style=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.14.rst b/Doc/whatsnew/3.14.rst index ac5b53ef94bfb1..df6fef2b1d3835 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -888,6 +888,16 @@ platform (Contributed by Bénédikt Tran in :gh:`122549`.) +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 in :gh:`129274`.) + + pydoc ----- @@ -1200,7 +1210,7 @@ Deprecated .. include:: ../deprecations/pending-removal-in-future.rst Removed -======= +======== argparse -------- diff --git a/Lib/pprint.py b/Lib/pprint.py index dc0953cec67a58..a3503939260300 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): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=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): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=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): + compact=False, sort_dicts=True, underscore_numbers=False, + block_style=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,38 @@ 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}" + else: + return start_str + + def _format_block_end(self, end_str, indent): + if self._block_style: + return f"\n{' ' * indent}{end_str}" + else: + 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 + indent += len(cls_name) + 1 if not self._block_style else self._indent_per_level 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 +257,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,27 +267,28 @@ def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level return cls = object.__class__ stream.write(cls.__name__ + '(') + recursive_indent = indent + len(cls.__name__) + 1 if not self._block_style else indent self._format(list(object.items()), stream, - indent + len(cls.__name__) + 1, allowance + 1, + recursive_indent, allowance + 1, context, level) stream.write(')') _dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict 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 @@ -271,16 +298,16 @@ 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 + indent += len(typ.__name__) + 1 if not self._block_style else 0 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 @@ -346,9 +373,9 @@ def _pprint_bytes(self, object, stream, indent, allowance, context, level): return parens = level == 1 if parens: - indent += 1 + indent += 1 if not self._block_style else self._indent_per_level allowance += 1 - write('(') + write(self._format_block_start('(', indent)) delim = '' for rep in _wrap_bytes_repr(object, self._width - indent, allowance): write(delim) @@ -356,22 +383,26 @@ 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 + 10 if not self._block_style else indent + self._indent_per_level + 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, + recursive_indent = indent + 13 if not self._block_style else indent + self._format(object.copy(), stream, recursive_indent, allowance + 1, context, level) stream.write(')') @@ -384,11 +415,11 @@ def _pprint_simplenamespace(self, object, stream, indent, allowance, context, le cls_name = 'namespace' else: cls_name = object.__class__.__name__ - indent += len(cls_name) + 1 + indent += len(cls_name) + 1 if not self._block_style else self._indent_per_level items = object.__dict__.items() - 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[_types.SimpleNamespace.__repr__] = _pprint_simplenamespace @@ -403,7 +434,8 @@ 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, + recursive_indent = indent + len(rep) + 2 if not self._block_style else indent + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -422,7 +454,8 @@ def _format_namespace_items(self, items, stream, indent, allowance, context, lev # recursive dataclass repr. write("...") else: - self._format(ent, stream, indent + len(key) + 1, + recursive_indent = indent + len(key) + 1 if not self._block_style else indent + self._format(ent, stream, recursive_indent, allowance if last else 1, context, level) if not last: @@ -431,8 +464,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 @@ -491,8 +526,11 @@ 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)) + indent += len(cls.__name__) + 1 if not self._block_style else 0 + if self._block_style: + stream.write('%s(%s, ' % (cls.__name__, rdf)) + else: + stream.write('%s(%s,\n%s' % (cls.__name__, rdf, ' ' * indent)) self._pprint_dict(object, stream, indent, allowance + 1, context, level) stream.write(')') @@ -503,14 +541,17 @@ 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() + recursive_indent = indent + len(cls.__name__) + 1 if not self._block_style else indent self._format_dict_items(items, stream, - indent + len(cls.__name__) + 1, allowance + 2, + recursive_indent, allowance + 2, context, level) - stream.write('})') + stream.write(self._format_block_end('})', indent)) _dispatch[_collections.Counter.__repr__] = _pprint_counter @@ -519,12 +560,12 @@ 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)) + indent += len(cls.__name__) + 1 if not self._block_style else self._indent_per_level 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) @@ -536,18 +577,20 @@ 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)) + indent += len(cls.__name__) + 1 if not self._block_style else 0 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 dfbc2a06e7346f..705e98ae06c08b 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -141,6 +141,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 @@ -1123,6 +1124,361 @@ 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..b165ce4827cf82 --- /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. 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