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
|
Post-mortem debugging
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
Setting OS_POST_MORTEM_DEBUG=1 in the shell environment will ensure
|
Setting OS_POST_MORTEM_DEBUGGER in the shell environment will ensure
|
||||||
that pdb.post_mortem() will be invoked on test failure::
|
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()
|
super(BaseTestCase, self).setUp()
|
||||||
|
|
||||||
# Configure this first to ensure pm debugging support for setUp()
|
# Configure this first to ensure pm debugging support for setUp()
|
||||||
if os.environ.get('OS_POST_MORTEM_DEBUG') in TRUE_STRING:
|
debugger = os.environ.get('OS_POST_MORTEM_DEBUGGER')
|
||||||
self.addOnException(post_mortem_debug.exception_handler)
|
if debugger:
|
||||||
|
self.addOnException(post_mortem_debug.get_exception_handler(
|
||||||
|
debugger))
|
||||||
|
|
||||||
if os.environ.get('OS_DEBUG') in TRUE_STRING:
|
if os.environ.get('OS_DEBUG') in TRUE_STRING:
|
||||||
_level = std_logging.DEBUG
|
_level = std_logging.DEBUG
|
||||||
|
@ -13,18 +13,38 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import pdb
|
import functools
|
||||||
import traceback
|
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.
|
"""Exception handler enabling post-mortem debugging.
|
||||||
|
|
||||||
A class extending testtools.TestCase can add this handler in setUp():
|
A class extending testtools.TestCase can add this handler in setUp():
|
||||||
|
|
||||||
self.addOnException(post_mortem_debug.exception_handler)
|
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.
|
session in the execution environment of the failure.
|
||||||
|
|
||||||
Frames associated with the testing framework are excluded so that
|
Frames associated with the testing framework are excluded so that
|
||||||
@ -37,7 +57,7 @@ def exception_handler(exc_info):
|
|||||||
if ignored_traceback:
|
if ignored_traceback:
|
||||||
tb = FilteredTraceback(tb, ignored_traceback)
|
tb = FilteredTraceback(tb, ignored_traceback)
|
||||||
traceback.print_exception(exc_info[0], exc_info[1], tb)
|
traceback.print_exception(exc_info[0], exc_info[1], tb)
|
||||||
pdb.post_mortem(tb)
|
debugger.post_mortem(tb)
|
||||||
|
|
||||||
|
|
||||||
def get_ignored_traceback(tb):
|
def get_ignored_traceback(tb):
|
||||||
|
@ -34,13 +34,26 @@ class TestTesttoolsExceptionHandler(base.BaseTestCase):
|
|||||||
with mock.patch.object(post_mortem_debug,
|
with mock.patch.object(post_mortem_debug,
|
||||||
'get_ignored_traceback',
|
'get_ignored_traceback',
|
||||||
return_value=mock.Mock()):
|
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
|
# traceback will become post_mortem_debug.FilteredTraceback
|
||||||
filtered_exc_info = (exc_info[0], exc_info[1], mock.ANY)
|
filtered_exc_info = (exc_info[0], exc_info[1], mock.ANY)
|
||||||
mock_print_exception.assert_called_once_with(*filtered_exc_info)
|
mock_print_exception.assert_called_once_with(*filtered_exc_info)
|
||||||
mock_post_mortem.assert_called_once_with(mock.ANY)
|
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):
|
class TestFilteredTraceback(base.BaseTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user