diff options
-rw-r--r-- | cliff/app.py | 9 | ||||
-rw-r--r-- | cliff/tests/test_app.py | 39 |
2 files changed, 48 insertions, 0 deletions
diff --git a/cliff/app.py b/cliff/app.py index d185214..798b41f 100644 --- a/cliff/app.py +++ b/cliff/app.py @@ -33,6 +33,7 @@ logging.getLogger('cliff').addHandler(logging.NullHandler()) # Exit code for exiting due to a signal is 128 + the signal number _SIGINT_EXIT = 130 +_SIGPIPE_EXIT = 141 class App(object): @@ -256,6 +257,8 @@ class App(object): remainder.insert(0, "help") self.initialize_app(remainder) self.print_help_if_requested() + except BrokenPipeError: + return _SIGPIPE_EXIT except Exception as err: if hasattr(self, 'options'): debug = self.options.debug @@ -275,6 +278,8 @@ class App(object): else: try: result = self.run_subcommand(remainder) + except BrokenPipeError: + return _SIGPIPE_EXIT except KeyboardInterrupt: return _SIGINT_EXIT return result @@ -400,6 +405,10 @@ class App(object): except SystemExit as ex: raise cmd2.exceptions.Cmd2ArgparseError from ex result = cmd.run(parsed_args) + except BrokenPipeError as err1: + result = _SIGPIPE_EXIT + err = err1 + raise except help.HelpExit: result = 0 except Exception as err1: diff --git a/cliff/tests/test_app.py b/cliff/tests/test_app.py index 12a42f4..d38861c 100644 --- a/cliff/tests/test_app.py +++ b/cliff/tests/test_app.py @@ -54,6 +54,15 @@ def make_app(**kwargs): interrupt_command.return_value = interrupt_command_inst cmd_mgr.add_command('interrupt', interrupt_command) + # Register a command that is interrrupted by a broken pipe + pipeclose_command = mock.Mock(name='pipeclose_command', spec=c_cmd.Command) + pipeclose_command_inst = mock.Mock(spec=c_cmd.Command) + pipeclose_command_inst.run = mock.Mock( + side_effect=BrokenPipeError + ) + pipeclose_command.return_value = pipeclose_command_inst + cmd_mgr.add_command('pipe-close', pipeclose_command) + app = application.App('testing interactive mode', '1', cmd_mgr, @@ -121,6 +130,11 @@ class TestInitAndCleanup(base.TestBase): result = app.run(['interrupt']) self.assertEqual(result, 130) + def test_pipeclose_command(self): + app, command = make_app() + result = app.run(['pipe-close']) + self.assertEqual(result, 141) + def test_clean_up_success(self): app, command = make_app() app.clean_up = mock.MagicMock(name='clean_up') @@ -169,6 +183,19 @@ class TestInitAndCleanup(base.TestBase): args, kwargs = call_args self.assertIsInstance(args[2], KeyboardInterrupt) + def test_clean_up_pipeclose(self): + app, command = make_app() + + app.clean_up = mock.MagicMock(name='clean_up') + ret = app.run(['pipe-close']) + self.assertNotEqual(ret, 0) + + app.clean_up.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY) + call_args = app.clean_up.call_args_list[0] + self.assertEqual(mock.call(mock.ANY, 141, mock.ANY), call_args) + args, kwargs = call_args + self.assertIsInstance(args[2], BrokenPipeError) + def test_error_handling_clean_up_raises_exception(self): app, command = make_app() @@ -356,6 +383,18 @@ class TestHelpHandling(base.TestBase): def test_interrupted_deferred_help(self): self._test_interrupted_help(True) + def _test_pipeclose_help(self, deferred_help): + app, _ = make_app(deferred_help=deferred_help) + with mock.patch('cliff.help.HelpAction.__call__', + side_effect=BrokenPipeError): + app.run(['--help']) + + def test_pipeclose_help(self): + self._test_pipeclose_help(False) + + def test_pipeclose_deferred_help(self): + self._test_pipeclose_help(True) + def test_subcommand_help(self): app, _ = make_app(deferred_help=False) |