Decouple job lifecycle from screen workers

Jobs were tied to screen @work tasks, so switch_screen cancelled the
worker and lost the process reference. Now start_job uses
asyncio.create_task so jobs survive screen changes and can be killed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-06 18:18:39 +02:00
parent 6a0389f437
commit 0110b00b67
3 changed files with 9 additions and 11 deletions

View File

@@ -54,6 +54,9 @@ class JobManager:
def remove_finished(self) -> None:
self._jobs = {k: v for k, v in self._jobs.items() if v.status == "running"}
def start_job(self, app, job: Job, *cli_args: str) -> None:
asyncio.create_task(self.run_job(app, job, *cli_args))
async def run_job(self, app, job: Job, *cli_args: str) -> int:
proc = await start_cli_process(*cli_args)
job._proc = proc

View File

@@ -2,8 +2,6 @@ from textual.app import ComposeResult
from textual.screen import Screen
from textual.widgets import Header, Footer, Static, Button, Select
from textual.containers import Vertical, Horizontal
from textual import work
from tui.config import list_conf_dir, has_targets, has_remotes
from tui.jobs import job_manager
from tui.widgets import ConfirmDialog
@@ -65,20 +63,18 @@ class BackupScreen(Screen):
callback=lambda ok: self._do_backup_all() if ok else None,
)
@work
async def _do_backup(self, target: str, remote: str) -> None:
def _do_backup(self, target: str, remote: str) -> None:
job = job_manager.create_job("backup", f"Backup: {target}")
args = ["backup", f"--target={target}"]
if remote:
args.append(f"--remote={remote}")
job_manager.start_job(self.app, job, *args)
self.app.switch_screen("running_tasks")
await job_manager.run_job(self.app, job, *args)
@work
async def _do_backup_all(self) -> None:
def _do_backup_all(self) -> None:
job = job_manager.create_job("backup", "Backup All Targets")
job_manager.start_job(self.app, job, "backup", "--all")
self.app.switch_screen("running_tasks")
await job_manager.run_job(self.app, job, "backup", "--all")
def action_go_back(self) -> None:
self.app.pop_screen()

View File

@@ -143,16 +143,15 @@ class RestoreScreen(Screen):
callback=lambda ok: self._do_restore(target, remote, snapshot, dest, skip_mysql) if ok else None,
)
@work
async def _do_restore(self, target: str, remote: str, snapshot: str, dest: str, skip_mysql: bool = False) -> None:
def _do_restore(self, target: str, remote: str, snapshot: str, dest: str, skip_mysql: bool = False) -> None:
job = job_manager.create_job("restore", f"Restore: {target}")
args = ["restore", f"--target={target}", f"--remote={remote}", f"--snapshot={snapshot}"]
if dest:
args.append(f"--dest={dest}")
if skip_mysql:
args.append("--skip-mysql")
job_manager.start_job(self.app, job, *args)
self.app.switch_screen("running_tasks")
await job_manager.run_job(self.app, job, *args)
def action_go_back(self) -> None:
self.app.pop_screen()