Bug 1316844 - Improve function unwrapping to properly cover templates. r?chmanchester
--- a/python/mozbuild/mozbuild/configure/__init__.py
+++ b/python/mozbuild/mozbuild/configure/__init__.py
@@ -655,39 +655,42 @@ class ConfigureSandbox(dict):
return func
return obj
# The following function may end up being prepared to be sandboxed,
# so it mustn't depend on anything from the global scope in this
# file. It can however depend on variables from the closure, thus
# maybe_prepare_function and isfunction are declared above to be
# available there.
- @wraps(template)
+ @self.wraps(template)
def wrapper(*args, **kwargs):
args = [maybe_prepare_function(arg) for arg in args]
kwargs = {k: maybe_prepare_function(v)
for k, v in kwargs.iteritems()}
ret = template(*args, **kwargs)
if isfunction(ret):
# We can't expect the sandboxed code to think about all the
# details of implementing decorators, so do some of the
# work for them. If the function takes exactly one function
# as argument and returns a function, it must be a
# decorator, so mark the returned function as wrapping the
# function passed in.
if len(args) == 1 and not kwargs and isfunction(args[0]):
- ret = wraps(args[0])(ret)
+ ret = self.wraps(args[0])(ret)
return wrap_template(ret)
return ret
return wrapper
wrapper = wrap_template(template)
self._templates.add(wrapper)
return wrapper
+ def wraps(self, func):
+ return wraps(func)
+
RE_MODULE = re.compile('^[a-zA-Z0-9_\.]+$')
def imports_impl(self, _import, _from=None, _as=None):
'''Implementation of @imports.
This decorator imports the given _import from the given _from module
optionally under a different _as name.
The options correspond to the various forms for the import builtin.
@imports('sys')
@@ -912,24 +915,24 @@ class ConfigureSandbox(dict):
def makecell(content):
def f():
content
return f.func_closure[0]
closure = tuple(makecell(cell.cell_contents)
for cell in func.func_closure)
- new_func = wraps(func)(types.FunctionType(
+ new_func = self.wraps(func)(types.FunctionType(
func.func_code,
glob,
func.__name__,
func.func_defaults,
closure
))
- @wraps(new_func)
+ @self.wraps(new_func)
def wrapped(*args, **kwargs):
if func in self._imports:
self._apply_imports(func, glob)
del self._imports[func]
return new_func(*args, **kwargs)
self._prepared_functions.add(wrapped)
return wrapped, glob
--- a/python/mozbuild/mozbuild/configure/lint.py
+++ b/python/mozbuild/mozbuild/configure/lint.py
@@ -1,20 +1,22 @@
# 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/.
from __future__ import absolute_import, print_function, unicode_literals
+from functools import wraps
from StringIO import StringIO
from . import (
CombinedDependsFunction,
ConfigureError,
ConfigureSandbox,
DependsFunction,
+ SandboxedGlobal,
)
from .lint_util import disassemble_as_iter
from mozbuild.util import memoize
class LintSandbox(ConfigureSandbox):
def __init__(self, environ=None, argv=None, stdout=None, stderr=None):
out = StringIO()
@@ -32,17 +34,17 @@ class LintSandbox(ConfigureSandbox):
def _missing_help_dependency(self, obj):
if isinstance(obj, CombinedDependsFunction):
return False
if isinstance(obj, DependsFunction):
if (self._help_option in obj.dependencies or
obj in (self._always, self._never)):
return False
- func, glob = self._wrapped[obj.func]
+ func, glob = self.unwrap(obj.func)
# We allow missing --help dependencies for functions that:
# - don't use @imports
# - don't have a closure
# - don't use global variables
if func in self._imports or func.func_closure:
return True
for op, arg in disassemble_as_iter(func):
if op in ('LOAD_GLOBAL', 'STORE_GLOBAL'):
@@ -66,13 +68,21 @@ class LintSandbox(ConfigureSandbox):
% (obj.name, arg.name, arg.name))
elif ((self._help or need_help_dependency) and
self._missing_help_dependency(obj)):
raise ConfigureError("Missing @depends for `%s`: '--help'" %
obj.name)
return super(LintSandbox, self)._value_for_depends(
obj, need_help_dependency)
- def _prepare_function(self, func):
- wrapped, glob = super(LintSandbox, self)._prepare_function(func)
- if wrapped not in self._wrapped:
- self._wrapped[wrapped] = func, glob
- return wrapped, glob
+ def unwrap(self, func):
+ glob = func.func_globals
+ while func in self._wrapped:
+ if isinstance(func.func_globals, SandboxedGlobal):
+ glob = func.func_globals
+ func = self._wrapped[func]
+ return func, glob
+
+ def wraps(self, func):
+ def do_wraps(wrapper):
+ self._wrapped[wrapper] = func
+ return wraps(func)(wrapper)
+ return do_wraps