Kaydet (Commit) 64c16c33 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Issue #17150: pprint now uses line continuations to wrap long string literals.

üst 4a8ea9e2
...@@ -14,8 +14,8 @@ The :mod:`pprint` module provides a capability to "pretty-print" arbitrary ...@@ -14,8 +14,8 @@ The :mod:`pprint` module provides a capability to "pretty-print" arbitrary
Python data structures in a form which can be used as input to the interpreter. Python data structures in a form which can be used as input to the interpreter.
If the formatted structures include objects which are not fundamental Python If the formatted structures include objects which are not fundamental Python
types, the representation may not be loadable. This may be the case if objects types, the representation may not be loadable. This may be the case if objects
such as files, sockets, classes, or instances are included, as well as many such as files, sockets or classes are included, as well as many other
other built-in objects which are not representable as Python constants. objects which are not representable as Python literals.
The formatted representation keeps objects on a single line if it can, and The formatted representation keeps objects on a single line if it can, and
breaks them onto multiple lines if they don't fit within the allowed width. breaks them onto multiple lines if they don't fit within the allowed width.
...@@ -65,7 +65,7 @@ The :mod:`pprint` module defines one class: ...@@ -65,7 +65,7 @@ The :mod:`pprint` module defines one class:
('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', (...))))))) ('spam', ('eggs', ('lumberjack', ('knights', ('ni', ('dead', (...)))))))
The :class:`PrettyPrinter` class supports several derivative functions: The :mod:`pprint` module also provides several shortcut functions:
.. function:: pformat(object, indent=1, width=80, depth=None) .. function:: pformat(object, indent=1, width=80, depth=None)
...@@ -193,101 +193,141 @@ Example ...@@ -193,101 +193,141 @@ Example
------- -------
To demonstrate several uses of the :func:`pprint` function and its parameters, To demonstrate several uses of the :func:`pprint` function and its parameters,
let's fetch information about a project from PyPI:: let's fetch information about a project from `PyPI <https://pypi.python.org>`_::
>>> import json >>> import json
>>> import pprint >>> import pprint
>>> from urllib.request import urlopen >>> from urllib.request import urlopen
>>> with urlopen('http://pypi.python.org/pypi/configparser/json') as url: >>> with urlopen('http://pypi.python.org/pypi/Twisted/json') as url:
... http_info = url.info() ... http_info = url.info()
... raw_data = url.read().decode(http_info.get_content_charset()) ... raw_data = url.read().decode(http_info.get_content_charset())
>>> project_info = json.loads(raw_data) >>> project_info = json.loads(raw_data)
>>> result = {'headers': http_info.items(), 'body': project_info}
In its basic form, :func:`pprint` shows the whole object:: In its basic form, :func:`pprint` shows the whole object::
>>> pprint.pprint(result) >>> pprint.pprint(project_info)
{'body': {'info': {'_pypi_hidden': False, {'info': {'_pypi_hidden': False,
'_pypi_ordering': 12, '_pypi_ordering': 125,
'classifiers': ['Development Status :: 4 - Beta', 'author': 'Glyph Lefkowitz',
'Intended Audience :: Developers', 'author_email': 'glyph@twistedmatrix.com',
'License :: OSI Approved :: MIT License', 'bugtrack_url': '',
'Natural Language :: English', 'cheesecake_code_kwalitee_id': None,
'Operating System :: OS Independent', 'cheesecake_documentation_id': None,
'Programming Language :: Python', 'cheesecake_installability_id': None,
'Programming Language :: Python :: 2', 'classifiers': ['Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 2 :: Only'],
'Topic :: Software Development :: Libraries', 'description': 'An extensible framework for Python programming, '
'Topic :: Software Development :: Libraries :: Python Modules'], 'with special focus\r\n'
'download_url': 'UNKNOWN', 'on event-based network programming and '
'home_page': 'http://docs.python.org/py3k/library/configparser.html', 'multiprotocol integration.',
'keywords': 'configparser ini parsing conf cfg configuration file', 'docs_url': '',
'license': 'MIT', 'download_url': 'UNKNOWN',
'name': 'configparser', 'home_page': 'http://twistedmatrix.com/',
'package_url': 'http://pypi.python.org/pypi/configparser', 'keywords': '',
'platform': 'any', 'license': 'MIT',
'release_url': 'http://pypi.python.org/pypi/configparser/3.2.0r3', 'maintainer': '',
'requires_python': None, 'maintainer_email': '',
'stable_version': None, 'name': 'Twisted',
'summary': 'This library brings the updated configparser from Python 3.2+ to Python 2.6-2.7.', 'package_url': 'http://pypi.python.org/pypi/Twisted',
'version': '3.2.0r3'}, 'platform': 'UNKNOWN',
'urls': [{'comment_text': '', 'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
'downloads': 47, 'requires_python': None,
'filename': 'configparser-3.2.0r3.tar.gz', 'stable_version': None,
'has_sig': False, 'summary': 'An asynchronous networking framework written in Python',
'md5_digest': '8500fd87c61ac0de328fc996fce69b96', 'version': '12.3.0'},
'packagetype': 'sdist', 'urls': [{'comment_text': '',
'python_version': 'source', 'downloads': 71844,
'size': 32281, 'filename': 'Twisted-12.3.0.tar.bz2',
'upload_time': '2011-05-10T16:28:50', 'has_sig': False,
'url': 'http://pypi.python.org/packages/source/c/configparser/configparser-3.2.0r3.tar.gz'}]}, 'md5_digest': '6e289825f3bf5591cfd670874cc0862d',
'headers': [('Date', 'Sat, 14 May 2011 12:48:52 GMT'), 'packagetype': 'sdist',
('Server', 'Apache/2.2.16 (Debian)'), 'python_version': 'source',
('Content-Disposition', 'inline'), 'size': 2615733,
('Connection', 'close'), 'upload_time': '2012-12-26T12:47:03',
('Transfer-Encoding', 'chunked'), 'url': 'https://pypi.python.org/packages/source/T/Twisted/Twisted-12.3.0.tar.bz2'},
('Content-Type', 'application/json; charset="UTF-8"')]} {'comment_text': '',
'downloads': 5224,
'filename': 'Twisted-12.3.0.win32-py2.7.msi',
'has_sig': False,
'md5_digest': '6b778f5201b622a5519a2aca1a2fe512',
'packagetype': 'bdist_msi',
'python_version': '2.7',
'size': 2916352,
'upload_time': '2012-12-26T12:48:15',
'url': 'https://pypi.python.org/packages/2.7/T/Twisted/Twisted-12.3.0.win32-py2.7.msi'}]}
The result can be limited to a certain *depth* (ellipsis is used for deeper The result can be limited to a certain *depth* (ellipsis is used for deeper
contents):: contents)::
>>> pprint.pprint(result, depth=3) >>> pprint.pprint(project_info, depth=2)
{'body': {'info': {'_pypi_hidden': False, {'info': {'_pypi_hidden': False,
'_pypi_ordering': 12, '_pypi_ordering': 125,
'classifiers': [...], 'author': 'Glyph Lefkowitz',
'download_url': 'UNKNOWN', 'author_email': 'glyph@twistedmatrix.com',
'home_page': 'http://docs.python.org/py3k/library/configparser.html', 'bugtrack_url': '',
'keywords': 'configparser ini parsing conf cfg configuration file', 'cheesecake_code_kwalitee_id': None,
'license': 'MIT', 'cheesecake_documentation_id': None,
'name': 'configparser', 'cheesecake_installability_id': None,
'package_url': 'http://pypi.python.org/pypi/configparser', 'classifiers': [...],
'platform': 'any', 'description': 'An extensible framework for Python programming, '
'release_url': 'http://pypi.python.org/pypi/configparser/3.2.0r3', 'with special focus\r\n'
'requires_python': None, 'on event-based network programming and '
'stable_version': None, 'multiprotocol integration.',
'summary': 'This library brings the updated configparser from Python 3.2+ to Python 2.6-2.7.', 'docs_url': '',
'version': '3.2.0r3'}, 'download_url': 'UNKNOWN',
'urls': [{...}]}, 'home_page': 'http://twistedmatrix.com/',
'headers': [('Date', 'Sat, 14 May 2011 12:48:52 GMT'), 'keywords': '',
('Server', 'Apache/2.2.16 (Debian)'), 'license': 'MIT',
('Content-Disposition', 'inline'), 'maintainer': '',
('Connection', 'close'), 'maintainer_email': '',
('Transfer-Encoding', 'chunked'), 'name': 'Twisted',
('Content-Type', 'application/json; charset="UTF-8"')]} 'package_url': 'http://pypi.python.org/pypi/Twisted',
'platform': 'UNKNOWN',
Additionally, maximum *width* can be suggested. If a long object cannot be 'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
split, the specified width will be exceeded:: 'requires_python': None,
'stable_version': None,
>>> pprint.pprint(result['headers'], width=30) 'summary': 'An asynchronous networking framework written in Python',
[('Date', 'version': '12.3.0'},
'Sat, 14 May 2011 12:48:52 GMT'), 'urls': [{...}, {...}]}
('Server',
'Apache/2.2.16 (Debian)'), Additionally, maximum character *width* can be suggested. If a long object
('Content-Disposition', cannot be split, the specified width will be exceeded::
'inline'),
('Connection', 'close'), >>> pprint.pprint(project_info, depth=2, width=50)
('Transfer-Encoding', {'info': {'_pypi_hidden': False,
'chunked'), '_pypi_ordering': 125,
('Content-Type', 'author': 'Glyph Lefkowitz',
'application/json; charset="UTF-8"')] 'author_email': 'glyph@twistedmatrix.com',
'bugtrack_url': '',
'cheesecake_code_kwalitee_id': None,
'cheesecake_documentation_id': None,
'cheesecake_installability_id': None,
'classifiers': [...],
'description': 'An extensible '
'framework for '
'Python programming, '
'with special '
'focus\r\n'
'on event-based '
'network programming '
'and multiprotocol '
'integration.',
'docs_url': '',
'download_url': 'UNKNOWN',
'home_page': 'http://twistedmatrix.com/',
'keywords': '',
'license': 'MIT',
'maintainer': '',
'maintainer_email': '',
'name': 'Twisted',
'package_url': 'http://pypi.python.org/pypi/Twisted',
'platform': 'UNKNOWN',
'release_url': 'http://pypi.python.org/pypi/Twisted/12.3.0',
'requires_python': None,
'stable_version': None,
'summary': 'An asynchronous '
'networking framework '
'written in Python',
'version': '12.3.0'},
'urls': [{...}, {...}]}
...@@ -34,6 +34,7 @@ saferepr() ...@@ -34,6 +34,7 @@ saferepr()
""" """
import re
import sys as _sys import sys as _sys
from collections import OrderedDict as _OrderedDict from collections import OrderedDict as _OrderedDict
from io import StringIO as _StringIO from io import StringIO as _StringIO
...@@ -158,13 +159,10 @@ class PrettyPrinter: ...@@ -158,13 +159,10 @@ class PrettyPrinter:
return return
rep = self._repr(object, context, level - 1) rep = self._repr(object, context, level - 1)
typ = _type(object) 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 write = stream.write
if self._depth and level > self._depth:
write(rep)
return
if sepLines: if sepLines:
r = getattr(typ, "__repr__", None) r = getattr(typ, "__repr__", None)
if issubclass(typ, dict): if issubclass(typ, dict):
...@@ -242,6 +240,37 @@ class PrettyPrinter: ...@@ -242,6 +240,37 @@ class PrettyPrinter:
write(endchar) write(endchar)
return return
if issubclass(typ, str) and len(object) > 0 and r is str.__repr__:
def _str_parts(s):
"""
Return a list of string literals comprising the repr()
of the given string using literal concatenation.
"""
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:
if current:
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) write(rep)
def _repr(self, object, context, level): def _repr(self, object, context, level):
......
# -*- coding: utf-8 -*-
import pprint import pprint
import test.support import test.support
import unittest import unittest
...@@ -475,6 +477,42 @@ class QueryTestCase(unittest.TestCase): ...@@ -475,6 +477,42 @@ class QueryTestCase(unittest.TestCase):
self.assertEqual(pprint.pformat(dict.fromkeys(keys, 0)), self.assertEqual(pprint.pformat(dict.fromkeys(keys, 0)),
'{%r: 0, %r: 0}' % tuple(sorted(keys, key=id))) '{%r: 0, %r: 0}' % tuple(sorted(keys, key=id)))
def test_str_wrap(self):
# pprint tries to wrap strings intelligently
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'""")
# An unwrappable string is formatted as its repr
unwrappable = "x" * 100
self.assertEqual(pprint.pformat(unwrappable, width=80), repr(unwrappable))
self.assertEqual(pprint.pformat(''), "''")
# 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): class DottedPrettyPrinter(pprint.PrettyPrinter):
......
...@@ -294,6 +294,9 @@ Core and Builtins ...@@ -294,6 +294,9 @@ Core and Builtins
Library Library
------- -------
- Issue #17150: pprint now uses line continuations to wrap long string
literals.
- Issue #17488: Change the subprocess.Popen bufsize parameter default value - Issue #17488: Change the subprocess.Popen bufsize parameter default value
from unbuffered (0) to buffering (-1) to match the behavior existing code from unbuffered (0) to buffering (-1) to match the behavior existing code
expects and match the behavior of the subprocess module in Python 2 to avoid expects and match the behavior of the subprocess module in Python 2 to avoid
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment