| 31 | return [p for p in self._processes if not p.finished] |
| 32 | |
| 33 | def dump_active(self) -> None: |
| 34 | active: list[RunningProcess] = self.list_active() |
| 35 | if not active: |
| 36 | print("\nNO ACTIVE SUBPROCESSES DETECTED - MAIN PROCESS LIKELY HUNG") |
| 37 | return |
| 38 | |
| 39 | print("\nSTUCK SUBPROCESS COMMANDS:") |
| 40 | import time |
| 41 | |
| 42 | now = time.time() |
| 43 | for idx, p in enumerate(active, 1): |
| 44 | pid: Optional[int] = None |
| 45 | try: |
| 46 | if p.proc is not None: |
| 47 | pid = p.proc.pid |
| 48 | except KeyboardInterrupt as ki: |
| 49 | handle_keyboard_interrupt(ki) |
| 50 | raise |
| 51 | except Exception: |
| 52 | pid = None |
| 53 | |
| 54 | start = p.start_time |
| 55 | duration_str = f"{(now - start):.1f}s" if start is not None else "?" |
| 56 | has_output = bool(p.stdout) if p.finished or p.is_running() else False |
| 57 | since_out_str = "has-output" if has_output else "no-output" |
| 58 | |
| 59 | print( |
| 60 | f" {idx}. cmd={p.command} pid={pid} duration={duration_str} last_output={since_out_str}" |
| 61 | ) |
| 62 | |
| 63 | |
| 64 | # Global singleton instance for convenient access |