From: <mi...@us...> - 2011-05-11 06:45:44
|
Author: milde Date: 2011-05-11 08:45:36 +0200 (Wed, 11 May 2011) New Revision: 7031 Modified: trunk/docutils/test/test_io.py trunk/docutils/tools/buildhtml.py Log: Test and use robust the error printing Modified: trunk/docutils/test/test_io.py =================================================================== --- trunk/docutils/test/test_io.py 2011-05-10 10:02:54 UTC (rev 7030) +++ trunk/docutils/test/test_io.py 2011-05-11 06:45:36 UTC (rev 7031) @@ -8,7 +8,13 @@ Test module for io.py. """ -import unittest +import unittest, sys +try: + from io import StringIO, BytesIO +except ImportError: # io is new in Python 2.6 + from StringIO import StringIO + BytesIO = StringIO + import DocutilsTestSupport # must be imported before docutils from docutils import io from docutils._compat import b @@ -63,5 +69,73 @@ self.assertEquals(input.successful_encoding, 'utf-8') +# ErrorOutput tests +# ----------------- + +# Stub: Buffer with 'strict' auto-conversion of input to byte string: +class BBuf(BytesIO, object): + def write(self, data): + if type(data) == unicode: + data.encode('ascii', 'strict') + super(BBuf, self).write(data) + +# Stub: Buffer expecting unicode string: +class UBuf(StringIO, object): + def write(self, data): + # emulate Python 3 handling of stdout, stderr + if type(data) == b: + raise TypeError('must be unicode, not bytes') + super(UBuf, self).write(data) + +class ErrorOutputTests(unittest.TestCase): + def test_defaults(self): + e = io.ErrorOutput() + self.assertEquals(e.stream, sys.stderr) + + def test_bbuf(self): + buf = BBuf() # buffer storing byte string + e = io.ErrorOutput(buf, encoding='ascii') + # write byte-string as-is + e.write(b('b\xfc')) + self.assertEquals(buf.getvalue(), b('b\xfc')) + # encode unicode data with backslashescape fallback replacement: + e.write(u' u\xfc') + self.assertEquals(buf.getvalue(), b('b\xfc u\\xfc')) + # handle Exceptions with Unicode string args + # unicode(Exception(u'e\xfc')) # fails in Python < 2.6 + e.write(AttributeError(u' e\xfc')) + self.assertEquals(buf.getvalue(), b('b\xfc u\\xfc e\\xfc')) + # encode with `encoding` attribute + e.encoding = 'utf8' + e.write(u' u\xfc') + self.assertEquals(buf.getvalue(), b('b\xfc u\\xfc e\\xfc u\xc3\xbc')) + + def test_ubuf(self): + buf = UBuf() # buffer only accepting unicode string + # decode of binary strings + e = io.ErrorOutput(buf, encoding='ascii') + e.write(b('b\xfc')) + self.assertEquals(buf.getvalue(), u'b\ufffd') # use REPLACEMENT CHARACTER + # write Unicode string and Exceptions with Unicode args + e.write(u' u\xfc') + self.assertEquals(buf.getvalue(), u'b\ufffd u\xfc') + e.write(AttributeError(u' e\xfc')) + self.assertEquals(buf.getvalue(), u'b\ufffd u\xfc e\xfc') + # decode with `encoding` attribute + e.encoding = 'latin1' + e.write(b(' b\xfc')) + self.assertEquals(buf.getvalue(), u'b\ufffd u\xfc e\xfc b\xfc') + + +# class FileInputTests(unittest.TestCase): +# def test_io_error_reporting(self): +# # it seems like IOError and SystemExit are not catched by assertRaises: +# self.assertRaises(IOError, open('foo')) +# self.assertRaises(IOError, +# io.FileInput(source_path=u'u\xfc', handle_io_errors=False)) +# self.assertRaises(IOError, +# io.FileInput(source_path=u'u\xfc', handle_io_errors=True)) + + if __name__ == '__main__': unittest.main() Modified: trunk/docutils/tools/buildhtml.py =================================================================== --- trunk/docutils/tools/buildhtml.py 2011-05-10 10:02:54 UTC (rev 7030) +++ trunk/docutils/tools/buildhtml.py 2011-05-11 06:45:36 UTC (rev 7031) @@ -28,7 +28,7 @@ from fnmatch import fnmatch import docutils from docutils import ApplicationError -from docutils import core, frontend, utils +from docutils import core, frontend, utils, io from docutils.parsers import rst from docutils.readers import standalone, pep from docutils.writers import html4css1, pep_html @@ -194,17 +194,15 @@ def visit(self, directory, names): # BUG prune and ignore do not work settings = self.get_settings('', directory) + stderr = io.ErrorOutput(encoding=settings.error_encoding) if settings.prune and (os.path.abspath(directory) in settings.prune): - sys.stderr.write((u'/// ...Skipping directory (pruned): %s\n' % - directory).encode(settings.error_encoding, - settings.error_encoding_error_handler)) + stderr.write('/// ...Skipping directory (pruned): %s\n' % + directory) sys.stderr.flush() names[:] = [] return if not self.initial_settings.silent: - sys.stderr.write((u'/// Processing directory: %s\n' % - directory).encode(settings.error_encoding, - settings.error_encoding_error_handler)) + stderr.write('/// Processing directory: %s\n' % directory) sys.stderr.flush() # settings.ignore grows many duplicate entries as we recurse # if we add patterns in config files or on the command line. @@ -226,13 +224,14 @@ else: publisher = '.txt' settings = self.get_settings(publisher, directory) + stderr = io.ErrorOutput(encoding=settings.error_encoding) pub_struct = self.publishers[publisher] if settings.prune and (directory in settings.prune): return 1 settings._source = os.path.normpath(os.path.join(directory, name)) settings._destination = settings._source[:-4]+'.html' if not self.initial_settings.silent: - sys.stderr.write(' ::: Processing: %s\n'% name) + stderr.write(' ::: Processing: %s\n'% name) sys.stderr.flush() try: if not settings.dry_run: @@ -243,8 +242,8 @@ writer_name=pub_struct.writer_name, settings=settings) except ApplicationError, error: - sys.stderr.write(' Error (%s): %s' % - (error.__class__.__name__, error)) + stderr.write(' Error (%s): %s' % + (error.__class__.__name__, error)) if __name__ == "__main__": |