fix(langgraph): prefer executable in venv over path (#341)
This commit is contained in:
@@ -306,14 +306,19 @@ def is_async_subagents_available() -> bool:
|
||||
|
||||
def _langgraph_exe() -> str | None:
|
||||
"""Return the path to the langgraph CLI binary, or None if not found."""
|
||||
import sys
|
||||
|
||||
executable_dir = os.path.dirname(sys.executable)
|
||||
candidate_names = (
|
||||
["langgraph.exe", "langgraph"] if os.name == "nt" else ["langgraph"]
|
||||
)
|
||||
for candidate_name in candidate_names:
|
||||
candidate = os.path.join(executable_dir, candidate_name)
|
||||
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
||||
return candidate
|
||||
found = shutil.which("langgraph")
|
||||
if found:
|
||||
return found
|
||||
import sys as _sys
|
||||
|
||||
candidate = os.path.join(os.path.dirname(_sys.executable), "langgraph")
|
||||
if os.path.isfile(candidate) and os.access(candidate, os.X_OK):
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
@@ -709,6 +714,7 @@ def start_langgraph_dev(
|
||||
sub_env["EVOSCIENTIST_DEPLOY_MODE"] = "full" if deploy_mode else "stripped"
|
||||
|
||||
try:
|
||||
logger.info("Starting langgraph dev with CLI: %s", exe)
|
||||
proc = subprocess.Popen(
|
||||
[
|
||||
exe,
|
||||
|
||||
@@ -8,6 +8,7 @@ to be available.
|
||||
from __future__ import annotations
|
||||
|
||||
import dataclasses
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
@@ -32,6 +33,77 @@ def reset_module_state():
|
||||
manager._LOG_OFFSET_AT_START = 0
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# langgraph CLI resolution
|
||||
# =============================================================================
|
||||
|
||||
|
||||
class TestLanggraphCliResolution:
|
||||
def _make_executable(self, path):
|
||||
path.write_text("#!/bin/sh\n", encoding="utf-8")
|
||||
path.chmod(0o755)
|
||||
|
||||
def test_prefers_current_python_environment_over_path(self, tmp_path, monkeypatch):
|
||||
local_bin = tmp_path / "local" / "bin"
|
||||
local_bin.mkdir(parents=True)
|
||||
local_langgraph = local_bin / "langgraph"
|
||||
self._make_executable(local_langgraph)
|
||||
|
||||
path_bin = tmp_path / "path" / "bin"
|
||||
path_bin.mkdir(parents=True)
|
||||
path_langgraph = path_bin / "langgraph"
|
||||
self._make_executable(path_langgraph)
|
||||
|
||||
monkeypatch.setattr(sys, "executable", str(local_bin / "python"))
|
||||
monkeypatch.setattr(
|
||||
manager.shutil,
|
||||
"which",
|
||||
lambda command: str(path_langgraph) if command == "langgraph" else None,
|
||||
)
|
||||
|
||||
assert manager._langgraph_exe() == str(local_langgraph)
|
||||
|
||||
def test_falls_back_to_path_when_environment_binary_missing(
|
||||
self, tmp_path, monkeypatch
|
||||
):
|
||||
path_bin = tmp_path / "path" / "bin"
|
||||
path_bin.mkdir(parents=True)
|
||||
path_langgraph = path_bin / "langgraph"
|
||||
self._make_executable(path_langgraph)
|
||||
|
||||
monkeypatch.setattr(
|
||||
sys, "executable", str(tmp_path / "local" / "bin" / "python")
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
manager.shutil,
|
||||
"which",
|
||||
lambda command: str(path_langgraph) if command == "langgraph" else None,
|
||||
)
|
||||
|
||||
assert manager._langgraph_exe() == str(path_langgraph)
|
||||
|
||||
def test_checks_windows_suffix_next_to_current_python(self, tmp_path, monkeypatch):
|
||||
scripts_dir = tmp_path / "Scripts"
|
||||
scripts_dir.mkdir()
|
||||
local_langgraph = scripts_dir / "langgraph.exe"
|
||||
self._make_executable(local_langgraph)
|
||||
|
||||
path_bin = tmp_path / "path" / "bin"
|
||||
path_bin.mkdir(parents=True)
|
||||
path_langgraph = path_bin / "langgraph.exe"
|
||||
self._make_executable(path_langgraph)
|
||||
|
||||
monkeypatch.setattr(sys, "executable", str(scripts_dir / "python.exe"))
|
||||
monkeypatch.setattr(manager.os, "name", "nt", raising=False)
|
||||
monkeypatch.setattr(
|
||||
manager.shutil,
|
||||
"which",
|
||||
lambda command: str(path_langgraph) if command == "langgraph" else None,
|
||||
)
|
||||
|
||||
assert manager._langgraph_exe() == str(local_langgraph)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# is_langgraph_dev_running
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user