Commits

Anonymous committed 052594f

Added :returncode: option to programoutput.

Some programs use non-zero exit codes to signal other, non-error states. To
avoid superfluous warnings in such cases, :returncode: is added to explicitly
specify the expected return code, instead of hard-coding 0.

Comments (0)

Files changed (4)

programoutput/CHANGES.rst

 
 - :confval:`programoutput_prompt_template` is interpreted as format string now!
 - Require Python 2.6 now
+- Added ``returncode`` option to :rst:dir:`program-output` (thanks to Jan-Marek
+  Glogowski)
 - Warn on unexpected return codes instead of raising
   :exc:`~subprocess.CalledProcessError`
 

programoutput/doc/index.rst

 syntax!
 
 
+Error handling
+^^^^^^^^^^^^^^
+
+If an unexpected exit code (also known as *return code*) is returned by a
+command, it is considered to have failed.  In this case, a build warning is
+emitted to help you to detect misspelled commands or similar errors.  By
+default, a command is expected to exit with an exit code of 0, all other codes
+indicate an error.  In some cases however, it may be reasonable to demonstrate
+failed programs.  To avoid a (superfluous) warning in such a case, you can
+specify the expected return code of a command with the ``returncode`` option::
+
+   .. command-output:: python -c 'import sys; sys.exit(1)'
+      :returncode: 1
+
+The above command returns the exit code 1 (as given to :py:func:`sys.exit()`),
+but no warning will be emitted.  On the contrary, a warning will be emitted,
+should the command return 0!
+
+
 Reference
 ---------
 
    affects the immediate output of ``command``, but never any additional text
    inserted by option ``prompt``.
 
+   If the command does return an exit code different from the expected one, a
+   build warning is issued.  The expected return code defaults to 0, and can be
+   changed with the ``returncode`` option.
+
 .. directive:: command-output
 
    Same as :dir:`program-output`, but with enabled ``prompt`` option.

programoutput/sphinxcontrib/programoutput.py

 
 from docutils import nodes
 from docutils.parsers import rst
-from docutils.parsers.rst.directives import flag, unchanged
+from docutils.parsers.rst.directives import flag, unchanged, nonnegative_int
 
 
 __version__ = '0.5'
     required_arguments = 1
 
     option_spec = dict(shell=flag, prompt=flag, nostderr=flag,
-                       ellipsis=_slice, extraargs=unchanged)
+                       ellipsis=_slice, extraargs=unchanged,
+                       returncode=nonnegative_int)
 
     def run(self):
         node = program_output()
         node['hide_standard_error'] = 'nostderr' in self.options
         node['extraargs'] = self.options.get('extraargs', '')
         node['use_shell'] = 'shell' in self.options
+        node['returncode'] = self.options.get('returncode', 0)
         if 'ellipsis' in self.options:
             node['strip_lines'] = self.options['ellipsis']
         return [node]
         command = Command.from_program_output_node(node)
         returncode, output = cache[command]
 
-        if returncode != 0:
-            app.warn('Command {0} failed with return code {1}'.format(
-                command, returncode))
+        if returncode != node['returncode']:
+            app.warn('Unexpected return code {0} from command {1}'.format(
+                returncode, command))
 
         # replace lines with ..., if ellipsis is specified
         if 'strip_lines' in node:

programoutput/tests/test_directive.py

 
 @pytest.mark.with_content("""\
 .. program-output:: python -c 'import sys; sys.exit(1)'""")
-def test_non_zero_return_code(app):
+def test_unexpected_return_code(app):
     with pytest.raises(SphinxWarning) as excinfo:
         app.build()
-    exc_message = 'WARNING: Command {0!r} failed with return code {1}\n'.format(
-        "python -c 'import sys; sys.exit(1)'", 1)
+    exc_message = 'WARNING: Unexpected return code 1 from command {0!r}\n'.format(
+        "python -c 'import sys; sys.exit(1)'")
     assert str(excinfo.value) == exc_message
 
 
 @pytest.mark.with_content("""\
 .. program-output:: python -c 'import sys; sys.exit(1)'
    :shell:""")
-def test_shell_non_zero_return_code(app):
+def test_shell_with_unexpected_return_code(app):
     with pytest.raises(SphinxWarning) as excinfo:
         app.build()
-    exc_message = 'WARNING: Command {0!r} failed with return code {1}\n'.format(
-        "python -c 'import sys; sys.exit(1)'", 1)
+    exc_message = 'WARNING: Unexpected return code 1 from command {0!r}\n'.format(
+        "python -c 'import sys; sys.exit(1)'")
     assert str(excinfo.value) == exc_message
+
+
+@pytest.mark.with_content("""\
+.. program-output:: python -c 'import sys; print("foo"); sys.exit(1)'
+   :returncode: 1""")
+def test_expected_non_zero_return_code(doctree, cache):
+    assert_output(doctree, 'foo')
+    assert_cache(cache, 'python -c \'import sys; print("foo"); sys.exit(1)\'',
+                 'foo', returncode=1)