From 085768b673c58ecf8a23f5ad98ac6f1d8a17c335 Mon Sep 17 00:00:00 2001 From: Jonas Obrist Date: Wed, 25 Apr 2018 19:01:31 +0900 Subject: [PATCH 1/2] Use python -mvenv if possible --- pre_commit/languages/python.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py index 7fc5443e..2ef92901 100644 --- a/pre_commit/languages/python.py +++ b/pre_commit/languages/python.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import contextlib import os +import subprocess import sys from pre_commit.envcontext import envcontext @@ -14,6 +15,11 @@ from pre_commit.util import clean_path_on_failure from pre_commit.util import cmd_output from pre_commit.xargs import xargs +try: + devnull = subprocess.DEVNULL +except AttributeError: + devnull = open(os.devnull, 'w') + ENVIRONMENT_DIR = 'py_env' @@ -134,11 +140,26 @@ def install_environment(prefix, version, additional_dependencies): # Install a virtualenv env_dir = prefix.path(directory) with clean_path_on_failure(env_dir): - venv_cmd = [sys.executable, '-m', 'virtualenv', env_dir] if version != 'default': - venv_cmd.extend(['-p', norm_version(version)]) + target_python = norm_version(version) else: - venv_cmd.extend(['-p', os.path.realpath(sys.executable)]) + target_python = os.path.realpath(sys.executable) + + try: + subprocess.check_call( + [target_python, '-c', 'import venv'], + stderr=devnull, + stdout=devnull, + ) + venv_python = target_python + venv_module = 'venv' + extra_cmd = [] + except subprocess.CalledProcessError: + venv_python = sys.executable + venv_module = 'virtualenv' + extra_cmd = ['-p', target_python] + + venv_cmd = [venv_python, '-m', venv_module, env_dir] + extra_cmd venv_env = dict(os.environ, VIRTUALENV_NO_DOWNLOAD='1') cmd_output(*venv_cmd, cwd='/', env=venv_env) with in_env(prefix, version): From 495dc33dac9534e2c66a6aa93193bfa6368b0cb7 Mon Sep 17 00:00:00 2001 From: Jonas Obrist Date: Thu, 26 Apr 2018 12:46:33 +0900 Subject: [PATCH 2/2] allow opt-in for using venv over virtualenv --- pre_commit/languages/python.py | 43 ++++++++++++++++++---------------- tests/languages/python_test.py | 14 ++++++++++- 2 files changed, 36 insertions(+), 21 deletions(-) diff --git a/pre_commit/languages/python.py b/pre_commit/languages/python.py index 2ef92901..9505577e 100644 --- a/pre_commit/languages/python.py +++ b/pre_commit/languages/python.py @@ -114,23 +114,29 @@ def healthy(prefix, language_version): def norm_version(version): + if version.endswith('+venv'): + use_venv = True + version = version[:-5] + else: + use_venv = False if os.name == 'nt': # pragma: no cover (windows) # Try looking up by name version_exec = find_executable(version) if version_exec and version_exec != version: - return version_exec + return version_exec, use_venv version_exec = _find_by_py_launcher(version) if version_exec: - return version_exec + return version_exec, use_venv # If it is in the form pythonx.x search in the default # place on windows if version.startswith('python'): - return r'C:\{}\python.exe'.format(version.replace('.', '')) + path = r'C:\{}\python.exe'.format(version.replace('.', '')) + return path, use_venv # Otherwise assume it is a path - return os.path.expanduser(version) + return os.path.expanduser(version), use_venv def install_environment(prefix, version, additional_dependencies): @@ -141,25 +147,22 @@ def install_environment(prefix, version, additional_dependencies): env_dir = prefix.path(directory) with clean_path_on_failure(env_dir): if version != 'default': - target_python = norm_version(version) + target_python, use_venv = norm_version(version) else: target_python = os.path.realpath(sys.executable) + use_venv = False - try: - subprocess.check_call( - [target_python, '-c', 'import venv'], - stderr=devnull, - stdout=devnull, - ) - venv_python = target_python - venv_module = 'venv' - extra_cmd = [] - except subprocess.CalledProcessError: - venv_python = sys.executable - venv_module = 'virtualenv' - extra_cmd = ['-p', target_python] - - venv_cmd = [venv_python, '-m', venv_module, env_dir] + extra_cmd + if use_venv: + venv_cmd = [target_python, '-m', 'venv', env_dir] + else: + venv_cmd = [ + sys.executable, + '-m', + 'virtualenv', + '-p', + target_python, + env_dir, + ] venv_env = dict(os.environ, VIRTUALENV_NO_DOWNLOAD='1') cmd_output(*venv_cmd, cwd='/', env=venv_env) with in_env(prefix, version): diff --git a/tests/languages/python_test.py b/tests/languages/python_test.py index 78211cb9..b8331f91 100644 --- a/tests/languages/python_test.py +++ b/tests/languages/python_test.py @@ -3,6 +3,8 @@ from __future__ import unicode_literals import os.path +import pytest + from pre_commit.languages import python @@ -15,4 +17,14 @@ def test_norm_version_expanduser(): path = '~/.pyenv/versions/3.4.3/bin/python' expected_path = home + '/.pyenv/versions/3.4.3/bin/python' result = python.norm_version(path) - assert result == expected_path + assert result == (expected_path, False) + + +@pytest.mark.parametrize( + 'version,use_venv', [ + ('python3.6', False), + ('python3.6+venv', True), + ], +) +def test_norm_version_venv(version, use_venv): + assert python.norm_version(version)[1] == use_venv