]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-122666: Tests for ast optimizations (GH-122667) (#123359)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 27 Aug 2024 13:41:37 +0000 (15:41 +0200)
committerGitHub <noreply@github.com>
Tue, 27 Aug 2024 13:41:37 +0000 (06:41 -0700)
(cherry picked from commit 9f9b00d52ceafab6c183e8b0f502071d59dc6d22)

Co-authored-by: Kirill Podoprigora <kirill.bast9@mail.ru>
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 b47a4a33d51bec61b214337e2996b759b23a4cc0..df853693e50dc93c35365ed737e4111448650aac 100644 (file)
@@ -3172,3 +3172,219 @@ class ASTMainTests(unittest.TestCase):
         self.assertEqual(res.err, b"")
         self.assertEqual(expected.splitlines(), res.out.decode("utf8").splitlines())
         self.assertEqual(res.rc, 0)
+
+def compare(left, right):
+    return ast.dump(left) == ast.dump(right)
+
+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(
+            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(
+            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(
+            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()