Merging upstream version 2.5.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
344ec6ad68
commit
46a56c0856
37 changed files with 457 additions and 213 deletions
|
@ -2,8 +2,7 @@ import contextlib
|
|||
import functools
|
||||
import os
|
||||
import sys
|
||||
from typing import Callable
|
||||
from typing import ContextManager
|
||||
from typing import Dict
|
||||
from typing import Generator
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
|
@ -26,6 +25,28 @@ from pre_commit.util import cmd_output_b
|
|||
ENVIRONMENT_DIR = 'py_env'
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=None)
|
||||
def _version_info(exe: str) -> str:
|
||||
prog = 'import sys;print(".".join(str(p) for p in sys.version_info))'
|
||||
try:
|
||||
return cmd_output(exe, '-S', '-c', prog)[1].strip()
|
||||
except CalledProcessError:
|
||||
return f'<<error retrieving version from {exe}>>'
|
||||
|
||||
|
||||
def _read_pyvenv_cfg(filename: str) -> Dict[str, str]:
|
||||
ret = {}
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
try:
|
||||
k, v = line.split('=')
|
||||
except ValueError: # blank line / comment / etc.
|
||||
continue
|
||||
else:
|
||||
ret[k.strip()] = v.strip()
|
||||
return ret
|
||||
|
||||
|
||||
def bin_dir(venv: str) -> str:
|
||||
"""On windows there's a different directory for the virtualenv"""
|
||||
bin_part = 'Scripts' if os.name == 'nt' else 'bin'
|
||||
|
@ -34,6 +55,7 @@ def bin_dir(venv: str) -> str:
|
|||
|
||||
def get_env_patch(venv: str) -> PatchesT:
|
||||
return (
|
||||
('PIP_DISABLE_PIP_VERSION_CHECK', '1'),
|
||||
('PYTHONHOME', UNSET),
|
||||
('VIRTUAL_ENV', venv),
|
||||
('PATH', (bin_dir(venv), os.pathsep, Var('PATH'))),
|
||||
|
@ -45,9 +67,10 @@ def _find_by_py_launcher(
|
|||
) -> Optional[str]: # pragma: no cover (windows only)
|
||||
if version.startswith('python'):
|
||||
num = version[len('python'):]
|
||||
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
|
||||
env = dict(os.environ, PYTHONIOENCODING='UTF-8')
|
||||
try:
|
||||
cmd = ('py', f'-{num}', '-c', 'import sys; print(sys.executable)')
|
||||
return cmd_output(*cmd)[1].strip()
|
||||
return cmd_output(*cmd, env=env)[1].strip()
|
||||
except CalledProcessError:
|
||||
pass
|
||||
return None
|
||||
|
@ -115,6 +138,9 @@ def _sys_executable_matches(version: str) -> bool:
|
|||
|
||||
|
||||
def norm_version(version: str) -> str:
|
||||
if version == C.DEFAULT:
|
||||
return os.path.realpath(sys.executable)
|
||||
|
||||
# first see if our current executable is appropriate
|
||||
if _sys_executable_matches(version):
|
||||
return sys.executable
|
||||
|
@ -139,70 +165,59 @@ def norm_version(version: str) -> str:
|
|||
return os.path.expanduser(version)
|
||||
|
||||
|
||||
def py_interface(
|
||||
_dir: str,
|
||||
_make_venv: Callable[[str, str], None],
|
||||
) -> Tuple[
|
||||
Callable[[Prefix, str], ContextManager[None]],
|
||||
Callable[[Prefix, str], bool],
|
||||
Callable[[Hook, Sequence[str], bool], Tuple[int, bytes]],
|
||||
Callable[[Prefix, str, Sequence[str]], None],
|
||||
]:
|
||||
@contextlib.contextmanager
|
||||
def in_env(
|
||||
prefix: Prefix,
|
||||
language_version: str,
|
||||
) -> Generator[None, None, None]:
|
||||
envdir = prefix.path(helpers.environment_dir(_dir, language_version))
|
||||
with envcontext(get_env_patch(envdir)):
|
||||
yield
|
||||
|
||||
def healthy(prefix: Prefix, language_version: str) -> bool:
|
||||
envdir = helpers.environment_dir(_dir, language_version)
|
||||
exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
|
||||
py_exe = prefix.path(bin_dir(envdir), exe_name)
|
||||
with in_env(prefix, language_version):
|
||||
retcode, _, _ = cmd_output_b(
|
||||
py_exe, '-c', 'import ctypes, datetime, io, os, ssl, weakref',
|
||||
cwd='/',
|
||||
retcode=None,
|
||||
)
|
||||
return retcode == 0
|
||||
|
||||
def run_hook(
|
||||
hook: Hook,
|
||||
file_args: Sequence[str],
|
||||
color: bool,
|
||||
) -> Tuple[int, bytes]:
|
||||
with in_env(hook.prefix, hook.language_version):
|
||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
||||
|
||||
def install_environment(
|
||||
prefix: Prefix,
|
||||
version: str,
|
||||
additional_dependencies: Sequence[str],
|
||||
) -> None:
|
||||
directory = helpers.environment_dir(_dir, version)
|
||||
install = ('python', '-mpip', 'install', '.', *additional_dependencies)
|
||||
|
||||
env_dir = prefix.path(directory)
|
||||
with clean_path_on_failure(env_dir):
|
||||
if version != C.DEFAULT:
|
||||
python = norm_version(version)
|
||||
else:
|
||||
python = os.path.realpath(sys.executable)
|
||||
_make_venv(env_dir, python)
|
||||
with in_env(prefix, version):
|
||||
helpers.run_setup_cmd(prefix, install)
|
||||
|
||||
return in_env, healthy, run_hook, install_environment
|
||||
@contextlib.contextmanager
|
||||
def in_env(
|
||||
prefix: Prefix,
|
||||
language_version: str,
|
||||
) -> Generator[None, None, None]:
|
||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
|
||||
envdir = prefix.path(directory)
|
||||
with envcontext(get_env_patch(envdir)):
|
||||
yield
|
||||
|
||||
|
||||
def make_venv(envdir: str, python: str) -> None:
|
||||
env = dict(os.environ, VIRTUALENV_NO_DOWNLOAD='1')
|
||||
cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
|
||||
cmd_output_b(*cmd, env=env, cwd='/')
|
||||
def healthy(prefix: Prefix, language_version: str) -> bool:
|
||||
directory = helpers.environment_dir(ENVIRONMENT_DIR, language_version)
|
||||
envdir = prefix.path(directory)
|
||||
pyvenv_cfg = os.path.join(envdir, 'pyvenv.cfg')
|
||||
|
||||
# created with "old" virtualenv
|
||||
if not os.path.exists(pyvenv_cfg):
|
||||
return False
|
||||
|
||||
exe_name = 'python.exe' if sys.platform == 'win32' else 'python'
|
||||
py_exe = prefix.path(bin_dir(envdir), exe_name)
|
||||
cfg = _read_pyvenv_cfg(pyvenv_cfg)
|
||||
|
||||
return (
|
||||
'version_info' in cfg and
|
||||
_version_info(py_exe) == cfg['version_info'] and (
|
||||
'base-executable' not in cfg or
|
||||
_version_info(cfg['base-executable']) == cfg['version_info']
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
_interface = py_interface(ENVIRONMENT_DIR, make_venv)
|
||||
in_env, healthy, run_hook, install_environment = _interface
|
||||
def install_environment(
|
||||
prefix: Prefix,
|
||||
version: str,
|
||||
additional_dependencies: Sequence[str],
|
||||
) -> None:
|
||||
envdir = prefix.path(helpers.environment_dir(ENVIRONMENT_DIR, version))
|
||||
python = norm_version(version)
|
||||
venv_cmd = (sys.executable, '-mvirtualenv', envdir, '-p', python)
|
||||
install_cmd = ('python', '-mpip', 'install', '.', *additional_dependencies)
|
||||
|
||||
with clean_path_on_failure(envdir):
|
||||
cmd_output_b(*venv_cmd, cwd='/')
|
||||
with in_env(prefix, version):
|
||||
helpers.run_setup_cmd(prefix, install_cmd)
|
||||
|
||||
|
||||
def run_hook(
|
||||
hook: Hook,
|
||||
file_args: Sequence[str],
|
||||
color: bool,
|
||||
) -> Tuple[int, bytes]:
|
||||
with in_env(hook.prefix, hook.language_version):
|
||||
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue