Support pudb as a different post mortem debugger
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
This commit is contained in:
parent
89554315da
commit
2c99a24351
21
TESTING.rst
21
TESTING.rst
@ -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
|
||||
|
@ -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
|
||||
|
@ -13,18 +13,38 @@
|
||||
# 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):
|
||||
|
@ -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):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user