Skip to content

Commit 3392a3b

Browse files
Fosowlclaude
andcommitted
fix(runner): allow WorkflowRunner to use an explicit interpreter
The per-claim verifier runs verifier scripts under the interpreter running Mimosa (sys.executable), where the verifier helper packages were installed. It did so by overriding runner._python_cmd AFTER constructing WorkflowRunner. But WorkflowRunner.__init__ validates that a pythonX.Y matching RuntimeConfig.python_version (default "3.12") exists on PATH and raises *before* that override runs. On any host without python3.12 on PATH (e.g. a uv-managed 3.12 that isn't symlinked, with Mimosa itself on 3.13), evaluation crashed with "Python 3.12 not available" — aborting the whole run after claim extraction and verifier-spec generation had already completed. Add RuntimeConfig.python_executable: when set, it short-circuits the PATH/version resolver (after a --version smoke check), so the construction check uses the chosen interpreter too. The verifier now passes python_executable=sys.executable via the config instead of mutating _python_cmd post-construction. PATH/version resolution is unchanged when python_executable is unset. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 01e640f commit 3392a3b

2 files changed

Lines changed: 46 additions & 7 deletions

File tree

sources/core/evaluators/verifier_per_claim.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -648,17 +648,21 @@ def _run_verifier(self, uuid: str, claim_id: str, code: str) -> dict[str, Any]:
648648
"""
649649
scratch = self._runner_temp_root / uuid
650650
scratch.mkdir(parents=True, exist_ok=True)
651+
# Run verifiers under the exact interpreter running Mimosa: that is
652+
# where the verifier helper packages were installed, and it avoids
653+
# depending on a system pythonX.Y being on PATH. Passing this via the
654+
# config (rather than overriding runner._python_cmd after construction)
655+
# ensures WorkflowRunner's construction-time availability check uses
656+
# this interpreter too, instead of failing when no matching
657+
# python_version is found on PATH.
651658
runner_config = RuntimeConfig(
659+
python_executable=sys.executable,
652660
timeout=self.verifier_timeout,
653661
temp_dir=scratch,
654662
requirements_file=None,
655663
use_pty=False,
656664
)
657665
runner = WorkflowRunner(runner_config, execution_dir=str(self.workspace_dir))
658-
# Use the Python that's running Mimosa, not the system python3.12 the
659-
# runner's resolver picks: that interpreter is where the verifier
660-
# helper packages were installed.
661-
runner._python_cmd = [sys.executable]
662666
execution_id = f"verify_{claim_id}"
663667
thread_timeout = self.verifier_timeout + 10
664668
result = None

sources/core/workflow_runner.py

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,14 @@ class RuntimeConfig:
5353
"""Configuration controlling how the workflow runner spawns Python.
5454
5555
Attributes:
56-
python_version: Target Python version string (e.g. ``"3.12"``).
56+
python_version: Target Python version string (e.g. ``"3.12"``). Used
57+
to resolve an interpreter from ``PATH`` when ``python_executable``
58+
is not set.
59+
python_executable: Explicit path to a Python interpreter (e.g.
60+
``sys.executable``). When provided it overrides ``python_version``
61+
entirely: the runner uses this exact interpreter and skips the
62+
``PATH``-based version resolution, so it never depends on a
63+
matching ``pythonX.Y`` being installed on ``PATH``.
5764
timeout: Maximum execution time per command, in seconds.
5865
max_memory_mb: Soft memory cap, in megabytes (advisory).
5966
max_cpu_percent: Soft CPU cap, as a percentage (advisory).
@@ -68,6 +75,8 @@ class RuntimeConfig:
6875
"""
6976

7077
python_version: str = "3.12"
78+
# Explicit interpreter path; when set, overrides python_version resolution.
79+
python_executable: str | None = None
7180
timeout: int = 1800
7281
max_memory_mb: int = 1024
7382
max_cpu_percent: int = 100
@@ -120,14 +129,25 @@ def _setup_environment(self) -> None:
120129
# Convert temp_dir to absolute path to ensure it's created in the right location
121130
self.config.temp_dir = os.path.abspath(self.config.temp_dir)
122131
os.makedirs(self.config.temp_dir, exist_ok=True)
123-
# Validate python version availability and resolve the executable
132+
# Validate python availability and resolve the executable
124133
if not self._check_python_version():
125-
raise RuntimeError(f"Python {self.config.python_version} not available")
134+
target = (
135+
self.config.python_executable
136+
or f"Python {self.config.python_version}"
137+
)
138+
raise RuntimeError(f"{target} not available")
126139

127140
def _resolve_python_executable(self) -> list[str] | None:
128141
"""
129142
Find a working Python executable for the configured version.
130143
144+
When ``config.python_executable`` is set, it short-circuits this
145+
resolution: the caller has deliberately chosen an interpreter (e.g.
146+
``sys.executable``, where verifier helper packages were installed), so
147+
it is used directly after a ``--version`` smoke check, bypassing the
148+
``PATH``/version search entirely. This is what lets verifiers run on a
149+
host that has no ``pythonX.Y`` matching ``python_version`` on ``PATH``.
150+
131151
Versioned candidates are always tried first and accepted as-is because
132152
they target the exact version by construction:
133153
- Unix/macOS: ``python3.10`` (versioned binary)
@@ -144,6 +164,21 @@ def _resolve_python_executable(self) -> list[str] | None:
144164
import subprocess
145165
import sys
146166

167+
# An explicit interpreter path overrides version-based PATH resolution.
168+
explicit = self.config.python_executable
169+
if explicit:
170+
try:
171+
result = subprocess.run(
172+
[explicit, "--version"],
173+
capture_output=True,
174+
timeout=10,
175+
)
176+
if result.returncode == 0:
177+
return [explicit]
178+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
179+
pass
180+
return None
181+
147182
version = self.config.python_version # e.g. "3.10"
148183

149184
if sys.platform == "win32":

0 commit comments

Comments
 (0)