Bug 1264831 - Work around issues with the exec statement in older python 2.7 versions. r?gps draft
authorMike Hommey <mh+mozilla@glandium.org>
Fri, 15 Apr 2016 10:41:52 +0900
changeset 351823 543ee065efc0ee7dcccbf94c1e6bd032ace2af12
parent 351822 f614307bc205ad279bd8370e1954736b6f75002e
child 351824 cab2140741129ada0dee2503ebcd7674ad21f4bd
push id15531
push userbmo:mh+mozilla@glandium.org
push dateFri, 15 Apr 2016 02:18:00 +0000
reviewersgps
bugs1264831
milestone48.0a1
Bug 1264831 - Work around issues with the exec statement in older python 2.7 versions. r?gps
python/mozbuild/mozbuild/configure/__init__.py
python/mozbuild/mozbuild/frontend/sandbox.py
python/mozbuild/mozbuild/util.py
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -23,20 +23,22 @@ from mozbuild.configure.options import (
     PositiveOptionValue,
 )
 from mozbuild.configure.help import HelpFormatter
 from mozbuild.configure.util import (
     ConfigureOutputHandler,
     LineIO,
 )
 from mozbuild.util import (
+    exec_,
     memoize,
     ReadOnlyDict,
     ReadOnlyNamespace,
 )
+
 import mozpack.path as mozpath
 
 
 class ConfigureError(Exception):
     pass
 
 
 class DependsFunction(object):
@@ -195,17 +197,17 @@ class ConfigureSandbox(dict):
                 'Cannot include `%s` because it was included already.' % path)
         self._paths.append(path)
         self._all_paths.add(path)
 
         source = open(path, 'rb').read()
 
         code = compile(source, path, 'exec')
 
-        exec(code, self)
+        exec_(code, self)
 
         self._paths.pop(-1)
 
     def run(self, path=None):
         '''Executes the given file within the sandbox, as well as everything
         pending from any other included file, and ensure the overall
         consistency of the executed script(s).'''
         if path:
@@ -565,20 +567,17 @@ class ConfigureSandbox(dict):
             # Until this proves to be a performance problem, just construct an
             # import statement and execute it.
             import_line = ''
             if _from:
                 import_line += 'from %s ' % _from
             import_line += 'import %s' % _import
             if _as:
                 import_line += ' as %s' % _as
-            # Some versions of python fail with "SyntaxError: unqualified exec
-            # is not allowed in function '_apply_imports' it contains a nested
-            # function with free variable" when using the exec function.
-            exec import_line in {}, glob
+            exec_(import_line, {}, glob)
 
     def _resolve_and_set(self, data, name, value):
         # Don't set anything when --help was on the command line
         if self._help:
             return
         name = self._resolve(name, need_help_dependency=False)
         if name is None:
             return
--- a/python/mozbuild/mozbuild/frontend/sandbox.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox.py
@@ -18,17 +18,20 @@ user-friendly error messages in the case
 """
 
 from __future__ import absolute_import, unicode_literals
 
 import os
 import sys
 import weakref
 
-from mozbuild.util import ReadOnlyDict
+from mozbuild.util import (
+    exec_,
+    ReadOnlyDict,
+)
 from .context import Context
 from mozpack.files import FileFinder
 
 
 default_finder = FileFinder('/', find_executables=False)
 
 
 def alphabetical_sorted(iterable, cmp=None, key=lambda x: x.lower(),
@@ -169,21 +172,17 @@ class Sandbox(dict):
             # do want Unicode literals.
             code = compile(source, path, 'exec')
             # We use ourself as the global namespace for the execution. There
             # is no need for a separate local namespace as moz.build execution
             # is flat, namespace-wise.
             old_source = self._current_source
             self._current_source = source
             try:
-                # Ideally, we'd use exec(code, self), but that yield the
-                # following error:
-                # SyntaxError: unqualified exec is not allowed in function
-                # 'execute' it is a nested function.
-                exec code in self
+                exec_(code, self)
             finally:
                 self._current_source = old_source
 
         self.exec_function(execute, path=path)
 
     def exec_function(self, func, args=(), kwargs={}, path='',
                       becomes_current_path=True):
         """Execute function with the given arguments in the sandbox.
--- a/python/mozbuild/mozbuild/util.py
+++ b/python/mozbuild/mozbuild/util.py
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # This file contains miscellaneous utility functions that don't belong anywhere
 # in particular.
 
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, unicode_literals, print_function
 
 import argparse
 import collections
 import ctypes
 import difflib
 import errno
 import functools
 import hashlib
@@ -37,16 +37,32 @@ if sys.version_info[0] == 3:
 else:
     str_type = basestring
 
 if sys.platform == 'win32':
     _kernel32 = ctypes.windll.kernel32
     _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
 
 
+def exec_(object, globals=None, locals=None):
+    """Wrapper around the exec statement to avoid bogus errors like:
+
+    SyntaxError: unqualified exec is not allowed in function ...
+    it is a nested function.
+
+    or
+
+    SyntaxError: unqualified exec is not allowed in function ...
+    it contains a nested function with free variable
+
+    which happen with older versions of python 2.7.
+    """
+    exec(object, globals, locals)
+
+
 def hash_file(path, hasher=None):
     """Hashes a file specified by the path given and returns the hex digest."""
 
     # If the default hashing function changes, this may invalidate
     # lots of cached data.  Don't change it lightly.
     h = hasher or hashlib.sha1()
 
     with open(path, 'rb') as fh: