我是否可以在使用 Python 的调试器逐步执行代码时显示上下文,pdb
而无需明确调用 list 命令?
我尝试使用类似n & l
或n && l
或nl
或n + 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 ;; l
s
u
d
u
d
next 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)