mirror of https://github.com/python/cpython
#5142: add module skipping feature to pdb.
This commit is contained in:
parent
e3869c41f2
commit
4d4313d59d
|
@ -1,4 +1,3 @@
|
|||
|
||||
.. _debugger:
|
||||
|
||||
:mod:`pdb` --- The Python Debugger
|
||||
|
@ -53,7 +52,16 @@ useful than quitting the debugger upon program's exit.
|
|||
.. versionadded:: 2.4
|
||||
Restarting post-mortem behavior added.
|
||||
|
||||
Typical usage to inspect a crashed program is::
|
||||
The typical usage to break into the debugger from a running program is to
|
||||
insert ::
|
||||
|
||||
import pdb; pdb.set_trace()
|
||||
|
||||
at the location you want to break into the debugger. You can then step through
|
||||
the code following this statement, and continue running without debugger using
|
||||
the ``c`` command.
|
||||
|
||||
The typical usage to inspect a crashed program is::
|
||||
|
||||
>>> import pdb
|
||||
>>> import mymodule
|
||||
|
@ -70,10 +78,10 @@ Typical usage to inspect a crashed program is::
|
|||
-> print spam
|
||||
(Pdb)
|
||||
|
||||
|
||||
The module defines the following functions; each enters the debugger in a
|
||||
slightly different way:
|
||||
|
||||
|
||||
.. function:: run(statement[, globals[, locals]])
|
||||
|
||||
Execute the *statement* (given as a string) under debugger control. The
|
||||
|
@ -117,7 +125,38 @@ slightly different way:
|
|||
|
||||
.. function:: pm()
|
||||
|
||||
Enter post-mortem debugging of the traceback found in ``sys.last_traceback``.
|
||||
Enter post-mortem debugging of the traceback found in
|
||||
:data:`sys.last_traceback`.
|
||||
|
||||
|
||||
The ``run_*`` functions and :func:`set_trace` are aliases for instantiating the
|
||||
:class:`Pdb` class and calling the method of the same name. If you want to
|
||||
access further features, you have to do this yourself:
|
||||
|
||||
.. class:: Pdb(completekey='tab', stdin=None, stdout=None, skip=None)
|
||||
|
||||
:class:`Pdb` is the debugger class.
|
||||
|
||||
The *completekey*, *stdin* and *stdout* arguments are passed to the
|
||||
underlying :class:`cmd.Cmd` class; see the description there.
|
||||
|
||||
The *skip* argument, if given, must be an iterable of glob-style module name
|
||||
patterns. The debugger will not step into frames that originate in a module
|
||||
that matches one of these patterns. [1]_
|
||||
|
||||
Example call to enable tracing with *skip*::
|
||||
|
||||
import pdb; pdb.Pdb(skip=['django.*']).set_trace()
|
||||
|
||||
.. versionadded:: 2.7
|
||||
The *skip* argument.
|
||||
|
||||
.. method:: run(statement[, globals[, locals]])
|
||||
runeval(expression[, globals[, locals]])
|
||||
runcall(function[, argument, ...])
|
||||
set_trace()
|
||||
|
||||
See the documentation for the functions explained above.
|
||||
|
||||
|
||||
.. _debugger-commands:
|
||||
|
@ -351,3 +390,9 @@ run [*args* ...]
|
|||
|
||||
q(uit)
|
||||
Quit from the debugger. The program being executed is aborted.
|
||||
|
||||
|
||||
.. rubric:: Footnotes
|
||||
|
||||
.. [1] Whether a frame is considered to originate in a certain module
|
||||
is determined by the ``__name__`` in the frame globals.
|
||||
|
|
13
Lib/bdb.py
13
Lib/bdb.py
|
@ -1,5 +1,6 @@
|
|||
"""Debugger basics"""
|
||||
|
||||
import fnmatch
|
||||
import sys
|
||||
import os
|
||||
import types
|
||||
|
@ -19,7 +20,8 @@ class Bdb:
|
|||
The standard debugger class (pdb.Pdb) is an example.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, skip=None):
|
||||
self.skip = set(skip) if skip else None
|
||||
self.breaks = {}
|
||||
self.fncache = {}
|
||||
|
||||
|
@ -94,9 +96,18 @@ class Bdb:
|
|||
# methods, but they may if they want to redefine the
|
||||
# definition of stopping and breakpoints.
|
||||
|
||||
def is_skipped_module(self, module_name):
|
||||
for pattern in self.skip:
|
||||
if fnmatch.fnmatch(module_name, pattern):
|
||||
return True
|
||||
return False
|
||||
|
||||
def stop_here(self, frame):
|
||||
# (CT) stopframe may now also be None, see dispatch_call.
|
||||
# (CT) the former test for None is therefore removed from here.
|
||||
if self.skip and \
|
||||
self.is_skipped_module(frame.f_globals.get('__name__')):
|
||||
return False
|
||||
if frame is self.stopframe:
|
||||
return frame.f_lineno >= self.stoplineno
|
||||
while frame is not None and frame is not self.stopframe:
|
||||
|
|
|
@ -58,8 +58,8 @@ line_prefix = '\n-> ' # Probably a better default
|
|||
|
||||
class Pdb(bdb.Bdb, cmd.Cmd):
|
||||
|
||||
def __init__(self, completekey='tab', stdin=None, stdout=None):
|
||||
bdb.Bdb.__init__(self)
|
||||
def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None):
|
||||
bdb.Bdb.__init__(self, skip=skip)
|
||||
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
||||
if stdout:
|
||||
self.use_rawinput = 0
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# A test suite for pdb; at the moment, this only validates skipping of
|
||||
# specified test modules (RFE #5142).
|
||||
|
||||
import imp
|
||||
import os
|
||||
import sys
|
||||
import doctest
|
||||
import tempfile
|
||||
|
||||
from test import test_support
|
||||
# This little helper class is essential for testing pdb under doctest.
|
||||
from test_doctest import _FakeInput
|
||||
|
||||
|
||||
def test_pdb_skip_modules():
|
||||
"""This illustrates the simple case of module skipping.
|
||||
|
||||
>>> def skip_module():
|
||||
... import string
|
||||
... import pdb;pdb.Pdb(skip=['string*']).set_trace()
|
||||
... string.lower('FOO')
|
||||
>>> real_stdin = sys.stdin
|
||||
>>> sys.stdin = _FakeInput([
|
||||
... 'step',
|
||||
... 'continue',
|
||||
... ])
|
||||
|
||||
>>> try:
|
||||
... skip_module()
|
||||
... finally:
|
||||
... sys.stdin = real_stdin
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()
|
||||
-> string.lower('FOO')
|
||||
(Pdb) step
|
||||
--Return--
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules[0]>(4)skip_module()->None
|
||||
-> string.lower('FOO')
|
||||
(Pdb) continue
|
||||
"""
|
||||
|
||||
|
||||
# Module for testing skipping of module that makes a callback
|
||||
mod = imp.new_module('module_to_skip')
|
||||
exec 'def foo_pony(callback): x = 1; callback(); return None' in mod.__dict__
|
||||
|
||||
|
||||
def test_pdb_skip_modules_with_callback():
|
||||
"""This illustrates skipping of modules that call into other code.
|
||||
|
||||
>>> def skip_module():
|
||||
... def callback():
|
||||
... return None
|
||||
... import pdb;pdb.Pdb(skip=['module_to_skip*']).set_trace()
|
||||
... mod.foo_pony(callback)
|
||||
>>> real_stdin = sys.stdin
|
||||
>>> sys.stdin = _FakeInput([
|
||||
... 'step',
|
||||
... 'step',
|
||||
... 'step',
|
||||
... 'step',
|
||||
... 'step',
|
||||
... 'continue',
|
||||
... ])
|
||||
|
||||
>>> try:
|
||||
... skip_module()
|
||||
... finally:
|
||||
... sys.stdin = real_stdin
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()
|
||||
-> mod.foo_pony(callback)
|
||||
(Pdb) step
|
||||
--Call--
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(2)callback()
|
||||
-> def callback():
|
||||
(Pdb) step
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback()
|
||||
-> return None
|
||||
(Pdb) step
|
||||
--Return--
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(3)callback()->None
|
||||
-> return None
|
||||
(Pdb) step
|
||||
--Return--
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[0]>(5)skip_module()->None
|
||||
-> mod.foo_pony(callback)
|
||||
(Pdb) step
|
||||
> <doctest test.test_pdb.test_pdb_skip_modules_with_callback[3]>(4)<module>()
|
||||
-> sys.stdin = real_stdin
|
||||
(Pdb) continue
|
||||
"""
|
||||
|
||||
|
||||
def test_main():
|
||||
from test import test_pdb
|
||||
test_support.run_doctest(test_pdb, verbosity=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_main()
|
Loading…
Reference in New Issue