Skip to content

Commit

Permalink
feat: install missing Python versions for virtualenv with pyenv
Browse files Browse the repository at this point in the history
ghstack-source-id: c1e16c81bf4cd18a44dd656eb73963e1a3754706
Pull Request resolved: #52
  • Loading branch information
isidentical committed Nov 25, 2022
1 parent 0e76847 commit 7617a2a
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 6 deletions.
29 changes: 25 additions & 4 deletions src/isolate/backends/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,38 @@ def install_requirements(self, path: Path) -> None:
except subprocess.SubprocessError as exc:
raise EnvironmentCreationError("Failure during 'pip install'.") from exc

def _install_python_through_pyenv(self) -> str:
from isolate.backends.pyenv import PyenvEnvironment

self.log(
f"Requested Python version of {self.python_version} is not available "
"in the system, attempting to install it through pyenv."
)

pyenv = PyenvEnvironment.from_config(
{"python_version": self.python_version},
settings=self.settings,
)
return str(get_executable_path(pyenv.create(), "python"))

def _decide_python(self) -> str:
from virtualenv.discovery import builtin

from isolate.backends.pyenv import _get_pyenv_executable

interpreter = builtin.get_interpreter(self.python_version, ())
if interpreter is not None:
return interpreter.executable

raise EnvironmentCreationError(
f"Python {self.python_version} is not available in your "
"system. Please install it first."
)
try:
_get_pyenv_executable()
except Exception:
raise EnvironmentCreationError(
f"Python {self.python_version} is not available in your "
"system. Please install it first."
) from None
else:
return self._install_python_through_pyenv()

def create(self) -> Path:
from virtualenv import cli_run
Expand Down
38 changes: 36 additions & 2 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,12 @@ def test_caching_with_constraints(self, tmp_path):
)
assert environment_1.key != environment_2.key != environment_3.key

def test_custom_python_version(self, tmp_path):
def test_custom_python_version(self, tmp_path, monkeypatch):
# Disable pyenv to prevent auto-installation
monkeypatch.setattr(
"isolate.backends.pyenv._get_pyenv_executable", lambda: 1 / 0
)

for python_type, expected_python_version in [
("old-python", "3.7"),
("new-python", "3.10"),
Expand All @@ -294,7 +299,12 @@ def test_custom_python_version(self, tmp_path):
python_version = self.get_python_version(environment, connection)
assert python_version.startswith(expected_python_version)

def test_invalid_python_version_raises(self, tmp_path):
def test_invalid_python_version_raises(self, tmp_path, monkeypatch):
# Disable pyenv to prevent auto-installation
monkeypatch.setattr(
"isolate.backends.pyenv._get_pyenv_executable", lambda: 1 / 0
)

# Hopefully there will never be a Python 9.9.9
environment = self.get_environment(tmp_path, {"python_version": "9.9.9"})
with pytest.raises(
Expand Down Expand Up @@ -636,3 +646,27 @@ def test_pyenv_environment(python_version, tmp_path):

different_python.destroy(connection_key)
assert not different_python.exists()


@pytest.mark.skipif(not IS_PYENV_AVAILABLE, reason="Pyenv is not available")
def test_virtual_env_custom_python_version_with_pyenv(tmp_path, monkeypatch):
pyjokes_env = VirtualPythonEnvironment(
requirements=["pyjokes==0.6.0"],
python_version="3.9",
)

test_settings = IsolateSettings(Path(tmp_path))
pyjokes_env.apply_settings(test_settings)

# Force it to choose pyenv as the python version manager.
pyjokes_env._decide_python = pyjokes_env._install_python_through_pyenv

connection_key = pyjokes_env.create()
with pyjokes_env.open_connection(connection_key) as connection:
assert connection.run(partial(eval, "__import__('sys').version")).startswith(
"3.9"
)
assert (
connection.run(partial(eval, "__import__('pyjokes').__version__"))
== "0.6.0"
)

0 comments on commit 7617a2a

Please sign in to comment.