Run node code on the venv Fluksio was installed into

The workflow this serves: make a venv, install what you work with, then `pip
install fluksio` into the same one. Building a second environment beside it
was exactly wrong — the packages the nodes need are already here, and the
Modules screen was asking for them a second time.

`NODE_VENV=auto` (the default) adopts that venv. It declines in the three
cases where adopting would be wrong: `managed` says otherwise, a managed venv
already exists and may hold packages somebody installed on purpose, or the
engine is not running from a venv at all. The images set `managed`, since the
venv in them holds the app and nothing of anybody else's.

An adopted venv is never written to. `uv pip sync` makes a venv hold exactly
the manifest, so pointed at somebody's own environment it uninstalls their
work and the engine with it — `sync()` refuses outright and `reconcile()`
returns before it can be called at startup, which is where that would have
happened first. The Modules screen lists what is installed and drops its
editor; `pip` is how that environment changes.

`fluksio serve` now names the interpreter node code runs on, which is the
thing a data scientist most needs to know at that moment. `fluksio-worker`
already defaulted `--python` to its own interpreter, so a GPU box works the
same way — that was only ever undocumented.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012ue1tkFWB1bcGy3aWhCKpU
This commit is contained in:
2026-08-24 10:35:13 +02:00
co-authored by Claude Fable 5
parent 68fa5527b1
commit fea57064f9
15 changed files with 357 additions and 69 deletions
+87
View File
@@ -75,3 +75,90 @@ def test_a_manifest_that_does_not_resolve_leaves_the_venv_alone(venv: Path):
assert output
# The marker still describes the manifest that actually installed.
assert (venv / ".applied").read_text() == modules._digest("")
# ---------------------------------------------------------------------------
# Adopting the venv Fluksio was installed into
#
# The workflow this exists for: make a venv, install what you work with, then
# `pip install fluksio` into the same one. Building a second environment beside
# it would leave node code unable to import the packages that are the point.
# ---------------------------------------------------------------------------
@pytest.fixture
def their_venv(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
"""Pretend the engine is running from a venv somebody else made."""
directory = tmp_path / "research"
(directory / "bin").mkdir(parents=True)
(directory / "bin" / "python").write_text("")
monkeypatch.setattr(modules.sys, "prefix", str(directory))
monkeypatch.setattr(modules.sys, "base_prefix", str(tmp_path / "usr"))
# Undoes the suite-wide "managed" in tests/__init__.py.
monkeypatch.setattr(modules.settings, "NODE_VENV", "auto")
return directory
def test_a_venv_of_your_own_is_adopted(venv: Path, their_venv: Path):
assert modules.adopted() == their_venv
assert modules.venv_python() == str(their_venv / "bin" / "python")
def test_an_adopted_venv_is_never_synced(venv: Path, their_venv: Path):
"""The whole hazard: sync means "hold exactly this", so it would empty it."""
ok, output = modules.sync("numpy>=2")
assert ok is False
assert "not Fluksio's to install" in output
# Nothing was built beside it either.
assert not venv.exists()
def test_reconcile_leaves_an_adopted_venv_alone(
venv: Path, their_venv: Path, tmp_path: Path
):
store = FlowStore(tmp_path / "flows")
store.write_requirements("numpy>=2\n")
modules.reconcile(store)
assert not venv.exists()
assert modules.venv_python() == str(their_venv / "bin" / "python")
def test_a_venv_the_engine_already_owns_is_kept(venv: Path, their_venv: Path):
"""An upgrade must not take away packages somebody installed on purpose."""
(venv / "bin").mkdir(parents=True)
(venv / "bin" / "python").write_text("")
assert modules.adopted() is None
assert modules.venv_python() == str(venv / "bin" / "python")
def test_managed_is_what_a_container_asks_for(
venv: Path, their_venv: Path, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.setattr(modules.settings, "NODE_VENV", "managed")
assert modules.adopted() is None
def test_an_interpreter_can_be_named_outright(
venv: Path, their_venv: Path, monkeypatch: pytest.MonkeyPatch
):
monkeypatch.setattr(modules.settings, "NODE_VENV", "/opt/conda/bin/python")
assert modules.venv_python() == "/opt/conda/bin/python"
def test_info_describes_an_adopted_venv(venv: Path, their_venv: Path, tmp_path: Path):
store = FlowStore(tmp_path / "flows")
store.write_requirements("numpy>=2\n")
information = modules.info(store)
assert information.adopted is True
assert information.venv_path == str(their_venv)
assert information.python_version
# The manifest does not describe it, so it cannot be out of step with it.
assert information.applied is True