bpo-39313: Add an option to RefactoringTool for using exec as a function (GH-17967)



https://bugs.python.org/issue39313


Automerge-Triggered-By: @pablogsal
diff --git a/Doc/library/2to3.rst b/Doc/library/2to3.rst
index c3ff3e60..eb4c918 100644
--- a/Doc/library/2to3.rst
+++ b/Doc/library/2to3.rst
@@ -102,7 +102,7 @@
 modifies its internal grammar to interpret :func:`print` as a function.  This
 change can also be enabled manually with the :option:`!-p` flag.  Use
 :option:`!-p` to run fixers on code that already has had its print statements
-converted.
+converted. Also :option:`!-e` can be used to make :func:`exec` a function.
 
 The :option:`!-o` or :option:`!--output-dir` option allows specification of an
 alternate directory for processed output files to be written to.  The
diff --git a/Lib/lib2to3/main.py b/Lib/lib2to3/main.py
index c51626b..f2849fd 100644
--- a/Lib/lib2to3/main.py
+++ b/Lib/lib2to3/main.py
@@ -154,6 +154,8 @@
                       help="List available transformations")
     parser.add_option("-p", "--print-function", action="store_true",
                       help="Modify the grammar so that print() is a function")
+    parser.add_option("-e", "--exec-function", action="store_true",
+                      help="Modify the grammar so that exec() is a function")
     parser.add_option("-v", "--verbose", action="store_true",
                       help="More verbose logging")
     parser.add_option("--no-diffs", action="store_true",
@@ -211,6 +213,9 @@
     if options.print_function:
         flags["print_function"] = True
 
+    if options.exec_function:
+        flags["exec_function"] = True
+
     # Set up logging handler
     level = logging.DEBUG if options.verbose else logging.INFO
     logging.basicConfig(format='%(name)s: %(message)s', level=level)
diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py
index 55fd60f..3a5aaff 100644
--- a/Lib/lib2to3/refactor.py
+++ b/Lib/lib2to3/refactor.py
@@ -155,6 +155,7 @@
 class RefactoringTool(object):
 
     _default_options = {"print_function" : False,
+                        "exec_function": False,
                         "write_unchanged_files" : False}
 
     CLASS_PREFIX = "Fix" # The prefix for fixer classes
@@ -173,10 +174,13 @@
         self.options = self._default_options.copy()
         if options is not None:
             self.options.update(options)
-        if self.options["print_function"]:
-            self.grammar = pygram.python_grammar_no_print_statement
-        else:
-            self.grammar = pygram.python_grammar
+        self.grammar = pygram.python_grammar.copy()
+
+        if self.options['print_function']:
+            del self.grammar.keywords["print"]
+        elif self.options['exec_function']:
+            del self.grammar.keywords["exec"]
+
         # When this is True, the refactor*() methods will call write_file() for
         # files processed even if they were not changed during refactoring. If
         # and only if the refactor method's write parameter was True.
diff --git a/Lib/lib2to3/tests/test_refactor.py b/Lib/lib2to3/tests/test_refactor.py
index 9e3b8fb..be70567 100644
--- a/Lib/lib2to3/tests/test_refactor.py
+++ b/Lib/lib2to3/tests/test_refactor.py
@@ -44,9 +44,13 @@
 
     def test_print_function_option(self):
         rt = self.rt({"print_function" : True})
-        self.assertIs(rt.grammar, pygram.python_grammar_no_print_statement)
-        self.assertIs(rt.driver.grammar,
-                      pygram.python_grammar_no_print_statement)
+        self.assertNotIn("print", rt.grammar.keywords)
+        self.assertNotIn("print", rt.driver.grammar.keywords)
+
+    def test_exec_function_option(self):
+        rt = self.rt({"exec_function" : True})
+        self.assertNotIn("exec", rt.grammar.keywords)
+        self.assertNotIn("exec", rt.driver.grammar.keywords)
 
     def test_write_unchanged_files_option(self):
         rt = self.rt()
diff --git a/Misc/NEWS.d/next/Library/2020-01-12-18-17-00.bpo-39313.DCTsnm.rst b/Misc/NEWS.d/next/Library/2020-01-12-18-17-00.bpo-39313.DCTsnm.rst
new file mode 100644
index 0000000..784d73c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-01-12-18-17-00.bpo-39313.DCTsnm.rst
@@ -0,0 +1,2 @@
+Add a new ``exec_function`` option (*--exec-function* in the CLI) to
+``RefactoringTool`` for making ``exec`` a function. Patch by Batuhan Taskaya.