# HG changeset patch # User Laurent Peuch # Date 1563787270 -7200 # Node ID eb83daa6949545bb81bcac6b5fda41d73a37837a # Parent 38dfd90c335a24e418e96635243e9f366a28516b [cubicweb-ctl] respect sys.exit status code when aborting a command When exploring the stack of all calls to a cubicweb-ctl command, it has been discovered than on a KeyboardInterrupt and on a SystemExit exception the base class InstanceCommand (for commands that works on one instance) will always set the return code of cubicweb-ctl to 8: this mean that if another command do a `sys.exit(some_code)` the exit code will be ignored and overwritten by '8'. This behavior is not intuitive, apparently not documented and doesn't seems to have any justification. It also prevent commands from exciting with different return codes which could be a desired behavior in the situation of scripting. diff -r 38dfd90c335a -r eb83daa69495 cubicweb/cwctl.py --- a/cubicweb/cwctl.py Mon Jul 22 11:32:12 2019 +0200 +++ b/cubicweb/cwctl.py Mon Jul 22 11:21:10 2019 +0200 @@ -150,9 +150,12 @@ appid, self.actionverb, ex)) status = 8 - except (KeyboardInterrupt, SystemExit): + except (KeyboardInterrupt, SystemExit) as ex: sys.stderr.write('%s aborted\n' % self.name) - status = 2 # specific error code + if isinstance(ex, KeyboardInterrupt): + status = 2 # specific error code + else: + status = ex.code if status != 0 and self.config.pdb: exception_type, exception, traceback_ = sys.exc_info() diff -r 38dfd90c335a -r eb83daa69495 cubicweb/test/unittest_cwctl.py --- a/cubicweb/test/unittest_cwctl.py Mon Jul 22 11:32:12 2019 +0200 +++ b/cubicweb/test/unittest_cwctl.py Mon Jul 22 11:21:10 2019 +0200 @@ -148,6 +148,22 @@ ipdb.post_mortem.assert_called_once() + @patch.object(_TestFailCommand, 'test_fail_instance', side_effect=SystemExit(42)) + def test_respect_return_error_code(self, test_fail_instance): + with self.assertRaises(SystemExit) as cm: + self.CWCTL.run(["test_fail", "some_instance"]) + self.assertEqual(cm.exception.code, 42) + + test_fail_instance.assert_called_once() + + @patch.object(_TestFailCommand, 'test_fail_instance', side_effect=KeyboardInterrupt) + def test_error_code_keyboardinterupt_2(self, test_fail_instance): + with self.assertRaises(SystemExit) as cm: + self.CWCTL.run(["test_fail", "some_instance"]) + self.assertEqual(cm.exception.code, 2) + + test_fail_instance.assert_called_once() + if __name__ == '__main__': unittest.main()