Skip to content

Commit 5b87adb

Browse files
committed
core: fix os.kill(pid, 0) sending CTRL_C on Windows
Root cause: on Windows, signal 0 = CTRL_C_EVENT, so os.kill(pid, 0) sends Ctrl+C to the process group instead of checking existence. test_pid_exists_true called os.kill(os.getpid(), 0) which delivered an asynchronous KeyboardInterrupt during pytest cleanup, causing exit code 1 despite all 436 tests passing. Fix: replace os.kill(pid, 0) with psutil.pid_exists(pid) in AutorunDaemon._pid_exists() for cross-platform correctness. Reverts: timeout_method="thread", -p no:timeout CI flag, and KeyboardInterrupt guard in conftest — all were treating symptoms.
1 parent 1790741 commit 5b87adb

4 files changed

Lines changed: 10 additions & 27 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,12 @@ jobs:
7777
# Safe subset: excludes tests requiring real tmux sessions, running daemon,
7878
# subprocess spawning, external APIs, or >5s wall-clock time.
7979
# Coverage threshold (fail_under=60) omitted -- calibrated for full suite.
80-
# --timeout=0 on Windows: pytest-timeout thread method sends spurious
81-
# KeyboardInterrupt during session teardown on Windows (all tests pass
82-
# but exit code is 1). Disable timeout on Windows CI as a workaround.
8380
working-directory: plugins/autorun
8481
run: >-
8582
uv run pytest tests/
8683
-m "not tmux and not daemon and not subprocess and not e2e and not interactive and not stress and not race and not slow"
8784
--tb=short -v
8885
--junitxml=${{ github.workspace }}/test_results_${{ matrix.os }}_py${{ matrix.python-version }}.xml
89-
${{ runner.os == 'Windows' && '-p no:timeout' || '' }}
9086
9187
- name: Publish test results
9288
# dorny/test-reporter: 1.1k stars, updated 2026, pure Node.js (no Docker).

plugins/autorun/pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,6 @@ python_files = ["test_*.py"]
9999
python_classes = ["Test*"]
100100
python_functions = ["test_*"]
101101
timeout = 30
102-
timeout_method = "thread"
103102
markers = [
104103
"unit: marks tests as unit tests",
105104
"integration: marks tests as integration tests",

plugins/autorun/src/autorun/core.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1199,12 +1199,14 @@ def __init__(self, app: AutorunApp):
11991199
self._cleanup_registered = False
12001200

12011201
def _pid_exists(self, pid: int) -> bool:
1202-
"""Check if process with given PID exists."""
1203-
try:
1204-
os.kill(pid, 0)
1205-
return True
1206-
except OSError:
1207-
return False
1202+
"""Check if process with given PID exists.
1203+
1204+
Uses psutil for cross-platform correctness. On Windows, os.kill(pid, 0)
1205+
sends CTRL_C_EVENT (signal 0 = CTRL_C_EVENT) instead of checking
1206+
existence, which causes a spurious KeyboardInterrupt.
1207+
"""
1208+
import psutil
1209+
return psutil.pid_exists(pid)
12081210

12091211
async def handle_client(self, reader: asyncio.StreamReader,
12101212
writer: asyncio.StreamWriter):

plugins/autorun/tests/conftest.py

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@ def pytest_configure(config):
4242
config.addinivalue_line("markers", "e2e: marks end-to-end tests")
4343
config.addinivalue_line("markers", "serial: marks tests that must run serially")
4444

45-
# Disable pytest-timeout on Windows: thread-based timeout sends spurious
46-
# KeyboardInterrupt during session teardown, causing exit code 1 even
47-
# though all tests pass. This overrides pyproject.toml timeout=30.
48-
if sys.platform == "win32":
49-
try:
50-
config.option.timeout = 0
51-
except AttributeError:
52-
pass
5345

5446

5547
# Test file groups for automatic serial/parallel assignment
@@ -386,14 +378,8 @@ def pytest_sessionstart(session):
386378

387379
def pytest_sessionfinish(session, exitstatus):
388380
"""Clean up test sessions and test-spawned daemons after pytest finishes."""
389-
try:
390-
cleanup_test_sessions()
391-
DaemonManager.cleanup()
392-
except KeyboardInterrupt:
393-
# On Windows, pytest-timeout thread method can send spurious
394-
# KeyboardInterrupt during session teardown. Suppress it since
395-
# cleanup is best-effort.
396-
pass
381+
cleanup_test_sessions()
382+
DaemonManager.cleanup()
397383

398384

399385
@pytest.fixture(scope="session")

0 commit comments

Comments
 (0)