gh-127637: add tests for dis command-line interface (#127759)

This commit is contained in:
Bénédikt Tran 2024-12-09 19:02:22 +01:00 committed by GitHub
parent 5eb7fd4d0f
commit e85f2f1703
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 123 additions and 4 deletions

View file

@ -1115,7 +1115,7 @@ class Bytecode:
return output.getvalue() return output.getvalue()
def main(): def main(args=None):
import argparse import argparse
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@ -1128,7 +1128,7 @@ def main():
parser.add_argument('-S', '--specialized', action='store_true', parser.add_argument('-S', '--specialized', action='store_true',
help='show specialized bytecode') help='show specialized bytecode')
parser.add_argument('infile', nargs='?', default='-') parser.add_argument('infile', nargs='?', default='-')
args = parser.parse_args() args = parser.parse_args(args=args)
if args.infile == '-': if args.infile == '-':
name = '<stdin>' name = '<stdin>'
source = sys.stdin.buffer.read() source = sys.stdin.buffer.read()

View file

@ -5,15 +5,19 @@ import contextlib
import dis import dis
import functools import functools
import io import io
import itertools
import opcode
import re import re
import sys import sys
import tempfile
import textwrap
import types import types
import unittest import unittest
from test.support import (captured_stdout, requires_debug_ranges, 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 from test.support.bytecode_helper import BytecodeTestCase
import opcode
CACHE = dis.opmap["CACHE"] CACHE = dis.opmap["CACHE"]
@ -2426,5 +2430,119 @@ def _unroll_caches_as_Instructions(instrs, show_caches=False):
False, None, None, instr.positions) 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__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -0,0 +1 @@
Add tests for the :mod:`dis` command-line interface. Patch by Bénédikt Tran.