如何让pdb识别源在运行之间发生了变化?

use*_*028 12 python debugging pdb

据我所知,pdb无法识别源代码何时在"运行"之间发生了变化.也就是说,如果我正在调试,注意一个错误,修复该错误,并在pdb中重新运行程序(即不退出pdb),pdb将不会重新编译代码.即使pdb列出了新的源代码,我仍然会调试旧版本的代码.

那么,pdb是否在源更改时不更新已编译的代码?如果没有,有没有办法让它这样做?我希望能够留在单个pdb会话中以保持我的断点等.

FWIW,gdb会注意到它正在调试的程序在它下面发生变化,尽管只是重新启动该程序.这是我试图在pdb中复制的行为.

pou*_*aus 6

以下迷你模块可能会有所帮助。如果在 pdb 会话中导入它,则可以使用:

pdb> pdbs.r()
Run Code Online (Sandbox Code Playgroud)

随时强制重新加载除main之外的所有非系统模块。代码跳过它,因为它抛出一个 ImportError('Cannot re-init internal module main ') 异常。

pdb> pdbs.r()
Run Code Online (Sandbox Code Playgroud)


ank*_*tis 5

基于 @pourhaus 的回答(2014 年起),本秘籍通过命令增强了pdb++调试器reload(预计适用于 Linux 和 Windows,任何 Python 安装)。

提示:新reload命令接受一个可选的模块前缀列表来重新加载(并排除),而不是在恢复调试时破坏已加载的全局变量

只需将以下Python-3.6代码插入到您的~/.pdbrc.py文件中:

## Augment `pdb++` with a `reload` command
#
#  See https://stackoverflow.com/questions/724924/how-to-make-pdb-recognize-that-the-source-has-changed-between-runs/64194585#64194585

from pdb import Pdb


def _pdb_reload(pdb, modules):
    """
    Reload all non system/__main__ modules, without restarting debugger.

    SYNTAX:
        reload [<reload-module>, ...] [-x [<exclude-module>, ...]]

    * a dot(`.`) matches current frame's module `__name__`;
    * given modules are matched by prefix;
    * any <exclude-modules> are applied over any <reload-modules>.

    EXAMPLES:
        (Pdb++) reload                  # reload everything (brittle!)
        (Pdb++) reload  myapp.utils     # reload just `myapp.utils`
        (Pdb++) reload  myapp  -x .     # reload `myapp` BUT current module

    """
    import importlib
    import sys

    ## Derive sys-lib path prefix.
    #
    SYS_PREFIX = importlib.__file__
    SYS_PREFIX = SYS_PREFIX[: SYS_PREFIX.index("importlib")]

    ## Parse args to decide prefixes to Include/Exclude.
    #
    has_excludes = False
    to_include = set()
    # Default prefixes to Exclude, or `pdb++` will break.
    to_exclude = {"__main__", "pdb", "fancycompleter", "pygments", "pyrepl"}
    for m in modules.split():
        if m == "-x":
            has_excludes = True
            continue

        if m == ".":
            m = pdb._getval("__name__")

        if has_excludes:
            to_exclude.add(m)
        else:
            to_include.add(m)

    to_reload = [
        (k, v)
        for k, v in sys.modules.items()
        if (not to_include or any(k.startswith(i) for i in to_include))
        and not any(k.startswith(i) for i in to_exclude)
        and getattr(v, "__file__", None)
        and not v.__file__.startswith(SYS_PREFIX)
    ]
    print(
        f"PDB-reloading {len(to_reload)} modules:",
        *[f"  +--{k:28s}:{getattr(v, '__file__', '')}" for k, v in to_reload],
        sep="\n",
        file=sys.stderr,
    )

    for k, v in to_reload:
        try:
            importlib.reload(v)
        except Exception as ex:
            print(
                f"Failed to PDB-reload module: {k} ({v.__file__}) due to: {ex!r}",
                file=sys.stderr,
            )


Pdb.do_reload = _pdb_reload
Run Code Online (Sandbox Code Playgroud)


Nic*_*ley 4

“在 pdb 中重新运行程序”是什么意思?如果您导入了一个模块,Python 不会重新读取它,除非您明确要求这样做,即使用reload(module). 然而,reload它远非万无一失(请参阅xreload了解另一种策略)。

Python 代码重载存在很多陷阱。为了更可靠地解决您的问题,您可以使用一个类来包装 pdb,该类将断点信息记录到磁盘上的文件中,并根据命令回放它们。

(抱歉,忽略这个答案的第一个版本;现在还很早,我没有足够仔细地阅读你的问题。)

  • @NicholasRiley 我想将 xreload 作为命令合并到 python trepan 调试器中(trepan3k https://pypi.python.org/pypi/trepan3k 和 trepan2 https://pypi.python.org/pypi/trepan2)。这些是 GPL3。这个可以吗?你是作者吗? (2认同)