19

Is there a way to tell pprint in Python 3 not to split strings on whitespace? Python 2's pprint did not do this. Can this behavior be disabled? I looked through the source for pprint and it doesn't look like there's an option I saw for this.

Can I trick it somehow?

Here's an example of what I'm getting:

>>> PP.pprint("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ",width=-1,compact=True)
('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ '
 'ZZZZZ')

And here's what I want:

>>> PP.pprint("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ",width=-1,compact=True)
('ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ')

To clarify, I do want it to obey widths, just not for strings. So when I see eg:

PP.pprint(["ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZ","CATS"])

I want:

['ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZZZZZZZZZ',
 'CATS']

Not:

['ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ '
 'ZZZZZZZZZZZZZ',
 'CATS']
wjandrea
  • 28,235
  • 9
  • 60
  • 81
Carbon
  • 3,828
  • 3
  • 24
  • 51

4 Answers4

17

You could just set the width option to a really large value, such as sys.maxsize:

>>> import sys
>>> import pprint as PP
>>> PP.pprint("ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ",width=sys.maxsize,compact=True)
'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ'

Setting width to -1 has no special meaning.

The only other option is to subclass the PrettyPrinter() class, and wrapping the _format() method:

import sys
from pprint import PrettyPrinter

class NoStringWrappingPrettyPrinter(PrettyPrinter):
    def _format(self, object, *args):
        if isinstance(object, str):
            width = self._width
            self._width = sys.maxsize
            try:
                super()._format(object, *args)
            finally:
                self._width = width
        else:
            super()._format(object, *args)

NoStringWrappingPrettyPrinter().pprint(yourobject)

This sets the width to sys.maxsize only for strings:

>>> NoStringWrappingPrettyPrinter().pprint(["ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ", "CATS"])
['ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ',
 'CATS']
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I guess it didn't grow an option for this in the meantime? – Jürgen A. Erhard Feb 08 '17 at 17:21
  • @JürgenA.Erhard: nope. – Martijn Pieters Feb 08 '17 at 17:36
  • We already subclassed PrettyPrinter so the latter solution worked fine. I agree with the OP that Python 2 behavior is better when formatting lists and dicts, but I can see use cases where Python 3 approach works better. It's also strange that only Unicode strings, not bytes, are split like this. It would be best if there was an option to control this behavior. – Pekka Klärck Apr 25 '19 at 20:47
  • 1
    @PekkaKlärck: the `pprint` module is in dire need of a rewrite. I fear it currently is too gnarly to support new features like this easily. – Martijn Pieters Apr 25 '19 at 20:50
  • @MartijnPieters I've noticed that. We wanted to slightly modify formatting strings and bytes and that wasn't as easy as it could have been. That said, adding support for not splitting strings shouldn't be that complicated. – Pekka Klärck Apr 25 '19 at 22:16
  • Just noticed in my testing that Python 3.4 doesn't split long byte strings but Python 3.5+ do. Needed to change `isinstance(object, str)`in the above solution to `isinstance(object, (str, bytes, bytearray))` to fix that. – Pekka Klärck Apr 25 '19 at 22:30
3

For list, you can try json.dump. Code:

import json
from pprint import pprint
import sys

li = ['zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz zzz', 'cats']

print("pprint directly:")
pprint(li)
print('-' * 20)
print("pprint with large width:")
pprint(li, width=sys.maxsize, compact=True)
print('-' * 20)
print("json dumps:")
print(json.dumps(li, indent=4))

output:

pprint directly:
['zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz '
 'zzz',
 'cats']
--------------------
pprint with large width:
['zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz zzz', 'cats']
--------------------
json dumps:
[
    "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz zzz",
    "cats"
]

Setting a large width makes pprint not wraping even the list object is very long

Vespene Gas
  • 3,210
  • 2
  • 16
  • 27
1

It appears that you need to set the width to be the length of your string + 3 (for the quotes on either side and the newline) at an absolute minimum.

>>> x = 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ'
>>> PP.pprint(x, width=len(x))
'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ '
'ZZZZZ'
>>> PP.pprint(x, width=len(x) + 3)
'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ZZZZZ'
Ned Rockson
  • 1,095
  • 7
  • 16
1

Another approach, similar to that of Martijn Pieters, but which relies less on the internals of pprint is to subclass PrettyPrinter and override format. Inside you can delegate to super().format but in the result you can fake the length of strings, such that they won't be split. This relies on the output stream (e.g. sys.stdout or StringIO()) not looking at the string length.

class Python2PrettyPrinter(pprint.PrettyPrinter):
    class _fake_short_str(str):
        def __len__(self):
            return 1 if super().__len__() else 0

    def format(self, object, context, maxlevels, level):
        res = super().format(object, context, maxlevels, level)
        if isinstance(object, str):
            return (self._fake_short_str(res[0]), ) + res[1:]
        return res

    from io import StringIO
    assert StringIO().write(_fake_short_str('_' * 1000)) == 1000


Python2PrettyPrinter().pprint(["TEST " * 20] * 2)
# ['TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ',
#  'TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST TEST ']

The 100% clean way would be to modify the behavior of format for all supported container classes, and substitute every string for a custom object which has the same representation.

Rosen Matev
  • 1,828
  • 19
  • 20
  • I couldn't get this to work for a list of strings until I set `__len__()` to always return `0`. I'm not sure if that would work in general though. – wjandrea Mar 18 '21 at 01:29