[PATCH 7 of 7 V2] [cubicweb-ctl/fix] correctly get exception traceback_ for pdb.post_mortem

Laurent Peuch cortex at worlddomination.be
Fri Sep 20 06:52:41 CEST 2019


# HG changeset patch
# User Laurent Peuch <cortex at worlddomination.be>
# Date 1564018381 -7200
#      Thu Jul 25 03:33:01 2019 +0200
# Node ID 9afd320c675c3a03035c3dc1fcd04bc1e5af39d4
# Parent  848959d73d6e33c1290f20eb1813d07e5cef7d67
[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 --git a/cubicweb/cwctl.py b/cubicweb/cwctl.py
--- a/cubicweb/cwctl.py
+++ b/cubicweb/cwctl.py
@@ -22,6 +22,7 @@ provide a pluggable commands system.
 # 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
@@ -174,14 +175,22 @@ class InstanceCommand(Command):
         if self['dbglevel']:
             set_debug('|'.join('DBG_' + x.upper() for x in self['dbglevel']))
 
+        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' % (
@@ -189,6 +198,9 @@ class InstanceCommand(Command):
             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
@@ -196,9 +208,15 @@ class InstanceCommand(Command):
                 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 --git a/cubicweb/test/unittest_cwctl.py b/cubicweb/test/unittest_cwctl.py
--- a/cubicweb/test/unittest_cwctl.py
+++ b/cubicweb/test/unittest_cwctl.py
@@ -142,6 +142,9 @@ class InstanceCommandTest(unittest.TestC
         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']



More information about the cubicweb-devel mailing list