Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/isolate/backends/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ContainerizedPythonEnvironment(BaseEnvironment[Path]):
image: dict[str, Any] = field(default_factory=dict)
python_version: str | None = None
requirements: list[str] = field(default_factory=list)
install_requirements: list[str] = field(default_factory=list)
tags: list[str] = field(default_factory=list)
resolver: str | None = None

Expand Down Expand Up @@ -46,6 +47,7 @@ def key(self) -> str:
dockerfile_str = self.image.get("dockerfile_str", "")
return sha256_digest_of(
dockerfile_str,
*self.install_requirements,
*self.requirements,
*sorted(self.tags),
*extras,
Expand Down
13 changes: 8 additions & 5 deletions src/isolate/backends/virtualenv.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class VirtualPythonEnvironment(BaseEnvironment[Path]):
BACKEND_NAME: ClassVar[str] = "virtualenv"

requirements: list[str] = field(default_factory=list)
install_requirements: list[str] = field(default_factory=list)
constraints_file: os.PathLike | None = None
python_version: str | None = None
extra_index_urls: list[str] = field(default_factory=list)
Expand Down Expand Up @@ -65,6 +66,7 @@ def key(self) -> str:
active_python_version = self.python_version or active_python()
return sha256_digest_of(
active_python_version,
*self.install_requirements,
*self.requirements,
*constraints,
*self.extra_index_urls,
Expand All @@ -74,17 +76,17 @@ def key(self) -> str:
*extras,
)

def install_requirements(self, path: Path) -> None:
def _install_packages(self, path: Path, requirements: list[str]) -> None:
"""Install the requirements of this environment using 'pip' to the
given virtualenv path.

If there are any constraint files specified, they will be also passed to
the package resolver.
"""
if not self.requirements:
if not requirements:
return None

self.log(f"Installing requirements: {', '.join(self.requirements)}")
self.log(f"Installing requirements: {', '.join(requirements)}")
environ = os.environ.copy()

if self.resolver == "uv":
Expand All @@ -102,7 +104,7 @@ def install_requirements(self, path: Path) -> None:
pip_cmd: list[str | os.PathLike] = [
*base_pip_cmd, # type: ignore
"install",
*self.requirements,
*requirements,
]
if self.constraints_file:
pip_cmd.extend(["-c", self.constraints_file])
Expand Down Expand Up @@ -183,7 +185,8 @@ def create(self, *, force: bool = False) -> Path:
f"Failed to create the environment at '{venv_path}': {exc}"
)

self.install_requirements(venv_path)
self._install_packages(venv_path, self.install_requirements)
self._install_packages(venv_path, self.requirements)
completion_marker.touch()

self.log(f"New environment cached at '{venv_path}'")
Expand Down
38 changes: 38 additions & 0 deletions tests/test_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,44 @@ def test_tags_in_key(self, tmp_path, monkeypatch):
tagged_environment.key == tagged_environment_2.key
), "Tag order should not matter"

def test_install_requirements_in_key(self, tmp_path):
base = self.get_environment(tmp_path, {"requirements": ["pyjokes==0.6.0"]})
with_install = self.get_environment(
tmp_path,
{
"requirements": ["pyjokes==0.6.0"],
"install_requirements": ["pip==23.0.1"],
},
)
assert base.key != with_install.key

def test_install_requirements_order(self, tmp_path, monkeypatch):
installed = []

def fake_install_packages(self, path, requirements):
installed.append(list(requirements))

class DummyVirtualenv:
@staticmethod
def cli_run(args):
Path(args[0]).mkdir(parents=True, exist_ok=True)

monkeypatch.setattr(
"isolate.backends.virtualenv.optional_import", lambda _: DummyVirtualenv
)
monkeypatch.setattr(
VirtualPythonEnvironment, "_install_packages", fake_install_packages
)

environment = VirtualPythonEnvironment(
requirements=["pyjokes==0.6.0"],
install_requirements=["pip==23.0.1"],
)
environment.apply_settings(IsolateSettings(Path(tmp_path)))
environment.create()

assert installed == [["pip==23.0.1"], ["pyjokes==0.6.0"]]

@pytest.mark.skipif(not UV_PATH, reason="uv is not available")
def test_try_using_uv(self, tmp_path):
environment = self.get_environment(
Expand Down
Loading