Bug 952280 - mach python commands now use virtualenv APIs; r=mshal
authorGregory Szorc <gps@mozilla.com>
Thu, 19 Dec 2013 13:48:37 -0800
changeset 161462 d6810b2fd6e2c2d0ce2f33cbd7638809c65a6a8b
parent 161461 9c3731bf49a745e81cd9a09c8da5c8eef564cd34
child 161463 c88a31d8f8560302ac9e0205fd33f4ecff972f89
push id37919
push usergszorc@mozilla.com
push dateFri, 20 Dec 2013 19:12:55 +0000
treeherdermozilla-inbound@d6810b2fd6e2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmshal
bugs952280
milestone29.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 952280 - mach python commands now use virtualenv APIs; r=mshal The Python-related mach commands were written before we had a virtualenv API exposed to the mach command context. This patch updates those commands to use the newer APIs. As a bonus, these commands now work without running configure!
python/mach_commands.py
--- a/python/mach_commands.py
+++ b/python/mach_commands.py
@@ -19,51 +19,27 @@ from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
 
 @CommandProvider
 class MachCommands(MachCommandBase):
-    '''
-    Easily run Python and Python unit tests.
-    '''
-    def __init__(self, context):
-        MachCommandBase.__init__(self, context)
-        self._python_executable = None
-
-    @property
-    def python_executable(self):
-        '''
-        Return path to Python executable, or print and sys.exit(1) if
-        executable does not exist.
-        '''
-        if self._python_executable:
-            return self._python_executable
-        if self._is_windows():
-            executable = '_virtualenv/Scripts/python.exe'
-        else:
-            executable = '_virtualenv/bin/python'
-        path = mozpack.path.join(self.topobjdir, executable)
-        if not os.path.exists(path):
-            print("Could not find Python executable at %s." % path,
-                  "Run |mach configure| or |mach build| to install it.")
-            sys.exit(1)
-        self._python_executable = path
-        return path
-
     @Command('python', category='devenv',
         allow_all_args=True,
         description='Run Python.')
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def python(self, args):
         # Avoid logging the command
         self.log_manager.terminal_handler.setLevel(logging.CRITICAL)
-        return self.run_process([self.python_executable] + args,
+
+        self._activate_virtualenv()
+
+        return self.run_process([self.virtualenv_manager.python_path] + args,
             pass_thru=True, # Allow user to run Python interactively.
             ensure_exit_code=False, # Don't throw on non-zero exit code.
             # Note: subprocess requires native strings in os.environ on Windows
             append_env={b'PYTHONDONTWRITEBYTECODE': str('1')})
 
     @Command('python-test', category='testing',
         description='Run Python unit tests.')
     @CommandArgument('--verbose',
@@ -73,18 +49,17 @@ class MachCommands(MachCommandBase):
     @CommandArgument('--stop',
         default=False,
         action='store_true',
         help='Stop running tests after the first error or failure.')
     @CommandArgument('tests', nargs='+',
         metavar='TEST',
         help='Tests to run. Each test can be a single file or a directory.')
     def python_test(self, tests, verbose=False, stop=False):
-        # Make sure we can find Python before doing anything else.
-        self.python_executable
+        self._activate_virtualenv()
 
         # Python's unittest, and in particular discover, has problems with
         # clashing namespaces when importing multiple test modules. What follows
         # is a simple way to keep environments separate, at the price of
         # launching Python multiple times. This also runs tests via mozunit,
         # which produces output in the format Mozilla infrastructure expects.
         return_code = 0
         files = []
@@ -97,38 +72,38 @@ class MachCommands(MachCommandBase):
                 files += glob.glob(mozpack.path.join(test, 'test*.py'))
                 files += glob.glob(mozpack.path.join(test, 'unit*.py'))
             else:
                 self.log(logging.WARN, 'python-test', {'test': test},
                          'TEST-UNEXPECTED-FAIL | Invalid test: {test}')
                 if stop:
                     return 1
 
-        for file in files:
+        for f in files:
             file_displayed_test = [] # Used as a boolean.
             def _line_handler(line):
                 if not file_displayed_test and line.startswith('TEST-'):
                     file_displayed_test.append(True)
 
             inner_return_code = self.run_process(
-                [self.python_executable, file],
+                [self.virtualenv_manager.python_path, f],
                 ensure_exit_code=False, # Don't throw on non-zero exit code.
                 log_name='python-test',
                 # subprocess requires native strings in os.environ on Windows
                 append_env={b'PYTHONDONTWRITEBYTECODE': str('1')},
                 line_handler=_line_handler)
             return_code += inner_return_code
 
             if not file_displayed_test:
-                self.log(logging.WARN, 'python-test', {'file': file},
+                self.log(logging.WARN, 'python-test', {'file': f},
                          'TEST-UNEXPECTED-FAIL | No test output (missing mozunit.main() call?): {file}')
 
             if verbose:
                 if inner_return_code != 0:
-                    self.log(logging.INFO, 'python-test', {'file': file},
+                    self.log(logging.INFO, 'python-test', {'file': f},
                              'Test failed: {file}')
                 else:
-                    self.log(logging.INFO, 'python-test', {'file': file},
+                    self.log(logging.INFO, 'python-test', {'file': f},
                              'Test passed: {file}')
             if stop and return_code > 0:
                 return 1
 
         return 0 if return_code == 0 else 1