diff -r c02f464dd721 Lib/pprint.py --- a/Lib/pprint.py Thu Sep 26 09:35:39 2013 -0700 +++ b/Lib/pprint.py Fri Sep 27 13:55:34 2013 +0300 @@ -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"] @@ -134,8 +135,27 @@ self._stream = _sys.stdout def pprint(self, object): - self._format(object, self._stream, 0, 0, {}, 0) - self._stream.write("\n") + oldstream = stream = self._stream + encoding = getattr(stream, 'encoding', None) + if encoding: + 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) + try: + self._format(object, stream, 0, 0, {}, 0) + stream.write("\n") + finally: + if stream is not oldstream: + stream.detach() def pformat(self, object): sio = _StringIO() diff -r c02f464dd721 Lib/test/test_pprint.py --- a/Lib/test/test_pprint.py Thu Sep 26 09:35:39 2013 -0700 +++ b/Lib/test/test_pprint.py Fri Sep 27 13:55:34 2013 +0300 @@ -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): @@ -513,6 +514,38 @@ formatted = pprint.pformat(special, width=width) self.assertEqual(eval("(" + formatted + ")"), special) + 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):