Bug 1172800 - Avoid using inspect.getsourcelines() and inspect.getfile(). r=gps
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 09 Jun 2015 15:18:39 +0900
changeset 247836 e8d4ce4d30c0498befccfc388f0974f48a77b416
parent 247835 d7067d8099ff55d967492c3ac7a93c6765357144
child 247837 3171a5626b6499784f0e57ab275b12cc9524cdba
push id28885
push usercbook@mozilla.com
push dateWed, 10 Jun 2015 13:18:59 +0000
treeherdermozilla-central@e101c589c242 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1172800
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1172800 - Avoid using inspect.getsourcelines() and inspect.getfile(). r=gps inspect.getsourcelines() and inspect.getfile() involve I/O out of our control. Our use of those functions, however, doesn't require all their smarts. In fact, we only use them on function objects, for which we can just do the work ourselves without involving inspect functions that trigger I/O.
python/mozbuild/mozbuild/frontend/reader.py
python/mozbuild/mozbuild/frontend/sandbox.py
--- a/python/mozbuild/mozbuild/frontend/reader.py
+++ b/python/mozbuild/mozbuild/frontend/reader.py
@@ -319,17 +319,17 @@ class MozbuildSandbox(Sandbox):
         if name in self.templates:
             raise KeyError(
                 'A template named "%s" was already declared in %s.' % (name,
                 self.templates[name].path))
 
         if name.islower() or name.isupper() or name[0].islower():
             raise NameError('Template function names must be CamelCase.')
 
-        self.templates[name] = TemplateFunction(func)
+        self.templates[name] = TemplateFunction(func, self)
 
     @memoize
     def _create_subcontext(self, cls):
         """Return a function object that creates SubContext instances."""
         def fn(*args, **kwargs):
             return cls(self._context, *args, **kwargs)
 
         return fn
@@ -403,21 +403,23 @@ class MozbuildSandbox(Sandbox):
 
             for p in context.all_paths:
                 self._context.add_source(p)
 
         return template_wrapper
 
 
 class TemplateFunction(object):
-    def __init__(self, func):
-        self.path = inspect.getfile(func)
+    def __init__(self, func, sandbox):
+        self.path = func.func_code.co_filename
         self.name = func.func_name
 
-        lines, firstlineno = inspect.getsourcelines(func)
+        firstlineno = func.func_code.co_firstlineno
+        lines = sandbox._current_source.splitlines(True)
+        lines = inspect.getblock(lines[firstlineno - 1:])
         first_op = None
         generator = tokenize.generate_tokens(iter(lines).next)
         # Find the first indent token in the source of this template function,
         # which corresponds to the beginning of the function body.
         for typ, s, begin, end, line in generator:
             if typ == tokenize.OP:
                 first_op = True
             if first_op and typ == tokenize.INDENT:
--- a/python/mozbuild/mozbuild/frontend/sandbox.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox.py
@@ -124,16 +124,19 @@ class Sandbox(dict):
         # Seen sub-contexts. Will be populated with other Context instances
         # that were related to execution of this instance.
         self.subcontexts = []
 
         # We need to record this because it gets swallowed as part of
         # evaluation.
         self._last_name_error = None
 
+        # Current literal source being executed.
+        self._current_source = None
+
     @property
     def _context(self):
         return self._active_contexts[-1]
 
     def exec_file(self, path):
         """Execute code at a path in the sandbox.
 
         The path must be absolute.
@@ -166,16 +169,18 @@ class Sandbox(dict):
 
         old_sandbox = self._context._sandbox
         self._context._sandbox = weakref.ref(self)
 
         # We don't have to worry about bytecode generation here because we are
         # too low-level for that. However, we could add bytecode generation via
         # the marshall module if parsing performance were ever an issue.
 
+        old_source = self._current_source
+        self._current_source = source
         try:
             # compile() inherits the __future__ from the module by default. We
             # 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.
             exec(code, self)
@@ -207,16 +212,17 @@ class Sandbox(dict):
             exc = sys.exc_info()
             source_stack = self._context.source_stack
             if not becomes_current_path:
                 # Add current file to the stack because it wasn't added before
                 # sandbox execution.
                 source_stack.append(path)
             raise SandboxExecutionError(source_stack, exc[0], exc[1], exc[2])
         finally:
+            self._current_source = old_source
             self._context._sandbox = old_sandbox
             if path and becomes_current_path:
                 self._context.pop_source()
 
     def push_subcontext(self, context):
         """Push a SubContext onto the execution stack.
 
         When called, the active context will be set to the specified context,