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