Bug 794506 - Part 2: Add virtualenv APIs; r=ted

This commit is contained in:
Gregory Szorc 2013-09-20 15:46:25 -07:00
parent d2658b6a1f
commit 3048de697a
2 changed files with 66 additions and 8 deletions

View File

@ -25,6 +25,8 @@ from .mozconfig import (
MozconfigLoadException,
MozconfigLoader,
)
from .virtualenv import VirtualenvManager
def ancestors(path):
"""Emit the parent directories of a path."""
@ -86,6 +88,7 @@ class MozbuildObject(ProcessExecutionMixin):
self._mozconfig = None
self._config_guess_output = None
self._config_environment = None
self._virtualenv_manager = None
@classmethod
def from_environment(cls, cwd=None, detect_virtualenv_mozinfo=True):
@ -206,6 +209,16 @@ class MozbuildObject(ProcessExecutionMixin):
return self._topobjdir
@property
def virtualenv_manager(self):
if self._virtualenv_manager is None:
self._virtualenv_manager = VirtualenvManager(self.topsrcdir,
self.topobjdir, os.path.join(self.topobjdir, '_virtualenv'),
sys.stdout, os.path.join(self.topsrcdir, 'build',
'virtualenv_packages.txt'))
return self._virtualenv_manager
@property
def mozconfig(self):
"""Returns information about the current mozconfig file.
@ -475,6 +488,10 @@ class MozbuildObject(ProcessExecutionMixin):
return cls(self.topsrcdir, self.settings, self.log_manager,
topobjdir=self.topobjdir)
def _activate_virtualenv(self):
self.virtualenv_manager.ensure()
self.virtualenv_manager.activate()
class MachCommandBase(MozbuildObject):
"""Base class for mach command providers that wish to be MozbuildObjects.

View File

@ -60,19 +60,27 @@ class VirtualenvManager(object):
'virtualenv.py')
@property
def python_path(self):
def bin_path(self):
# virtualenv.py provides a similar API via path_locations(). However,
# we have a bit of a chicken-and-egg problem and can't reliably
# import virtualenv. The functionality is trivial, so just implement
# it here.
if sys.platform in ('win32', 'cygwin'):
return os.path.join(self.virtualenv_root, 'Scripts', 'python.exe')
return os.path.join(self.virtualenv_root, 'Scripts')
return os.path.join(self.virtualenv_root, 'bin', 'python')
return os.path.join(self.virtualenv_root, 'bin')
@property
def python_path(self):
binary = 'python'
if sys.platform in ('win32', 'cygwin'):
binary += '.exe'
return os.path.join(self.bin_path, binary)
@property
def activate_path(self):
if sys.platform in ('win32', 'cygwin'):
return os.path.join(self.virtualenv_root, 'Scripts',
'activate_this.py')
return os.path.join(self.virtualenv_root, 'bin', 'activate_this.py')
return os.path.join(self.bin_path, 'activate_this.py')
def up_to_date(self):
"""Returns whether the virtualenv is present and up to date."""
@ -388,6 +396,39 @@ class VirtualenvManager(object):
execfile(self.activate_path, dict(__file__=self.activate_path))
def install_pip_package(self, package):
"""Install a package via pip.
The supplied package is specified using a pip requirement specifier.
e.g. 'foo' or 'foo==1.0'.
If the package is already installed, this is a no-op.
"""
from pip.req import InstallRequirement
req = InstallRequirement.from_line(package)
if req.check_if_exists():
return
args = [
'install',
'--use-wheel',
package,
]
return self._run_pip(args)
def _run_pip(self, args):
# It's tempting to call pip natively via pip.main(). However,
# the current Python interpreter may not be the virtualenv python.
# This will confuse pip and cause the package to attempt to install
# against the executing interpreter. By creating a new process, we
# force the virtualenv's interpreter to be used and all is well.
# It /might/ be possible to cheat and set sys.executable to
# self.python_path. However, this seems more risk than it's worth.
subprocess.check_call([os.path.join(self.bin_path, 'pip')] + args,
stderr=subprocess.STDOUT)
def verify_python_version(log_handle):
"""Ensure the current version of Python is sufficient."""