From: <mi...@us...> - 2012-03-19 22:59:16
|
Revision: 7384 http://docutils.svn.sourceforge.net/docutils/?rev=7384&view=rev Author: milde Date: 2012-03-19 22:59:09 +0000 (Mon, 19 Mar 2012) Log Message: ----------- Backwards-compatible fix for system-exit on IOError. Modified Paths: -------------- trunk/docutils/HISTORY.txt trunk/docutils/docutils/core.py trunk/docutils/docutils/io.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py trunk/docutils/test/test_parsers/test_rst/test_directives/test_raw.py trunk/docutils/test/test_publisher.py Modified: trunk/docutils/HISTORY.txt =================================================================== --- trunk/docutils/HISTORY.txt 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/HISTORY.txt 2012-03-19 22:59:09 UTC (rev 7384) @@ -38,7 +38,15 @@ - Fix [ 3395948 ] (Work around encoding problems in Py3k). - `mode` argument for FileOutput avoids code replication in BinaryFileOutput. + - New exceptions InputError and OutputError for IO errors in + FileInput/FileOutput. + +* docutils/core.py: + - No "hard" system exit on file IO errors: catch and report them in + `Publisher.reportException` instead. Allows handling by a calling + application if the configuration setting `traceback` is True. + * docutils/utils.py -> docutils/utils/__init__.py - docutils.utils is now a package (providing a place for sub-modules) Modified: trunk/docutils/docutils/core.py =================================================================== --- trunk/docutils/docutils/core.py 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/docutils/core.py 2012-03-19 22:59:09 UTC (rev 7384) @@ -135,7 +135,7 @@ if self.settings is None: defaults = (settings_overrides or {}).copy() # Propagate exceptions by default when used programmatically: - defaults.setdefault('traceback', 1) + defaults.setdefault('traceback', True) self.get_settings(settings_spec=settings_spec, config_section=config_section, **defaults) @@ -171,9 +171,17 @@ source_path = self.settings._source else: self.settings._source = source_path - self.source = self.source_class( - source=source, source_path=source_path, - encoding=self.settings.input_encoding) + # Raise IOError instead of system exit with `tracback == True` + # TODO: change io.FileInput's default behaviour and remove this hack + try: + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding, + handle_io_errors=False) + except TypeError: + self.source = self.source_class( + source=source, source_path=source_path, + encoding=self.settings.input_encoding) def set_destination(self, destination=None, destination_path=None): if destination_path is None: @@ -184,6 +192,9 @@ destination=destination, destination_path=destination_path, encoding=self.settings.output_encoding, error_handler=self.settings.output_encoding_error_handler) + # Raise IOError instead of system exit with `tracback == True` + # TODO: change io.FileInput's default behaviour and remove this hack + self.destination.handle_io_errors=False def apply_transforms(self): self.document.transformer.populate_from_components( @@ -221,7 +232,7 @@ self.debugging_dumps() raise self.report_Exception(error) - exit = 1 + exit = True exit_status = 1 self.debugging_dumps() if (enable_exit_status and self.document @@ -260,6 +271,13 @@ self.report_SystemMessage(error) elif isinstance(error, UnicodeEncodeError): self.report_UnicodeError(error) + elif isinstance(error, io.InputError): + self._stderr.write(u'Unable to open source file for reading:\n' + u' %s\n' % ErrorString(error)) + elif isinstance(error, io.OutputError): + self._stderr.write( + u'Unable to open destination file for writing:\n' + u' %s\n' % ErrorString(error)) else: print >>self._stderr, u'%s' % ErrorString(error) print >>self._stderr, ("""\ Modified: trunk/docutils/docutils/io.py =================================================================== --- trunk/docutils/docutils/io.py 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/docutils/io.py 2012-03-19 22:59:09 UTC (rev 7384) @@ -17,6 +17,11 @@ from docutils._compat import b from docutils.error_reporting import locale_encoding, ErrorString, ErrorOutput + +class InputError(IOError): pass +class OutputError(IOError): pass + + class Input(TransformSpec): """ @@ -216,12 +221,13 @@ try: self.source = open(source_path, mode, **kwargs) except IOError, error: - if not handle_io_errors: - raise - print >>self._stderr, ErrorString(error) - print >>self._stderr, (u'Unable to open source' - u" file for reading ('%s'). Exiting." % source_path) - sys.exit(1) + if handle_io_errors: + print >>self._stderr, ErrorString(error) + print >>self._stderr, ( + u'Unable to open source file for reading ("%s").' + u'Exiting.' % source_path) + sys.exit(1) + raise InputError(error.errno, error.strerror, source_path) else: self.source = sys.stdin elif (sys.version_info >= (3,0) and @@ -292,7 +298,7 @@ def __init__(self, destination=None, destination_path=None, encoding=None, error_handler='strict', autoclose=True, - handle_io_errors=None, mode=None): + handle_io_errors=True, mode=None): """ :Parameters: - `destination`: either a file-like object (which is written @@ -338,12 +344,13 @@ try: self.destination = open(self.destination_path, self.mode, **kwargs) except IOError, error: - if not self.handle_io_errors: - raise - print >>self._stderr, ErrorString(error) - print >>self._stderr, (u'Unable to open destination file' - u" for writing ('%s'). Exiting." % self.destination_path) - sys.exit(1) + if self.handle_io_errors: + print >>self._stderr, ErrorString(error) + print >>self._stderr, (u'Unable to open destination file' + u" for writing ('%s'). Exiting." % self.destination_path) + sys.exit(1) + raise OutputError(error.errno, error.strerror, + self.destination_path) self.opened = True def write(self, data): Modified: trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/test/test_parsers/test_rst/test_directives/test_include.py 2012-03-19 22:59:09 UTC (rev 7384) @@ -212,7 +212,7 @@ <system_message level="4" line="4" source="test data" type="SEVERE"> <paragraph> Problems with "include" directive path: - IOError: [Errno 2] No such file or directory: 'nonexistent.txt'. + InputError: [Errno 2] No such file or directory: 'nonexistent.txt'. <literal_block xml:space="preserve"> .. include:: nonexistent.txt <paragraph> @@ -414,7 +414,7 @@ <system_message level="4" line="3" source="test data" type="SEVERE"> <paragraph> Problems with "include" directive path: - IOError: [Errno 2] No such file or directory: '\u043c\u0438\u0440.txt'. + InputError: [Errno 2] No such file or directory: '\u043c\u0438\u0440.txt'. <literal_block xml:space="preserve"> .. include:: \u043c\u0438\u0440.txt """], @@ -458,7 +458,7 @@ <system_message level="4" line="12" source="%(source)s" type="SEVERE"> <paragraph> Problems with "include" directive path: - IOError: [Errno 2] No such file or directory: '%(nonexistent)s'. + InputError: [Errno 2] No such file or directory: '%(nonexistent)s'. <literal_block xml:space="preserve"> .. include:: <nonexistent> <system_message level="3" line="14" source="%(source)s" type="ERROR"> @@ -697,7 +697,7 @@ <system_message level="4" line="3" source="test data" type="SEVERE"> <paragraph> Problems with "include" directive path: - IOError: [Errno 2] No such file or directory: '%s'. + InputError: [Errno 2] No such file or directory: '%s'. <literal_block xml:space="preserve"> .. include:: <nonexistent> """ % nonexistent_rel], Modified: trunk/docutils/test/test_parsers/test_rst/test_directives/test_raw.py =================================================================== --- trunk/docutils/test/test_parsers/test_rst/test_directives/test_raw.py 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/test/test_parsers/test_rst/test_directives/test_raw.py 2012-03-19 22:59:09 UTC (rev 7384) @@ -158,7 +158,7 @@ <system_message level="4" line="1" source="test data" type="SEVERE"> <paragraph> Problems with "raw" directive path: - IOError: [Errno 2] No such file or directory: 'non-existent.file'. + InputError: [Errno 2] No such file or directory: 'non-existent.file'. <literal_block xml:space="preserve"> .. raw:: html :file: non-existent.file Modified: trunk/docutils/test/test_publisher.py =================================================================== --- trunk/docutils/test/test_publisher.py 2012-03-19 17:04:49 UTC (rev 7383) +++ trunk/docutils/test/test_publisher.py 2012-03-19 22:59:09 UTC (rev 7384) @@ -53,6 +53,29 @@ """ % u_prefix) +class PublisherTests(DocutilsTestSupport.StandardTestCase): + + def test_input_error_handling(self): + # core.publish_cmdline(argv=['nonexisting/path']) + # exits with a short message, if `traceback` is False, + + # pass IOErrors to calling application if `traceback` is True + try: + core.publish_cmdline(argv=['nonexisting/path'], + settings_overrides={'traceback': True}) + except IOError, e: + self.assertTrue(isinstance(e, io.InputError)) + + + def test_output_error_handling(self): + # pass IOErrors to calling application if `traceback` is True + try: + core.publish_cmdline(argv=['data/include.txt', 'nonexisting/path'], + settings_overrides={'traceback': True}) + except IOError, e: + self.assertTrue(isinstance(e, io.OutputError)) + + class PublishDoctreeTestCase(DocutilsTestSupport.StandardTestCase, docutils.SettingsSpec): settings_default_overrides = { @@ -100,7 +123,7 @@ def test_publish_pickle(self): # Test publishing a document tree with pickling and unpickling. - + # Produce the document tree. doctree = core.publish_doctree( source=test_document, This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |