python (3.12.0)
1 # Minimal tests for dis module
2
3 import contextlib
4 import dis
5 import io
6 import re
7 import sys
8 import types
9 import unittest
10 from test.support import (captured_stdout, requires_debug_ranges,
11 requires_specialization, cpython_only)
12 from test.support.bytecode_helper import BytecodeTestCase
13
14 import opcode
15
16
17 def get_tb():
18 def _error():
19 try:
20 1 / 0
21 except Exception as e:
22 tb = e.__traceback__
23 return tb
24
25 tb = _error()
26 while tb.tb_next:
27 tb = tb.tb_next
28 return tb
29
30 TRACEBACK_CODE = get_tb().tb_frame.f_code
31
32 class ESC[4;38;5;81m_C:
33 def __init__(self, x):
34 self.x = x == 1
35
36 @staticmethod
37 def sm(x):
38 x = x == 1
39
40 @classmethod
41 def cm(cls, x):
42 cls.x = x == 1
43
44 dis_c_instance_method = """\
45 %3d RESUME 0
46
47 %3d LOAD_FAST 1 (x)
48 LOAD_CONST 1 (1)
49 COMPARE_OP 40 (==)
50 LOAD_FAST 0 (self)
51 STORE_ATTR 0 (x)
52 RETURN_CONST 0 (None)
53 """ % (_C.__init__.__code__.co_firstlineno, _C.__init__.__code__.co_firstlineno + 1,)
54
55 dis_c_instance_method_bytes = """\
56 RESUME 0
57 LOAD_FAST 1
58 LOAD_CONST 1
59 COMPARE_OP 40 (==)
60 LOAD_FAST 0
61 STORE_ATTR 0
62 RETURN_CONST 0
63 """
64
65 dis_c_class_method = """\
66 %3d RESUME 0
67
68 %3d LOAD_FAST 1 (x)
69 LOAD_CONST 1 (1)
70 COMPARE_OP 40 (==)
71 LOAD_FAST 0 (cls)
72 STORE_ATTR 0 (x)
73 RETURN_CONST 0 (None)
74 """ % (_C.cm.__code__.co_firstlineno, _C.cm.__code__.co_firstlineno + 2,)
75
76 dis_c_static_method = """\
77 %3d RESUME 0
78
79 %3d LOAD_FAST 0 (x)
80 LOAD_CONST 1 (1)
81 COMPARE_OP 40 (==)
82 STORE_FAST 0 (x)
83 RETURN_CONST 0 (None)
84 """ % (_C.sm.__code__.co_firstlineno, _C.sm.__code__.co_firstlineno + 2,)
85
86 # Class disassembling info has an extra newline at end.
87 dis_c = """\
88 Disassembly of %s:
89 %s
90 Disassembly of %s:
91 %s
92 Disassembly of %s:
93 %s
94 """ % (_C.__init__.__name__, dis_c_instance_method,
95 _C.cm.__name__, dis_c_class_method,
96 _C.sm.__name__, dis_c_static_method)
97
98 def _f(a):
99 print(a)
100 return 1
101
102 dis_f = """\
103 %3d RESUME 0
104
105 %3d LOAD_GLOBAL 1 (NULL + print)
106 LOAD_FAST 0 (a)
107 CALL 1
108 POP_TOP
109
110 %3d RETURN_CONST 1 (1)
111 """ % (_f.__code__.co_firstlineno,
112 _f.__code__.co_firstlineno + 1,
113 _f.__code__.co_firstlineno + 2)
114
115
116 dis_f_co_code = """\
117 RESUME 0
118 LOAD_GLOBAL 1
119 LOAD_FAST 0
120 CALL 1
121 POP_TOP
122 RETURN_CONST 1
123 """
124
125
126 def bug708901():
127 for res in range(1,
128 10):
129 pass
130
131 dis_bug708901 = """\
132 %3d RESUME 0
133
134 %3d LOAD_GLOBAL 1 (NULL + range)
135 LOAD_CONST 1 (1)
136
137 %3d LOAD_CONST 2 (10)
138
139 %3d CALL 2
140 GET_ITER
141 >> FOR_ITER 2 (to 34)
142 STORE_FAST 0 (res)
143
144 %3d JUMP_BACKWARD 4 (to 26)
145
146 %3d >> END_FOR
147 RETURN_CONST 0 (None)
148 """ % (bug708901.__code__.co_firstlineno,
149 bug708901.__code__.co_firstlineno + 1,
150 bug708901.__code__.co_firstlineno + 2,
151 bug708901.__code__.co_firstlineno + 1,
152 bug708901.__code__.co_firstlineno + 3,
153 bug708901.__code__.co_firstlineno + 1)
154
155
156 def bug1333982(x=[]):
157 assert 0, ((s for s in x) +
158 1)
159 pass
160
161 dis_bug1333982 = """\
162 %3d RESUME 0
163
164 %3d LOAD_ASSERTION_ERROR
165 LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>)
166 MAKE_FUNCTION 0
167 LOAD_FAST 0 (x)
168 GET_ITER
169 CALL 0
170
171 %3d LOAD_CONST 2 (1)
172
173 %3d BINARY_OP 0 (+)
174 CALL 0
175 RAISE_VARARGS 1
176 """ % (bug1333982.__code__.co_firstlineno,
177 bug1333982.__code__.co_firstlineno + 1,
178 __file__,
179 bug1333982.__code__.co_firstlineno + 1,
180 bug1333982.__code__.co_firstlineno + 2,
181 bug1333982.__code__.co_firstlineno + 1)
182
183
184 def bug42562():
185 pass
186
187
188 # Set line number for 'pass' to None
189 bug42562.__code__ = bug42562.__code__.replace(co_linetable=b'\xf8')
190
191
192 dis_bug42562 = """\
193 RESUME 0
194 RETURN_CONST 0 (None)
195 """
196
197 # Extended arg followed by NOP
198 code_bug_45757 = bytes([
199 0x90, 0x01, # EXTENDED_ARG 0x01
200 0x09, 0xFF, # NOP 0xFF
201 0x90, 0x01, # EXTENDED_ARG 0x01
202 0x64, 0x29, # LOAD_CONST 0x29
203 0x53, 0x00, # RETURN_VALUE 0x00
204 ])
205
206 dis_bug_45757 = """\
207 EXTENDED_ARG 1
208 NOP
209 EXTENDED_ARG 1
210 LOAD_CONST 297
211 RETURN_VALUE
212 """
213
214 # [255, 255, 255, 252] is -4 in a 4 byte signed integer
215 bug46724 = bytes([
216 opcode.EXTENDED_ARG, 255,
217 opcode.EXTENDED_ARG, 255,
218 opcode.EXTENDED_ARG, 255,
219 opcode.opmap['JUMP_FORWARD'], 252,
220 ])
221
222
223 dis_bug46724 = """\
224 >> EXTENDED_ARG 255
225 EXTENDED_ARG 65535
226 EXTENDED_ARG 16777215
227 JUMP_FORWARD -4 (to 0)
228 """
229
230 def func_w_kwargs(a, b, **c):
231 pass
232
233 def wrap_func_w_kwargs():
234 func_w_kwargs(1, 2, c=5)
235
236 dis_kw_names = """\
237 %3d RESUME 0
238
239 %3d LOAD_GLOBAL 1 (NULL + func_w_kwargs)
240 LOAD_CONST 1 (1)
241 LOAD_CONST 2 (2)
242 LOAD_CONST 3 (5)
243 KW_NAMES 4 (('c',))
244 CALL 3
245 POP_TOP
246 RETURN_CONST 0 (None)
247 """ % (wrap_func_w_kwargs.__code__.co_firstlineno,
248 wrap_func_w_kwargs.__code__.co_firstlineno + 1)
249
250 dis_intrinsic_1_2 = """\
251 0 RESUME 0
252
253 1 LOAD_CONST 0 (0)
254 LOAD_CONST 1 (('*',))
255 IMPORT_NAME 0 (math)
256 CALL_INTRINSIC_1 2 (INTRINSIC_IMPORT_STAR)
257 POP_TOP
258 RETURN_CONST 2 (None)
259 """
260
261 dis_intrinsic_1_5 = """\
262 0 RESUME 0
263
264 1 LOAD_NAME 0 (a)
265 CALL_INTRINSIC_1 5 (INTRINSIC_UNARY_POSITIVE)
266 RETURN_VALUE
267 """
268
269 dis_intrinsic_1_6 = """\
270 0 RESUME 0
271
272 1 BUILD_LIST 0
273 LOAD_NAME 0 (a)
274 LIST_EXTEND 1
275 CALL_INTRINSIC_1 6 (INTRINSIC_LIST_TO_TUPLE)
276 RETURN_VALUE
277 """
278
279 _BIG_LINENO_FORMAT = """\
280 1 RESUME 0
281
282 %3d LOAD_GLOBAL 0 (spam)
283 POP_TOP
284 RETURN_CONST 0 (None)
285 """
286
287 _BIG_LINENO_FORMAT2 = """\
288 1 RESUME 0
289
290 %4d LOAD_GLOBAL 0 (spam)
291 POP_TOP
292 RETURN_CONST 0 (None)
293 """
294
295 dis_module_expected_results = """\
296 Disassembly of f:
297 4 RESUME 0
298 RETURN_CONST 0 (None)
299
300 Disassembly of g:
301 5 RESUME 0
302 RETURN_CONST 0 (None)
303
304 """
305
306 expr_str = "x + 1"
307
308 dis_expr_str = """\
309 0 RESUME 0
310
311 1 LOAD_NAME 0 (x)
312 LOAD_CONST 0 (1)
313 BINARY_OP 0 (+)
314 RETURN_VALUE
315 """
316
317 simple_stmt_str = "x = x + 1"
318
319 dis_simple_stmt_str = """\
320 0 RESUME 0
321
322 1 LOAD_NAME 0 (x)
323 LOAD_CONST 0 (1)
324 BINARY_OP 0 (+)
325 STORE_NAME 0 (x)
326 RETURN_CONST 1 (None)
327 """
328
329 annot_stmt_str = """\
330
331 x: int = 1
332 y: fun(1)
333 lst[fun(0)]: int = 1
334 """
335 # leading newline is for a reason (tests lineno)
336
337 dis_annot_stmt_str = """\
338 0 RESUME 0
339
340 2 SETUP_ANNOTATIONS
341 LOAD_CONST 0 (1)
342 STORE_NAME 0 (x)
343 LOAD_NAME 1 (int)
344 LOAD_NAME 2 (__annotations__)
345 LOAD_CONST 1 ('x')
346 STORE_SUBSCR
347
348 3 PUSH_NULL
349 LOAD_NAME 3 (fun)
350 LOAD_CONST 0 (1)
351 CALL 1
352 LOAD_NAME 2 (__annotations__)
353 LOAD_CONST 2 ('y')
354 STORE_SUBSCR
355
356 4 LOAD_CONST 0 (1)
357 LOAD_NAME 4 (lst)
358 PUSH_NULL
359 LOAD_NAME 3 (fun)
360 LOAD_CONST 3 (0)
361 CALL 1
362 STORE_SUBSCR
363 LOAD_NAME 1 (int)
364 POP_TOP
365 RETURN_CONST 4 (None)
366 """
367
368 compound_stmt_str = """\
369 x = 0
370 while 1:
371 x += 1"""
372 # Trailing newline has been deliberately omitted
373
374 dis_compound_stmt_str = """\
375 0 RESUME 0
376
377 1 LOAD_CONST 0 (0)
378 STORE_NAME 0 (x)
379
380 2 NOP
381
382 3 >> LOAD_NAME 0 (x)
383 LOAD_CONST 1 (1)
384 BINARY_OP 13 (+=)
385 STORE_NAME 0 (x)
386
387 2 JUMP_BACKWARD 6 (to 8)
388 """
389
390 dis_traceback = """\
391 %3d RESUME 0
392
393 %3d NOP
394
395 %3d LOAD_CONST 1 (1)
396 LOAD_CONST 2 (0)
397 --> BINARY_OP 11 (/)
398 POP_TOP
399
400 %3d LOAD_FAST_CHECK 1 (tb)
401 RETURN_VALUE
402 >> PUSH_EXC_INFO
403
404 %3d LOAD_GLOBAL 0 (Exception)
405 CHECK_EXC_MATCH
406 POP_JUMP_IF_FALSE 23 (to 80)
407 STORE_FAST 0 (e)
408
409 %3d LOAD_FAST 0 (e)
410 LOAD_ATTR 2 (__traceback__)
411 STORE_FAST 1 (tb)
412 POP_EXCEPT
413 LOAD_CONST 0 (None)
414 STORE_FAST 0 (e)
415 DELETE_FAST 0 (e)
416
417 %3d LOAD_FAST 1 (tb)
418 RETURN_VALUE
419 >> LOAD_CONST 0 (None)
420 STORE_FAST 0 (e)
421 DELETE_FAST 0 (e)
422 RERAISE 1
423
424 %3d >> RERAISE 0
425 >> COPY 3
426 POP_EXCEPT
427 RERAISE 1
428 ExceptionTable:
429 4 rows
430 """ % (TRACEBACK_CODE.co_firstlineno,
431 TRACEBACK_CODE.co_firstlineno + 1,
432 TRACEBACK_CODE.co_firstlineno + 2,
433 TRACEBACK_CODE.co_firstlineno + 5,
434 TRACEBACK_CODE.co_firstlineno + 3,
435 TRACEBACK_CODE.co_firstlineno + 4,
436 TRACEBACK_CODE.co_firstlineno + 5,
437 TRACEBACK_CODE.co_firstlineno + 3)
438
439 def _fstring(a, b, c, d):
440 return f'{a} {b:4} {c!r} {d!r:4}'
441
442 dis_fstring = """\
443 %3d RESUME 0
444
445 %3d LOAD_FAST 0 (a)
446 FORMAT_VALUE 0
447 LOAD_CONST 1 (' ')
448 LOAD_FAST 1 (b)
449 LOAD_CONST 2 ('4')
450 FORMAT_VALUE 4 (with format)
451 LOAD_CONST 1 (' ')
452 LOAD_FAST 2 (c)
453 FORMAT_VALUE 2 (repr)
454 LOAD_CONST 1 (' ')
455 LOAD_FAST 3 (d)
456 LOAD_CONST 2 ('4')
457 FORMAT_VALUE 6 (repr, with format)
458 BUILD_STRING 7
459 RETURN_VALUE
460 """ % (_fstring.__code__.co_firstlineno, _fstring.__code__.co_firstlineno + 1)
461
462 def _with(c):
463 with c:
464 x = 1
465 y = 2
466
467 dis_with = """\
468 %3d RESUME 0
469
470 %3d LOAD_FAST 0 (c)
471 BEFORE_WITH
472 POP_TOP
473
474 %3d LOAD_CONST 1 (1)
475 STORE_FAST 1 (x)
476
477 %3d LOAD_CONST 0 (None)
478 LOAD_CONST 0 (None)
479 LOAD_CONST 0 (None)
480 CALL 2
481 POP_TOP
482
483 %3d LOAD_CONST 2 (2)
484 STORE_FAST 2 (y)
485 RETURN_CONST 0 (None)
486
487 %3d >> PUSH_EXC_INFO
488 WITH_EXCEPT_START
489 POP_JUMP_IF_TRUE 1 (to 42)
490 RERAISE 2
491 >> POP_TOP
492 POP_EXCEPT
493 POP_TOP
494 POP_TOP
495
496 %3d LOAD_CONST 2 (2)
497 STORE_FAST 2 (y)
498 RETURN_CONST 0 (None)
499 >> COPY 3
500 POP_EXCEPT
501 RERAISE 1
502 ExceptionTable:
503 2 rows
504 """ % (_with.__code__.co_firstlineno,
505 _with.__code__.co_firstlineno + 1,
506 _with.__code__.co_firstlineno + 2,
507 _with.__code__.co_firstlineno + 1,
508 _with.__code__.co_firstlineno + 3,
509 _with.__code__.co_firstlineno + 1,
510 _with.__code__.co_firstlineno + 3,
511 )
512
513 async def _asyncwith(c):
514 async with c:
515 x = 1
516 y = 2
517
518 dis_asyncwith = """\
519 %3d RETURN_GENERATOR
520 POP_TOP
521 RESUME 0
522
523 %3d LOAD_FAST 0 (c)
524 BEFORE_ASYNC_WITH
525 GET_AWAITABLE 1
526 LOAD_CONST 0 (None)
527 >> SEND 3 (to 24)
528 YIELD_VALUE 2
529 RESUME 3
530 JUMP_BACKWARD_NO_INTERRUPT 5 (to 14)
531 >> END_SEND
532 POP_TOP
533
534 %3d LOAD_CONST 1 (1)
535 STORE_FAST 1 (x)
536
537 %3d LOAD_CONST 0 (None)
538 LOAD_CONST 0 (None)
539 LOAD_CONST 0 (None)
540 CALL 2
541 GET_AWAITABLE 2
542 LOAD_CONST 0 (None)
543 >> SEND 3 (to 60)
544 YIELD_VALUE 2
545 RESUME 3
546 JUMP_BACKWARD_NO_INTERRUPT 5 (to 50)
547 >> END_SEND
548 POP_TOP
549
550 %3d LOAD_CONST 2 (2)
551 STORE_FAST 2 (y)
552 RETURN_CONST 0 (None)
553
554 %3d >> CLEANUP_THROW
555 JUMP_BACKWARD 25 (to 24)
556 >> CLEANUP_THROW
557 JUMP_BACKWARD 9 (to 60)
558 >> PUSH_EXC_INFO
559 WITH_EXCEPT_START
560 GET_AWAITABLE 2
561 LOAD_CONST 0 (None)
562 >> SEND 4 (to 98)
563 YIELD_VALUE 3
564 RESUME 3
565 JUMP_BACKWARD_NO_INTERRUPT 5 (to 86)
566 >> CLEANUP_THROW
567 >> END_SEND
568 POP_JUMP_IF_TRUE 1 (to 104)
569 RERAISE 2
570 >> POP_TOP
571 POP_EXCEPT
572 POP_TOP
573 POP_TOP
574
575 %3d LOAD_CONST 2 (2)
576 STORE_FAST 2 (y)
577 RETURN_CONST 0 (None)
578 >> COPY 3
579 POP_EXCEPT
580 RERAISE 1
581 >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
582 RERAISE 1
583 ExceptionTable:
584 12 rows
585 """ % (_asyncwith.__code__.co_firstlineno,
586 _asyncwith.__code__.co_firstlineno + 1,
587 _asyncwith.__code__.co_firstlineno + 2,
588 _asyncwith.__code__.co_firstlineno + 1,
589 _asyncwith.__code__.co_firstlineno + 3,
590 _asyncwith.__code__.co_firstlineno + 1,
591 _asyncwith.__code__.co_firstlineno + 3,
592 )
593
594
595 def _tryfinally(a, b):
596 try:
597 return a
598 finally:
599 b()
600
601 def _tryfinallyconst(b):
602 try:
603 return 1
604 finally:
605 b()
606
607 dis_tryfinally = """\
608 %3d RESUME 0
609
610 %3d NOP
611
612 %3d LOAD_FAST 0 (a)
613
614 %3d PUSH_NULL
615 LOAD_FAST 1 (b)
616 CALL 0
617 POP_TOP
618 RETURN_VALUE
619 >> PUSH_EXC_INFO
620 PUSH_NULL
621 LOAD_FAST 1 (b)
622 CALL 0
623 POP_TOP
624 RERAISE 0
625 >> COPY 3
626 POP_EXCEPT
627 RERAISE 1
628 ExceptionTable:
629 2 rows
630 """ % (_tryfinally.__code__.co_firstlineno,
631 _tryfinally.__code__.co_firstlineno + 1,
632 _tryfinally.__code__.co_firstlineno + 2,
633 _tryfinally.__code__.co_firstlineno + 4,
634 )
635
636 dis_tryfinallyconst = """\
637 %3d RESUME 0
638
639 %3d NOP
640
641 %3d NOP
642
643 %3d PUSH_NULL
644 LOAD_FAST 0 (b)
645 CALL 0
646 POP_TOP
647 RETURN_CONST 1 (1)
648 PUSH_EXC_INFO
649 PUSH_NULL
650 LOAD_FAST 0 (b)
651 CALL 0
652 POP_TOP
653 RERAISE 0
654 >> COPY 3
655 POP_EXCEPT
656 RERAISE 1
657 ExceptionTable:
658 1 row
659 """ % (_tryfinallyconst.__code__.co_firstlineno,
660 _tryfinallyconst.__code__.co_firstlineno + 1,
661 _tryfinallyconst.__code__.co_firstlineno + 2,
662 _tryfinallyconst.__code__.co_firstlineno + 4,
663 )
664
665 def _g(x):
666 yield x
667
668 async def _ag(x):
669 yield x
670
671 async def _co(x):
672 async for item in _ag(x):
673 pass
674
675 def _h(y):
676 def foo(x):
677 '''funcdoc'''
678 return list(x + z for z in y)
679 return foo
680
681 dis_nested_0 = """\
682 MAKE_CELL 0 (y)
683
684 %3d RESUME 0
685
686 %3d LOAD_CLOSURE 0 (y)
687 BUILD_TUPLE 1
688 LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
689 MAKE_FUNCTION 8 (closure)
690 STORE_FAST 1 (foo)
691
692 %3d LOAD_FAST 1 (foo)
693 RETURN_VALUE
694 """ % (_h.__code__.co_firstlineno,
695 _h.__code__.co_firstlineno + 1,
696 __file__,
697 _h.__code__.co_firstlineno + 1,
698 _h.__code__.co_firstlineno + 4,
699 )
700
701 dis_nested_1 = """%s
702 Disassembly of <code object foo at 0x..., file "%s", line %d>:
703 COPY_FREE_VARS 1
704 MAKE_CELL 0 (x)
705
706 %3d RESUME 0
707
708 %3d LOAD_GLOBAL 1 (NULL + list)
709 LOAD_CLOSURE 0 (x)
710 BUILD_TUPLE 1
711 LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>)
712 MAKE_FUNCTION 8 (closure)
713 LOAD_DEREF 1 (y)
714 GET_ITER
715 CALL 0
716 CALL 1
717 RETURN_VALUE
718 """ % (dis_nested_0,
719 __file__,
720 _h.__code__.co_firstlineno + 1,
721 _h.__code__.co_firstlineno + 1,
722 _h.__code__.co_firstlineno + 3,
723 __file__,
724 _h.__code__.co_firstlineno + 3,
725 )
726
727 dis_nested_2 = """%s
728 Disassembly of <code object <genexpr> at 0x..., file "%s", line %d>:
729 COPY_FREE_VARS 1
730
731 %3d RETURN_GENERATOR
732 POP_TOP
733 RESUME 0
734 LOAD_FAST 0 (.0)
735 >> FOR_ITER 9 (to 32)
736 STORE_FAST 1 (z)
737 LOAD_DEREF 2 (x)
738 LOAD_FAST 1 (z)
739 BINARY_OP 0 (+)
740 YIELD_VALUE 1
741 RESUME 1
742 POP_TOP
743 JUMP_BACKWARD 11 (to 10)
744 >> END_FOR
745 RETURN_CONST 0 (None)
746 >> CALL_INTRINSIC_1 3 (INTRINSIC_STOPITERATION_ERROR)
747 RERAISE 1
748 ExceptionTable:
749 1 row
750 """ % (dis_nested_1,
751 __file__,
752 _h.__code__.co_firstlineno + 3,
753 _h.__code__.co_firstlineno + 3,
754 )
755
756 def load_test(x, y=0):
757 a, b = x, y
758 return a, b
759
760 dis_load_test_quickened_code = """\
761 %3d 0 RESUME 0
762
763 %3d 2 LOAD_FAST__LOAD_FAST 0 (x)
764 4 LOAD_FAST 1 (y)
765 6 STORE_FAST__STORE_FAST 3 (b)
766 8 STORE_FAST__LOAD_FAST 2 (a)
767
768 %3d 10 LOAD_FAST__LOAD_FAST 2 (a)
769 12 LOAD_FAST 3 (b)
770 14 BUILD_TUPLE 2
771 16 RETURN_VALUE
772 """ % (load_test.__code__.co_firstlineno,
773 load_test.__code__.co_firstlineno + 1,
774 load_test.__code__.co_firstlineno + 2)
775
776 def loop_test():
777 for i in [1, 2, 3] * 3:
778 load_test(i)
779
780 dis_loop_test_quickened_code = """\
781 %3d RESUME 0
782
783 %3d BUILD_LIST 0
784 LOAD_CONST 1 ((1, 2, 3))
785 LIST_EXTEND 1
786 LOAD_CONST 2 (3)
787 BINARY_OP 5 (*)
788 GET_ITER
789 >> FOR_ITER_LIST 13 (to 46)
790 STORE_FAST 0 (i)
791
792 %3d LOAD_GLOBAL_MODULE 1 (NULL + load_test)
793 LOAD_FAST 0 (i)
794 CALL_PY_WITH_DEFAULTS 1
795 POP_TOP
796 JUMP_BACKWARD 15 (to 16)
797
798 %3d >> END_FOR
799 RETURN_CONST 0 (None)
800 """ % (loop_test.__code__.co_firstlineno,
801 loop_test.__code__.co_firstlineno + 1,
802 loop_test.__code__.co_firstlineno + 2,
803 loop_test.__code__.co_firstlineno + 1,)
804
805 def extended_arg_quick():
806 *_, _ = ...
807
808 dis_extended_arg_quick_code = """\
809 %3d 0 RESUME 0
810
811 %3d 2 LOAD_CONST 1 (Ellipsis)
812 4 EXTENDED_ARG 1
813 6 UNPACK_EX 256
814 8 STORE_FAST 0 (_)
815 10 STORE_FAST 0 (_)
816 12 RETURN_CONST 0 (None)
817 """% (extended_arg_quick.__code__.co_firstlineno,
818 extended_arg_quick.__code__.co_firstlineno + 1,)
819
820 ADAPTIVE_WARMUP_DELAY = 2
821
822 class ESC[4;38;5;81mDisTestBase(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
823 "Common utilities for DisTests and TestDisTraceback"
824
825 def strip_addresses(self, text):
826 return re.sub(r'\b0x[0-9A-Fa-f]+\b', '0x...', text)
827
828 def find_offset_column(self, lines):
829 for line in lines:
830 if line and not line.startswith("Disassembly"):
831 break
832 else:
833 return 0, 0
834 offset = 5
835 while (line[offset] == " "):
836 offset += 1
837 if (line[offset] == ">"):
838 offset += 2
839 while (line[offset] == " "):
840 offset += 1
841 end = offset
842 while line[end] in "0123456789":
843 end += 1
844 return end-5, end
845
846 def assert_offsets_increasing(self, text, delta):
847 expected_offset = 0
848 lines = text.splitlines()
849 start, end = self.find_offset_column(lines)
850 for line in lines:
851 if not line:
852 continue
853 if line.startswith("Disassembly"):
854 expected_offset = 0
855 continue
856 if line.startswith("Exception"):
857 break
858 offset = int(line[start:end])
859 self.assertGreaterEqual(offset, expected_offset, line)
860 expected_offset = offset + delta
861
862 def assert_exception_table_increasing(self, lines):
863 prev_start, prev_end = -1, -1
864 count = 0
865 for line in lines:
866 m = re.match(r' (\d+) to (\d+) -> \d+ \[\d+\]', line)
867 start, end = [int(g) for g in m.groups()]
868 self.assertGreaterEqual(end, start)
869 self.assertGreater(start, prev_end)
870 prev_start, prev_end = start, end
871 count += 1
872 return count
873
874 def strip_offsets(self, text):
875 lines = text.splitlines(True)
876 start, end = self.find_offset_column(lines)
877 res = []
878 lines = iter(lines)
879 for line in lines:
880 if line.startswith("Exception"):
881 res.append(line)
882 break
883 if not line or line.startswith("Disassembly"):
884 res.append(line)
885 else:
886 res.append(line[:start] + line[end:])
887 num_rows = self.assert_exception_table_increasing(lines)
888 if num_rows:
889 res.append(f"{num_rows} row{'s' if num_rows > 1 else ''}\n")
890 return "".join(res)
891
892 def do_disassembly_compare(self, got, expected, with_offsets=False):
893 if not with_offsets:
894 self.assert_offsets_increasing(got, 2)
895 got = self.strip_offsets(got)
896 if got != expected:
897 got = self.strip_addresses(got)
898 self.assertEqual(got, expected)
899
900
901 class ESC[4;38;5;81mDisTests(ESC[4;38;5;149mDisTestBase):
902
903 maxDiff = None
904
905 def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
906 # We want to test the default printing behaviour, not the file arg
907 output = io.StringIO()
908 with contextlib.redirect_stdout(output):
909 if wrapper:
910 dis.dis(func, **kwargs)
911 else:
912 dis.disassemble(func, lasti, **kwargs)
913 return output.getvalue()
914
915 def get_disassemble_as_string(self, func, lasti=-1):
916 return self.get_disassembly(func, lasti, False)
917
918 def do_disassembly_test(self, func, expected, with_offsets=False):
919 self.maxDiff = None
920 got = self.get_disassembly(func, depth=0)
921 self.do_disassembly_compare(got, expected, with_offsets)
922 # Add checks for dis.disco
923 if hasattr(func, '__code__'):
924 got_disco = io.StringIO()
925 with contextlib.redirect_stdout(got_disco):
926 dis.disco(func.__code__)
927 self.do_disassembly_compare(got_disco.getvalue(), expected,
928 with_offsets)
929
930 def test_opmap(self):
931 self.assertEqual(dis.opmap["NOP"], 9)
932 self.assertIn(dis.opmap["LOAD_CONST"], dis.hasconst)
933 self.assertIn(dis.opmap["STORE_NAME"], dis.hasname)
934
935 def test_opname(self):
936 self.assertEqual(dis.opname[dis.opmap["LOAD_FAST"]], "LOAD_FAST")
937
938 def test_boundaries(self):
939 self.assertEqual(dis.opmap["EXTENDED_ARG"], dis.EXTENDED_ARG)
940 self.assertEqual(dis.opmap["STORE_NAME"], dis.HAVE_ARGUMENT)
941
942 def test_widths(self):
943 long_opcodes = set(['JUMP_BACKWARD_NO_INTERRUPT',
944 'INSTRUMENTED_CALL_FUNCTION_EX'])
945 for opcode, opname in enumerate(dis.opname):
946 if opname in long_opcodes or opname.startswith("INSTRUMENTED"):
947 continue
948 with self.subTest(opname=opname):
949 width = dis._OPNAME_WIDTH
950 if opcode in dis.hasarg:
951 width += 1 + dis._OPARG_WIDTH
952 self.assertLessEqual(len(opname), width)
953
954 def test_dis(self):
955 self.do_disassembly_test(_f, dis_f)
956
957 def test_bug_708901(self):
958 self.do_disassembly_test(bug708901, dis_bug708901)
959
960 def test_bug_1333982(self):
961 # This one is checking bytecodes generated for an `assert` statement,
962 # so fails if the tests are run with -O. Skip this test then.
963 if not __debug__:
964 self.skipTest('need asserts, run without -O')
965
966 self.do_disassembly_test(bug1333982, dis_bug1333982)
967
968 def test_bug_42562(self):
969 self.do_disassembly_test(bug42562, dis_bug42562)
970
971 def test_bug_45757(self):
972 # Extended arg followed by NOP
973 self.do_disassembly_test(code_bug_45757, dis_bug_45757)
974
975 def test_bug_46724(self):
976 # Test that negative operargs are handled properly
977 self.do_disassembly_test(bug46724, dis_bug46724)
978
979 def test_kw_names(self):
980 # Test that value is displayed for KW_NAMES
981 self.do_disassembly_test(wrap_func_w_kwargs, dis_kw_names)
982
983 def test_intrinsic_1(self):
984 # Test that argrepr is displayed for CALL_INTRINSIC_1
985 self.do_disassembly_test("from math import *", dis_intrinsic_1_2)
986 self.do_disassembly_test("+a", dis_intrinsic_1_5)
987 self.do_disassembly_test("(*a,)", dis_intrinsic_1_6)
988
989 def test_intrinsic_2(self):
990 self.assertIn("CALL_INTRINSIC_2 1 (INTRINSIC_PREP_RERAISE_STAR)",
991 self.get_disassembly("try: pass\nexcept* Exception: x"))
992
993 def test_big_linenos(self):
994 def func(count):
995 namespace = {}
996 func = "def foo():\n " + "".join(["\n "] * count + ["spam\n"])
997 exec(func, namespace)
998 return namespace['foo']
999
1000 # Test all small ranges
1001 for i in range(1, 300):
1002 expected = _BIG_LINENO_FORMAT % (i + 2)
1003 self.do_disassembly_test(func(i), expected)
1004
1005 # Test some larger ranges too
1006 for i in range(300, 1000, 10):
1007 expected = _BIG_LINENO_FORMAT % (i + 2)
1008 self.do_disassembly_test(func(i), expected)
1009
1010 for i in range(1000, 5000, 10):
1011 expected = _BIG_LINENO_FORMAT2 % (i + 2)
1012 self.do_disassembly_test(func(i), expected)
1013
1014 from test import dis_module
1015 self.do_disassembly_test(dis_module, dis_module_expected_results)
1016
1017 def test_big_offsets(self):
1018 self.maxDiff = None
1019 def func(count):
1020 namespace = {}
1021 func = "def foo(x):\n " + ";".join(["x = x + 1"] * count) + "\n return x"
1022 exec(func, namespace)
1023 return namespace['foo']
1024
1025 def expected(count, w):
1026 s = ['''\
1027 1 %*d RESUME 0
1028
1029 ''' % (w, 0)]
1030 s += ['''\
1031 %*d LOAD_FAST 0 (x)
1032 %*d LOAD_CONST 1 (1)
1033 %*d BINARY_OP 0 (+)
1034 %*d STORE_FAST 0 (x)
1035 ''' % (w, 10*i + 2, w, 10*i + 4, w, 10*i + 6, w, 10*i + 10)
1036 for i in range(count)]
1037 s += ['''\
1038
1039 3 %*d LOAD_FAST 0 (x)
1040 %*d RETURN_VALUE
1041 ''' % (w, 10*count + 2, w, 10*count + 4)]
1042 s[1] = ' 2' + s[1][3:]
1043 return ''.join(s)
1044
1045 for i in range(1, 5):
1046 self.do_disassembly_test(func(i), expected(i, 4), True)
1047 self.do_disassembly_test(func(999), expected(999, 4), True)
1048 self.do_disassembly_test(func(1000), expected(1000, 5), True)
1049
1050 def test_disassemble_str(self):
1051 self.do_disassembly_test(expr_str, dis_expr_str)
1052 self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str)
1053 self.do_disassembly_test(annot_stmt_str, dis_annot_stmt_str)
1054 self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str)
1055
1056 def test_disassemble_bytes(self):
1057 self.do_disassembly_test(_f.__code__.co_code, dis_f_co_code)
1058
1059 def test_disassemble_class(self):
1060 self.do_disassembly_test(_C, dis_c)
1061
1062 def test_disassemble_instance_method(self):
1063 self.do_disassembly_test(_C(1).__init__, dis_c_instance_method)
1064
1065 def test_disassemble_instance_method_bytes(self):
1066 method_bytecode = _C(1).__init__.__code__.co_code
1067 self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes)
1068
1069 def test_disassemble_static_method(self):
1070 self.do_disassembly_test(_C.sm, dis_c_static_method)
1071
1072 def test_disassemble_class_method(self):
1073 self.do_disassembly_test(_C.cm, dis_c_class_method)
1074
1075 def test_disassemble_generator(self):
1076 gen_func_disas = self.get_disassembly(_g) # Generator function
1077 gen_disas = self.get_disassembly(_g(1)) # Generator iterator
1078 self.assertEqual(gen_disas, gen_func_disas)
1079
1080 def test_disassemble_async_generator(self):
1081 agen_func_disas = self.get_disassembly(_ag) # Async generator function
1082 agen_disas = self.get_disassembly(_ag(1)) # Async generator iterator
1083 self.assertEqual(agen_disas, agen_func_disas)
1084
1085 def test_disassemble_coroutine(self):
1086 coro_func_disas = self.get_disassembly(_co) # Coroutine function
1087 coro = _co(1) # Coroutine object
1088 coro.close() # Avoid a RuntimeWarning (never awaited)
1089 coro_disas = self.get_disassembly(coro)
1090 self.assertEqual(coro_disas, coro_func_disas)
1091
1092 def test_disassemble_fstring(self):
1093 self.do_disassembly_test(_fstring, dis_fstring)
1094
1095 def test_disassemble_with(self):
1096 self.do_disassembly_test(_with, dis_with)
1097
1098 def test_disassemble_asyncwith(self):
1099 self.do_disassembly_test(_asyncwith, dis_asyncwith)
1100
1101 def test_disassemble_try_finally(self):
1102 self.do_disassembly_test(_tryfinally, dis_tryfinally)
1103 self.do_disassembly_test(_tryfinallyconst, dis_tryfinallyconst)
1104
1105 def test_dis_none(self):
1106 try:
1107 del sys.last_exc
1108 except AttributeError:
1109 pass
1110 try:
1111 del sys.last_traceback
1112 except AttributeError:
1113 pass
1114 self.assertRaises(RuntimeError, dis.dis, None)
1115
1116 def test_dis_traceback(self):
1117 self.maxDiff = None
1118 try:
1119 del sys.last_traceback
1120 except AttributeError:
1121 pass
1122
1123 try:
1124 1/0
1125 except Exception as e:
1126 tb = e.__traceback__
1127 sys.last_exc = e
1128
1129 tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti)
1130 self.do_disassembly_test(None, tb_dis, True)
1131
1132 def test_dis_object(self):
1133 self.assertRaises(TypeError, dis.dis, object())
1134
1135 def test_disassemble_recursive(self):
1136 def check(expected, **kwargs):
1137 dis = self.get_disassembly(_h, **kwargs)
1138 dis = self.strip_addresses(dis)
1139 dis = self.strip_offsets(dis)
1140 self.assertEqual(dis, expected)
1141
1142 check(dis_nested_0, depth=0)
1143 check(dis_nested_1, depth=1)
1144 check(dis_nested_2, depth=2)
1145 check(dis_nested_2, depth=3)
1146 check(dis_nested_2, depth=None)
1147 check(dis_nested_2)
1148
1149 def test__try_compile_no_context_exc_on_error(self):
1150 # see gh-102114
1151 try:
1152 dis._try_compile(")", "")
1153 except Exception as e:
1154 self.assertIsNone(e.__context__)
1155
1156 @staticmethod
1157 def code_quicken(f, times=ADAPTIVE_WARMUP_DELAY):
1158 for _ in range(times):
1159 f()
1160
1161 @cpython_only
1162 @requires_specialization
1163 def test_super_instructions(self):
1164 self.code_quicken(lambda: load_test(0, 0))
1165 got = self.get_disassembly(load_test, adaptive=True)
1166 self.do_disassembly_compare(got, dis_load_test_quickened_code, True)
1167
1168 @cpython_only
1169 @requires_specialization
1170 def test_binary_specialize(self):
1171 binary_op_quicken = """\
1172 0 0 RESUME 0
1173
1174 1 2 LOAD_NAME 0 (a)
1175 4 LOAD_NAME 1 (b)
1176 6 %s
1177 10 RETURN_VALUE
1178 """
1179 co_int = compile('a + b', "<int>", "eval")
1180 self.code_quicken(lambda: exec(co_int, {}, {'a': 1, 'b': 2}))
1181 got = self.get_disassembly(co_int, adaptive=True)
1182 self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_INT 0 (+)", True)
1183
1184 co_unicode = compile('a + b', "<unicode>", "eval")
1185 self.code_quicken(lambda: exec(co_unicode, {}, {'a': 'a', 'b': 'b'}))
1186 got = self.get_disassembly(co_unicode, adaptive=True)
1187 self.do_disassembly_compare(got, binary_op_quicken % "BINARY_OP_ADD_UNICODE 0 (+)", True)
1188
1189 binary_subscr_quicken = """\
1190 0 0 RESUME 0
1191
1192 1 2 LOAD_NAME 0 (a)
1193 4 LOAD_CONST 0 (0)
1194 6 %s
1195 10 RETURN_VALUE
1196 """
1197 co_list = compile('a[0]', "<list>", "eval")
1198 self.code_quicken(lambda: exec(co_list, {}, {'a': [0]}))
1199 got = self.get_disassembly(co_list, adaptive=True)
1200 self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_LIST_INT", True)
1201
1202 co_dict = compile('a[0]', "<dict>", "eval")
1203 self.code_quicken(lambda: exec(co_dict, {}, {'a': {0: '1'}}))
1204 got = self.get_disassembly(co_dict, adaptive=True)
1205 self.do_disassembly_compare(got, binary_subscr_quicken % "BINARY_SUBSCR_DICT", True)
1206
1207 @cpython_only
1208 @requires_specialization
1209 def test_load_attr_specialize(self):
1210 load_attr_quicken = """\
1211 0 0 RESUME 0
1212
1213 1 2 LOAD_CONST 0 ('a')
1214 4 LOAD_ATTR_SLOT 0 (__class__)
1215 24 RETURN_VALUE
1216 """
1217 co = compile("'a'.__class__", "", "eval")
1218 self.code_quicken(lambda: exec(co, {}, {}))
1219 got = self.get_disassembly(co, adaptive=True)
1220 self.do_disassembly_compare(got, load_attr_quicken, True)
1221
1222 @cpython_only
1223 @requires_specialization
1224 def test_call_specialize(self):
1225 call_quicken = """\
1226 0 RESUME 0
1227
1228 1 PUSH_NULL
1229 LOAD_NAME 0 (str)
1230 LOAD_CONST 0 (1)
1231 CALL_NO_KW_STR_1 1
1232 RETURN_VALUE
1233 """
1234 co = compile("str(1)", "", "eval")
1235 self.code_quicken(lambda: exec(co, {}, {}))
1236 got = self.get_disassembly(co, adaptive=True)
1237 self.do_disassembly_compare(got, call_quicken)
1238
1239 @cpython_only
1240 @requires_specialization
1241 def test_loop_quicken(self):
1242 # Loop can trigger a quicken where the loop is located
1243 self.code_quicken(loop_test, 1)
1244 got = self.get_disassembly(loop_test, adaptive=True)
1245 self.do_disassembly_compare(got, dis_loop_test_quickened_code)
1246
1247 @cpython_only
1248 def test_extended_arg_quick(self):
1249 got = self.get_disassembly(extended_arg_quick)
1250 self.do_disassembly_compare(got, dis_extended_arg_quick_code, True)
1251
1252 def get_cached_values(self, quickened, adaptive):
1253 def f():
1254 l = []
1255 for i in range(42):
1256 l.append(i)
1257 if quickened:
1258 self.code_quicken(f)
1259 else:
1260 # "copy" the code to un-quicken it:
1261 f.__code__ = f.__code__.replace()
1262 for instruction in dis.get_instructions(
1263 f, show_caches=True, adaptive=adaptive
1264 ):
1265 if instruction.opname == "CACHE":
1266 yield instruction.argrepr
1267
1268 @cpython_only
1269 def test_show_caches(self):
1270 for quickened in (False, True):
1271 for adaptive in (False, True):
1272 with self.subTest(f"{quickened=}, {adaptive=}"):
1273 if adaptive:
1274 pattern = r"^(\w+: \d+)?$"
1275 else:
1276 pattern = r"^(\w+: 0)?$"
1277 caches = list(self.get_cached_values(quickened, adaptive))
1278 for cache in caches:
1279 self.assertRegex(cache, pattern)
1280 total_caches = 20
1281 empty_caches = 7
1282 self.assertEqual(caches.count(""), empty_caches)
1283 self.assertEqual(len(caches), total_caches)
1284
1285 @cpython_only
1286 def test_show_currinstr_with_cache(self):
1287 """
1288 Make sure that with lasti pointing to CACHE, it still shows the current
1289 line correctly
1290 """
1291 def f():
1292 print(a)
1293 # The code above should generate a LOAD_GLOBAL which has CACHE instr after
1294 # However, this might change in the future. So we explicitly try to find
1295 # a CACHE entry in the instructions. If we can't do that, fail the test
1296
1297 for inst in dis.get_instructions(f, show_caches=True):
1298 if inst.opname == "CACHE":
1299 op_offset = inst.offset - 2
1300 cache_offset = inst.offset
1301 break
1302 else:
1303 self.fail("Can't find a CACHE entry in the function provided to do the test")
1304
1305 assem_op = self.get_disassembly(f.__code__, lasti=op_offset, wrapper=False)
1306 assem_cache = self.get_disassembly(f.__code__, lasti=cache_offset, wrapper=False)
1307
1308 # Make sure --> exists and points to the correct offset
1309 self.assertRegex(assem_op, fr"-->\s+{op_offset}")
1310 # Make sure when lasti points to cache, it shows the same disassembly
1311 self.assertEqual(assem_op, assem_cache)
1312
1313
1314 class ESC[4;38;5;81mDisWithFileTests(ESC[4;38;5;149mDisTests):
1315
1316 # Run the tests again, using the file arg instead of print
1317 def get_disassembly(self, func, lasti=-1, wrapper=True, **kwargs):
1318 output = io.StringIO()
1319 if wrapper:
1320 dis.dis(func, file=output, **kwargs)
1321 else:
1322 dis.disassemble(func, lasti, file=output, **kwargs)
1323 return output.getvalue()
1324
1325
1326 if dis.code_info.__doc__ is None:
1327 code_info_consts = "0: None"
1328 else:
1329 code_info_consts = "0: 'Formatted details of methods, functions, or code.'"
1330
1331 code_info_code_info = f"""\
1332 Name: code_info
1333 Filename: (.*)
1334 Argument count: 1
1335 Positional-only arguments: 0
1336 Kw-only arguments: 0
1337 Number of locals: 1
1338 Stack size: \\d+
1339 Flags: OPTIMIZED, NEWLOCALS
1340 Constants:
1341 {code_info_consts}
1342 Names:
1343 0: _format_code_info
1344 1: _get_code_object
1345 Variable names:
1346 0: x"""
1347
1348
1349 @staticmethod
1350 def tricky(a, b, /, x, y, z=True, *args, c, d, e=[], **kwds):
1351 def f(c=c):
1352 print(a, b, x, y, z, c, d, e, f)
1353 yield a, b, x, y, z, c, d, e, f
1354
1355 code_info_tricky = """\
1356 Name: tricky
1357 Filename: (.*)
1358 Argument count: 5
1359 Positional-only arguments: 2
1360 Kw-only arguments: 3
1361 Number of locals: 10
1362 Stack size: \\d+
1363 Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
1364 Constants:
1365 0: None
1366 1: <code object f at (.*), file "(.*)", line (.*)>
1367 Variable names:
1368 0: a
1369 1: b
1370 2: x
1371 3: y
1372 4: z
1373 5: c
1374 6: d
1375 7: e
1376 8: args
1377 9: kwds
1378 Cell variables:
1379 0: [abedfxyz]
1380 1: [abedfxyz]
1381 2: [abedfxyz]
1382 3: [abedfxyz]
1383 4: [abedfxyz]
1384 5: [abedfxyz]"""
1385 # NOTE: the order of the cell variables above depends on dictionary order!
1386
1387 co_tricky_nested_f = tricky.__func__.__code__.co_consts[1]
1388
1389 code_info_tricky_nested_f = """\
1390 Filename: (.*)
1391 Argument count: 1
1392 Positional-only arguments: 0
1393 Kw-only arguments: 0
1394 Number of locals: 1
1395 Stack size: \\d+
1396 Flags: OPTIMIZED, NEWLOCALS, NESTED
1397 Constants:
1398 0: None
1399 Names:
1400 0: print
1401 Variable names:
1402 0: c
1403 Free variables:
1404 0: [abedfxyz]
1405 1: [abedfxyz]
1406 2: [abedfxyz]
1407 3: [abedfxyz]
1408 4: [abedfxyz]
1409 5: [abedfxyz]"""
1410
1411 code_info_expr_str = """\
1412 Name: <module>
1413 Filename: <disassembly>
1414 Argument count: 0
1415 Positional-only arguments: 0
1416 Kw-only arguments: 0
1417 Number of locals: 0
1418 Stack size: \\d+
1419 Flags: 0x0
1420 Constants:
1421 0: 1
1422 Names:
1423 0: x"""
1424
1425 code_info_simple_stmt_str = """\
1426 Name: <module>
1427 Filename: <disassembly>
1428 Argument count: 0
1429 Positional-only arguments: 0
1430 Kw-only arguments: 0
1431 Number of locals: 0
1432 Stack size: \\d+
1433 Flags: 0x0
1434 Constants:
1435 0: 1
1436 1: None
1437 Names:
1438 0: x"""
1439
1440 code_info_compound_stmt_str = """\
1441 Name: <module>
1442 Filename: <disassembly>
1443 Argument count: 0
1444 Positional-only arguments: 0
1445 Kw-only arguments: 0
1446 Number of locals: 0
1447 Stack size: \\d+
1448 Flags: 0x0
1449 Constants:
1450 0: 0
1451 1: 1
1452 Names:
1453 0: x"""
1454
1455
1456 async def async_def():
1457 await 1
1458 async for a in b: pass
1459 async with c as d: pass
1460
1461 code_info_async_def = """\
1462 Name: async_def
1463 Filename: (.*)
1464 Argument count: 0
1465 Positional-only arguments: 0
1466 Kw-only arguments: 0
1467 Number of locals: 2
1468 Stack size: \\d+
1469 Flags: OPTIMIZED, NEWLOCALS, COROUTINE
1470 Constants:
1471 0: None
1472 1: 1
1473 Names:
1474 0: b
1475 1: c
1476 Variable names:
1477 0: a
1478 1: d"""
1479
1480 class ESC[4;38;5;81mCodeInfoTests(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1481 test_pairs = [
1482 (dis.code_info, code_info_code_info),
1483 (tricky, code_info_tricky),
1484 (co_tricky_nested_f, code_info_tricky_nested_f),
1485 (expr_str, code_info_expr_str),
1486 (simple_stmt_str, code_info_simple_stmt_str),
1487 (compound_stmt_str, code_info_compound_stmt_str),
1488 (async_def, code_info_async_def)
1489 ]
1490
1491 def test_code_info(self):
1492 self.maxDiff = 1000
1493 for x, expected in self.test_pairs:
1494 self.assertRegex(dis.code_info(x), expected)
1495
1496 def test_show_code(self):
1497 self.maxDiff = 1000
1498 for x, expected in self.test_pairs:
1499 with captured_stdout() as output:
1500 dis.show_code(x)
1501 self.assertRegex(output.getvalue(), expected+"\n")
1502 output = io.StringIO()
1503 dis.show_code(x, file=output)
1504 self.assertRegex(output.getvalue(), expected)
1505
1506 def test_code_info_object(self):
1507 self.assertRaises(TypeError, dis.code_info, object())
1508
1509 def test_pretty_flags_no_flags(self):
1510 self.assertEqual(dis.pretty_flags(0), '0x0')
1511
1512
1513 # Fodder for instruction introspection tests
1514 # Editing any of these may require recalculating the expected output
1515 def outer(a=1, b=2):
1516 def f(c=3, d=4):
1517 def inner(e=5, f=6):
1518 print(a, b, c, d, e, f)
1519 print(a, b, c, d)
1520 return inner
1521 print(a, b, '', 1, [], {}, "Hello world!")
1522 return f
1523
1524 def jumpy():
1525 # This won't actually run (but that's OK, we only disassemble it)
1526 for i in range(10):
1527 print(i)
1528 if i < 4:
1529 continue
1530 if i > 6:
1531 break
1532 else:
1533 print("I can haz else clause?")
1534 while i:
1535 print(i)
1536 i -= 1
1537 if i > 6:
1538 continue
1539 if i < 4:
1540 break
1541 else:
1542 print("Who let lolcatz into this test suite?")
1543 try:
1544 1 / 0
1545 except ZeroDivisionError:
1546 print("Here we go, here we go, here we go...")
1547 else:
1548 with i as dodgy:
1549 print("Never reach this")
1550 finally:
1551 print("OK, now we're done")
1552
1553 # End fodder for opinfo generation tests
1554 expected_outer_line = 1
1555 _line_offset = outer.__code__.co_firstlineno - 1
1556 code_object_f = outer.__code__.co_consts[1]
1557 expected_f_line = code_object_f.co_firstlineno - _line_offset
1558 code_object_inner = code_object_f.co_consts[1]
1559 expected_inner_line = code_object_inner.co_firstlineno - _line_offset
1560 expected_jumpy_line = 1
1561
1562 # The following lines are useful to regenerate the expected results after
1563 # either the fodder is modified or the bytecode generation changes
1564 # After regeneration, update the references to code_object_f and
1565 # code_object_inner before rerunning the tests
1566
1567 def _stringify_instruction(instr):
1568 # Since line numbers and other offsets change a lot for these
1569 # test cases, ignore them.
1570 return str(instr._replace(positions=None))
1571
1572 def _prepare_test_cases():
1573 _instructions = dis.get_instructions(outer, first_line=expected_outer_line)
1574 print('expected_opinfo_outer = [\n ',
1575 ',\n '.join(map(_stringify_instruction, _instructions)), ',\n]', sep='')
1576 _instructions = dis.get_instructions(outer(), first_line=expected_f_line)
1577 print('expected_opinfo_f = [\n ',
1578 ',\n '.join(map(_stringify_instruction, _instructions)), ',\n]', sep='')
1579 _instructions = dis.get_instructions(outer()(), first_line=expected_inner_line)
1580 print('expected_opinfo_inner = [\n ',
1581 ',\n '.join(map(_stringify_instruction, _instructions)), ',\n]', sep='')
1582 _instructions = dis.get_instructions(jumpy, first_line=expected_jumpy_line)
1583 print('expected_opinfo_jumpy = [\n ',
1584 ',\n '.join(map(_stringify_instruction, _instructions)), ',\n]', sep='')
1585 dis.dis(outer)
1586
1587 #_prepare_test_cases()
1588
1589 Instruction = dis.Instruction
1590
1591 expected_opinfo_outer = [
1592 Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='a', argrepr='a', offset=0, starts_line=None, is_jump_target=False, positions=None),
1593 Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='b', argrepr='b', offset=2, starts_line=None, is_jump_target=False, positions=None),
1594 Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=4, starts_line=1, is_jump_target=False, positions=None),
1595 Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=(3, 4), argrepr='(3, 4)', offset=6, starts_line=2, is_jump_target=False, positions=None),
1596 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='a', argrepr='a', offset=8, starts_line=None, is_jump_target=False, positions=None),
1597 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='b', argrepr='b', offset=10, starts_line=None, is_jump_target=False, positions=None),
1598 Instruction(opname='BUILD_TUPLE', opcode=102, arg=2, argval=2, argrepr='', offset=12, starts_line=None, is_jump_target=False, positions=None),
1599 Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, starts_line=None, is_jump_target=False, positions=None),
1600 Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=16, starts_line=None, is_jump_target=False, positions=None),
1601 Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='f', argrepr='f', offset=18, starts_line=None, is_jump_target=False, positions=None),
1602 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='NULL + print', offset=20, starts_line=7, is_jump_target=False, positions=None),
1603 Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='a', argrepr='a', offset=30, starts_line=None, is_jump_target=False, positions=None),
1604 Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='b', argrepr='b', offset=32, starts_line=None, is_jump_target=False, positions=None),
1605 Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval='', argrepr="''", offset=34, starts_line=None, is_jump_target=False, positions=None),
1606 Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=1, argrepr='1', offset=36, starts_line=None, is_jump_target=False, positions=None),
1607 Instruction(opname='BUILD_LIST', opcode=103, arg=0, argval=0, argrepr='', offset=38, starts_line=None, is_jump_target=False, positions=None),
1608 Instruction(opname='BUILD_MAP', opcode=105, arg=0, argval=0, argrepr='', offset=40, starts_line=None, is_jump_target=False, positions=None),
1609 Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='Hello world!', argrepr="'Hello world!'", offset=42, starts_line=None, is_jump_target=False, positions=None),
1610 Instruction(opname='CALL', opcode=171, arg=7, argval=7, argrepr='', offset=44, starts_line=None, is_jump_target=False, positions=None),
1611 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=52, starts_line=None, is_jump_target=False, positions=None),
1612 Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='f', argrepr='f', offset=54, starts_line=8, is_jump_target=False, positions=None),
1613 Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=56, starts_line=None, is_jump_target=False, positions=None),
1614 ]
1615
1616 expected_opinfo_f = [
1617 Instruction(opname='COPY_FREE_VARS', opcode=149, arg=2, argval=2, argrepr='', offset=0, starts_line=None, is_jump_target=False, positions=None),
1618 Instruction(opname='MAKE_CELL', opcode=135, arg=0, argval='c', argrepr='c', offset=2, starts_line=None, is_jump_target=False, positions=None),
1619 Instruction(opname='MAKE_CELL', opcode=135, arg=1, argval='d', argrepr='d', offset=4, starts_line=None, is_jump_target=False, positions=None),
1620 Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=6, starts_line=2, is_jump_target=False, positions=None),
1621 Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, starts_line=3, is_jump_target=False, positions=None),
1622 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=3, argval='a', argrepr='a', offset=10, starts_line=None, is_jump_target=False, positions=None),
1623 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=4, argval='b', argrepr='b', offset=12, starts_line=None, is_jump_target=False, positions=None),
1624 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=0, argval='c', argrepr='c', offset=14, starts_line=None, is_jump_target=False, positions=None),
1625 Instruction(opname='LOAD_CLOSURE', opcode=136, arg=1, argval='d', argrepr='d', offset=16, starts_line=None, is_jump_target=False, positions=None),
1626 Instruction(opname='BUILD_TUPLE', opcode=102, arg=4, argval=4, argrepr='', offset=18, starts_line=None, is_jump_target=False, positions=None),
1627 Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, starts_line=None, is_jump_target=False, positions=None),
1628 Instruction(opname='MAKE_FUNCTION', opcode=132, arg=9, argval=9, argrepr='defaults, closure', offset=22, starts_line=None, is_jump_target=False, positions=None),
1629 Instruction(opname='STORE_FAST', opcode=125, arg=2, argval='inner', argrepr='inner', offset=24, starts_line=None, is_jump_target=False, positions=None),
1630 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='NULL + print', offset=26, starts_line=5, is_jump_target=False, positions=None),
1631 Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='a', argrepr='a', offset=36, starts_line=None, is_jump_target=False, positions=None),
1632 Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='b', argrepr='b', offset=38, starts_line=None, is_jump_target=False, positions=None),
1633 Instruction(opname='LOAD_DEREF', opcode=137, arg=0, argval='c', argrepr='c', offset=40, starts_line=None, is_jump_target=False, positions=None),
1634 Instruction(opname='LOAD_DEREF', opcode=137, arg=1, argval='d', argrepr='d', offset=42, starts_line=None, is_jump_target=False, positions=None),
1635 Instruction(opname='CALL', opcode=171, arg=4, argval=4, argrepr='', offset=44, starts_line=None, is_jump_target=False, positions=None),
1636 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=52, starts_line=None, is_jump_target=False, positions=None),
1637 Instruction(opname='LOAD_FAST', opcode=124, arg=2, argval='inner', argrepr='inner', offset=54, starts_line=6, is_jump_target=False, positions=None),
1638 Instruction(opname='RETURN_VALUE', opcode=83, arg=None, argval=None, argrepr='', offset=56, starts_line=None, is_jump_target=False, positions=None),
1639 ]
1640
1641 expected_opinfo_inner = [
1642 Instruction(opname='COPY_FREE_VARS', opcode=149, arg=4, argval=4, argrepr='', offset=0, starts_line=None, is_jump_target=False, positions=None),
1643 Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=2, starts_line=3, is_jump_target=False, positions=None),
1644 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='print', argrepr='NULL + print', offset=4, starts_line=4, is_jump_target=False, positions=None),
1645 Instruction(opname='LOAD_DEREF', opcode=137, arg=2, argval='a', argrepr='a', offset=14, starts_line=None, is_jump_target=False, positions=None),
1646 Instruction(opname='LOAD_DEREF', opcode=137, arg=3, argval='b', argrepr='b', offset=16, starts_line=None, is_jump_target=False, positions=None),
1647 Instruction(opname='LOAD_DEREF', opcode=137, arg=4, argval='c', argrepr='c', offset=18, starts_line=None, is_jump_target=False, positions=None),
1648 Instruction(opname='LOAD_DEREF', opcode=137, arg=5, argval='d', argrepr='d', offset=20, starts_line=None, is_jump_target=False, positions=None),
1649 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='e', argrepr='e', offset=22, starts_line=None, is_jump_target=False, positions=None),
1650 Instruction(opname='LOAD_FAST', opcode=124, arg=1, argval='f', argrepr='f', offset=24, starts_line=None, is_jump_target=False, positions=None),
1651 Instruction(opname='CALL', opcode=171, arg=6, argval=6, argrepr='', offset=26, starts_line=None, is_jump_target=False, positions=None),
1652 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=34, starts_line=None, is_jump_target=False, positions=None),
1653 Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=36, starts_line=None, is_jump_target=False, positions=None),
1654 ]
1655
1656 expected_opinfo_jumpy = [
1657 Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=0, starts_line=1, is_jump_target=False, positions=None),
1658 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=1, argval='range', argrepr='NULL + range', offset=2, starts_line=3, is_jump_target=False, positions=None),
1659 Instruction(opname='LOAD_CONST', opcode=100, arg=1, argval=10, argrepr='10', offset=12, starts_line=None, is_jump_target=False, positions=None),
1660 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=14, starts_line=None, is_jump_target=False, positions=None),
1661 Instruction(opname='GET_ITER', opcode=68, arg=None, argval=None, argrepr='', offset=22, starts_line=None, is_jump_target=False, positions=None),
1662 Instruction(opname='FOR_ITER', opcode=93, arg=26, argval=80, argrepr='to 80', offset=24, starts_line=None, is_jump_target=True, positions=None),
1663 Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=28, starts_line=None, is_jump_target=False, positions=None),
1664 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=30, starts_line=4, is_jump_target=False, positions=None),
1665 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=40, starts_line=None, is_jump_target=False, positions=None),
1666 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=42, starts_line=None, is_jump_target=False, positions=None),
1667 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=50, starts_line=None, is_jump_target=False, positions=None),
1668 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=52, starts_line=5, is_jump_target=False, positions=None),
1669 Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=54, starts_line=None, is_jump_target=False, positions=None),
1670 Instruction(opname='COMPARE_OP', opcode=107, arg=2, argval='<', argrepr='<', offset=56, starts_line=None, is_jump_target=False, positions=None),
1671 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=64, argrepr='to 64', offset=60, starts_line=None, is_jump_target=False, positions=None),
1672 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=20, argval=24, argrepr='to 24', offset=62, starts_line=6, is_jump_target=False, positions=None),
1673 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=64, starts_line=7, is_jump_target=True, positions=None),
1674 Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=66, starts_line=None, is_jump_target=False, positions=None),
1675 Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=68, starts_line=None, is_jump_target=False, positions=None),
1676 Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=76, argrepr='to 76', offset=72, starts_line=None, is_jump_target=False, positions=None),
1677 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=26, argval=24, argrepr='to 24', offset=74, starts_line=None, is_jump_target=False, positions=None),
1678 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=76, starts_line=8, is_jump_target=True, positions=None),
1679 Instruction(opname='JUMP_FORWARD', opcode=110, arg=12, argval=104, argrepr='to 104', offset=78, starts_line=None, is_jump_target=False, positions=None),
1680 Instruction(opname='END_FOR', opcode=4, arg=None, argval=None, argrepr='', offset=80, starts_line=3, is_jump_target=True, positions=None),
1681 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=82, starts_line=10, is_jump_target=False, positions=None),
1682 Instruction(opname='LOAD_CONST', opcode=100, arg=4, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=92, starts_line=None, is_jump_target=False, positions=None),
1683 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=94, starts_line=None, is_jump_target=False, positions=None),
1684 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=102, starts_line=None, is_jump_target=False, positions=None),
1685 Instruction(opname='LOAD_FAST_CHECK', opcode=127, arg=0, argval='i', argrepr='i', offset=104, starts_line=11, is_jump_target=True, positions=None),
1686 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=31, argval=170, argrepr='to 170', offset=106, starts_line=None, is_jump_target=False, positions=None),
1687 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=108, starts_line=12, is_jump_target=True, positions=None),
1688 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=118, starts_line=None, is_jump_target=False, positions=None),
1689 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=120, starts_line=None, is_jump_target=False, positions=None),
1690 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=128, starts_line=None, is_jump_target=False, positions=None),
1691 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=130, starts_line=13, is_jump_target=False, positions=None),
1692 Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=132, starts_line=None, is_jump_target=False, positions=None),
1693 Instruction(opname='BINARY_OP', opcode=122, arg=23, argval=23, argrepr='-=', offset=134, starts_line=None, is_jump_target=False, positions=None),
1694 Instruction(opname='STORE_FAST', opcode=125, arg=0, argval='i', argrepr='i', offset=138, starts_line=None, is_jump_target=False, positions=None),
1695 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=140, starts_line=14, is_jump_target=False, positions=None),
1696 Instruction(opname='LOAD_CONST', opcode=100, arg=3, argval=6, argrepr='6', offset=142, starts_line=None, is_jump_target=False, positions=None),
1697 Instruction(opname='COMPARE_OP', opcode=107, arg=68, argval='>', argrepr='>', offset=144, starts_line=None, is_jump_target=False, positions=None),
1698 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=152, argrepr='to 152', offset=148, starts_line=None, is_jump_target=False, positions=None),
1699 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=24, argval=104, argrepr='to 104', offset=150, starts_line=15, is_jump_target=False, positions=None),
1700 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=152, starts_line=16, is_jump_target=True, positions=None),
1701 Instruction(opname='LOAD_CONST', opcode=100, arg=2, argval=4, argrepr='4', offset=154, starts_line=None, is_jump_target=False, positions=None),
1702 Instruction(opname='COMPARE_OP', opcode=107, arg=2, argval='<', argrepr='<', offset=156, starts_line=None, is_jump_target=False, positions=None),
1703 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=164, argrepr='to 164', offset=160, starts_line=None, is_jump_target=False, positions=None),
1704 Instruction(opname='JUMP_FORWARD', opcode=110, arg=14, argval=192, argrepr='to 192', offset=162, starts_line=17, is_jump_target=False, positions=None),
1705 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=164, starts_line=11, is_jump_target=True, positions=None),
1706 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=1, argval=170, argrepr='to 170', offset=166, starts_line=None, is_jump_target=False, positions=None),
1707 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=31, argval=108, argrepr='to 108', offset=168, starts_line=None, is_jump_target=False, positions=None),
1708 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=170, starts_line=19, is_jump_target=True, positions=None),
1709 Instruction(opname='LOAD_CONST', opcode=100, arg=6, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=180, starts_line=None, is_jump_target=False, positions=None),
1710 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=182, starts_line=None, is_jump_target=False, positions=None),
1711 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=190, starts_line=None, is_jump_target=False, positions=None),
1712 Instruction(opname='NOP', opcode=9, arg=None, argval=None, argrepr='', offset=192, starts_line=20, is_jump_target=True, positions=None),
1713 Instruction(opname='LOAD_CONST', opcode=100, arg=5, argval=1, argrepr='1', offset=194, starts_line=21, is_jump_target=False, positions=None),
1714 Instruction(opname='LOAD_CONST', opcode=100, arg=7, argval=0, argrepr='0', offset=196, starts_line=None, is_jump_target=False, positions=None),
1715 Instruction(opname='BINARY_OP', opcode=122, arg=11, argval=11, argrepr='/', offset=198, starts_line=None, is_jump_target=False, positions=None),
1716 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=202, starts_line=None, is_jump_target=False, positions=None),
1717 Instruction(opname='LOAD_FAST', opcode=124, arg=0, argval='i', argrepr='i', offset=204, starts_line=25, is_jump_target=False, positions=None),
1718 Instruction(opname='BEFORE_WITH', opcode=53, arg=None, argval=None, argrepr='', offset=206, starts_line=None, is_jump_target=False, positions=None),
1719 Instruction(opname='STORE_FAST', opcode=125, arg=1, argval='dodgy', argrepr='dodgy', offset=208, starts_line=None, is_jump_target=False, positions=None),
1720 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=210, starts_line=26, is_jump_target=False, positions=None),
1721 Instruction(opname='LOAD_CONST', opcode=100, arg=8, argval='Never reach this', argrepr="'Never reach this'", offset=220, starts_line=None, is_jump_target=False, positions=None),
1722 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=222, starts_line=None, is_jump_target=False, positions=None),
1723 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=230, starts_line=None, is_jump_target=False, positions=None),
1724 Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=232, starts_line=25, is_jump_target=False, positions=None),
1725 Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=234, starts_line=None, is_jump_target=False, positions=None),
1726 Instruction(opname='LOAD_CONST', opcode=100, arg=0, argval=None, argrepr='None', offset=236, starts_line=None, is_jump_target=False, positions=None),
1727 Instruction(opname='CALL', opcode=171, arg=2, argval=2, argrepr='', offset=238, starts_line=None, is_jump_target=False, positions=None),
1728 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=246, starts_line=None, is_jump_target=False, positions=None),
1729 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=248, starts_line=28, is_jump_target=True, positions=None),
1730 Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=258, starts_line=None, is_jump_target=False, positions=None),
1731 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=260, starts_line=None, is_jump_target=False, positions=None),
1732 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=268, starts_line=None, is_jump_target=False, positions=None),
1733 Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=270, starts_line=None, is_jump_target=False, positions=None),
1734 Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=272, starts_line=25, is_jump_target=False, positions=None),
1735 Instruction(opname='WITH_EXCEPT_START', opcode=49, arg=None, argval=None, argrepr='', offset=274, starts_line=None, is_jump_target=False, positions=None),
1736 Instruction(opname='POP_JUMP_IF_TRUE', opcode=115, arg=1, argval=280, argrepr='to 280', offset=276, starts_line=None, is_jump_target=False, positions=None),
1737 Instruction(opname='RERAISE', opcode=119, arg=2, argval=2, argrepr='', offset=278, starts_line=None, is_jump_target=False, positions=None),
1738 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=280, starts_line=None, is_jump_target=True, positions=None),
1739 Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=282, starts_line=None, is_jump_target=False, positions=None),
1740 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=284, starts_line=None, is_jump_target=False, positions=None),
1741 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=286, starts_line=None, is_jump_target=False, positions=None),
1742 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=21, argval=248, argrepr='to 248', offset=288, starts_line=None, is_jump_target=False, positions=None),
1743 Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=290, starts_line=None, is_jump_target=False, positions=None),
1744 Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=292, starts_line=None, is_jump_target=False, positions=None),
1745 Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=294, starts_line=None, is_jump_target=False, positions=None),
1746 Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=296, starts_line=None, is_jump_target=False, positions=None),
1747 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=4, argval='ZeroDivisionError', argrepr='ZeroDivisionError', offset=298, starts_line=22, is_jump_target=False, positions=None),
1748 Instruction(opname='CHECK_EXC_MATCH', opcode=36, arg=None, argval=None, argrepr='', offset=308, starts_line=None, is_jump_target=False, positions=None),
1749 Instruction(opname='POP_JUMP_IF_FALSE', opcode=114, arg=14, argval=340, argrepr='to 340', offset=310, starts_line=None, is_jump_target=False, positions=None),
1750 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=312, starts_line=None, is_jump_target=False, positions=None),
1751 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=314, starts_line=23, is_jump_target=False, positions=None),
1752 Instruction(opname='LOAD_CONST', opcode=100, arg=9, argval='Here we go, here we go, here we go...', argrepr="'Here we go, here we go, here we go...'", offset=324, starts_line=None, is_jump_target=False, positions=None),
1753 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=326, starts_line=None, is_jump_target=False, positions=None),
1754 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=334, starts_line=None, is_jump_target=False, positions=None),
1755 Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=336, starts_line=None, is_jump_target=False, positions=None),
1756 Instruction(opname='JUMP_BACKWARD', opcode=140, arg=46, argval=248, argrepr='to 248', offset=338, starts_line=None, is_jump_target=False, positions=None),
1757 Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=340, starts_line=22, is_jump_target=True, positions=None),
1758 Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=342, starts_line=None, is_jump_target=False, positions=None),
1759 Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=344, starts_line=None, is_jump_target=False, positions=None),
1760 Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=346, starts_line=None, is_jump_target=False, positions=None),
1761 Instruction(opname='PUSH_EXC_INFO', opcode=35, arg=None, argval=None, argrepr='', offset=348, starts_line=None, is_jump_target=False, positions=None),
1762 Instruction(opname='LOAD_GLOBAL', opcode=116, arg=3, argval='print', argrepr='NULL + print', offset=350, starts_line=28, is_jump_target=False, positions=None),
1763 Instruction(opname='LOAD_CONST', opcode=100, arg=10, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=360, starts_line=None, is_jump_target=False, positions=None),
1764 Instruction(opname='CALL', opcode=171, arg=1, argval=1, argrepr='', offset=362, starts_line=None, is_jump_target=False, positions=None),
1765 Instruction(opname='POP_TOP', opcode=1, arg=None, argval=None, argrepr='', offset=370, starts_line=None, is_jump_target=False, positions=None),
1766 Instruction(opname='RERAISE', opcode=119, arg=0, argval=0, argrepr='', offset=372, starts_line=None, is_jump_target=False, positions=None),
1767 Instruction(opname='COPY', opcode=120, arg=3, argval=3, argrepr='', offset=374, starts_line=None, is_jump_target=False, positions=None),
1768 Instruction(opname='POP_EXCEPT', opcode=89, arg=None, argval=None, argrepr='', offset=376, starts_line=None, is_jump_target=False, positions=None),
1769 Instruction(opname='RERAISE', opcode=119, arg=1, argval=1, argrepr='', offset=378, starts_line=None, is_jump_target=False, positions=None),
1770 ]
1771
1772 # One last piece of inspect fodder to check the default line number handling
1773 def simple(): pass
1774 expected_opinfo_simple = [
1775 Instruction(opname='RESUME', opcode=151, arg=0, argval=0, argrepr='', offset=0, starts_line=simple.__code__.co_firstlineno, is_jump_target=False, positions=None),
1776 Instruction(opname='RETURN_CONST', opcode=121, arg=0, argval=None, argrepr='None', offset=2, starts_line=None, is_jump_target=False),
1777 ]
1778
1779
1780 class ESC[4;38;5;81mInstructionTestCase(ESC[4;38;5;149mBytecodeTestCase):
1781
1782 def assertInstructionsEqual(self, instrs_1, instrs_2, /):
1783 instrs_1 = [instr_1._replace(positions=None) for instr_1 in instrs_1]
1784 instrs_2 = [instr_2._replace(positions=None) for instr_2 in instrs_2]
1785 self.assertEqual(instrs_1, instrs_2)
1786
1787 class ESC[4;38;5;81mInstructionTests(ESC[4;38;5;149mInstructionTestCase):
1788
1789 def __init__(self, *args):
1790 super().__init__(*args)
1791 self.maxDiff = None
1792
1793 def test_default_first_line(self):
1794 actual = dis.get_instructions(simple)
1795 self.assertInstructionsEqual(list(actual), expected_opinfo_simple)
1796
1797 def test_first_line_set_to_None(self):
1798 actual = dis.get_instructions(simple, first_line=None)
1799 self.assertInstructionsEqual(list(actual), expected_opinfo_simple)
1800
1801 def test_outer(self):
1802 actual = dis.get_instructions(outer, first_line=expected_outer_line)
1803 self.assertInstructionsEqual(list(actual), expected_opinfo_outer)
1804
1805 def test_nested(self):
1806 with captured_stdout():
1807 f = outer()
1808 actual = dis.get_instructions(f, first_line=expected_f_line)
1809 self.assertInstructionsEqual(list(actual), expected_opinfo_f)
1810
1811 def test_doubly_nested(self):
1812 with captured_stdout():
1813 inner = outer()()
1814 actual = dis.get_instructions(inner, first_line=expected_inner_line)
1815 self.assertInstructionsEqual(list(actual), expected_opinfo_inner)
1816
1817 def test_jumpy(self):
1818 actual = dis.get_instructions(jumpy, first_line=expected_jumpy_line)
1819 self.assertInstructionsEqual(list(actual), expected_opinfo_jumpy)
1820
1821 @requires_debug_ranges()
1822 def test_co_positions(self):
1823 code = compile('f(\n x, y, z\n)', '<test>', 'exec')
1824 positions = [
1825 instr.positions
1826 for instr in dis.get_instructions(code)
1827 ]
1828 expected = [
1829 (0, 1, 0, 0),
1830 (1, 1, 0, 1),
1831 (1, 1, 0, 1),
1832 (2, 2, 2, 3),
1833 (2, 2, 5, 6),
1834 (2, 2, 8, 9),
1835 (1, 3, 0, 1),
1836 (1, 3, 0, 1),
1837 (1, 3, 0, 1)
1838 ]
1839 self.assertEqual(positions, expected)
1840
1841 named_positions = [
1842 (pos.lineno, pos.end_lineno, pos.col_offset, pos.end_col_offset)
1843 for pos in positions
1844 ]
1845 self.assertEqual(named_positions, expected)
1846
1847 @requires_debug_ranges()
1848 def test_co_positions_missing_info(self):
1849 code = compile('x, y, z', '<test>', 'exec')
1850 code_without_location_table = code.replace(co_linetable=b'')
1851 actual = dis.get_instructions(code_without_location_table)
1852 for instruction in actual:
1853 with self.subTest(instruction=instruction):
1854 positions = instruction.positions
1855 self.assertEqual(len(positions), 4)
1856 if instruction.opname == "RESUME":
1857 continue
1858 self.assertIsNone(positions.lineno)
1859 self.assertIsNone(positions.end_lineno)
1860 self.assertIsNone(positions.col_offset)
1861 self.assertIsNone(positions.end_col_offset)
1862
1863 @requires_debug_ranges()
1864 def test_co_positions_with_lots_of_caches(self):
1865 def roots(a, b, c):
1866 d = b**2 - 4 * a * c
1867 yield (-b - cmath.sqrt(d)) / (2 * a)
1868 if d:
1869 yield (-b + cmath.sqrt(d)) / (2 * a)
1870 code = roots.__code__
1871 ops = code.co_code[::2]
1872 cache_opcode = opcode.opmap["CACHE"]
1873 caches = sum(op == cache_opcode for op in ops)
1874 non_caches = len(ops) - caches
1875 # Make sure we have "lots of caches". If not, roots should be changed:
1876 assert 1 / 3 <= caches / non_caches, "this test needs more caches!"
1877 for show_caches in (False, True):
1878 for adaptive in (False, True):
1879 with self.subTest(f"{adaptive=}, {show_caches=}"):
1880 co_positions = [
1881 positions
1882 for op, positions in zip(ops, code.co_positions(), strict=True)
1883 if show_caches or op != cache_opcode
1884 ]
1885 dis_positions = [
1886 instruction.positions
1887 for instruction in dis.get_instructions(
1888 code, adaptive=adaptive, show_caches=show_caches
1889 )
1890 ]
1891 self.assertEqual(co_positions, dis_positions)
1892
1893 # get_instructions has its own tests above, so can rely on it to validate
1894 # the object oriented API
1895 class ESC[4;38;5;81mBytecodeTests(ESC[4;38;5;149mInstructionTestCase, ESC[4;38;5;149mDisTestBase):
1896
1897 def test_instantiation(self):
1898 # Test with function, method, code string and code object
1899 for obj in [_f, _C(1).__init__, "a=1", _f.__code__]:
1900 with self.subTest(obj=obj):
1901 b = dis.Bytecode(obj)
1902 self.assertIsInstance(b.codeobj, types.CodeType)
1903
1904 self.assertRaises(TypeError, dis.Bytecode, object())
1905
1906 def test_iteration(self):
1907 for obj in [_f, _C(1).__init__, "a=1", _f.__code__]:
1908 with self.subTest(obj=obj):
1909 via_object = list(dis.Bytecode(obj))
1910 via_generator = list(dis.get_instructions(obj))
1911 self.assertInstructionsEqual(via_object, via_generator)
1912
1913 def test_explicit_first_line(self):
1914 actual = dis.Bytecode(outer, first_line=expected_outer_line)
1915 self.assertInstructionsEqual(list(actual), expected_opinfo_outer)
1916
1917 def test_source_line_in_disassembly(self):
1918 # Use the line in the source code
1919 actual = dis.Bytecode(simple).dis()
1920 actual = actual.strip().partition(" ")[0] # extract the line no
1921 expected = str(simple.__code__.co_firstlineno)
1922 self.assertEqual(actual, expected)
1923 # Use an explicit first line number
1924 actual = dis.Bytecode(simple, first_line=350).dis()
1925 actual = actual.strip().partition(" ")[0] # extract the line no
1926 self.assertEqual(actual, "350")
1927
1928 def test_info(self):
1929 self.maxDiff = 1000
1930 for x, expected in CodeInfoTests.test_pairs:
1931 b = dis.Bytecode(x)
1932 self.assertRegex(b.info(), expected)
1933
1934 def test_disassembled(self):
1935 actual = dis.Bytecode(_f).dis()
1936 self.do_disassembly_compare(actual, dis_f)
1937
1938 def test_from_traceback(self):
1939 tb = get_tb()
1940 b = dis.Bytecode.from_traceback(tb)
1941 while tb.tb_next: tb = tb.tb_next
1942
1943 self.assertEqual(b.current_offset, tb.tb_lasti)
1944
1945 def test_from_traceback_dis(self):
1946 self.maxDiff = None
1947 tb = get_tb()
1948 b = dis.Bytecode.from_traceback(tb)
1949 self.assertEqual(self.strip_offsets(b.dis()), dis_traceback)
1950
1951 @requires_debug_ranges()
1952 def test_bytecode_co_positions(self):
1953 bytecode = dis.Bytecode("a=1")
1954 for instr, positions in zip(bytecode, bytecode.codeobj.co_positions()):
1955 assert instr.positions == positions
1956
1957 class ESC[4;38;5;81mTestBytecodeTestCase(ESC[4;38;5;149mBytecodeTestCase):
1958 def test_assert_not_in_with_op_not_in_bytecode(self):
1959 code = compile("a = 1", "<string>", "exec")
1960 self.assertInBytecode(code, "LOAD_CONST", 1)
1961 self.assertNotInBytecode(code, "LOAD_NAME")
1962 self.assertNotInBytecode(code, "LOAD_NAME", "a")
1963
1964 def test_assert_not_in_with_arg_not_in_bytecode(self):
1965 code = compile("a = 1", "<string>", "exec")
1966 self.assertInBytecode(code, "LOAD_CONST")
1967 self.assertInBytecode(code, "LOAD_CONST", 1)
1968 self.assertNotInBytecode(code, "LOAD_CONST", 2)
1969
1970 def test_assert_not_in_with_arg_in_bytecode(self):
1971 code = compile("a = 1", "<string>", "exec")
1972 with self.assertRaises(AssertionError):
1973 self.assertNotInBytecode(code, "LOAD_CONST", 1)
1974
1975 class ESC[4;38;5;81mTestFinderMethods(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
1976 def test__find_imports(self):
1977 cases = [
1978 ("import a.b.c", ('a.b.c', 0, None)),
1979 ("from a.b import c", ('a.b', 0, ('c',))),
1980 ("from a.b import c as d", ('a.b', 0, ('c',))),
1981 ("from a.b import *", ('a.b', 0, ('*',))),
1982 ("from ...a.b import c as d", ('a.b', 3, ('c',))),
1983 ("from ..a.b import c as d, e as f", ('a.b', 2, ('c', 'e'))),
1984 ("from ..a.b import *", ('a.b', 2, ('*',))),
1985 ]
1986 for src, expected in cases:
1987 with self.subTest(src=src):
1988 code = compile(src, "<string>", "exec")
1989 res = tuple(dis._find_imports(code))
1990 self.assertEqual(len(res), 1)
1991 self.assertEqual(res[0], expected)
1992
1993 def test__find_store_names(self):
1994 cases = [
1995 ("x+y", ()),
1996 ("x=y=1", ('x', 'y')),
1997 ("x+=y", ('x',)),
1998 ("global x\nx=y=1", ('x', 'y')),
1999 ("global x\nz=x", ('z',)),
2000 ]
2001 for src, expected in cases:
2002 with self.subTest(src=src):
2003 code = compile(src, "<string>", "exec")
2004 res = tuple(dis._find_store_names(code))
2005 self.assertEqual(res, expected)
2006
2007 def test_findlabels(self):
2008 labels = dis.findlabels(jumpy.__code__.co_code)
2009 jumps = [
2010 instr.offset
2011 for instr in expected_opinfo_jumpy
2012 if instr.is_jump_target
2013 ]
2014
2015 self.assertEqual(sorted(labels), sorted(jumps))
2016
2017 def test_findlinestarts(self):
2018 def func():
2019 pass
2020
2021 code = func.__code__
2022 offsets = [linestart[0] for linestart in dis.findlinestarts(code)]
2023 self.assertEqual(offsets, [0, 2])
2024
2025
2026 class ESC[4;38;5;81mTestDisTraceback(ESC[4;38;5;149mDisTestBase):
2027 def setUp(self) -> None:
2028 try: # We need to clean up existing tracebacks
2029 del sys.last_exc
2030 except AttributeError:
2031 pass
2032 try: # We need to clean up existing tracebacks
2033 del sys.last_traceback
2034 except AttributeError:
2035 pass
2036 return super().setUp()
2037
2038 def get_disassembly(self, tb):
2039 output = io.StringIO()
2040 with contextlib.redirect_stdout(output):
2041 dis.distb(tb)
2042 return output.getvalue()
2043
2044 def test_distb_empty(self):
2045 with self.assertRaises(RuntimeError):
2046 dis.distb()
2047
2048 def test_distb_last_traceback(self):
2049 self.maxDiff = None
2050 # We need to have an existing last traceback in `sys`:
2051 tb = get_tb()
2052 sys.last_traceback = tb
2053
2054 self.do_disassembly_compare(self.get_disassembly(None), dis_traceback)
2055
2056 def test_distb_explicit_arg(self):
2057 self.maxDiff = None
2058 tb = get_tb()
2059
2060 self.do_disassembly_compare(self.get_disassembly(tb), dis_traceback)
2061
2062
2063 class ESC[4;38;5;81mTestDisTracebackWithFile(ESC[4;38;5;149mTestDisTraceback):
2064 # Run the `distb` tests again, using the file arg instead of print
2065 def get_disassembly(self, tb):
2066 output = io.StringIO()
2067 with contextlib.redirect_stdout(output):
2068 dis.distb(tb, file=output)
2069 return output.getvalue()
2070
2071
2072 if __name__ == "__main__":
2073 unittest.main()