使用 Python pdb 在每次移动后显示上下文

使用 Python pdb 在每次移动后显示上下文

我是否可以在使用 Python 的调试器逐步执行代码时显示上下文,pdb而无需明确调用 list 命令?

我尝试使用类似n & ln && lnln + l之类的命令链接命令n; l。但我找不到有关此内容的任何文档。

问题是,每当我单步执行代码时,我最终都会输入n RET然后...每次l RET..l l RET查看一些上下文。

Steve Ferg 的指南准确描述了明显的预期pdb工作流程:

因此与 pdb 的典型交互可能是这样的

  • 遇到 pdb.set_trace() 语句,然后使用 (Pdb) 提示符开始跟踪
  • 按“n”然后按 ENTER,开始逐步执​​行代码。
  • 您只需按 ENTER 即可再次执行。
  • 您只需按 ENTER 即可再次执行。
  • 您只需按 ENTER 即可再次执行步骤。等等。等等。
  • 最终,你意识到自己有点迷失了。你不再清楚自己在程序中的位置。所以……
  • 按“l”,然后按 ENTER。这将列出当前正在执行的程序区域。
  • 您检查了显示屏,了解了方向,准备重新开始。所以……
  • 按“n”然后按 ENTER,开始逐步执​​行代码。
  • 您只需按 ENTER 即可再次执行。
  • 您只需按 ENTER 即可再次执行步骤。等等。等等。

在我看来,在每次移动后显示上下文显然是有用的,也是用户所希望的。但是,由于似乎没有一个简单的选项可以做到这一点,这让我认为我的使用方式pdb不正确。也许我不断需要查看上下文就表明我使用不当?但我还能如何使用呢pdb

答案1

.pdbrc这可以通过创建文件并使用命令来完成alias

在您的.pdbrc文件中,

alias n next ;; l
alias s step ;; l
alias u up ;; l
alias d down ;; l

然后,当您按下 时,将发出n命令,并且对于、和 也是如此(感谢@Mathias 建议和)。双分号将命令分隔开,因此就像您按下 一样。next ;; lsududnext RET l RET

您可以alias在以下位置阅读有关该命令和其他命令的更多信息pdb 文档

请注意,在 Windows 上pdb读取.pdbrc文件有点麻烦。 pdb查找HOME系统变量,该变量在 Windows 上默认不可用。您必须手动创建一个HOME系统变量并将包含的文件夹放入.pdcrc其中。我在另一个回复中具体记录了如何执行此操作:如何在 Windows 机器上定义 .pdbrc?

答案2

尝试这个。

在你的.pdbrc文件中

import os
exec(open(os.path.expanduser("~/.pdbrc.py")).read())


alias n next ;; list_restore_lastcmd .
alias s step ;; list_restore_lastcmd .
alias u up ;; list_restore_lastcmd .
alias d down ;; list_restore_lastcmd .
alias r return ;; list_restore_lastcmd .

然后在你的.pdbrc.py文件中



import pdb

import inspect

actual_precmd_source = inspect.getsource(pdb.Pdb.precmd)
# Store this source code string in pdb.Pdb object, so that it can be used later
# on by the monkeypatch functions.
pdb.Pdb._reserverd_actual_precmd_source = actual_precmd_source


def new_precmd(self, line, *args, **kwargs):
    # This function calls the original precmd function,
    # but also stores the `self.lastcmd` at the time of the call in a new member variable.
    # This new state end up being the last-last-command.

    # This function is monkeypatched in the most bizarre way.
    # Since this function ends up calling its own original implementation, we
    # cannot simply override it.
    # If we do, it becomes an infinite recursion.

    # So instead, we read the code of the original function in a string with
    # `inspect` package,
    # do some string manipulation to build a trampoline function's source code
    # and then execute that function.

    # These re-imports are required because otherwise falling into pdb with a
    # breakpoint() instruction does not see the top-of-file imports.
    import pdb
    original_source = pdb.Pdb._reserverd_actual_precmd_source

    ss = f"""
def trampoline_precmd(self, line, *args, **kwargs):
{original_source}

    # Store the last-last-command
    self._reserved_last_last_cmd = self.lastcmd
    return precmd(self, line, *args, **kwargs)
"""

    d = {}
    exec(ss, d)
    return d["trampoline_precmd"](self, line, *args, **kwargs)


def do_list_restore_lastcmd(self, arg):
    # A simple monkeypatch function to call the actual list function,
    # but without recording the execution in the self.lastcmd state.

    # At the end, it restores the lastcmd state to the actual previously
    # executed command that was specified by the user.

    # To do that, it uses the newly added _reserved_last_last_cmd state, which
    # is now maintained in the monkeypatched precmd function.
    self.do_list(arg)
    self.lastcmd = self._reserved_last_last_cmd


def new_emptyline(self):
    # This monkeypatch function handles the use case where the user merely
    # presses RET on an empty line to execute the previous command as is.
    # That is a pretty common use case in command line debuggers.

    # We only support the `next` command and whenever we encounter the next
    # command that is indirectly executed via an empty line and RET, we enqueue
    # a `list` command right after.

    # The `list` command we enqueue does not record itself in the self.lastcmd history,
    # so this becomes hidden from the user.
    # Hence the user can press RET again to execute the original `next` command again.

    # These re-imports are required because otherwise falling into pdb with a
    # breakpoint() instruction does not see the top-of-file imports.
    import pdb
    rv = super(pdb.Pdb, self).emptyline()

    # If the repeated command is next, then also print the context
    if self.lastcmd in ["next"]:
        self.cmdqueue.append("list_restore_lastcmd .")

    return rv


# Monkeypatch the functions, using the approach here https://stackoverflow.com/a/19546169/5771861
pdb.Pdb.precmd = new_precmd
pdb.Pdb.do_list_restore_lastcmd = do_list_restore_lastcmd
pdb.Pdb.emptyline = new_emptyline

这最终会变成一个相当微妙的 monkeypatch,它在某种程度上依赖于 pdb.Pdb 类的实现。所以这可能并不符合每个人的期望。绝对不是一种零维护方法。

但这为命令重复和以不可见的方式进行上下文打印留下了合理的体验。


例如调试以下脚本

def complex_calculation(a, b, c, d, e):
    f = a + b
    g = c * d
    h = e - f
    i = g / h
    j = i ** 2
    k = abs(j - g)
    return k



def bubble_sort(array):
    n = len(array)

    for i in range(n):
        already_sorted = True

        for j in range(n - i - 1):
            if array[j] > array[j + 1]:
                array[j], array[j + 1] = array[j + 1], array[j]
                already_sorted = False

        if already_sorted:
            break

    return array


out = complex_calculation(1, 2, 3, 4, 5)
out = bubble_sort([1, 5, 2, 7, 3, 9, 4, 6, 8])

pdb 命令行中如下所示

hakan@hakan:~/misc$ python3 -m pdb dummy.py
> /Users/hakan/misc/dummy.py(2)<module>()
-> def complex_calculation(a, b, c, d, e):
(Pdb) n
> /Users/hakan/misc/dummy.py(13)<module>()
-> def bubble_sort(array):
  8         k = abs(j - g)
  9         return k
 10
 11
 12
 13  -> def bubble_sort(array):
 14         n = len(array)
 15
 16         for i in range(n):
 17             already_sorted = True
 18
(Pdb) n
> /Users/hakan/misc/dummy.py(30)<module>()
-> out = complex_calculation(1, 2, 3, 4, 5)
 25                 break
 26
 27         return array
 28
 29
 30  -> out = complex_calculation(1, 2, 3, 4, 5)
 31     out = bubble_sort([1, 5, 2, 7, 3, 9, 4, 6, 8])
 32
 33
 34
 35
(Pdb) s
--Call--
> /Users/hakan/misc/dummy.py(2)complex_calculation()
-> def complex_calculation(a, b, c, d, e):
  1
  2  -> def complex_calculation(a, b, c, d, e):
  3         f = a + b
  4         g = c * d
  5         h = e - f
  6         i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9         return k
 10
 11
(Pdb) n
> /Users/hakan/misc/dummy.py(3)complex_calculation()
-> f = a + b
  1
  2     def complex_calculation(a, b, c, d, e):
  3  ->     f = a + b
  4         g = c * d
  5         h = e - f
  6         i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9         return k
 10
 11
(Pdb)
> /Users/hakan/misc/dummy.py(4)complex_calculation()
-> g = c * d
  1
  2     def complex_calculation(a, b, c, d, e):
  3         f = a + b
  4  ->     g = c * d
  5         h = e - f
  6         i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9         return k
 10
 11
(Pdb)
> /Users/hakan/misc/dummy.py(5)complex_calculation()
-> h = e - f
  1
  2     def complex_calculation(a, b, c, d, e):
  3         f = a + b
  4         g = c * d
  5  ->     h = e - f
  6         i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9         return k
 10
 11
(Pdb)
> /Users/hakan/misc/dummy.py(6)complex_calculation()
-> i = g / h
  1
  2     def complex_calculation(a, b, c, d, e):
  3         f = a + b
  4         g = c * d
  5         h = e - f
  6  ->     i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9         return k
 10
 11
(Pdb) r
--Return--
> /Users/hakan/misc/dummy.py(9)complex_calculation()->24.0
-> return k
  4         g = c * d
  5         h = e - f
  6         i = g / h
  7         j = i ** 2
  8         k = abs(j - g)
  9  ->     return k
 10
 11
 12
 13     def bubble_sort(array):
 14         n = len(array)
(Pdb) n
> /Users/hakan/misc/dummy.py(31)<module>()
-> out = bubble_sort([1, 5, 2, 7, 3, 9, 4, 6, 8])
 26
 27         return array
 28
 29
 30     out = complex_calculation(1, 2, 3, 4, 5)
 31  -> out = bubble_sort([1, 5, 2, 7, 3, 9, 4, 6, 8])
 32
 33
 34
 35
 36     print(out)
(Pdb) s
--Call--
> /Users/hakan/misc/dummy.py(13)bubble_sort()
-> def bubble_sort(array):
  8         k = abs(j - g)
  9         return k
 10
 11
 12
 13  -> def bubble_sort(array):
 14         n = len(array)
 15
 16         for i in range(n):
 17             already_sorted = True
 18
(Pdb) n
> /Users/hakan/misc/dummy.py(14)bubble_sort()
-> n = len(array)
  9         return k
 10
 11
 12
 13     def bubble_sort(array):
 14  ->     n = len(array)
 15
 16         for i in range(n):
 17             already_sorted = True
 18
 19             for j in range(n - i - 1):
(Pdb)
> /Users/hakan/misc/dummy.py(16)bubble_sort()
-> for i in range(n):
 11
 12
 13     def bubble_sort(array):
 14         n = len(array)
 15
 16  ->     for i in range(n):
 17             already_sorted = True
 18
 19             for j in range(n - i - 1):
 20                 if array[j] > array[j + 1]:
 21                     array[j], array[j + 1] = array[j + 1], array[j]
(Pdb)
> /Users/hakan/misc/dummy.py(17)bubble_sort()
-> already_sorted = True
 12
 13     def bubble_sort(array):
 14         n = len(array)
 15
 16         for i in range(n):
 17  ->         already_sorted = True
 18
 19             for j in range(n - i - 1):
 20                 if array[j] > array[j + 1]:
 21                     array[j], array[j + 1] = array[j + 1], array[j]
 22                     already_sorted = False
(Pdb)
> /Users/hakan/misc/dummy.py(19)bubble_sort()
-> for j in range(n - i - 1):
 14         n = len(array)
 15
 16         for i in range(n):
 17             already_sorted = True
 18
 19  ->         for j in range(n - i - 1):
 20                 if array[j] > array[j + 1]:
 21                     array[j], array[j + 1] = array[j + 1], array[j]
 22                     already_sorted = False
 23
 24             if already_sorted:
(Pdb)
> /Users/hakan/misc/dummy.py(20)bubble_sort()
-> if array[j] > array[j + 1]:
 15
 16         for i in range(n):
 17             already_sorted = True
 18
 19             for j in range(n - i - 1):
 20  ->             if array[j] > array[j + 1]:
 21                     array[j], array[j + 1] = array[j + 1], array[j]
 22                     already_sorted = False
 23
 24             if already_sorted:
 25                 break
(Pdb)

相关内容