17

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

那么,pdb 不会随着源代码的变化而更新编译后的代码吗?如果没有,有没有办法让它这样做?我希望能够留在一个 pdb 会话中,以保留我的断点等。

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

4

6 回答 6

6

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

pdb> pdbs.r()

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

# pdbs.py - PDB support

from __future__ import print_function

def r():
    """Reload all non-system modules, to reload stuff on pbd restart. """
    import importlib
    import sys

    # This is likely to be OS-specific
    SYS_PREFIX = '/usr/lib'

    for k, v in list(sys.modules.items()):
        if (
            k == "__main__" or
            k.startswith("pdb") or
            not getattr(v, "__file__", None)
            or v.__file__.startswith(SYS_PREFIX)
        ):
            continue
        print("reloading %s [%s]" % (k, v.__file__), file=sys.stderr)
        importlib.reload(v)
于 2014-04-21T23:22:56.930 回答
4

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

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

(对不起,忽略这个答案的第一个版本;现在还早,我没有仔细阅读你的问题。)

于 2009-04-07T11:44:45.700 回答
2

基于@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
于 2020-10-04T12:27:15.890 回答
1

我决定在我的输入脚本中注释一些行,然后

(Pdb) run

我让 pdb 认识到这种变化。坏事:它从一开始就运行脚本。下面的好东西。

(Pdb) help run
run [args...]
        Restart the debugged python program. If a string is supplied
        it is split with "shlex", and the result is used as the new
        sys.argv.  History, breakpoints, actions and debugger options
        are preserved.  "restart" is an alias for "run".
于 2019-10-30T14:26:19.127 回答
0

可能不适用于更复杂的程序,但对于importlib.reload()使用 Python v3.5.3 的简单示例:

[user@machine ~] cat test.py
print('Test Message')

#
# start and run with debugger
#
[user@machine ~] python3 -m pdb test.py
> /home/user/test.py(1)<module>()
-> print('Test Message')
(Pdb) c
Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Test Message')

#
# in another terminal, change test.py to say "Changed Test Message"
#

#
# back in PDB:
#
(Pdb) import importlib; import test; importlib.reload(test)
Changed Test Message
<module 'test' from '/home/user/test.py'>
(Pdb) c
Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Changed Test Message')
(Pdb) c
Changed Test Message
The program finished and will be restarted
> /home/user/test.py(1)<module>()
-> print('Changed Test Message')
于 2021-10-07T13:02:21.883 回答
-1

ipdb%autoreload扩展

6.2.0 文档文档http://ipython.readthedocs.io/en/stable/config/extensions/autoreload.html#module-IPython.extensions.autoreload

In [1]: %load_ext autoreload

In [2]: %autoreload 2

In [3]: from foo import some_function

In [4]: some_function()
Out[4]: 42

In [5]: # open foo.py in an editor and change some_function to return 43

In [6]: some_function()
Out[6]: 43
于 2017-11-21T14:03:53.143 回答