Kill a process and all its children
(pid: int)
| 70 | |
| 71 | |
| 72 | def kill_process_tree(pid: int) -> None: |
| 73 | """Kill a process and all its children""" |
| 74 | import psutil # noqa: PLC0415 - lazy: deferred to cleanup-only path |
| 75 | |
| 76 | try: |
| 77 | parent = psutil.Process(pid) |
| 78 | children = parent.children(recursive=True) |
| 79 | |
| 80 | # First try graceful termination |
| 81 | for child in children: |
| 82 | try: |
| 83 | child.terminate() |
| 84 | except psutil.NoSuchProcess: |
| 85 | pass |
| 86 | |
| 87 | # Give them a moment to terminate |
| 88 | _, alive = psutil.wait_procs(children, timeout=3) |
| 89 | |
| 90 | # Force kill any that are still alive |
| 91 | for child in alive: |
| 92 | try: |
| 93 | child.kill() |
| 94 | except psutil.NoSuchProcess: |
| 95 | pass |
| 96 | |
| 97 | # Finally terminate the parent |
| 98 | try: |
| 99 | parent.terminate() |
| 100 | parent.wait(3) # Give it 3 seconds to terminate |
| 101 | except (psutil.NoSuchProcess, psutil.TimeoutExpired): |
| 102 | try: |
| 103 | parent.kill() # Force kill if still alive |
| 104 | except psutil.NoSuchProcess: |
| 105 | pass |
| 106 | except psutil.NoSuchProcess: |
| 107 | pass # Process already gone |
| 108 | |
| 109 | |
| 110 | def dump_thread_stacks() -> None: |
no test coverage detected