No instruction sets up an exception handler on entry to a try block.
Instead, co_exceptiontable lists which instructions are covered and where
each goes when an exception is raised. If the interpreter is running an instruction not
in any entry, the exception leaves the frame. A code object with no handlers has an empty
table.
Entries
Each entry is four numbers, written one after the other:
| Number | Meaning |
|---|---|
| Start | The first covered code unit |
| Length | How many code units are covered |
| Target | The code unit of the handler to jump to |
| Depth and lasti | (depth << 1) | lasti |
The first three are in 16-bit code units; multiply by 2 for a byte offset in
co_code. Depth is the value-stack depth to unwind to before the
handler runs. When lasti is 1, the offset of the instruction that raised is
pushed first, so a later RERAISE can restore it. Entries are sorted by
start, and an instruction is covered by at most one entry.
Varint encoding
Each number is a big-endian varint: the highest bits come first, with 6 bits a byte. Bit 6 means another byte follows. Bit 7 is set only on the first byte of an entry, so a reader can find entries by their first byte, and it is clear on every other byte.
SBit 7: set on the first byte of an entry’s first number1 bitXBit 6: another byte follows, and extends this number1 bitdBits 0–5: data, highest first6 bits
The value is built by adding the data of each byte in turn, multiplying what you have so far by 64 before each next byte.
Worked example
There is no Python 3.15 interpreter here, so this table was constructed by hand from the rules above. It has three entries; the last uses numbers too large for one byte.
00000000 82 08 0B 00 8B 0B 1A 03 C1 24 04 41 38 05
co_exceptiontable| Bytes | Start | Length | Target | Depth | lasti | Byte offsets |
|---|---|---|---|---|---|---|
82 08 0B 00 | 2 | 8 | 11 | 0 | No | 4 to 20 → 22 |
8B 0B 1A 03 | 11 | 11 | 26 | 1 | Yes | 22 to 44 → 52 |
C1 24 04 41 38 05 | 100 | 4 | 120 | 2 | Yes | 200 to 208 → 240 |
Reading the first entry: 82 has bit 7 set, the start of an entry, and
data 2, so the start is code unit 2, byte offset 4. 08 is a length of 8
units, ending at byte 20. 0B is target 11, byte 22. 00 gives
depth 0 and no lasti.
Reading the third: C1 has bit 7 set, a new entry, and bit 6 set, so the
start goes on into the next byte. Its data is 1; the next byte, 24, has
data 36 and bit 6 clear, so the start is 1 × 64 + 36 = 100. 04 is a length
of 4. 41 and 38 make the target 1 × 64 + 56 = 120.
05 is binary 101: depth 2, with lasti.
dis shows each entry as a range of labels, the handler’s label, the depth
in brackets, and lasti when that bit is set, for example
L1 to L2 -> L3 [0].