A command in a child process, with a live log and a gentle cancel.
The child starts in its own session and process group with PYTHONUNBUFFERED=1, so its lines arrive as they are written. Two pump threads read its output into a queue, and the interface drains the queue from its own thread with poll. Every line is also appended to the log file.
When the child has ended, whatever is left in its process group is swept: SIGTERM, then SIGKILL after terminate_after seconds. A grandchild that holds the pipes open therefore cannot keep the job from ending, and JobExit always arrives.
cancel sends SIGINT to the whole process group, SIGTERM after interrupt_after seconds and SIGKILL after terminate_after more. SIGINT lets TemporaryDirectory and git worktree clean-ups in the child run; a plain terminate() would skip every finally block and leave temporary copies, pytest processes and stale worktrees behind.
import sysfrom spotregen.gui.jobs import JobExit, SubprocessJobjob = SubprocessJob([sys.executable, "-c", "print('one'); print('two')"], cwd=".")job.start()print(job.wait())print([e.text ifnotisinstance(e, JobExit) else e for e in job.poll()])