Skip to content
Open
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
26 changes: 26 additions & 0 deletions hermes_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9041,6 +9041,30 @@ def _detect_venv_python_processes(
except OSError:
root_prefix = str(PROJECT_ROOT).lower().rstrip(os.sep) + os.sep

def _is_windows_system_process(name: object, exe: object) -> bool:
name_low = str(name or "").lower()
exe_raw = str(exe or "").lower().replace("\\", "/")
system_names = {
"registry",
"memcompression",
"memory compression",
"system",
"idle",
}
if name_low in system_names:
return True
if (
"/windows/system32/" in exe_raw
or exe_raw.startswith("c:/windows/")
or exe_raw.startswith("c:/winnt/")
):
executable = Path(exe_raw).name
return not (
executable.startswith("python")
or executable in {"py.exe", "hermes.exe", "hermes-gateway.exe"}
)
return False

skip: set[int] = set(exclude_pids or set())
skip.add(os.getpid())
try:
Expand All @@ -9063,6 +9087,8 @@ def _detect_venv_python_processes(
exe = info.get("exe")
if not exe or pid is None or int(pid) in skip:
continue
if _is_windows_system_process(info.get("name"), exe):
continue
try:
exe_norm = str(Path(exe).resolve()).lower()
except (OSError, ValueError):
Expand Down
49 changes: 49 additions & 0 deletions tests/hermes_cli/test_update_venv_health.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,55 @@ def test_detect_venv_python_catches_outside_venv_trampoline(_winp, tmp_path):
assert sorted(m[0] for m in matches) == [201, 202]


@patch.object(cli_main, "_is_windows", return_value=True)
def test_detect_venv_python_ignores_windows_system_processes(_winp, tmp_path):
"""Windows pseudo/system processes may expose noisy cwd/cmdline metadata.

The venv-holder guard should only block update on user-space Python/Hermes
processes, not Registry/Memory Compression style system processes.
"""
system_exe = "C:\\Windows\\System32\\svchost.exe"
root = str(tmp_path)
venv_py = str(tmp_path / "venv" / "Scripts" / "python.exe")

me = MagicMock()
me.parents.return_value = []
fake_psutil = types.SimpleNamespace(
process_iter=lambda attrs: iter(
[
_proc(
401,
system_exe,
"Registry",
[system_exe, venv_py],
cwd=root,
),
_proc(
402,
system_exe,
"MemCompression",
[system_exe, "-m", "hermes_cli.main", "serve"],
cwd=root,
),
_proc(
403,
"C:\\Python311\\python.exe",
"python.exe",
["python.exe", "-m", "hermes_cli.main", "serve"],
cwd=root,
),
]
),
Process=lambda *a, **k: me,
)
with patch.object(cli_main, "PROJECT_ROOT", tmp_path), patch.dict(
sys.modules, {"psutil": fake_psutil}
):
matches = cli_main._detect_venv_python_processes()

assert [m[0] for m in matches] == [403]


@patch.object(cli_main, "_is_windows", return_value=True)
def test_detect_venv_hermes_cli_cmdline_outside_install_not_matched(_winp, tmp_path):
"""A hermes_cli.main process belonging to a DIFFERENT install (neither
Expand Down
Loading