]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Support pudb as a different post mortem debugger
authorMiguel Angel Ajo <mangelajo@redhat.com>
Tue, 28 Oct 2014 16:09:46 +0000 (17:09 +0100)
committerMiguel Angel Ajo <mangelajo@redhat.com>
Fri, 14 Nov 2014 11:30:20 +0000 (12:30 +0100)
Post mortem debugger starts pdb at the assert failure place
when OS_POST_MORTEM_DEBUGGER is set and an exception or assert
failure happens.

This patch adds a neutron env variable OS_POST_MORTEM_DEBUGGER
to allow invocation of pudb instead of pdb. pudb module is not
a hard requisite as it will be only imported if you set
OS_POST_MORTEM_DEBUGGER=pudb.

The old OS_POST_MORTEM_DEBUG env variable is removed now.

Change-Id: I5d40913add439cf9c30305bafc98af9f8cd4d12f

TESTING.rst
neutron/tests/base.py
neutron/tests/post_mortem_debug.py
neutron/tests/unit/test_post_mortem_debug.py

index f5ca3bb3ee434ef52ff88edbbe6a852114604728..d99696f8bf05ea7fc5e3886641e8e8426ee2abf2 100644 (file)
@@ -184,7 +184,22 @@ overwritten during the next tox run.
 Post-mortem debugging
 ~~~~~~~~~~~~~~~~~~~~~
 
-Setting OS_POST_MORTEM_DEBUG=1 in the shell environment will ensure
-that pdb.post_mortem() will be invoked on test failure::
+Setting OS_POST_MORTEM_DEBUGGER in the shell environment will ensure
+that the debugger .post_mortem() method will be invoked on test failure::
 
-    $ OS_POST_MORTEM_DEBUG=1 ./run_tests.sh -d [test module path]
+    $ OS_POST_MORTEM_DEBUGGER=pdb ./run_tests.sh -d [test module path]
+
+Supported debuggers are pdb, and pudb. Pudb is full-screen, console-based
+visual debugger for Python which let you inspect variables, the stack,
+and breakpoints in a very visual way, keeping a high degree of compatibility
+with pdb::
+
+    $ ./.venv/bin/pip install pudb
+
+    $ OS_POST_MORTEM_DEBUGGER=pudb ./run_tests.sh -d [test module path]
+
+References
+==========
+
+.. [#pudb] PUDB debugger:
+   https://pypi.python.org/pypi/pudb
index dae1320de5cb50b5b3fccec487d9def68b7d2fab..b4bd93a4cdc101301f82ae422ea8ee63cb20d983 100644 (file)
@@ -73,8 +73,10 @@ class BaseTestCase(testtools.TestCase):
         super(BaseTestCase, self).setUp()
 
         # Configure this first to ensure pm debugging support for setUp()
-        if os.environ.get('OS_POST_MORTEM_DEBUG') in TRUE_STRING:
-            self.addOnException(post_mortem_debug.exception_handler)
+        debugger = os.environ.get('OS_POST_MORTEM_DEBUGGER')
+        if debugger:
+            self.addOnException(post_mortem_debug.get_exception_handler(
+                debugger))
 
         if os.environ.get('OS_DEBUG') in TRUE_STRING:
             _level = std_logging.DEBUG
index 70b99412b03fbcd52f102b4e1c3fd51782eb0e7c..8e6ac61a2cc71c1aa9226a5ee24fd6bd54ccb9e9 100644 (file)
 # License for the specific language governing permissions and limitations
 # under the License.
 
-import pdb
+import functools
 import traceback
 
+DEFAULT_DEBUGGER = 'pdb'
 
-def exception_handler(exc_info):
+
+def get_exception_handler(debugger_name=None):
+    debugger = _get_debugger(debugger_name or DEFAULT_DEBUGGER)
+    return functools.partial(_exception_handler, debugger)
+
+
+def _get_debugger(debugger_name):
+    try:
+        debugger = __import__(debugger_name)
+        if 'post_mortem' in dir(debugger):
+            return debugger
+    except ImportError:
+        raise ValueError(
+            _("can't import %s module as a post mortem debugger") %
+            debugger_name)
+    raise ValueError(
+        _("%s is not a supported post mortem debugger") % debugger_name)
+
+
+def _exception_handler(debugger, exc_info):
     """Exception handler enabling post-mortem debugging.
 
     A class extending testtools.TestCase can add this handler in setUp():
 
         self.addOnException(post_mortem_debug.exception_handler)
 
-    When an exception occurs, the user will be dropped into a pdb
+    When an exception occurs, the user will be dropped into a debugger
     session in the execution environment of the failure.
 
     Frames associated with the testing framework are excluded so that
@@ -37,7 +57,7 @@ def exception_handler(exc_info):
     if ignored_traceback:
         tb = FilteredTraceback(tb, ignored_traceback)
     traceback.print_exception(exc_info[0], exc_info[1], tb)
-    pdb.post_mortem(tb)
+    debugger.post_mortem(tb)
 
 
 def get_ignored_traceback(tb):
index c42a3d68fb3b7e2d8881480309934eadb6c424bd..3237558c1769720d33b559ad5d054b83c7627998 100644 (file)
@@ -34,13 +34,26 @@ class TestTesttoolsExceptionHandler(base.BaseTestCase):
                 with mock.patch.object(post_mortem_debug,
                                        'get_ignored_traceback',
                                        return_value=mock.Mock()):
-                    post_mortem_debug.exception_handler(exc_info)
+                    post_mortem_debug.get_exception_handler()(exc_info)
 
         # traceback will become post_mortem_debug.FilteredTraceback
         filtered_exc_info = (exc_info[0], exc_info[1], mock.ANY)
         mock_print_exception.assert_called_once_with(*filtered_exc_info)
         mock_post_mortem.assert_called_once_with(mock.ANY)
 
+    def test__get_debugger(self):
+        def import_mock(name, *args):
+            mod_mock = mock.Mock()
+            mod_mock.__name__ = name
+            mod_mock.post_mortem = mock.Mock()
+            return mod_mock
+
+        with mock.patch('__builtin__.__import__', side_effect=import_mock):
+                pdb_debugger = post_mortem_debug._get_debugger('pdb')
+                pudb_debugger = post_mortem_debug._get_debugger('pudb')
+                self.assertEqual('pdb', pdb_debugger.__name__)
+                self.assertEqual('pudb', pudb_debugger.__name__)
+
 
 class TestFilteredTraceback(base.BaseTestCase):