Skip to content

Commit 323cce7

Browse files
Strontvodteknium1
authored andcommitted
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.
1 parent a3abeb5 commit 323cce7

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

hermes_cli/main.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7665,8 +7665,11 @@ def _detect_concurrent_hermes_instances(
76657665

76667666
This helper enumerates processes whose ``exe`` matches one of the venv's
76677667
shims (``hermes.exe`` / ``hermes-gateway.exe``) and returns ``(pid,
7668-
process_name)`` pairs. The caller's own PID is excluded so the running
7669-
``hermes update`` invocation never reports itself.
7668+
process_name)`` pairs. The caller's own PID and its entire ancestor
7669+
chain are excluded so the running ``hermes update`` invocation never
7670+
reports itself — this matters on Windows where the setuptools .exe
7671+
launcher (``hermes.exe``) is a separate process from the Python
7672+
interpreter it loads (``python.exe``).
76707673

76717674
Returns an empty list off-Windows, on missing psutil, or when no other
76727675
instances exist. Never raises — process enumeration is best-effort.
@@ -7679,8 +7682,32 @@ def _detect_concurrent_hermes_instances(
76797682
except Exception:
76807683
return []
76817684

7682-
if exclude_pid is None:
7683-
exclude_pid = os.getpid()
7685+
# Build a set of PIDs to exclude: the Python process itself plus its
7686+
# entire parent chain. On Windows the setuptools-generated hermes.exe
7687+
# launcher is a separate native process that spawns python.exe (the
7688+
# interpreter that runs our code). os.getpid() returns the Python PID,
7689+
# but the launcher (which holds the file lock) is the parent. Without
7690+
# walking the parent chain, every ``hermes update`` reports its own
7691+
# launcher as a concurrent instance — a false positive.
7692+
if exclude_pid is not None:
7693+
exclude_pids: set[int] = {exclude_pid}
7694+
else:
7695+
exclude_pids = {os.getpid()}
7696+
try:
7697+
current = psutil.Process(next(iter(exclude_pids)))
7698+
while True:
7699+
try:
7700+
parent = current.parent()
7701+
except (psutil.NoSuchProcess, psutil.AccessDenied):
7702+
break
7703+
if parent is None or parent.pid <= 0:
7704+
break
7705+
if parent.pid in exclude_pids:
7706+
break # loop detected
7707+
exclude_pids.add(parent.pid)
7708+
current = parent
7709+
except (psutil.NoSuchProcess, psutil.AccessDenied, ValueError):
7710+
pass
76847711

76857712
# Resolve every shim path to its canonical form once for cheap comparison.
76867713
shim_paths: set[str] = set()
@@ -7705,7 +7732,7 @@ def _detect_concurrent_hermes_instances(
77057732
continue
77067733
pid = info.get("pid")
77077734
exe = info.get("exe")
7708-
if not exe or pid is None or pid == exclude_pid:
7735+
if not exe or pid is None or pid in exclude_pids:
77097736
continue
77107737
try:
77117738
exe_norm = str(Path(exe).resolve()).lower()

0 commit comments

Comments
 (0)