pypa-hatch/hatch/venv/core.py

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()