我有一个非常简单的测试文件breakpoint_test.py
for i in range(3): #1
breakpoint() #2
print(f"first print in loop {i}") #3
breakpoint() #4
print(f"second print in loop {i}") #5
在运行时输出,并按住c
到继续。
(py3) nakita@machine:~/tmp $ python breakpoint_test.py
> /Users/nakita/tmp/breakpoint_test.py(3)<module>()
-> print(f"first print in loop {i}") <---------------looks good here!
(Pdb) c
first print in loop 0
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in loop {i}")
(Pdb) c
second print in loop 0
> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint() <---------------weird here!
(Pdb) c
first print in loop 1
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 1
> /Users/nakita/tmp/breakpoint_test.py(2)<module>()
-> breakpoint()
(Pdb) c
first print in loop 2
> /Users/nakita/tmp/breakpoint_test.py(5)<module>()
-> print(f"second print in circle {i}")
(Pdb) c
second print in loop 2
我希望breakpoint()
将提示将在breakpoint()
之后立即执行的行。当代码首次进入for
循环时,情况就是如此。但是,在第二个循环中进行迭代时,第一个breakpoint()会提示breakpoint()
本身而不是-> print(f"first print in loop {i}")
。但是,循环主体中的第二个breakpoint()
可以正常工作。看起来行为是首先要牺牲for循环体中的breakpoint()
。有人知道为什么吗?
我在python 3.7.3和3.8.0上进行了测试。我已阅读PEP 553 -- Built-in breakpoint()
Python正在尝试在下一行的开始处开始调试,但是其“下一行”检测有些奇怪。
默认情况下,breakpoint
调用pdb.set_trace
,它设置将在下一个跟踪事件上执行的trace function。在这种情况下,下一个跟踪事件是'line'
事件,当Python认为执行已进入新行时触发。
在循环中第一个breakpoint
的第一次执行,以及第二个breakpoint
的所有执行中,下一个'line'
事件在下一行的第一个操作码上触发。但是,在第一个breakpoint
的第二次及以后的执行中,会发生一些不同的事情。
Python通过检查当前字节码指令索引是对应于行的第一条指令,还是对应于最后一条执行指令之前的索引处的指令,来确定新的源代码行已开始。您可以在maybe_call_line_trace
的maybe_call_line_trace
中看到它。
Python仅更新Python/ceval.c
,该变量用于在跟踪处于活动状态时确定最后执行的指令。当您按下instr_prev
继续执行时,跟踪功能将被禁用。 (如果您使用PDB c
命令设置了任何断点,它将保持活动状态,因为跟踪功能需要处理这些断点,但是break
调用不会通过该机制。)
在“非奇怪”断点上,Python在“一行的第一条指令”条件下,在下一行的第一个操作码上触发了下一行事件。
在“怪异”的断点上,breakpoint()
仍具有上次您击中instr_prev
以来的值,因为在该点禁用了跟踪。该值用于当前行之后的行,因此在“执行最后一条指令之前的索引处的指令”条件下,Python会触发c
行的下一个操作码上的下一个行事件。 (breakpoint()
行的下一个操作码是breakpoint()
以清除POP_TOP
的返回值。)如果Python一直在更好地跟踪上一条执行的指令,则不会触发该事件。 >