co_linetable maps each instruction to its place in the source: a start
line, an end line, a start column and an end column. This is the location table, added
for PEP 657. It is a sequence of entries, each covering one or more code units of
co_code, one after the other from offset 0. There are no gaps.
Line numbers are kept as a running total. It starts at
co_firstlineno,
and each entry may move it.
Entries
Every entry starts with one byte whose top bit is set, so a reader can find the start of an entry. Below the top bit are a form number and a length.
| Bits | Meaning |
|---|---|
| 7 | Always 1 |
| 3–6 | Form, 0 to 15 |
| 0–2 | Length of the entry in code units, less 1: 1 to 8 units |
A run longer than 8 code units takes several entries.
| Form | Name | Bytes after the first | Effect |
|---|---|---|---|
| 0–9 | Short | One byte b | Same line as the running total. Start column is (form << 3) | (b >> 4); end column is start plus b & 15 |
| 10–12 | One line | Two bytes: start column, end column | The line moves down by form − 10, 0 to 2 |
| 13 | No columns | A signed varint | The line moves by the varint; no column is recorded |
| 14 | Long | Signed varint, then three varints | The line moves by the signed varint; the end line is that line plus the second varint; the third and fourth are the start and end columns plus 1, where 0 means unknown |
| 15 | None | None | No location at all for these code units; the running line does not change |
Varints
Unsigned numbers use 6 bits a byte, lowest first. Bit 6 is set on every byte except the last. Bit 7 is clear, which is how a varint is told from the start of an entry.
A signed number is first turned into an unsigned one: a value s of zero or more
is stored as s << 1, and a negative one as
((−s) << 1) | 1. So an even stored number is positive, and an odd one
is negative.
Worked example
This function was compiled under Python 3.14 from t.py, where
def f(a): is line 1:
def f(a):
try:
return a + 1
except KeyError:
return 0
Its co_linetable is 34 bytes:
00000000 80 00 F0 02 03 05 11 D8 0F 10 90 31 8D 75 88 0C
00000010 F8 DC 0B 13 F4 00 01 05 11 DA 0F 10 F0 03 01 05
00000020 11 FA
co_linetable of the function aboveDecoded, with the running line starting at co_firstlineno, 1:
| Bytes | Form | Units | Offsets | Location | Instructions |
|---|---|---|---|---|---|
80 00 | Short 0 | 1 | 0–2 | Line 1, columns 0–0 | RESUME |
F0 02 03 05 11 | Long | 1 | 2–4 | Line +1 → 2, to line 5, columns 4–16 | NOP |
D8 0F 10 | One line | 1 | 4–6 | Line +1 → 3, columns 15–16 | LOAD_FAST_BORROW |
90 31 | Short 2 | 1 | 6–8 | Line 3, columns 19–20 | LOAD_SMALL_INT |
8D 75 | Short 1 | 6 | 8–20 | Line 3, columns 15–20 | BINARY_OP and its 5 cache units |
88 0C | Short 1 | 1 | 20–22 | Line 3, columns 8–20 | RETURN_VALUE |
F8 | None | 1 | 22–24 | No location | PUSH_EXC_INFO |
DC 0B 13 | One line | 5 | 24–34 | Line +1 → 4, columns 11–19 | LOAD_GLOBAL and its 4 cache units |
F4 00 01 05 11 | Long | 5 | 34–44 | Line 4, to line 5, columns 4–16 | CHECK_EXC_MATCH, POP_JUMP_IF_FALSE and its cache, NOT_TAKEN, POP_TOP |
DA 0F 10 | One line | 3 | 44–50 | Line +1 → 5, columns 15–16 | POP_EXCEPT, LOAD_SMALL_INT, RETURN_VALUE |
F0 03 01 05 11 | Long | 1 | 50–52 | Line −1 → 4, to line 5, columns 4–16 | RERAISE |
FA | None | 3 | 52–58 | No location | COPY, POP_EXCEPT, RERAISE |
Reading the second entry by hand: F0 is 1111 0000, so form
(0xF0 >> 3) & 15 = 14, long, with length bits 0, one unit. The
signed varint 02 is even, so it is +1. The end-line varint 03
is 3, so the end is line 5. 05 and 11 are the columns plus 1,
giving 4 and 16.
In F0 03 01 05 11, the entry at offset 50, the signed varint
03 is odd, so it is −1. The running line moves up from 5 to 4.
Python’s code.co_positions() returns the same information, one tuple per code
unit, and code.co_lines() gives the line ranges.