diff options
author | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2021-03-02 10:26:45 -0500 |
---|---|---|
committer | Kevin Van Brunt <kmvanbrunt@gmail.com> | 2021-03-02 10:26:45 -0500 |
commit | 7024aa0e705e522e8e0cc9b4555bb17a5a35ed45 (patch) | |
tree | f27278d32dbc5d9ee19dbb2f5dd929b943df4789 | |
parent | 6b14aa6579d9c5bdd32bf8c3d67f11db8b575bb0 (diff) | |
download | cmd2-git-exception_passthrough.tar.gz |
Added cmd2.exceptions.PassThroughExceptionexception_passthrough
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | cmd2/cmd2.py | 5 | ||||
-rw-r--r-- | cmd2/exceptions.py | 15 | ||||
-rw-r--r-- | docs/api/exceptions.rst | 3 | ||||
-rwxr-xr-x | tests/test_cmd2.py | 17 | ||||
-rw-r--r-- | tests/test_plugin.py | 22 |
6 files changed, 60 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index afed0b53..1e049bc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ * 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) for an example. + * Added `cmd2.exceptions.PassThroughException` to raise unhandled command exceptions instead of printing them. ## 1.5.0 (January 31, 2021) * Bug Fixes diff --git a/cmd2/cmd2.py b/cmd2/cmd2.py index 0b198111..d01dfaa0 100644 --- a/cmd2/cmd2.py +++ b/cmd2/cmd2.py @@ -95,6 +95,7 @@ from .exceptions import ( CompletionError, EmbeddedConsoleExit, EmptyStatement, + PassThroughException, RedirectionError, SkipPostcommandHooks, ) @@ -2259,6 +2260,8 @@ class Cmd(cmd.Cmd): raise ex except SystemExit: stop = True + except PassThroughException as ex: + raise ex.wrapped_ex except Exception as ex: self.pexcept(ex) finally: @@ -2269,6 +2272,8 @@ class Cmd(cmd.Cmd): raise ex except SystemExit: stop = True + except PassThroughException as ex: + raise ex.wrapped_ex except Exception as ex: self.pexcept(ex) diff --git a/cmd2/exceptions.py b/cmd2/exceptions.py index 39b7e333..b45167d1 100644 --- a/cmd2/exceptions.py +++ b/cmd2/exceptions.py @@ -62,6 +62,21 @@ class CompletionError(Exception): super().__init__(*args) +class PassThroughException(Exception): + """ + Normally all unhandled exceptions raised during commands get printed to the user. + This class is used to wrap an exception that should be raised instead of printed. + """ + + def __init__(self, *args, wrapped_ex: BaseException): + """ + Initializer for PassThroughException + :param wrapped_ex: the exception that will be raised + """ + self.wrapped_ex = wrapped_ex + super().__init__(*args) + + ############################################################################################################ # The following exceptions are NOT part of the public API and are intended for internal use only. ############################################################################################################ diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst index 98afa97a..efa26957 100644 --- a/docs/api/exceptions.rst +++ b/docs/api/exceptions.rst @@ -15,3 +15,6 @@ Custom cmd2 exceptions .. autoclass:: cmd2.exceptions.CompletionError :members: + +.. autoclass:: cmd2.exceptions.PassThroughException + :members: diff --git a/tests/test_cmd2.py b/tests/test_cmd2.py index 0c3333c1..b4b13945 100755 --- a/tests/test_cmd2.py +++ b/tests/test_cmd2.py @@ -534,7 +534,7 @@ def test_in_script(request): def test_system_exit_in_command(base_app, capsys): - """Test raising SystemExit from a command""" + """Test raising SystemExit in a command""" import types def do_system_exit(self, _): @@ -546,6 +546,21 @@ def test_system_exit_in_command(base_app, capsys): assert stop +def test_passthrough_exception_in_command(base_app): + """Test raising a PassThroughException in a command""" + import types + + def do_passthrough(self, _): + wrapped_ex = OSError("Pass me up") + raise exceptions.PassThroughException(wrapped_ex=wrapped_ex) + + setattr(base_app, 'do_passthrough', types.MethodType(do_passthrough, base_app)) + + with pytest.raises(OSError) as excinfo: + base_app.onecmd_plus_hooks('passthrough') + assert 'Pass me up' in str(excinfo.value) + + def test_output_redirection(base_app): fd, filename = tempfile.mkstemp(prefix='cmd2_test', suffix='.txt') os.close(fd) diff --git a/tests/test_plugin.py b/tests/test_plugin.py index 97c046a2..1e12d655 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -238,6 +238,14 @@ class Plugin: self.called_cmdfinalization += 1 raise KeyboardInterrupt + def cmdfinalization_hook_passthrough_exception( + self, data: cmd2.plugin.CommandFinalizationData + ) -> cmd2.plugin.CommandFinalizationData: + """A command finalization hook which raises a PassThroughException""" + self.called_cmdfinalization += 1 + wrapped_ex = OSError("Pass me up") + raise exceptions.PassThroughException(wrapped_ex=wrapped_ex) + def cmdfinalization_hook_not_enough_parameters(self) -> plugin.CommandFinalizationData: """A command finalization hook with no parameters.""" pass @@ -916,7 +924,7 @@ def test_cmdfinalization_hook_exception(capsys): assert app.called_cmdfinalization == 1 -def test_cmdfinalization_hook_system_exit(capsys): +def test_cmdfinalization_hook_system_exit(): app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook_system_exit) stop = app.onecmd_plus_hooks('say hello') @@ -924,7 +932,7 @@ def test_cmdfinalization_hook_system_exit(capsys): assert app.called_cmdfinalization == 1 -def test_cmdfinalization_hook_keyboard_interrupt(capsys): +def test_cmdfinalization_hook_keyboard_interrupt(): app = PluggedApp() app.register_cmdfinalization_hook(app.cmdfinalization_hook_keyboard_interrupt) @@ -947,6 +955,16 @@ def test_cmdfinalization_hook_keyboard_interrupt(capsys): assert app.called_cmdfinalization == 1 +def test_cmdfinalization_hook_passthrough_exception(): + app = PluggedApp() + app.register_cmdfinalization_hook(app.cmdfinalization_hook_passthrough_exception) + + with pytest.raises(OSError) as excinfo: + app.onecmd_plus_hooks('say hello') + assert 'Pass me up' in str(excinfo.value) + assert app.called_cmdfinalization == 1 + + def test_skip_postcmd_hooks(capsys): app = PluggedApp() app.register_postcmd_hook(app.postcmd_hook) |