diff --git a/Lib/pprint.py b/Lib/pprint.py --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -34,6 +34,7 @@ saferepr() """ +import re import sys as _sys from collections import OrderedDict as _OrderedDict from io import StringIO as _StringIO @@ -158,7 +159,8 @@ class PrettyPrinter: return rep = self._repr(object, context, level - 1) typ = _type(object) - sepLines = _len(rep) > (self._width - 1 - indent - allowance) + max_width = self._width - 1 - indent - allowance + sepLines = _len(rep) > max_width write = stream.write if self._depth and level > self._depth: @@ -241,7 +243,32 @@ class PrettyPrinter: write(',') write(endchar) return - + if issubclass(typ, str) and len(object) > 0 and r is str.__repr__: + def _str_parts(s): + lines = s.splitlines(True) + for i, line in enumerate(lines): + rep = repr(line) + if _len(rep) <= max_width: + yield rep + else: + # A list of alternating (non-space, space) strings + parts = re.split(r'(\s+)', line) + [''] + current = '' + for i in range(0, len(parts), 2): + part = parts[i] + parts[i+1] + candidate = current + part + if len(repr(candidate)) > max_width: + yield repr(current) + current = part + else: + current = candidate + if current: + yield repr(current) + for i, rep in enumerate(_str_parts(object)): + if i > 0: + write('\n' + ' '*indent) + write(rep) + return write(rep) def _repr(self, object, context, level): diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + import pprint import test.support import unittest @@ -475,6 +477,36 @@ class QueryTestCase(unittest.TestCase): self.assertEqual(pprint.pformat(dict.fromkeys(keys, 0)), '{%r: 0, %r: 0}' % tuple(sorted(keys, key=id))) + def test_str_repr(self): + fox = 'the quick brown fox jumped over a lazy dog' + self.assertEqual(pprint.pformat(fox, width=20), """\ +'the quick brown ' +'fox jumped over ' +'a lazy dog'""") + self.assertEqual(pprint.pformat({'a': 1, 'b': fox, 'c': 2}, + width=26), """\ +{'a': 1, + 'b': 'the quick brown ' + 'fox jumped over ' + 'a lazy dog', + 'c': 2}""") + # With some special characters + # - \n always triggers a new line in the pprint + # - \t and \n are escaped + # - non-ASCII is allowed + # - an apostrophe doesn't disrupt the pprint + special = "Portons dix bons \"whiskys\"\nà l'avocat goujat\t qui fumait au zoo" + self.assertEqual(pprint.pformat(special, width=20), """\ +'Portons dix bons ' +'"whiskys"\\n' +"à l'avocat " +'goujat\\t qui ' +'fumait au zoo'""") + # Check that the pprint is a usable repr + special *= 10 + for width in range(3, 40): + formatted = pprint.pformat(special, width=width) + self.assertEqual(eval("(" + formatted + ")"), special) class DottedPrettyPrinter(pprint.PrettyPrinter):