mirror of https://github.com/pypa/hatch.git
123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
import os
|
|
from ast import literal_eval
|
|
from tempfile import TemporaryDirectory
|
|
|
|
from ..utils.fs import Path
|
|
from .utils import get_random_venv_name, handle_verbosity_flag
|
|
|
|
|
|
class VirtualEnv:
|
|
IGNORED_ENV_VARS = ('__PYVENV_LAUNCHER__', 'PYTHONHOME')
|
|
|
|
def __init__(self, directory, platform, verbosity=0):
|
|
self.directory = directory
|
|
self.platform = platform
|
|
self.verbosity = verbosity
|
|
|
|
self._env_vars_to_restore = {}
|
|
self._executables_directory = None
|
|
self._sys_path = None
|
|
|
|
def activate(self):
|
|
self._env_vars_to_restore['VIRTUAL_ENV'] = os.environ.pop('VIRTUAL_ENV', None)
|
|
os.environ['VIRTUAL_ENV'] = str(self.directory)
|
|
|
|
old_path = os.environ.pop('PATH', None)
|
|
self._env_vars_to_restore['PATH'] = old_path
|
|
if old_path is None:
|
|
os.environ['PATH'] = str(self.executables_directory)
|
|
else:
|
|
os.environ['PATH'] = f'{self.executables_directory}{os.pathsep}{old_path}'
|
|
|
|
for env_var in self.IGNORED_ENV_VARS:
|
|
self._env_vars_to_restore[env_var] = os.environ.pop(env_var, None)
|
|
|
|
def deactivate(self):
|
|
for env_var, value in self._env_vars_to_restore.items():
|
|
if value is None:
|
|
os.environ.pop(env_var, None)
|
|
else:
|
|
os.environ[env_var] = value
|
|
|
|
self._env_vars_to_restore.clear()
|
|
|
|
def create(self, python, allow_system_packages=False):
|
|
# WARNING: extremely slow import
|
|
from virtualenv import cli_run
|
|
|
|
self.directory.ensure_parent_dir_exists()
|
|
|
|
command = [str(self.directory), '--no-download', '--no-periodic-update', '--pip', 'embed', '--python', python]
|
|
|
|
if allow_system_packages:
|
|
command.append('--system-site-packages')
|
|
|
|
handle_verbosity_flag(command, self.verbosity)
|
|
|
|
cli_run(command)
|
|
|
|
def remove(self):
|
|
self.directory.remove()
|
|
|
|
def exists(self):
|
|
return self.directory.is_dir()
|
|
|
|
@property
|
|
def executables_directory(self):
|
|
if self._executables_directory is None:
|
|
exe_dir = self.directory / 'Scripts' if self.platform.windows else self.directory / 'bin'
|
|
if exe_dir.is_dir():
|
|
self._executables_directory = exe_dir
|
|
# PyPy
|
|
elif self.platform.windows:
|
|
exe_dir = self.directory / 'bin'
|
|
if exe_dir.is_dir():
|
|
self._executables_directory = exe_dir
|
|
else:
|
|
raise OSError('Unable to locate executables directory')
|
|
else:
|
|
raise OSError('Unable to locate executables directory')
|
|
|
|
return self._executables_directory
|
|
|
|
@property
|
|
def sys_path(self):
|
|
if self._sys_path is None:
|
|
# Assume caller will manage activation
|
|
process = self.platform.check_command(
|
|
['python', '-c', 'import sys;print([path for path in sys.path if path])'],
|
|
capture_output=True,
|
|
)
|
|
|
|
self._sys_path = literal_eval(process.stdout.strip().decode('utf-8'))
|
|
|
|
return self._sys_path
|
|
|
|
def __enter__(self):
|
|
self.activate()
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
self.deactivate()
|
|
|
|
|
|
class TempVirtualEnv(VirtualEnv):
|
|
def __init__(self, parent_python, platform, verbosity=0):
|
|
self.parent_python = parent_python
|
|
self.parent_dir = TemporaryDirectory()
|
|
directory = Path(self.parent_dir.name).resolve() / get_random_venv_name()
|
|
|
|
super().__init__(directory, platform, verbosity)
|
|
|
|
def remove(self):
|
|
super().remove()
|
|
self.parent_dir.cleanup()
|
|
|
|
def __enter__(self):
|
|
self.create(self.parent_python)
|
|
return super().__enter__()
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
super().__exit__(exc_type, exc_value, traceback)
|
|
self.remove()
|