import os import runpy import subprocess import sys from .config import g from .logger import getLogger from .process import Popen, system from .util import __work # extracted from https://pypi.org/simple/virtualenv/ VIRTUALENV_URL = 'https://files.pythonhosted.org/packages/57/6e' \ '/a13442adf18bada682f88f55638cd43cc7a39c3e00fdcf898ca4ceaeb682/virtualenv-20.0.21-py2.py3-none-any.whl' HASH = 'a730548b27366c5e6cbdf6f97406d861cccece2e22275e8e1a757aeff5e00c70' log = getLogger(__name__) def _locate_python_win(): return os.path.join(sys.exec_prefix, 'python.exe') def _locate_python(): if sys.platform == 'win32': executable = _locate_python_win() elif sys.platform == 'darwin': executable = sys.executable elif sys.platform == 'linux': # TODO: test linux version log.info( 'Linux virtualenv support is not tested. If this prints "Done!", it\'s working!') executable = sys.executable else: assert False, "this platform is not supported" return executable class FixInterpreter(object): def __init__(self): pass def __enter__(self): self.backup, sys.executable = sys.executable, _locate_python() self.backup_popen, subprocess.Popen = subprocess.Popen, Popen self.backup_system, os.system = os.system, system def __exit__(self, type_, value, traceback): sys.executable = self.backup subprocess.Popen = self.backup_popen os.system = self.backup_system def _install_virtualenv(path): from hashlib import sha256 from .downloader import download log.info('Downloading virtualenv from %r ...', VIRTUALENV_URL) data = download(VIRTUALENV_URL).read() assert sha256(data).hexdigest() == HASH, 'hash error... MITM?' import tempfile with tempfile.NamedTemporaryFile('wb', suffix=".zip", delete=False) as zf: zf.write(data) zf.flush() sys.path.insert(0, zf.name) import virtualenv with FixInterpreter(): log.info('Creating environment using virtualenv...') virtualenv.create_environment(path, site_packages=True) log.info('Done!') def prepare_virtualenv(path=None, callback=None, wait=False): if path is None: path = g['path']['virtualenv'] abspath = os.path.abspath(path) sys.path.insert(0, abspath) if not wait and callback: def callback(): return __work(callback) try: activator_path = os.path.join( abspath, 'Scripts' if sys.platform == 'win32' else 'bin', 'activate_this.py') if not os.path.isfile(activator_path): raise ImportError() runpy.run_path(activator_path) callback and callback() except ImportError: tasks = [ lambda: prepare_virtualenv(path) ] try: import pip if not os.path.abspath(pip.__file__).startswith(abspath): raise ImportError() except ImportError: log.info( 'Will install virtualenv at %r since pip module is not found...', path) tasks.insert(0, lambda: _install_virtualenv(path)) def handler(): return ([task() for task in tasks], callback and callback()) __work(handler) if not wait else handler()