Commit 4d4313d5 authored by Georg Brandl's avatar Georg Brandl

#5142: add module skipping feature to pdb.

parent e3869c41
.. _debugger: .. _debugger:
:mod:`pdb` --- The Python Debugger :mod:`pdb` --- The Python Debugger
...@@ -53,7 +52,16 @@ useful than quitting the debugger upon program's exit. ...@@ -53,7 +52,16 @@ useful than quitting the debugger upon program's exit.
.. versionadded:: 2.4 .. versionadded:: 2.4
Restarting post-mortem behavior added. 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 pdb
>>> import mymodule >>> import mymodule
...@@ -70,10 +78,10 @@ Typical usage to inspect a crashed program is:: ...@@ -70,10 +78,10 @@ Typical usage to inspect a crashed program is::
-> print spam -> print spam
(Pdb) (Pdb)
The module defines the following functions; each enters the debugger in a The module defines the following functions; each enters the debugger in a
slightly different way: slightly different way:
.. function:: run(statement[, globals[, locals]]) .. function:: run(statement[, globals[, locals]])
Execute the *statement* (given as a string) under debugger control. The Execute the *statement* (given as a string) under debugger control. The
...@@ -117,7 +125,38 @@ slightly different way: ...@@ -117,7 +125,38 @@ slightly different way:
.. function:: pm() .. 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: .. _debugger-commands:
...@@ -351,3 +390,9 @@ run [*args* ...] ...@@ -351,3 +390,9 @@ run [*args* ...]
q(uit) q(uit)
Quit from the debugger. The program being executed is aborted. 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.
"""Debugger basics""" """Debugger basics"""
import fnmatch
import sys import sys
import os import os
import types import types
...@@ -19,7 +20,8 @@ class Bdb: ...@@ -19,7 +20,8 @@ class Bdb:
The standard debugger class (pdb.Pdb) is an example. 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.breaks = {}
self.fncache = {} self.fncache = {}
...@@ -94,9 +96,18 @@ class Bdb: ...@@ -94,9 +96,18 @@ class Bdb:
# methods, but they may if they want to redefine the # methods, but they may if they want to redefine the
# definition of stopping and breakpoints. # 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): def stop_here(self, frame):
# (CT) stopframe may now also be None, see dispatch_call. # (CT) stopframe may now also be None, see dispatch_call.
# (CT) the former test for None is therefore removed from here. # (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: if frame is self.stopframe:
return frame.f_lineno >= self.stoplineno return frame.f_lineno >= self.stoplineno
while frame is not None and frame is not self.stopframe: while frame is not None and frame is not self.stopframe:
......
...@@ -58,8 +58,8 @@ line_prefix = '\n-> ' # Probably a better default ...@@ -58,8 +58,8 @@ line_prefix = '\n-> ' # Probably a better default
class Pdb(bdb.Bdb, cmd.Cmd): class Pdb(bdb.Bdb, cmd.Cmd):
def __init__(self, completekey='tab', stdin=None, stdout=None): def __init__(self, completekey='tab', stdin=None, stdout=None, skip=None):
bdb.Bdb.__init__(self) bdb.Bdb.__init__(self, skip=skip)
cmd.Cmd.__init__(self, completekey, stdin, stdout) cmd.Cmd.__init__(self, completekey, stdin, stdout)
if stdout: if stdout:
self.use_rawinput = 0 self.use_rawinput = 0
......
# 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()
...@@ -273,6 +273,8 @@ Core and Builtins ...@@ -273,6 +273,8 @@ Core and Builtins
Library Library
------- -------
- Issue #5142: Add the ability to skip modules while stepping to pdb.
- Issue #1309567: Fix linecache behavior of stripping subdirectories when - Issue #1309567: Fix linecache behavior of stripping subdirectories when
looking for files given by a relative filename. looking for files given by a relative filename.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment