Skip to content
Merged
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
38 changes: 38 additions & 0 deletions hermes_cli/_subprocess_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
__all__ = [
"IS_WINDOWS",
"resolve_node_command",
"suppress_platform_ver_console",
"windows_detach_flags",
"windows_detach_flags_without_breakaway",
"windows_hide_flags",
Expand Down Expand Up @@ -226,6 +227,43 @@ def windows_hide_flags() -> int:
return _CREATE_NO_WINDOW


def suppress_platform_ver_console() -> None:
"""Stub out ``platform._syscmd_ver`` on Windows so it can never flash a
console window. No-op on non-Windows.

CPython's ``platform.win32_ver()`` — reached by ``platform.uname()``,
``platform.version()``, and ``platform.platform()`` — unconditionally
shells out ``cmd /c ver`` via ``subprocess.check_output(..., shell=True)``
with no ``CREATE_NO_WINDOW``. From a windowless parent (the pythonw
gateway and every kanban worker it spawns) that allocates a fresh
*visible* console: one flashing ``cmd`` window per process, triggered by
any dependency that merely touches ``platform.uname()`` at import time.

With ``_syscmd_ver`` stubbed to return its inputs, ``win32_ver()`` hits
the documented ``ValueError`` fallback and reads the version from
``sys.getwindowsversion().platform_version`` — same information, queried
in-process, no subprocess, no window. Verified equivalent on
CPython 3.11 (``platform()`` → ``Windows-10-10.0.xxxxx-SP0`` either way).

Call early, before heavyweight imports — the flash typically happens
during a dependency's import, not from Hermes' own code.
"""
if not IS_WINDOWS:
return
try:
import platform

if hasattr(platform, "_syscmd_ver"):
def _quiet_syscmd_ver(system="", release="", version="",
supported_platforms=("win32", "win16", "dos")):
return system, release, version

platform._syscmd_ver = _quiet_syscmd_ver
except Exception:
# Purely cosmetic hardening — never let it break startup.
pass


def windows_detach_popen_kwargs() -> dict:
"""Return a dict of Popen kwargs that detach a child on Windows and
fall back to the POSIX equivalent (``start_new_session=True``) on
Expand Down
9 changes: 9 additions & 0 deletions hermes_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@
except ModuleNotFoundError:
pass

# Windows: neutralize CPython's ``platform._syscmd_ver`` before anything else
# imports — it shells out ``cmd /c ver`` (shell=True, no CREATE_NO_WINDOW), so
# any dependency touching ``platform.uname()`` at import time flashes a
# visible console when this process is windowless (pythonw gateway + every
# kanban worker). No-op on POSIX; never raises.
from hermes_cli._subprocess_compat import suppress_platform_ver_console

suppress_platform_ver_console()

import os
import sys

Expand Down
151 changes: 151 additions & 0 deletions tests/test_windows_subprocess_no_window_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,3 +941,154 @@ def fake_run(cmd, **kwargs):
assert kwargs["creationflags"] == _CREATE_NO_WINDOW
assert kwargs["stdin"] == subprocess.DEVNULL
assert kwargs["capture_output"] is True


# ── #67690 env probes, lazy installs, platform.win32_ver() (@m4r13y) ───────
#
# Windowless processes (pythonw gateway + kanban workers) flashed consoles
# from three more spawn families: tools/env_probe._run's interpreter/pip
# probes, tools/lazy_deps' uv→pip→ensurepip install ladder, and CPython
# 3.11/3.12's platform.win32_ver() which shells out `cmd /c ver` with
# shell=True and no CREATE_NO_WINDOW. All are hide-only (creationflags);
# win32_ver is neutralized by stubbing platform._syscmd_ver so the
# documented ValueError fallback reads sys.getwindowsversion() instead.


def test_env_probe_run_hides_console_window(monkeypatch):
from tools import env_probe

captured = []

def fake_run(cmd, **kwargs):
captured.append((cmd, kwargs))
return _Completed(stdout="", returncode=0)

monkeypatch.setattr(env_probe, "windows_hide_flags", lambda: _CREATE_NO_WINDOW)
monkeypatch.setattr(env_probe.subprocess, "run", fake_run)

rc, out, err = env_probe._run(["python3", "--version"], timeout=1.0)

assert rc == 0
assert len(captured) == 1, captured
cmd, kwargs = captured[0]
assert cmd == ["python3", "--version"]
assert kwargs["creationflags"] == _CREATE_NO_WINDOW
# The temp-file capture contract (#67964) must survive: stdout/stderr are
# file objects (not PIPE) so a lingering grandchild can't wedge the probe.
assert kwargs["stdout"] is not None and kwargs["stdout"] != subprocess.PIPE
assert kwargs["stderr"] is not None and kwargs["stderr"] != subprocess.PIPE
assert kwargs["stdin"] == subprocess.DEVNULL


def test_lazy_deps_uv_install_hides_console_window(monkeypatch):
from tools import lazy_deps

captured = []

def fake_run(cmd, **kwargs):
captured.append((cmd, kwargs))
return _Completed(stdout="installed", returncode=0)

monkeypatch.delenv(lazy_deps._LAZY_TARGET_ENV, raising=False)
monkeypatch.setattr(lazy_deps, "windows_hide_flags", lambda: _CREATE_NO_WINDOW)
monkeypatch.setattr(lazy_deps.subprocess, "run", fake_run)
monkeypatch.setattr(lazy_deps.shutil, "which", lambda name: "/usr/bin/uv" if name == "uv" else None)

res = lazy_deps._venv_pip_install(("left-pad",))

assert res.success
spawns = _spawns(captured, "pip", "install", "left-pad")
assert len(spawns) == 1, captured
cmd, kwargs = spawns[0]
assert cmd[:3] == ["/usr/bin/uv", "pip", "install"]
assert kwargs["creationflags"] == _CREATE_NO_WINDOW
assert kwargs["stdin"] == subprocess.DEVNULL


def test_lazy_deps_pip_probe_and_install_hide_console_window(monkeypatch):
"""No uv: the pip --version probe and the pip install fallback both hide."""
from tools import lazy_deps

captured = []

def fake_run(cmd, **kwargs):
captured.append((cmd, kwargs))
return _Completed(stdout="pip 25.0", returncode=0)

monkeypatch.delenv(lazy_deps._LAZY_TARGET_ENV, raising=False)
monkeypatch.setattr(lazy_deps, "windows_hide_flags", lambda: _CREATE_NO_WINDOW)
monkeypatch.setattr(lazy_deps.subprocess, "run", fake_run)
monkeypatch.setattr(lazy_deps.shutil, "which", lambda name: None)

res = lazy_deps._venv_pip_install(("left-pad",))

assert res.success
probes = _spawns(captured, "-m", "pip", "--version")
installs = _spawns(captured, "-m", "pip", "install", "left-pad")
assert len(probes) == 1 and len(installs) == 1, captured
for _cmd, kwargs in probes + installs:
assert kwargs["creationflags"] == _CREATE_NO_WINDOW
assert kwargs["stdin"] == subprocess.DEVNULL


def test_lazy_deps_ensurepip_hides_console_window(monkeypatch):
"""Failed pip probe: the ensurepip bootstrap spawn hides too."""
from tools import lazy_deps

captured = []

def fake_run(cmd, **kwargs):
captured.append((cmd, kwargs))
if "--version" in cmd:
return _Completed(stdout="", returncode=1) # probe fails → ensurepip
return _Completed(stdout="ok", returncode=0)

monkeypatch.delenv(lazy_deps._LAZY_TARGET_ENV, raising=False)
monkeypatch.setattr(lazy_deps, "windows_hide_flags", lambda: _CREATE_NO_WINDOW)
monkeypatch.setattr(lazy_deps.subprocess, "run", fake_run)
monkeypatch.setattr(lazy_deps.shutil, "which", lambda name: None)

res = lazy_deps._venv_pip_install(("left-pad",))

assert res.success
bootstraps = _spawns(captured, "-m", "ensurepip", "--upgrade")
assert len(bootstraps) == 1, captured
assert bootstraps[0][1]["creationflags"] == _CREATE_NO_WINDOW


def test_suppress_platform_ver_console_posix_noop(monkeypatch):
"""On POSIX the helper must do nothing at all and never raise."""
import platform

from hermes_cli import _subprocess_compat

monkeypatch.setattr(_subprocess_compat, "IS_WINDOWS", False)
original = platform._syscmd_ver

_subprocess_compat.suppress_platform_ver_console()

assert platform._syscmd_ver is original
# win32_ver stays functional (returns empty fields off Windows).
assert platform.win32_ver() == ("", "", "", "")


def test_suppress_platform_ver_console_stubs_syscmd_ver(monkeypatch):
"""Simulated Windows: _syscmd_ver is replaced by an in-process echo stub
so win32_ver() takes its ValueError fallback instead of `cmd /c ver`."""
import platform

from hermes_cli import _subprocess_compat

monkeypatch.setattr(_subprocess_compat, "IS_WINDOWS", True)
# Register the original with monkeypatch so it gets restored after.
monkeypatch.setattr(platform, "_syscmd_ver", platform._syscmd_ver)

_subprocess_compat.suppress_platform_ver_console()

# The stub echoes its inputs — win32_ver() treats the unparseable value
# as the documented ValueError path and falls back to
# sys.getwindowsversion().platform_version (no subprocess, no window).
assert platform._syscmd_ver("s", "r", "v") == ("s", "r", "v")
# Idempotent + never raises on repeat calls.
_subprocess_compat.suppress_platform_ver_console()
assert platform._syscmd_ver() == ("", "", "")
7 changes: 7 additions & 0 deletions tools/env_probe.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@
import threading
from typing import Optional

from hermes_cli._subprocess_compat import windows_hide_flags

logger = logging.getLogger(__name__)

# Module-level cache. The probe result is deterministic for the
Expand Down Expand Up @@ -105,6 +107,11 @@ def _run(cmd: list[str], timeout: float = 3.0) -> tuple[int, str, str]:
timeout=timeout,
check=False,
stdin=subprocess.DEVNULL,
# CREATE_NO_WINDOW (0 on POSIX): the probe runs in
# windowless processes (pythonw gateway / kanban workers)
# where a console child would otherwise flash a visible
# window per probe — ~5 flashes at every worker startup.
creationflags=windows_hide_flags(),
)
except subprocess.TimeoutExpired:
return -1, "", "timeout"
Expand Down
6 changes: 6 additions & 0 deletions tools/lazy_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
from pathlib import Path
from typing import Any, Callable, Optional

from hermes_cli._subprocess_compat import windows_hide_flags

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -668,6 +670,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install
[uv_bin, "pip", "install", *target_args, *constraint_args, *specs],
capture_output=True, text=True, timeout=timeout, env=uv_env,
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
if r.returncode == 0:
if target is not None:
Expand All @@ -684,6 +687,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install
pip_cmd + ["--version"],
capture_output=True, text=True, timeout=15,
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
if probe.returncode != 0:
raise FileNotFoundError("pip not in venv")
Expand All @@ -693,6 +697,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install
[sys.executable, "-m", "ensurepip", "--upgrade", "--default-pip"],
capture_output=True, text=True, timeout=120, check=True,
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
except (subprocess.CalledProcessError, subprocess.TimeoutExpired) as e:
return _InstallResult(False, "",
Expand All @@ -703,6 +708,7 @@ def _venv_pip_install(specs: tuple[str, ...], *, timeout: int = 300) -> _Install
pip_cmd + ["install", *target_args, *constraint_args, *specs],
capture_output=True, text=True, timeout=timeout,
stdin=subprocess.DEVNULL,
creationflags=windows_hide_flags(),
)
if r.returncode == 0 and target is not None:
_activate_target_on_syspath(target)
Expand Down
Loading