mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-29 06:31:32 +00:00
fix: exclude parent process chain from concurrent instance detection on Windows
On Windows, the setuptools-generated hermes.exe launcher is a separate native process that spawns python.exe (the interpreter running the update code). os.getpid() returns the Python PID, but the launcher (which holds the file lock) is the parent. Without walking the parent chain, every 'hermes update' reports its own launcher as a concurrent instance - a false positive. This patch builds an exclusion set containing the Python process and its entire ancestor chain, so the running invocation never reports itself.
This commit is contained in:
parent
a3abeb5954
commit
323cce7e94
1 changed files with 32 additions and 5 deletions
|
|
@ -7665,8 +7665,11 @@ def _detect_concurrent_hermes_instances(
|
|||
|
||||
This helper enumerates processes whose ``exe`` matches one of the venv's
|
||||
shims (``hermes.exe`` / ``hermes-gateway.exe``) and returns ``(pid,
|
||||
process_name)`` pairs. The caller's own PID is excluded so the running
|
||||
``hermes update`` invocation never reports itself.
|
||||
process_name)`` pairs. The caller's own PID and its entire ancestor
|
||||
chain are excluded so the running ``hermes update`` invocation never
|
||||
reports itself — this matters on Windows where the setuptools .exe
|
||||
launcher (``hermes.exe``) is a separate process from the Python
|
||||
interpreter it loads (``python.exe``).
|
||||
|
||||
Returns an empty list off-Windows, on missing psutil, or when no other
|
||||
instances exist. Never raises — process enumeration is best-effort.
|
||||
|
|
@ -7679,8 +7682,32 @@ def _detect_concurrent_hermes_instances(
|
|||
except Exception:
|
||||
return []
|
||||
|
||||
if exclude_pid is None:
|
||||
exclude_pid = os.getpid()
|
||||
# Build a set of PIDs to exclude: the Python process itself plus its
|
||||
# entire parent chain. On Windows the setuptools-generated hermes.exe
|
||||
# launcher is a separate native process that spawns python.exe (the
|
||||
# interpreter that runs our code). os.getpid() returns the Python PID,
|
||||
# but the launcher (which holds the file lock) is the parent. Without
|
||||
# walking the parent chain, every ``hermes update`` reports its own
|
||||
# launcher as a concurrent instance — a false positive.
|
||||
if exclude_pid is not None:
|
||||
exclude_pids: set[int] = {exclude_pid}
|
||||
else:
|
||||
exclude_pids = {os.getpid()}
|
||||
try:
|
||||
current = psutil.Process(next(iter(exclude_pids)))
|
||||
while True:
|
||||
try:
|
||||
parent = current.parent()
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied):
|
||||
break
|
||||
if parent is None or parent.pid <= 0:
|
||||
break
|
||||
if parent.pid in exclude_pids:
|
||||
break # loop detected
|
||||
exclude_pids.add(parent.pid)
|
||||
current = parent
|
||||
except (psutil.NoSuchProcess, psutil.AccessDenied, ValueError):
|
||||
pass
|
||||
|
||||
# Resolve every shim path to its canonical form once for cheap comparison.
|
||||
shim_paths: set[str] = set()
|
||||
|
|
@ -7705,7 +7732,7 @@ def _detect_concurrent_hermes_instances(
|
|||
continue
|
||||
pid = info.get("pid")
|
||||
exe = info.get("exe")
|
||||
if not exe or pid is None or pid == exclude_pid:
|
||||
if not exe or pid is None or pid in exclude_pids:
|
||||
continue
|
||||
try:
|
||||
exe_norm = str(Path(exe).resolve()).lower()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue