]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-122666: Tests for ast optimizations (#122667)
authorKirill Podoprigora <kirill.bast9@mail.ru>
Mon, 26 Aug 2024 19:27:22 +0000 (22:27 +0300)
committerGitHub <noreply@github.com>
Mon, 26 Aug 2024 19:27:22 +0000 (19:27 +0000)
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
Lib/test/test_ast/snippets.py
Lib/test/test_ast/test_ast.py

index 95dc3ca03cd38ba3f2faec1b0b66a71b71e76bea..28d32b2941f30d81e8e25a43a5856b2f8a7390f3 100644 (file)
@@ -380,7 +380,6 @@ def main():
             print("]")
         print("main()")
         raise SystemExit
-    unittest.main()
 
 #### EVERYTHING BELOW IS GENERATED BY python Lib/test/test_ast/snippets.py -g  #####
 exec_results = [
index e83cdbcb78d1b28fb9b116b4ab44a64a687a7723..c37b24adcc71550d9b1d72faaa445f32a8b3bf09 100644 (file)
@@ -3035,3 +3035,216 @@ class ASTMainTests(unittest.TestCase):
         self.assertEqual(expected.splitlines(),
                          res.out.decode("utf8").splitlines())
         self.assertEqual(res.rc, 0)
+
+
+class ASTOptimiziationTests(unittest.TestCase):
+    binop = {
+        "+": ast.Add(),
+        "-": ast.Sub(),
+        "*": ast.Mult(),
+        "/": ast.Div(),
+        "%": ast.Mod(),
+        "<<": ast.LShift(),
+        ">>": ast.RShift(),
+        "|": ast.BitOr(),
+        "^": ast.BitXor(),
+        "&": ast.BitAnd(),
+        "//": ast.FloorDiv(),
+        "**": ast.Pow(),
+    }
+
+    unaryop = {
+        "~": ast.Invert(),
+        "+": ast.UAdd(),
+        "-": ast.USub(),
+    }
+
+    def wrap_expr(self, expr):
+        return ast.Module(body=[ast.Expr(value=expr)])
+
+    def wrap_for(self, for_statement):
+        return ast.Module(body=[for_statement])
+
+    def assert_ast(self, code, non_optimized_target, optimized_target):
+        non_optimized_tree = ast.parse(code, optimize=-1)
+        optimized_tree = ast.parse(code, optimize=1)
+
+        # Is a non-optimized tree equal to a non-optimized target?
+        self.assertTrue(
+            ast.compare(non_optimized_tree, non_optimized_target),
+            f"{ast.dump(non_optimized_target)} must equal "
+            f"{ast.dump(non_optimized_tree)}",
+        )
+
+        # Is a optimized tree equal to a non-optimized target?
+        self.assertFalse(
+            ast.compare(optimized_tree, non_optimized_target),
+            f"{ast.dump(non_optimized_target)} must not equal "
+            f"{ast.dump(non_optimized_tree)}"
+        )
+
+        # Is a optimized tree is equal to an optimized target?
+        self.assertTrue(
+            ast.compare(optimized_tree,  optimized_target),
+            f"{ast.dump(optimized_target)} must equal "
+            f"{ast.dump(optimized_tree)}",
+        )
+
+    def test_folding_binop(self):
+        code = "1 %s 1"
+        operators = self.binop.keys()
+
+        def create_binop(operand, left=ast.Constant(1), right=ast.Constant(1)):
+            return ast.BinOp(left=left, op=self.binop[operand], right=right)
+
+        for op in operators:
+            result_code = code % op
+            non_optimized_target = self.wrap_expr(create_binop(op))
+            optimized_target = self.wrap_expr(ast.Constant(value=eval(result_code)))
+
+            with self.subTest(
+                result_code=result_code,
+                non_optimized_target=non_optimized_target,
+                optimized_target=optimized_target
+            ):
+                self.assert_ast(result_code, non_optimized_target, optimized_target)
+
+        # Multiplication of constant tuples must be folded
+        code = "(1,) * 3"
+        non_optimized_target = self.wrap_expr(create_binop("*", ast.Tuple(elts=[ast.Constant(value=1)]), ast.Constant(value=3)))
+        optimized_target = self.wrap_expr(ast.Constant(eval(code)))
+
+        self.assert_ast(code, non_optimized_target, optimized_target)
+
+    def test_folding_unaryop(self):
+        code = "%s1"
+        operators = self.unaryop.keys()
+
+        def create_unaryop(operand):
+            return ast.UnaryOp(op=self.unaryop[operand], operand=ast.Constant(1))
+
+        for op in operators:
+            result_code = code % op
+            non_optimized_target = self.wrap_expr(create_unaryop(op))
+            optimized_target = self.wrap_expr(ast.Constant(eval(result_code)))
+
+            with self.subTest(
+                result_code=result_code,
+                non_optimized_target=non_optimized_target,
+                optimized_target=optimized_target
+            ):
+                self.assert_ast(result_code, non_optimized_target, optimized_target)
+
+    def test_folding_not(self):
+        code = "not (1 %s (1,))"
+        operators = {
+            "in": ast.In(),
+            "is": ast.Is(),
+        }
+        opt_operators = {
+            "is": ast.IsNot(),
+            "in": ast.NotIn(),
+        }
+
+        def create_notop(operand):
+            return ast.UnaryOp(op=ast.Not(), operand=ast.Compare(
+                left=ast.Constant(value=1),
+                ops=[operators[operand]],
+                comparators=[ast.Tuple(elts=[ast.Constant(value=1)])]
+            ))
+
+        for op in operators.keys():
+            result_code = code % op
+            non_optimized_target = self.wrap_expr(create_notop(op))
+            optimized_target = self.wrap_expr(
+                ast.Compare(left=ast.Constant(1), ops=[opt_operators[op]], comparators=[ast.Constant(value=(1,))])
+            )
+
+            with self.subTest(
+                result_code=result_code,
+                non_optimized_target=non_optimized_target,
+                optimized_target=optimized_target
+            ):
+                self.assert_ast(result_code, non_optimized_target, optimized_target)
+
+    def test_folding_format(self):
+        code = "'%s' % (a,)"
+
+        non_optimized_target = self.wrap_expr(
+            ast.BinOp(
+                left=ast.Constant(value="%s"),
+                op=ast.Mod(),
+                right=ast.Tuple(elts=[ast.Name(id='a')]))
+        )
+        optimized_target = self.wrap_expr(
+            ast.JoinedStr(
+                values=[
+                    ast.FormattedValue(value=ast.Name(id='a'), conversion=115)
+                ]
+            )
+        )
+
+        self.assert_ast(code, non_optimized_target, optimized_target)
+
+
+    def test_folding_tuple(self):
+        code = "(1,)"
+
+        non_optimized_target = self.wrap_expr(ast.Tuple(elts=[ast.Constant(1)]))
+        optimized_target = self.wrap_expr(ast.Constant(value=(1,)))
+
+        self.assert_ast(code, non_optimized_target, optimized_target)
+
+    def test_folding_comparator(self):
+        code = "1 %s %s1%s"
+        operators = [("in", ast.In()), ("not in", ast.NotIn())]
+        braces = [
+            ("[", "]", ast.List, (1,)),
+            ("{", "}", ast.Set, frozenset({1})),
+        ]
+        for left, right, non_optimized_comparator, optimized_comparator in braces:
+            for op, node in operators:
+                non_optimized_target = self.wrap_expr(ast.Compare(
+                    left=ast.Constant(1), ops=[node],
+                    comparators=[non_optimized_comparator(elts=[ast.Constant(1)])]
+                ))
+                optimized_target = self.wrap_expr(ast.Compare(
+                    left=ast.Constant(1), ops=[node],
+                    comparators=[ast.Constant(value=optimized_comparator)]
+                ))
+                self.assert_ast(code % (op, left, right), non_optimized_target, optimized_target)
+
+    def test_folding_iter(self):
+        code = "for _ in %s1%s: pass"
+        braces = [
+            ("[", "]", ast.List, (1,)),
+            ("{", "}", ast.Set, frozenset({1})),
+        ]
+
+        for left, right, ast_cls, optimized_iter in braces:
+            non_optimized_target = self.wrap_for(ast.For(
+                target=ast.Name(id="_", ctx=ast.Store()),
+                iter=ast_cls(elts=[ast.Constant(1)]),
+                body=[ast.Pass()]
+            ))
+            optimized_target = self.wrap_for(ast.For(
+                target=ast.Name(id="_", ctx=ast.Store()),
+                iter=ast.Constant(value=optimized_iter),
+                body=[ast.Pass()]
+            ))
+
+            self.assert_ast(code % (left, right), non_optimized_target, optimized_target)
+
+    def test_folding_subscript(self):
+        code = "(1,)[0]"
+
+        non_optimized_target = self.wrap_expr(
+            ast.Subscript(value=ast.Tuple(elts=[ast.Constant(value=1)]), slice=ast.Constant(value=0))
+        )
+        optimized_target = self.wrap_expr(ast.Constant(value=1))
+
+        self.assert_ast(code, non_optimized_target, optimized_target)
+
+
+if __name__ == "__main__":
+    unittest.main()