diff -r a0c687dc0039 Lib/pprint.py --- a/Lib/pprint.py Tue Dec 10 13:53:56 2013 +0200 +++ b/Lib/pprint.py Tue Dec 10 21:15:52 2013 +0200 @@ -37,7 +37,8 @@ import re import sys as _sys from collections import OrderedDict as _OrderedDict -from io import StringIO as _StringIO +from io import StringIO as _StringIO, TextIOWrapper as _TextIOWrapper, \ + RawIOBase as _RawIOBase __all__ = ["pprint","pformat","isreadable","isrecursive","saferepr", "PrettyPrinter"] @@ -129,10 +130,28 @@ self._depth = depth self._indent_per_level = indent self._width = width - if stream is not None: - self._stream = stream - else: - self._stream = _sys.stdout + if stream is None: + stream = _sys.stdout + encoding = getattr(stream, 'encoding', None) + if encoding: + oldstream = stream + buffer = getattr(stream, 'buffer', None) + if buffer is None: + class DecodeWriter(_RawIOBase): + def writable(self): + return True + def write(self, data): + return oldstream.write(data.decode(encoding)) + buffer = DecodeWriter() + stream.flush() + stream = _TextIOWrapper(buffer, encoding=encoding, + errors='backslashreplace', + newline='\n', write_through=True) + def close(): + oldstream.flush() + stream.detach() + stream.close = close + self._stream = stream self._compact = bool(compact) def pprint(self, object): diff -r a0c687dc0039 Lib/test/test_pprint.py --- a/Lib/test/test_pprint.py Tue Dec 10 13:53:56 2013 +0200 +++ b/Lib/test/test_pprint.py Tue Dec 10 21:15:52 2013 +0200 @@ -7,6 +7,7 @@ import random import collections import itertools +import io # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): @@ -580,6 +581,38 @@ [0, 1, 2, 3, 4]]""" self.assertEqual(pprint.pformat(o, width=48, compact=True), expected) + def test_unencodable(self): + # with encoding and buffer + with io.BytesIO() as bio, \ + io.TextIOWrapper(bio, encoding='latin1') as stream: + stream.write('\xab') + pprint.pprint('\xa3\u20ac', stream) + self.assertFalse(bio.closed) + self.assertEqual(bio.getvalue(), b"\xab'\xa3\\u20ac'\n") + stream.write('\xbb') + stream.flush() + self.assertFalse(bio.closed) + self.assertEqual(bio.getvalue(), b"\xab'\xa3\\u20ac'\n\xbb") + # without encoding and buffer + with io.StringIO() as stream: + stream.write('\xab') + pprint.pprint('\xa3\u20ac', stream) + self.assertEqual(stream.getvalue(), "\xab'\xa3\u20ac'\n") + stream.write('\xbb') + self.assertEqual(stream.getvalue(), "\xab'\xa3\u20ac'\n\xbb") + # with encoding but without buffer + class MockWriter(list): + encoding = 'latin1' + write = list.append + def flush(self): + pass + stream = MockWriter() + stream.write('\xab') + pprint.pprint('\xa3\u20ac', stream) + self.assertEqual(''.join(stream), "\xab'\xa3\\u20ac'\n") + stream.write('\xbb') + self.assertEqual(''.join(stream), "\xab'\xa3\\u20ac'\n\xbb") + class DottedPrettyPrinter(pprint.PrettyPrinter):