Kill a process and all its children. Args: pid: Root process ID to kill Returns: Number of processes killed
(self, pid: int)
| 143 | self._processes.clear() |
| 144 | |
| 145 | def _kill_process_tree(self, pid: int) -> int: |
| 146 | """Kill a process and all its children. |
| 147 | |
| 148 | Args: |
| 149 | pid: Root process ID to kill |
| 150 | |
| 151 | Returns: |
| 152 | Number of processes killed |
| 153 | """ |
| 154 | killed_count = 0 |
| 155 | |
| 156 | try: |
| 157 | parent = psutil.Process(pid) |
| 158 | except psutil.NoSuchProcess: |
| 159 | logger.debug(f"Process {pid} already terminated") |
| 160 | return 0 |
| 161 | except KeyboardInterrupt as ki: |
| 162 | handle_keyboard_interrupt(ki) |
| 163 | raise |
| 164 | |
| 165 | # Get all children recursively |
| 166 | try: |
| 167 | children = parent.children(recursive=True) |
| 168 | except psutil.NoSuchProcess: |
| 169 | children = [] |
| 170 | except KeyboardInterrupt as ki: |
| 171 | handle_keyboard_interrupt(ki) |
| 172 | raise |
| 173 | |
| 174 | # Build list of all processes to kill (children + parent) |
| 175 | processes_to_kill: list[psutil.Process] = [] |
| 176 | for child in reversed(children): # Kill children first |
| 177 | try: |
| 178 | processes_to_kill.append(child) |
| 179 | except KeyboardInterrupt as ki: |
| 180 | handle_keyboard_interrupt(ki) |
| 181 | raise |
| 182 | except Exception: |
| 183 | pass |
| 184 | |
| 185 | processes_to_kill.append(parent) |
| 186 | |
| 187 | # Terminate all processes |
| 188 | for proc in processes_to_kill: |
| 189 | try: |
| 190 | proc.terminate() |
| 191 | killed_count += 1 |
| 192 | logger.debug(f"Terminated process {proc.pid} ({proc.name()})") |
| 193 | except psutil.NoSuchProcess: |
| 194 | pass |
| 195 | except KeyboardInterrupt as ki: |
| 196 | handle_keyboard_interrupt(ki) |
| 197 | raise |
| 198 | except Exception as e: |
| 199 | logger.warning(f"Failed to terminate process {proc.pid}: {e}") |
| 200 | |
| 201 | # Wait for graceful termination |
| 202 | _gone, alive = psutil.wait_procs(processes_to_kill, timeout=3) |
no test coverage detected