Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1115,7 +1115,7 @@ def dis(self):
return output.getvalue()


def main():
def main(args=None):
import argparse

parser = argparse.ArgumentParser()
Expand All @@ -1128,7 +1128,7 @@ def main():
parser.add_argument('-S', '--specialized', action='store_true',
help='show specialized bytecode')
parser.add_argument('infile', nargs='?', default='-')
args = parser.parse_args()
args = parser.parse_args(args=args)
if args.infile == '-':
name = '<stdin>'
source = sys.stdin.buffer.read()
Expand Down
108 changes: 106 additions & 2 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
import dis
import functools
import io
import itertools
import opcode
import re
import sys
import tempfile
import textwrap
import types
import unittest
from test.support import (captured_stdout, requires_debug_ranges,
requires_specialization, cpython_only)
requires_specialization, cpython_only,
os_helper)
from test.support.bytecode_helper import BytecodeTestCase

import opcode

CACHE = dis.opmap["CACHE"]

Expand Down Expand Up @@ -2426,5 +2430,105 @@ def _unroll_caches_as_Instructions(instrs, show_caches=False):
False, None, None, instr.positions)


class TestDisCLI(unittest.TestCase):

def setUp(self):
self.filename = tempfile.mktemp()
self.addCleanup(os_helper.unlink, self.filename)

def set_source(self, content):
with open(self.filename, 'w') as fp:
fp.write(textwrap.dedent(content).strip())

def invoke_dis(self, *flags):
output = io.StringIO()
with contextlib.redirect_stdout(output):
dis.main(args=[*flags, self.filename])
return output.getvalue()

def check_output(self, source, expect, *flags):
with self.subTest(flags):
self.set_source(source)
res = self.invoke_dis(*flags)
res = textwrap.dedent(res)
self.assertListEqual(res.splitlines(), expect.splitlines())

def test_invokation(self):
# test various combinations of parameters
base_flags = [
('-C', '--show-caches'),
('-O', '--show-offsets'),
('-P', '--show-positions'),
('-S', '--specialized'),
]

self.set_source('''
def f():
print(x)
return None
''')

for r in range(1, len(base_flags) + 1):
for choices in itertools.combinations(base_flags, r=r):
for args in itertools.product(*choices):
with self.subTest(args=args[1:]):
_ = self.invoke_dis(*args)

def test_show_cache(self):
# test 'python -m dis -C/--show-caches'
source = 'print()'
expect = textwrap.dedent('''
0 RESUME 0

1 LOAD_NAME 0 (print)
PUSH_NULL
CALL 0
CACHE 0 (counter: 0)
CACHE 0 (func_version: 0)
CACHE 0
POP_TOP
LOAD_CONST 0 (None)
RETURN_VALUE
''').strip()
for flag in ['-C', '--show-caches']:
self.check_output(source, expect, flag)

def test_show_offsets(self):
# test 'python -m dis -O/--show-offsets'
source = 'pass'
expect = textwrap.dedent('''
0 0 RESUME 0

1 2 LOAD_CONST 0 (None)
4 RETURN_VALUE
''').strip()
for flag in ['-O', '--show-offsets']:
self.check_output(source, expect, flag)

def test_show_positions(self):
# test 'python -m dis -P/--show-positions'
source = 'pass'
expect = textwrap.dedent('''
0:0-1:0 RESUME 0

1:0-1:4 LOAD_CONST 0 (None)
1:0-1:4 RETURN_VALUE
''').strip()
for flag in ['-P', '--show-positions']:
self.check_output(source, expect, flag)

def test_specialized_code(self):
# test 'python -m dis -S/--specialized'
source = 'pass'
expect = textwrap.dedent('''
0 RESUME 0

1 LOAD_CONST_IMMORTAL 0 (None)
RETURN_VALUE
''').strip()
for flag in ['-S', '--specialized']:
self.check_output(source, expect, flag)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add tests for the :mod:`dis` command-line interface. Patch by Bénédikt Tran.
Loading