Skip to content

Commit 929a36a

Browse files
Add Windows-specific ResourceWarning filters with explanation
On Windows, process termination via TerminateProcess() is immediate and doesn't allow subprocess.Popen cleanup handlers to run. This causes ResourceWarnings when garbage collecting Popen objects from forcefully terminated processes. Added: - Windows-specific warning filters to the three process termination tests - Detailed comment explaining the Windows vs Unix termination differences - Documentation about where TerminateProcess() is called (via psutil/anyio) The warnings don't indicate process leaks - processes are properly terminated. They only indicate that Popen objects couldn't run cleanup code, which is expected behavior on Windows when forcefully terminating process trees.
1 parent 7c3e67c commit 929a36a

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

src/mcp/client/stdio/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ async def _terminate_process_tree(pid: int, timeout: float = 2.0) -> None:
258258
259259
This provides consistent behavior across platforms and properly
260260
handles process trees without shell commands.
261+
262+
Platform behavior:
263+
- On Unix: psutil.terminate() sends SIGTERM, allowing graceful shutdown
264+
- On Windows: psutil.terminate() calls TerminateProcess() which is immediate
265+
and doesn't allow cleanup handlers to run. This can cause ResourceWarnings
266+
for subprocess.Popen objects that don't get to clean up.
261267
"""
262268
try:
263269
parent = psutil.Process(pid)

tests/client/test_stdio.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,11 +365,24 @@ def sigterm_handler(signum, frame):
365365

366366

367367
@pytest.mark.anyio
368+
@pytest.mark.filterwarnings("ignore::ResourceWarning" if sys.platform == "win32" else "default")
368369
async def test_stdio_client_child_process_cleanup():
369370
"""
370371
Test that child processes are properly terminated when the parent is killed.
371372
This addresses the issue where processes like npx spawn child processes
372373
that need to be cleaned up.
374+
375+
Note on Windows ResourceWarning:
376+
On Windows, we may see ResourceWarning about subprocess still running. This is
377+
expected behavior due to how Windows process termination works:
378+
- anyio's process.terminate() calls Windows TerminateProcess() API
379+
- TerminateProcess() immediately kills the process without allowing cleanup
380+
- subprocess.Popen objects in the killed process can't run their cleanup code
381+
- Python detects this during garbage collection and issues a ResourceWarning
382+
383+
This warning does NOT indicate a process leak - the processes are properly
384+
terminated. It only means the Popen objects couldn't clean up gracefully.
385+
This is a fundamental difference between Windows and Unix process termination.
373386
"""
374387

375388
# Create a marker file for the child process to write to
@@ -468,6 +481,7 @@ async def test_stdio_client_child_process_cleanup():
468481

469482

470483
@pytest.mark.anyio
484+
@pytest.mark.filterwarnings("ignore::ResourceWarning" if sys.platform == "win32" else "default")
471485
async def test_stdio_client_nested_process_tree():
472486
"""
473487
Test that a nested process tree (parent → child → grandchild) is properly cleaned up.
@@ -574,6 +588,7 @@ async def test_stdio_client_nested_process_tree():
574588

575589

576590
@pytest.mark.anyio
591+
@pytest.mark.filterwarnings("ignore::ResourceWarning" if sys.platform == "win32" else "default")
577592
async def test_stdio_client_early_parent_exit():
578593
"""
579594
Test that child processes are cleaned up when parent exits during cleanup.

0 commit comments

Comments
 (0)