]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-127637: add tests for `dis` command-line interface (#127759) (#127781)
authorBénédikt Tran <10796600+picnixz@users.noreply.github.com>
Tue, 10 Dec 2024 12:32:32 +0000 (13:32 +0100)
committerGitHub <noreply@github.com>
Tue, 10 Dec 2024 12:32:32 +0000 (12:32 +0000)
Lib/dis.py
Lib/test/test_dis.py
Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst [new file with mode: 0644]

index 76934eb00e63f0a6bb4fce87e31d4bef849e899e..797e0f8a0888d0fb809fdb1fffd4c2512fc90a40 100644 (file)
@@ -1051,7 +1051,7 @@ class Bytecode:
             return output.getvalue()
 
 
-def main():
+def main(args=None):
     import argparse
 
     parser = argparse.ArgumentParser()
@@ -1060,7 +1060,7 @@ def main():
     parser.add_argument('-O', '--show-offsets', action='store_true',
                         help='show instruction offsets')
     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()
index a7f6919655b94ac4c6caadfd24eb1cca638f6343..73807d7fa95fbdcde04f8018788cba1190637c11 100644 (file)
@@ -4,15 +4,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"]
 
@@ -2281,5 +2285,91 @@ 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'),
+        ]
+
+        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
+                        RETURN_CONST             0 (None)
+        '''
+        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       RETURN_CONST             0 (None)
+        '''
+        for flag in ['-O', '--show-offsets']:
+            self.check_output(source, expect, flag)
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst b/Misc/NEWS.d/next/Tests/2024-12-09-12-35-44.gh-issue-127637.KLx-9I.rst
new file mode 100644 (file)
index 0000000..ac5d982
--- /dev/null
@@ -0,0 +1 @@
+Add tests for the :mod:`dis` command-line interface. Patch by Bénédikt Tran.