mirror of
https://github.com/python/cpython.git
synced 2024-11-22 05:26:10 +01:00
71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
|
import contextlib
|
||
|
import logging
|
||
|
import os
|
||
|
import subprocess
|
||
|
import shlex
|
||
|
import sys
|
||
|
import sysconfig
|
||
|
import tempfile
|
||
|
import venv
|
||
|
|
||
|
|
||
|
class VirtualEnvironment:
|
||
|
def __init__(self, prefix, **venv_create_args):
|
||
|
self._logger = logging.getLogger(self.__class__.__name__)
|
||
|
venv.create(prefix, **venv_create_args)
|
||
|
self._prefix = prefix
|
||
|
self._paths = sysconfig.get_paths(
|
||
|
scheme='venv',
|
||
|
vars={'base': self.prefix},
|
||
|
expand=True,
|
||
|
)
|
||
|
|
||
|
@classmethod
|
||
|
@contextlib.contextmanager
|
||
|
def from_tmpdir(cls, *, prefix=None, dir=None, **venv_create_args):
|
||
|
delete = not bool(os.environ.get('PYTHON_TESTS_KEEP_VENV'))
|
||
|
with tempfile.TemporaryDirectory(prefix=prefix, dir=dir, delete=delete) as tmpdir:
|
||
|
yield cls(tmpdir, **venv_create_args)
|
||
|
|
||
|
@property
|
||
|
def prefix(self):
|
||
|
return self._prefix
|
||
|
|
||
|
@property
|
||
|
def paths(self):
|
||
|
return self._paths
|
||
|
|
||
|
@property
|
||
|
def interpreter(self):
|
||
|
return os.path.join(self.paths['scripts'], os.path.basename(sys.executable))
|
||
|
|
||
|
def _format_output(self, name, data, indent='\t'):
|
||
|
if not data:
|
||
|
return indent + f'{name}: (none)'
|
||
|
if len(data.splitlines()) == 1:
|
||
|
return indent + f'{name}: {data}'
|
||
|
else:
|
||
|
prefixed_lines = '\n'.join(indent + '> ' + line for line in data.splitlines())
|
||
|
return indent + f'{name}:\n' + prefixed_lines
|
||
|
|
||
|
def run(self, *args, **subprocess_args):
|
||
|
if subprocess_args.get('shell'):
|
||
|
raise ValueError('Running the subprocess in shell mode is not supported.')
|
||
|
default_args = {
|
||
|
'capture_output': True,
|
||
|
'check': True,
|
||
|
}
|
||
|
try:
|
||
|
result = subprocess.run([self.interpreter, *args], **default_args | subprocess_args)
|
||
|
except subprocess.CalledProcessError as e:
|
||
|
if e.returncode != 0:
|
||
|
self._logger.error(
|
||
|
f'Interpreter returned non-zero exit status {e.returncode}.\n'
|
||
|
+ self._format_output('COMMAND', shlex.join(e.cmd)) + '\n'
|
||
|
+ self._format_output('STDOUT', e.stdout.decode()) + '\n'
|
||
|
+ self._format_output('STDERR', e.stderr.decode()) + '\n'
|
||
|
)
|
||
|
raise
|
||
|
else:
|
||
|
return result
|