Skip to content

Commit

Permalink
Merge pull request #1078 from python-cmd2/exit_code
Browse files Browse the repository at this point in the history
onecmd_plus_hooks() now sets self.exit_code when a SystemExit handled
  • Loading branch information
kmvanbrunt committed Mar 23, 2021
2 parents fabe4cd + 3126eb7 commit d8c3c7c
Show file tree
Hide file tree
Showing 7 changed files with 23 additions and 10 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
* Settables now have new initialization parameters. It is now a required parameter to supply the reference to the
object that holds the settable attribute. `cmd2.Cmd.settables` is no longer a public dict attribute - it is now a
property that aggregates all Settables across all registered CommandSets.
* Failed transcript testing now sets self.exit_code to 1 instead of -1.
* Enhancements
* Added support for custom tab completion and up-arrow input history to `cmd2.Cmd2.read_input`.
See [read_input.py](https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py)
Expand All @@ -34,6 +35,9 @@
may have a prepended prefix.
* Settables now allow changes to be applied to any arbitrary object attribute. It no longer needs to match an
attribute added to the cmd2 instance itself.
* Raising ``SystemExit`` or calling ``sys.exit()`` in a command or hook function will set ``self.exit_code``
to the exit code used in those calls. It will also result in the command loop stopping.

## 1.5.0 (January 31, 2021)
* Bug Fixes
* Fixed bug where setting `always_show_hint=True` did not show a hint when completing `Settables`
Expand Down
10 changes: 6 additions & 4 deletions cmd2/cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2362,7 +2362,8 @@ def onecmd_plus_hooks(
except KeyboardInterrupt as ex:
if raise_keyboard_interrupt and not stop:
raise ex
except SystemExit:
except SystemExit as ex:
self.exit_code = ex.code
stop = True
except PassThroughException as ex:
raise ex.wrapped_ex
Expand All @@ -2374,7 +2375,8 @@ def onecmd_plus_hooks(
except KeyboardInterrupt as ex:
if raise_keyboard_interrupt and not stop:
raise ex
except SystemExit:
except SystemExit as ex:
self.exit_code = ex.code
stop = True
except PassThroughException as ex:
raise ex.wrapped_ex
Expand Down Expand Up @@ -4748,7 +4750,7 @@ class TestMyAppCase(Cmd2TestCase):
transcripts_expanded = utils.files_from_glob_patterns(transcript_paths, access=os.R_OK)
if not transcripts_expanded:
self.perror('No test files found - nothing to test')
self.exit_code = -1
self.exit_code = 1
return

verinfo = ".".join(map(str, sys.version_info[:3]))
Expand Down Expand Up @@ -4785,7 +4787,7 @@ class TestMyAppCase(Cmd2TestCase):
self.perror(error_str[start:])

# Return a failure error code to support automated transcript-based testing
self.exit_code = -1
self.exit_code = 1

def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None: # pragma: no cover
"""
Expand Down
5 changes: 4 additions & 1 deletion docs/features/commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ The ``cmd2.Cmd`` object sets an ``exit_code`` attribute to zero when it is
instantiated. The value of this attribute is returned from the ``cmdloop()``
call. Therefore, if you don't do anything with this attribute in your code,
``cmdloop()`` will (almost) always return zero. There are a few built-in
``cmd2`` commands which set ``exit_code`` to ``-1`` if an error occurs.
``cmd2`` commands which set ``exit_code`` to ``1`` if an error occurs.

You can use this capability to easily return your own values to the operating
system shell::
Expand Down Expand Up @@ -175,6 +175,9 @@ the following interaction::
2


Raising ``SystemExit(code)`` or calling ``sys.exit(code)`` in a command
or hook function also sets ``self.exit_code`` and stops the program.

Exception Handling
------------------

Expand Down
2 changes: 1 addition & 1 deletion examples/exit_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def do_exit(self, arg_list: List[str]) -> bool:
self.exit_code = int(arg_list[0])
except ValueError:
self.perror("{} isn't a valid integer exit code".format(arg_list[0]))
self.exit_code = -1
self.exit_code = 1

return True

Expand Down
2 changes: 1 addition & 1 deletion examples/pirate.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def postcmd(self, stop, line):
self.poutput('Now we gots {0} doubloons'.format(self.gold))
if self.gold < 0:
self.poutput("Off to debtorrr's prison.")
self.exit_code = -1
self.exit_code = 1
stop = True
return stop

Expand Down
7 changes: 5 additions & 2 deletions tests/test_cmd2.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,13 +537,16 @@ def test_system_exit_in_command(base_app, capsys):
"""Test raising SystemExit in a command"""
import types

exit_code = 5

def do_system_exit(self, _):
raise SystemExit
raise SystemExit(exit_code)

setattr(base_app, 'do_system_exit', types.MethodType(do_system_exit, base_app))

stop = base_app.onecmd_plus_hooks('system_exit')
assert stop
assert base_app.exit_code == exit_code


def test_passthrough_exception_in_command(base_app):
Expand Down Expand Up @@ -2323,7 +2326,7 @@ def do_exit(self, arg_list) -> bool:
self.exit_code = int(arg_list[0])
except ValueError:
self.perror("{} isn't a valid integer exit code".format(arg_list[0]))
self.exit_code = -1
self.exit_code = 1

# Return True to stop the command loop
return True
Expand Down
3 changes: 2 additions & 1 deletion tests/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ def cmdfinalization_hook_system_exit(
) -> cmd2.plugin.CommandFinalizationData:
"""A command finalization hook which raises a SystemExit"""
self.called_cmdfinalization += 1
raise SystemExit
raise SystemExit(5)

def cmdfinalization_hook_keyboard_interrupt(
self, data: cmd2.plugin.CommandFinalizationData
Expand Down Expand Up @@ -930,6 +930,7 @@ def test_cmdfinalization_hook_system_exit():
stop = app.onecmd_plus_hooks('say hello')
assert stop
assert app.called_cmdfinalization == 1
assert app.exit_code == 5


def test_cmdfinalization_hook_keyboard_interrupt():
Expand Down

0 comments on commit d8c3c7c

Please sign in to comment.