Commits

Victor Stinner committed 195df21

Optimize ``if a: if b: print("true")``: ``if a and b: print("true")``

Comments (0)

Files changed (4)

 
    - ``not(x in y)`` => ``x not in y``
    - ``4 and 5 and x and 6`` => ``x and 6``
+   - ``if a: if b: print("true")`` => ``if a and b: print("true")``
 
  * Optimize loops (range => xrange needs "builtin_funcs" features). Examples:
 
  * Optimize print() on Python 2 with "from __future__ import print_function"
  * Optimize iterators, list, set and dict comprehension, and generators
  * Replace list with tuple
+ * Optimize ``if a: if b: print("true")``: ``if a and b: print("true")``
 
 Version 0.3.1 (2012-09-12)
 --------------------------

astoptimizer/optimizer.py

         if drop:
             if constant:
                 node.body = self.visit_list(node.body, conditional=True)
-                return self.if_block(node, node.body)
+                new_node = self.if_block(node, node.body)
             else:
                 node.orelse = self.visit_list(node.orelse, conditional=True)
-                return self.if_block(node, node.orelse)
-        else:
-            node.body = self.visit_list(node.body, conditional=True)
-            node.orelse = self.visit_list(node.orelse, conditional=True)
+                new_node = self.if_block(node, node.orelse)
+            return new_node
+
+        node.body = self.visit_list(node.body, conditional=True)
+        node.orelse = self.visit_list(node.orelse, conditional=True)
+
+        if not node.orelse and len(node.body) == 1:
+            node2 = node.body[0]
+            if isinstance(node2, ast.If) and not node2.orelse :
+                # if test: if test2: body
+                # => if test and test2: body
+                values = [node.test, node2.test]
+                new_test = ast.BoolOp(ast.And(), values)
+                node.test = copy_lineno(node.test, new_test)
+                node.body = node2.body
+                return node
 
     def visit_IfExp(self, node):
+        # a if test else b
         constant = self.get_constant(node.test)
         if constant is UNSET:
             return

astoptimizer/tests.py

                    self.text_ast('def f():\n yield 3'))
         self.check('def f():\n if g():\n  if True:\n   pass',
                    self.text_ast('def f():\n if g():\n  pass'))
+        self.check('if a:\n if b:\n  pass',
+                   self.text_ast('if a and b:\n pass'))
         self.check_not_optimized('def f():\n if 0:\n  yield')
         self.check_not_optimized('def f():\n if 1:\n  pass\n else:\n  yield')
         self.check_not_optimized('def f():\n if 0:\n  yield\n yield 3')
             return self.skipTest("specific to Python 2")
         if PYTHON3 and before.startswith('u"'):
             before = before[1:]
+        if before == 'if a: if b: print("true")':
+            before = 'if a:\n if b:\n  print("true")'
+        if after == 'if a and b: print("true")':
+            after = 'if a and b:\n print("true")'
 
         if 'if DEBUG:' in before:
             config = self.create_default_config()
     parser.add_option("--tuple-limit",
         help="Maximum length of frozenset, set and tuple",
         action="store", type="int", default=None)
+    parser.add_option("--no-exec",
+        help="Maximum length of frozenset, set and tuple",
+        action="store_true", default=False)
     options, args = parser.parse_args()
     if not args:
         parser.print_help()
         dump_bytecode(code)
         print("")
 
-        print("Execute code:")
-        exec(code)
+        if not options.no_exec:
+            print("Execute code:")
+            exec(code)
 
 if __name__ == "__main__":
     main()