fix(serve): only "no such process" from ps means the Desktop parent is gone
The stderr sniff also matched "not found", which BSD ps prints for an
unsupported column ("ps: lstart: keyword not found", rc=1, verified on
macOS). That would have classified a healthy parent as dead and os._exit'd
the backend — the fail-unsafe direction the watchdog must never take. Keep
the explicit missing-process message only; the macos_only test now pins the
"keyword not found" case as a plain OSError. The standalone
ProcessLookupError test (already green on main) folds into the degrade test.
This commit is contained in:
@@ -75,10 +75,10 @@ def _process_start_marker(pid: int) -> str:
|
||||
marker = result.stdout.strip()
|
||||
if result.returncode == 0 and marker:
|
||||
return f"ps:{marker}"
|
||||
stderr_lower = result.stderr.lower()
|
||||
if (result.returncode == 1 and not marker) or any(
|
||||
msg in stderr_lower for msg in ("no such process", "not found")
|
||||
):
|
||||
# rc==1 with empty output is the portable "no such pid"; "No such process" widens that for ps
|
||||
# builds that say so explicitly. Any other failure stays an OSError so the watchdog degrades to
|
||||
# pid liveness instead of exiting on a healthy backend.
|
||||
if (result.returncode == 1 and not marker) or "no such process" in result.stderr.lower():
|
||||
raise ProcessLookupError(pid)
|
||||
raise OSError(f"ps could not inspect PID {pid}: {result.stderr.strip()}")
|
||||
|
||||
|
||||
@@ -191,22 +191,19 @@ def test_parent_watchdog_degrades_to_pid_liveness_when_marker_probe_raises_oserr
|
||||
assert _is_serve_orphaned(4242, marker, pid_exists=lambda _pid: True,
|
||||
process_start_marker=broken_marker_probe) is False
|
||||
|
||||
|
||||
def test_parent_watchdog_detects_dead_parent_on_process_lookup_error():
|
||||
"""#80204: ``ProcessLookupError`` from the marker probe means the parent is gone, whatever
|
||||
a (possibly recycled) pid liveness check says."""
|
||||
def lookup_error_probe(pid: int) -> str:
|
||||
raise ProcessLookupError(pid)
|
||||
|
||||
assert _is_serve_orphaned(4242, "ps:Thu Aug 20 22:33:11 2026", pid_exists=lambda _pid: True,
|
||||
# ProcessLookupError is conclusive on its own, whatever a recycled-pid liveness check says.
|
||||
assert _is_serve_orphaned(4242, marker, pid_exists=lambda _pid: True,
|
||||
process_start_marker=lookup_error_probe) is True
|
||||
|
||||
|
||||
@pytest.mark.macos_only
|
||||
def test_ps_marker_probe_classifies_missing_process_vs_other_ps_failures(monkeypatch):
|
||||
"""The darwin ``ps`` probe raises ``ProcessLookupError`` only for a missing-process message;
|
||||
any other non-zero exit stays a plain ``OSError`` so the watchdog degrades instead of killing
|
||||
a healthy backend."""
|
||||
"""The darwin ``ps`` probe raises ``ProcessLookupError`` only for an explicit missing-process
|
||||
message; any other unknown failure stays a plain ``OSError`` so the watchdog degrades instead
|
||||
of killing a healthy backend."""
|
||||
import subprocess
|
||||
|
||||
from hermes_cli import web_server_lifecycle
|
||||
|
||||
Reference in New Issue
Block a user