summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>2024-12-09 18:02:22 (GMT)
committerGitHub <noreply@github.com>2024-12-09 18:02:22 (GMT)
commite85f2f1703e0f79cfd0d0e3010190b71c0eb18da (patch)
tree1cbb1de4bf4ea317c86bde63e59940b6f424ae54 /Lib
parent5eb7fd4d0fc37b91058086181afebec41e66e5ad (diff)
downloadcpython-e85f2f1703e0f79cfd0d0e3010190b71c0eb18da.zip
cpython-e85f2f1703e0f79cfd0d0e3010190b71c0eb18da.tar.gz
cpython-e85f2f1703e0f79cfd0d0e3010190b71c0eb18da.tar.bz2
gh-127637: add tests for `dis` command-line interface (#127759)
Diffstat (limited to 'Lib')
-rw-r--r--Lib/dis.py4
-rw-r--r--Lib/test/test_dis.py122
2 files changed, 122 insertions, 4 deletions
diff --git a/Lib/dis.py b/Lib/dis.py
index 6b3e9ef..aa22404 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -1115,7 +1115,7 @@ class Bytecode:
return output.getvalue()
-def main():
+def main(args=None):
import argparse
parser = argparse.ArgumentParser()
@@ -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()
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index 55890e5..c719f57 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -5,15 +5,19 @@ import contextlib
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"]
@@ -2426,5 +2430,119 @@ 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)
+
+ @staticmethod
+ def text_normalize(string):
+ """Dedent *string* and strip it from its surrounding whitespaces.
+
+ This method is used by the other utility functions so that any
+ string to write or to match against can be freely indented.
+ """
+ return textwrap.dedent(string).strip()
+
+ def set_source(self, content):
+ with open(self.filename, 'w') as fp:
+ fp.write(self.text_normalize(content))
+
+ def invoke_dis(self, *flags):
+ output = io.StringIO()
+ with contextlib.redirect_stdout(output):
+ dis.main(args=[*flags, self.filename])
+ return self.text_normalize(output.getvalue())
+
+ def check_output(self, source, expect, *flags):
+ with self.subTest(source=source, flags=flags):
+ self.set_source(source)
+ res = self.invoke_dis(*flags)
+ expect = self.text_normalize(expect)
+ self.assertListEqual(res.splitlines(), expect.splitlines())
+
+ def test_invocation(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)
+
+ with self.assertRaises(SystemExit):
+ # suppress argparse error message
+ with contextlib.redirect_stderr(io.StringIO()):
+ _ = self.invoke_dis('--unknown')
+
+ def test_show_cache(self):
+ # test 'python -m dis -C/--show-caches'
+ source = 'print()'
+ expect = '''
+ 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
+ '''
+ 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 = '''
+ 0 0 RESUME 0
+
+ 1 2 LOAD_CONST 0 (None)
+ 4 RETURN_VALUE
+ '''
+ 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 = '''
+ 0:0-1:0 RESUME 0
+
+ 1:0-1:4 LOAD_CONST 0 (None)
+ 1:0-1:4 RETURN_VALUE
+ '''
+ 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 = '''
+ 0 RESUME 0
+
+ 1 LOAD_CONST_IMMORTAL 0 (None)
+ RETURN_VALUE
+ '''
+ for flag in ['-S', '--specialized']:
+ self.check_output(source, expect, flag)
+
+
if __name__ == "__main__":
unittest.main()