Skip to content

Commit d8c3c7c

Browse files
authored
Merge pull request #1078 from python-cmd2/exit_code
onecmd_plus_hooks() now sets self.exit_code when a SystemExit handled
2 parents fabe4cd + 3126eb7 commit d8c3c7c

File tree

7 files changed

+23
-10
lines changed

7 files changed

+23
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
* Settables now have new initialization parameters. It is now a required parameter to supply the reference to the
2222
object that holds the settable attribute. `cmd2.Cmd.settables` is no longer a public dict attribute - it is now a
2323
property that aggregates all Settables across all registered CommandSets.
24+
* Failed transcript testing now sets self.exit_code to 1 instead of -1.
2425
* Enhancements
2526
* Added support for custom tab completion and up-arrow input history to `cmd2.Cmd2.read_input`.
2627
See [read_input.py](https://github.com/python-cmd2/cmd2/blob/master/examples/read_input.py)
@@ -34,6 +35,9 @@
3435
may have a prepended prefix.
3536
* Settables now allow changes to be applied to any arbitrary object attribute. It no longer needs to match an
3637
attribute added to the cmd2 instance itself.
38+
* Raising ``SystemExit`` or calling ``sys.exit()`` in a command or hook function will set ``self.exit_code``
39+
to the exit code used in those calls. It will also result in the command loop stopping.
40+
3741
## 1.5.0 (January 31, 2021)
3842
* Bug Fixes
3943
* Fixed bug where setting `always_show_hint=True` did not show a hint when completing `Settables`

cmd2/cmd2.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2362,7 +2362,8 @@ def onecmd_plus_hooks(
23622362
except KeyboardInterrupt as ex:
23632363
if raise_keyboard_interrupt and not stop:
23642364
raise ex
2365-
except SystemExit:
2365+
except SystemExit as ex:
2366+
self.exit_code = ex.code
23662367
stop = True
23672368
except PassThroughException as ex:
23682369
raise ex.wrapped_ex
@@ -2374,7 +2375,8 @@ def onecmd_plus_hooks(
23742375
except KeyboardInterrupt as ex:
23752376
if raise_keyboard_interrupt and not stop:
23762377
raise ex
2377-
except SystemExit:
2378+
except SystemExit as ex:
2379+
self.exit_code = ex.code
23782380
stop = True
23792381
except PassThroughException as ex:
23802382
raise ex.wrapped_ex
@@ -4748,7 +4750,7 @@ class TestMyAppCase(Cmd2TestCase):
47484750
transcripts_expanded = utils.files_from_glob_patterns(transcript_paths, access=os.R_OK)
47494751
if not transcripts_expanded:
47504752
self.perror('No test files found - nothing to test')
4751-
self.exit_code = -1
4753+
self.exit_code = 1
47524754
return
47534755

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

47874789
# Return a failure error code to support automated transcript-based testing
4788-
self.exit_code = -1
4790+
self.exit_code = 1
47894791

47904792
def async_alert(self, alert_msg: str, new_prompt: Optional[str] = None) -> None: # pragma: no cover
47914793
"""

docs/features/commands.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ The ``cmd2.Cmd`` object sets an ``exit_code`` attribute to zero when it is
142142
instantiated. The value of this attribute is returned from the ``cmdloop()``
143143
call. Therefore, if you don't do anything with this attribute in your code,
144144
``cmdloop()`` will (almost) always return zero. There are a few built-in
145-
``cmd2`` commands which set ``exit_code`` to ``-1`` if an error occurs.
145+
``cmd2`` commands which set ``exit_code`` to ``1`` if an error occurs.
146146

147147
You can use this capability to easily return your own values to the operating
148148
system shell::
@@ -175,6 +175,9 @@ the following interaction::
175175
2
176176

177177

178+
Raising ``SystemExit(code)`` or calling ``sys.exit(code)`` in a command
179+
or hook function also sets ``self.exit_code`` and stops the program.
180+
178181
Exception Handling
179182
------------------
180183

examples/exit_code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def do_exit(self, arg_list: List[str]) -> bool:
2828
self.exit_code = int(arg_list[0])
2929
except ValueError:
3030
self.perror("{} isn't a valid integer exit code".format(arg_list[0]))
31-
self.exit_code = -1
31+
self.exit_code = 1
3232

3333
return True
3434

examples/pirate.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def postcmd(self, stop, line):
4646
self.poutput('Now we gots {0} doubloons'.format(self.gold))
4747
if self.gold < 0:
4848
self.poutput("Off to debtorrr's prison.")
49-
self.exit_code = -1
49+
self.exit_code = 1
5050
stop = True
5151
return stop
5252

tests/test_cmd2.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,13 +537,16 @@ def test_system_exit_in_command(base_app, capsys):
537537
"""Test raising SystemExit in a command"""
538538
import types
539539

540+
exit_code = 5
541+
540542
def do_system_exit(self, _):
541-
raise SystemExit
543+
raise SystemExit(exit_code)
542544

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

545547
stop = base_app.onecmd_plus_hooks('system_exit')
546548
assert stop
549+
assert base_app.exit_code == exit_code
547550

548551

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

23282331
# Return True to stop the command loop
23292332
return True

tests/test_plugin.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ def cmdfinalization_hook_system_exit(
229229
) -> cmd2.plugin.CommandFinalizationData:
230230
"""A command finalization hook which raises a SystemExit"""
231231
self.called_cmdfinalization += 1
232-
raise SystemExit
232+
raise SystemExit(5)
233233

234234
def cmdfinalization_hook_keyboard_interrupt(
235235
self, data: cmd2.plugin.CommandFinalizationData
@@ -930,6 +930,7 @@ def test_cmdfinalization_hook_system_exit():
930930
stop = app.onecmd_plus_hooks('say hello')
931931
assert stop
932932
assert app.called_cmdfinalization == 1
933+
assert app.exit_code == 5
933934

934935

935936
def test_cmdfinalization_hook_keyboard_interrupt():

0 commit comments

Comments
 (0)