考虑以下两个例子:当一条线上的多条停靠点跟踪时,python停止在哪条线上?
x = 1; y = 2; z = 3
和:
for i in range(3): print(i)
在后者,如果你通过这个在调试步骤一样PDB,你会把它会停在print(i)
在循环的每次迭代。
但是在第一个例子中,它会停止一次。
进一步调查,拆分多语句行,我们看到实际上有两个条目用于第一行co_lnotab
。但dis.dis()
就在于此。
至于对于循环,lnotab
中只有一行,但是您停在每个迭代的位置偏移10处在跳转的目标处。那么,即使行号没有改变,什么会触发停止?
import dis
>>> x = compile('x = 1; y = 2; z = 3', 'foo', 'exec')
>>> x.co_lnotab
b'\x04\x00\x04\x00'
>>> dis.dis(x)
1 0 LOAD_CONST 0 (1)
2 STORE_NAME 0 (x)
4 LOAD_CONST 1 (2)
6 STORE_NAME 1 (y)
8 LOAD_CONST 2 (3)
10 STORE_NAME 2 (z)
12 LOAD_CONST 3 (4)
14 STORE_NAME 3 (a)
16 LOAD_CONST 4 (None)
18 RETURN_VALUE
>>> y = compile('for i in range(3): print(i)', 'foo', 'exec')
>>> y.co_lnotab
b'\x0e\x00'
>>> dis.dis(y)
1 0 SETUP_LOOP 24 (to 26)
2 LOAD_NAME 0 (range)
4 LOAD_CONST 0 (3)
6 CALL_FUNCTION 1
8 GET_ITER
>> 10 FOR_ITER 12 (to 24)
12 STORE_NAME 1 (i)
14 LOAD_NAME 2 (print)
16 LOAD_NAME 1 (i)
18 CALL_FUNCTION 1
20 POP_TOP
22 JUMP_ABSOLUTE 10
>> 24 POP_BLOCK
>> 26 LOAD_CONST 1 (None)
28 RETURN_VALUE
>>>
该逻辑的源代码在哪里?我看过Python C代码,但找不到它,请在ceval.c
中寻找PyTrace_LINE
。
编辑:
基于user2357112的答案和阅读代码建议在那里,我能够验证的代码的每个语句是一个可以停止/跟踪。 我用我的新的Python汇编,pyc-xasm,修改字节码这样:
2:
LOAD_CONST (1)
STORE_NAME (x)
JUMP_FORWARD L2B
L2A:
2:
LOAD_CONST (2)
STORE_NAME (y)
JUMP_FORWARD L2D
L2B:
JUMP_ABSOLUTE L2A
L2C:
2:
LOAD_CONST (3)
STORE_NAME (z)
JUMP_FORWARD L3
L2D:
JUMP_ABSOLUTE L2C
L3:
3:
LOAD_NAME (x)
LOAD_NAME (y)
BINARY_ADD
LOAD_NAME (z)
BINARY_ADD
PRINT_ITEM
PRINT_NEWLINE
LOAD_CONST (None)
RETURN_VALUE
并运行,这将导致Python来的每一行之前停止。
我很好奇你是什么让你看到这一点。你在做什么? –
很长一段时间,我一直在研究调试器,其中包括python调试器。请参阅https://pypi.python.org/pypi/trepan3k。 我想你应该可以在任何你想要的地方设置一个断点,这可以通过在函数中指定一个字节码偏移来完成。或者决定在多个报表中停止使用哪个报表。随着最近一些字节码操作工具的推进,我可以做到这一点。尽管如此,这还是很多的工作,并不一定涵盖所有情况。 – rocky
太棒了!我正在研究一个python调试器(字面上),您可能会感兴趣:https://github.com/alexmojaki/birdseye –