为什么breakpoint()不会在for循环中提示幂等位置?

问题描述 投票:0回答:1

我有一个非常简单的测试文件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 python-3.x breakpoints pdb
1个回答
0
投票

Python正在尝试在下一行的开始处开始调试,但是其“下一行”检测有些奇怪。


默认情况下,breakpoint调用pdb.set_trace,它设置将在下一个跟踪事件上执行的trace function。在这种情况下,下一个跟踪事件是'line'事件,当Python认为执行已进入新行时触发。

在循环中第一个breakpoint的第一次执行,以及第二个breakpoint的所有执行中,下一个'line'事件在下一行的第一个操作码上触发。但是,在第一个breakpoint的第二次及以后的执行中,会发生一些不同的事情。

Python通过检查当前字节码指令索引是对应于行的第一条指令,还是对应于最后一条执行指令之前的索引处的指令,来确定新的源代码行已开始。您可以在maybe_call_line_tracemaybe_call_line_trace中看到它。

Python仅更新Python/ceval.c,该变量用于在跟踪处于活动状态时确定最后执行的指令。当您按下instr_prev继续执行时,跟踪功能将被禁用。 (如果您使用PDB c命令设置了任何断点,它将保持活动状态,因为跟踪功能需要处理这些断点,但是break调用不会通过该机制。)

在“非奇怪”断点上,Python在“一行的第一条指令”条件下,在下一行的第一个操作码上触发了下一行事件。

在“怪异”的断点上,breakpoint()仍具有上次您击中instr_prev以来的值,因为在该点禁用了跟踪。该值用于当前行之后的行,因此在“执行最后一条指令之前的索引处的指令”条件下,Python会触发c行的下一个操作码上的下一个行事件。 (breakpoint()行的下一个操作码是breakpoint()以清除POP_TOP的返回值。)如果Python一直在更好地跟踪上一条执行的指令,则不会触发该事件。 >

© www.soinside.com 2019 - 2024. All rights reserved.