Skip to content

Instantly share code, notes, and snippets.

@miketheman
Last active February 3, 2026 15:14
Show Gist options
  • Select an option

  • Save miketheman/0ff15be2efea2573ae46e52bef069c77 to your computer and use it in GitHub Desktop.

Select an option

Save miketheman/0ff15be2efea2573ae46e52bef069c77 to your computer and use it in GitHub Desktop.
import dis
from contextlib import contextmanager
@contextmanager
def dummy_context(name):
yield name
def nested_with():
result = []
with dummy_context("outer"):
with dummy_context("middle"):
with dummy_context("inner"):
result.append("inside")
result.append("after")
return result
print('Bytecode with line numbers:')
for inst in dis.get_instructions(nested_with):
print(f'{inst.offset:4} line {inst.line_number or "--":>3} {inst.opname:25} {inst.argrepr}')
Bytecode with line numbers:
0 line 9 RESUME
2 line 10 BUILD_LIST
4 line 10 STORE_FAST result
6 line 11 LOAD_GLOBAL dummy_context + NULL
16 line 11 LOAD_CONST 'outer'
18 line 11 CALL
26 line 11 COPY
28 line 11 LOAD_SPECIAL __exit__
30 line 11 SWAP
32 line 11 SWAP
34 line 11 LOAD_SPECIAL __enter__
36 line 11 CALL
44 line 11 POP_TOP
46 line 12 LOAD_GLOBAL dummy_context + NULL
56 line 12 LOAD_CONST 'middle'
58 line 12 CALL
66 line 12 COPY
68 line 12 LOAD_SPECIAL __exit__
70 line 12 SWAP
72 line 12 SWAP
74 line 12 LOAD_SPECIAL __enter__
76 line 12 CALL
84 line 12 POP_TOP
86 line 13 LOAD_GLOBAL dummy_context + NULL
96 line 13 LOAD_CONST 'inner'
98 line 13 CALL
106 line 13 COPY
108 line 13 LOAD_SPECIAL __exit__
110 line 13 SWAP
112 line 13 SWAP
114 line 13 LOAD_SPECIAL __enter__
116 line 13 CALL
124 line 13 POP_TOP
126 line 14 LOAD_FAST_BORROW result
128 line 14 LOAD_ATTR append + NULL|self
148 line 14 LOAD_CONST 'inside'
150 line 14 CALL
158 line 14 POP_TOP
160 line 13 LOAD_CONST None
162 line 13 LOAD_CONST None
164 line 13 LOAD_CONST None
166 line 13 CALL
174 line 13 POP_TOP
176 line 12 LOAD_CONST None
178 line 12 LOAD_CONST None
180 line 12 LOAD_CONST None
182 line 12 CALL
190 line 12 POP_TOP
192 line 11 LOAD_CONST None
194 line 11 LOAD_CONST None
196 line 11 LOAD_CONST None
198 line 11 CALL
206 line 11 POP_TOP
208 line 15 LOAD_FAST_BORROW result
210 line 15 LOAD_ATTR append + NULL|self
230 line 15 LOAD_CONST 'after'
232 line 15 CALL
240 line 15 POP_TOP
242 line 16 LOAD_FAST_BORROW result
244 line 16 RETURN_VALUE
246 line 13 PUSH_EXC_INFO
248 line 13 WITH_EXCEPT_START
250 line 13 TO_BOOL
258 line 13 POP_JUMP_IF_TRUE to L4
262 line 13 NOT_TAKEN
264 line 13 RERAISE
266 line 13 POP_TOP
268 line 13 POP_EXCEPT
270 line 13 POP_TOP
272 line 13 POP_TOP
274 line 13 POP_TOP
276 line 13 JUMP_BACKWARD_NO_INTERRUPT to L1
278 line -- COPY
280 line -- POP_EXCEPT
282 line -- RERAISE
284 line 12 PUSH_EXC_INFO
286 line 12 WITH_EXCEPT_START
288 line 12 TO_BOOL
296 line 12 POP_JUMP_IF_TRUE to L5
300 line 12 NOT_TAKEN
302 line 12 RERAISE
304 line 12 POP_TOP
306 line 12 POP_EXCEPT
308 line 12 POP_TOP
310 line 12 POP_TOP
312 line 12 POP_TOP
314 line 12 JUMP_BACKWARD_NO_INTERRUPT to L2
316 line -- COPY
318 line -- POP_EXCEPT
320 line -- RERAISE
322 line 11 PUSH_EXC_INFO
324 line 11 WITH_EXCEPT_START
326 line 11 TO_BOOL
334 line 11 POP_JUMP_IF_TRUE to L6
338 line 11 NOT_TAKEN
340 line 11 RERAISE
342 line 11 POP_TOP
344 line 11 POP_EXCEPT
346 line 11 POP_TOP
348 line 11 POP_TOP
350 line 11 POP_TOP
352 line 11 JUMP_BACKWARD_NO_INTERRUPT to L3
354 line -- COPY
356 line -- POP_EXCEPT
358 line -- RERAISE
import dis
from collections import defaultdict
def multi_visit_lines(code):
line_offsets = defaultdict(list)
for inst in dis.get_instructions(code):
if inst.line_number:
line_offsets[inst.line_number].append(inst.offset)
result = set()
for line, offsets in line_offsets.items():
offsets = sorted(offsets)
# offsets are already in order from walk()
for i in range(1, len(offsets)):
print(f'Line {line} offsets: {offsets}')
# Bytecode instructions are 2 bytes apart; a larger gap means
# the line appears in multiple non-contiguous code sections.
if offsets[i] - offsets[i - 1] > 2:
result.add(line)
break
return result
code = compile("""\
import dis # unused, to help lines match in both examples
from contextlib import contextmanager
@contextmanager
def dummy_context(name):
yield name
def nested_with():
result = []
with dummy_context("outer"):
with dummy_context("middle"):
with dummy_context("inner"):
result.append("inside")
result.append("after")
return result
""", "<test>", "exec")
for const in code.co_consts:
if hasattr(const, 'co_name') and const.co_name == 'nested_with':
func_code = const
break
result = multi_visit_lines(func_code)
print()
print(f'Lines in function: {sorted(set(i.line_number for i in dis.get_instructions(func_code) if i.line_number))}')
print(f'Multi-visit lines: {sorted(result)}')
print()
Line 9 offsets: [2, 4]
Line 10 offsets: [6, 16, 18, 26, 28, 30, 32, 34, 36, 44, 192, 194, 196, 198, 206, 322, 324, 326, 334, 338, 340, 342, 344, 346, 348, 350, 352]
Line 11 offsets: [46, 56, 58, 66, 68, 70, 72, 74, 76, 84, 176, 178, 180, 182, 190, 284, 286, 288, 296, 300, 302, 304, 306, 308, 310, 312, 314]
Line 12 offsets: [86, 96, 98, 106, 108, 110, 112, 114, 116, 124, 160, 162, 164, 166, 174, 246, 248, 250, 258, 262, 264, 266, 268, 270, 272, 274, 276]
Line 13 offsets: [126, 128, 148, 150, 158]
Line 13 offsets: [126, 128, 148, 150, 158]
Line 14 offsets: [208, 210, 230, 232, 240]
Line 14 offsets: [208, 210, 230, 232, 240]
Line 15 offsets: [242, 244]
Lines in function: [8, 9, 10, 11, 12, 13, 14, 15]
Multi-visit lines: [10, 11, 12, 13, 14]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment