5
5
import dis
6
6
import functools
7
7
import io
8
+ import itertools
9
+ import opcode
8
10
import re
9
11
import sys
12
+ import tempfile
13
+ import textwrap
10
14
import types
11
15
import unittest
12
16
from test .support import (captured_stdout , requires_debug_ranges ,
13
- requires_specialization , cpython_only )
17
+ requires_specialization , cpython_only ,
18
+ os_helper )
14
19
from test .support .bytecode_helper import BytecodeTestCase
15
20
16
- import opcode
17
21
18
22
CACHE = dis .opmap ["CACHE" ]
19
23
@@ -2426,5 +2430,119 @@ def _unroll_caches_as_Instructions(instrs, show_caches=False):
2426
2430
False , None , None , instr .positions )
2427
2431
2428
2432
2433
+ class TestDisCLI (unittest .TestCase ):
2434
+
2435
+ def setUp (self ):
2436
+ self .filename = tempfile .mktemp ()
2437
+ self .addCleanup (os_helper .unlink , self .filename )
2438
+
2439
+ @staticmethod
2440
+ def text_normalize (string ):
2441
+ """Dedent *string* and strip it from its surrounding whitespaces.
2442
+
2443
+ This method is used by the other utility functions so that any
2444
+ string to write or to match against can be freely indented.
2445
+ """
2446
+ return textwrap .dedent (string ).strip ()
2447
+
2448
+ def set_source (self , content ):
2449
+ with open (self .filename , 'w' ) as fp :
2450
+ fp .write (self .text_normalize (content ))
2451
+
2452
+ def invoke_dis (self , * flags ):
2453
+ output = io .StringIO ()
2454
+ with contextlib .redirect_stdout (output ):
2455
+ dis .main (args = [* flags , self .filename ])
2456
+ return self .text_normalize (output .getvalue ())
2457
+
2458
+ def check_output (self , source , expect , * flags ):
2459
+ with self .subTest (source = source , flags = flags ):
2460
+ self .set_source (source )
2461
+ res = self .invoke_dis (* flags )
2462
+ expect = self .text_normalize (expect )
2463
+ self .assertListEqual (res .splitlines (), expect .splitlines ())
2464
+
2465
+ def test_invocation (self ):
2466
+ # test various combinations of parameters
2467
+ base_flags = [
2468
+ ('-C' , '--show-caches' ),
2469
+ ('-O' , '--show-offsets' ),
2470
+ ('-P' , '--show-positions' ),
2471
+ ('-S' , '--specialized' ),
2472
+ ]
2473
+
2474
+ self .set_source ('''
2475
+ def f():
2476
+ print(x)
2477
+ return None
2478
+ ''' )
2479
+
2480
+ for r in range (1 , len (base_flags ) + 1 ):
2481
+ for choices in itertools .combinations (base_flags , r = r ):
2482
+ for args in itertools .product (* choices ):
2483
+ with self .subTest (args = args [1 :]):
2484
+ _ = self .invoke_dis (* args )
2485
+
2486
+ with self .assertRaises (SystemExit ):
2487
+ # suppress argparse error message
2488
+ with contextlib .redirect_stderr (io .StringIO ()):
2489
+ _ = self .invoke_dis ('--unknown' )
2490
+
2491
+ def test_show_cache (self ):
2492
+ # test 'python -m dis -C/--show-caches'
2493
+ source = 'print()'
2494
+ expect = '''
2495
+ 0 RESUME 0
2496
+
2497
+ 1 LOAD_NAME 0 (print)
2498
+ PUSH_NULL
2499
+ CALL 0
2500
+ CACHE 0 (counter: 0)
2501
+ CACHE 0 (func_version: 0)
2502
+ CACHE 0
2503
+ POP_TOP
2504
+ LOAD_CONST 0 (None)
2505
+ RETURN_VALUE
2506
+ '''
2507
+ for flag in ['-C' , '--show-caches' ]:
2508
+ self .check_output (source , expect , flag )
2509
+
2510
+ def test_show_offsets (self ):
2511
+ # test 'python -m dis -O/--show-offsets'
2512
+ source = 'pass'
2513
+ expect = '''
2514
+ 0 0 RESUME 0
2515
+
2516
+ 1 2 LOAD_CONST 0 (None)
2517
+ 4 RETURN_VALUE
2518
+ '''
2519
+ for flag in ['-O' , '--show-offsets' ]:
2520
+ self .check_output (source , expect , flag )
2521
+
2522
+ def test_show_positions (self ):
2523
+ # test 'python -m dis -P/--show-positions'
2524
+ source = 'pass'
2525
+ expect = '''
2526
+ 0:0-1:0 RESUME 0
2527
+
2528
+ 1:0-1:4 LOAD_CONST 0 (None)
2529
+ 1:0-1:4 RETURN_VALUE
2530
+ '''
2531
+ for flag in ['-P' , '--show-positions' ]:
2532
+ self .check_output (source , expect , flag )
2533
+
2534
+ def test_specialized_code (self ):
2535
+ # test 'python -m dis -S/--specialized'
2536
+ source = 'pass'
2537
+ expect = '''
2538
+ 0 RESUME 0
2539
+
2540
+ 1 LOAD_CONST_IMMORTAL 0 (None)
2541
+ RETURN_VALUE
2542
+ '''
2543
+ for flag in ['-S' , '--specialized' ]:
2544
+ self .check_output (source , expect , flag )
2545
+
2546
+
2429
2547
if __name__ == "__main__" :
2430
2548
unittest .main ()
0 commit comments