youtube-summarizer/venv311/lib/python3.11/site-packages/mcp/os/posix/utilities.py

61 lines
1.9 KiB
Python

"""
POSIX-specific functionality for stdio client operations.
"""
import logging
import os
import signal
import anyio
from anyio.abc import Process
logger = logging.getLogger(__name__)
async def terminate_posix_process_tree(process: Process, timeout_seconds: float = 2.0) -> None:
"""
Terminate a process and all its children on POSIX systems.
Uses os.killpg() for atomic process group termination.
Args:
process: The process to terminate
timeout_seconds: Timeout in seconds before force killing (default: 2.0)
"""
pid = getattr(process, "pid", None) or getattr(getattr(process, "popen", None), "pid", None)
if not pid:
# No PID means there's no process to terminate - it either never started,
# already exited, or we have an invalid process object
return
try:
pgid = os.getpgid(pid)
os.killpg(pgid, signal.SIGTERM)
with anyio.move_on_after(timeout_seconds):
while True:
try:
# Check if process group still exists (signal 0 = check only)
os.killpg(pgid, 0)
await anyio.sleep(0.1)
except ProcessLookupError:
return
try:
os.killpg(pgid, signal.SIGKILL)
except ProcessLookupError:
pass
except (ProcessLookupError, PermissionError, OSError) as e:
logger.warning(f"Process group termination failed for PID {pid}: {e}, falling back to simple terminate")
try:
process.terminate()
with anyio.fail_after(timeout_seconds):
await process.wait()
except Exception:
logger.warning(f"Process termination failed for PID {pid}, attempting force kill")
try:
process.kill()
except Exception:
logger.exception(f"Failed to kill process {pid}")