Python 2.1 and 2.2 record source lines in two ways at once: SET_LINENO
instructions in the bytecode, and the table co_lnotab in the code object.
The explorer works out line numbers from the table.
SET_LINENO
In a normal compile a SET_LINENO instruction (opcode 127) comes before
each statement. Its argument is the line number. The -O option leaves
these instructions out, so a .pyo file has none. The compiler’s own
comment says that in -O mode the bytecode offsets are mapped to source
lines through the table instead. The table is written either way.
The co_lnotab table
co_lnotab is a string of byte pairs. The first byte of a pair is how far
the address moves on, in bytes of co_code. The second is how far the line
number moves on. Both are unsigned.
To find the line of an instruction, start with address 0 and line
co_firstlineno. For each pair, add the first byte to the address. If the
address has now gone past the instruction, stop: the line so far is its line.
Otherwise add the second byte to the line and go on.
A step too big for one byte is spread over several pairs. compile.c
writes pairs of 255 and 0 until the address step fits, then pairs of the remaining
address step and 255 until the line step fits, and then one last pair. So a step of
300 bytes and 3 lines is written as FF 00 2D 03, and a step of 6 bytes and
300 lines as 06 FF 00 2D.
Worked example
This example is constructed by hand from the rules above, with no Python this old running it. It is two statements of a function body on lines 5 and 6, and it leaves out the code that follows them.
| Offset | Bytes | Instruction |
|---|---|---|
| 0 | 7F 05 00 | SET_LINENO 5 |
| 3 | 7C 00 00 | LOAD_FAST 0 |
| 6 | 7D 01 00 | STORE_FAST 1 |
| 9 | 7F 06 00 | SET_LINENO 6 |
| 12 | 7C 01 00 | LOAD_FAST 1 |
| 15 | 53 | RETURN_VALUE |
co_firstlineno is 5, set by the first statement. The table is the single
pair 09 01: 9 bytes on and 1 line on. Reading it back, offsets 0 to 8 are
line 5 and offsets from 9 are line 6.
In a .pyo file the SET_LINENO instructions are not written,
so the second statement starts at offset 6 and the table is 06 01.