# HG changeset patch # User Laurent Peuch # Date 1564018381 -7200 # Node ID 17d1b1f4eddd19f4a1f6bf9fafae7d3581528459 # Parent d177d8ab4fd3bd7cbfb773688136a3c023e42fb0 [cubicweb-ctl/fix] correctly get exception traceback_ for pdb.post_mortem In python 3 the behavior of sys.exc_info had a very subtle change: - in python 2 you can call if whenever you want after a try/except statement and you'll get information about this last raise - ipython 3, once you get out of try/except, sys.exc_info is cleaned and you'll get (None, None, None) Hardened the test to avoid this error from happening again. diff -r d177d8ab4fd3 -r 17d1b1f4eddd cubicweb/cwctl.py --- a/cubicweb/cwctl.py Tue Oct 15 15:30:56 2019 +0200 +++ b/cubicweb/cwctl.py Thu Jul 25 03:33:01 2019 +0200 @@ -22,6 +22,7 @@ # possible (for cubicweb-ctl reactivity, necessary for instance for usable bash # completion). So import locally in command helpers. import sys +import traceback from warnings import filterwarnings from os import listdir from os.path import exists, join, isdir @@ -136,14 +137,22 @@ appid = args[0] cmdmeth = getattr(self, '%s_instance' % self.name) + traceback_ = None + try: status = cmdmeth(appid) or 0 except (ExecutionError, ConfigurationError) as ex: + # we need to do extract this information here for pdb since it is + # now lost in python 3 once we exit the try/catch statement + exception_type, exception, traceback_ = sys.exc_info() + sys.stderr.write('instance %s not %s: %s\n' % ( appid, self.actionverb, ex)) status = 4 except Exception as ex: - import traceback + # idem + exception_type, exception, traceback_ = sys.exc_info() + traceback.print_exc() sys.stderr.write('instance %s not %s: %s\n' % ( @@ -151,6 +160,9 @@ status = 8 except (KeyboardInterrupt, SystemExit) as ex: + # idem + exception_type, exception, traceback_ = sys.exc_info() + sys.stderr.write('%s aborted\n' % self.name) if isinstance(ex, KeyboardInterrupt): status = 2 # specific error code @@ -158,9 +170,15 @@ status = ex.code if status != 0 and self.config.pdb: - exception_type, exception, traceback_ = sys.exc_info() pdb = get_pdb() - pdb.post_mortem(traceback_) + + if traceback_ is not None: + pdb.post_mortem(traceback_) + else: + print("WARNING: Could not access to the traceback because the command return " + "code is different than 0 but the command didn't raised an exception.") + # we can't use "header=" of set_trace because ipdb doesn't supports it + pdb.set_trace() sys.exit(status) diff -r d177d8ab4fd3 -r 17d1b1f4eddd cubicweb/test/unittest_cwctl.py --- a/cubicweb/test/unittest_cwctl.py Tue Oct 15 15:30:56 2019 +0200 +++ b/cubicweb/test/unittest_cwctl.py Thu Jul 25 03:33:01 2019 +0200 @@ -139,6 +139,9 @@ get_pdb.assert_called_once() post_mortem.assert_called_once() + # we want post_mortem to actually receive the traceback + self.assertNotEqual(post_mortem.call_args, ((None,),)) + @patch.dict(sys.modules, ipdb=MagicMock()) def test_ipdb_selected_and_called(self): ipdb = sys.modules['ipdb']