co_lnotab
is the mapping from bytecode to code line number.
# python3.6
Python 3.6.5 (default, Apr 16 2018, 16:19:06)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import six
>>> import dis
>>> def f(x):
... x = x + 1
... x = x * x
... return x
...
>>> co = f.__code__
>>> dis.dis(co)
2 0 LOAD_FAST 0 (x)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (x)
3 8 LOAD_FAST 0 (x)
10 LOAD_FAST 0 (x)
12 BINARY_MULTIPLY
14 STORE_FAST 0 (x)
4 16 LOAD_FAST 0 (x)
18 RETURN_VALUE
>>> co.co_lnotab
b'\x00\x01\x08\x01\x08\x01'
>>> co.co_firstlineno
1
how to decode co_lnotab
?
for each byte in co_lnotab
, even numbered bytes are the gap of bytecode while odd numbered bytes are the gap of python code.
bytecode starts from 0 while python code starts from co_firstlineno
.
bytecode no | python no |
---|---|
0 | 1 |
0+0=0 | 1+1=2 |
0+8=8 | 2+1=3 |
8+8=16 | 3+1=4 |
so how to determine the line number of python code if bytecode line number A
?
import six
# code_obj is the code object of function
# pos is the position in bytecode
def get_line_no(code_obj, pos):
byte_increments = six.iterbytes(code_obj.co_lnotab[0::2])
line_increments = six.iterbytes(code_obj.co_lnotab[1::2])
byte_num = 0
line_num = code_obj.co_firstlineno
for byte_inc, line_inc in zip(byte_increments, line_increments):
byte_num += byte_inc
if byte_num > pos:
return line_num
line_num += line_inc
return None
the code snippet is from : https://github.com/nedbat/byterun
more info of lnotab
:
https://svn.python.org/projects/python/branches/pep-0384/Objects/lnotab_notes.txt