From c157ecb161f540c3b4331ae2b26130f8f05f376d Mon Sep 17 00:00:00 2001 From: Qubasa Date: Tue, 2 Jan 2024 05:54:19 +0100 Subject: [PATCH] Added show_error_dialogue on exception --- pkgs/clan-vm-manager/clan_vm_manager/app.py | 12 ++++- .../clan_vm_manager/executor.py | 47 ++++++++++++++----- .../clan_vm_manager/ui/clan_select_list.py | 6 --- .../clan_vm_manager/windows/join.py | 1 - .../clan_vm_manager/windows/overview.py | 2 - 5 files changed, 46 insertions(+), 22 deletions(-) diff --git a/pkgs/clan-vm-manager/clan_vm_manager/app.py b/pkgs/clan-vm-manager/clan_vm_manager/app.py index fc070845c..39b97da37 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/app.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/app.py @@ -10,10 +10,13 @@ from clan_vm_manager.windows.flash import FlashUSBWindow gi.require_version("Gtk", "3.0") +import multiprocessing as mp + from clan_cli.clan_uri import ClanURI from gi.repository import Gio, Gtk from .constants import constants +from .errors.show_error import show_error_dialog from .executor import ProcessManager from .interfaces import Callbacks, InitialFlashValues, InitialJoinValues from .windows.join import JoinWindow @@ -33,6 +36,11 @@ class ClanConfig: url: ClanURI | None +# Will be executed in the context of the child process +def on_except(error: Exception, proc: mp.process.BaseProcess) -> None: + show_error_dialog(str(error)) + + class Application(Gtk.Application): def __init__(self, windows: ClanWindows, config: ClanConfig) -> None: super().__init__( @@ -80,17 +88,17 @@ class Application(Gtk.Application): self.proc_manager.spawn( ident=url, wait_stdin_con=False, + on_except=on_except, log_path=log_path, func=vms.run.run_vm, vm=vm, ) def stop_vm(self, url: str, attr: str) -> None: - print(f"stop_vm {url}") self.proc_manager.kill(url) def running_vms(self) -> list[str]: - return list(self.proc_manager.procs.keys()) + return self.proc_manager.running_procs() def show_list(self) -> None: prev = self.window diff --git a/pkgs/clan-vm-manager/clan_vm_manager/executor.py b/pkgs/clan-vm-manager/clan_vm_manager/executor.py index 61bac998f..b888d5d4c 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/executor.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/executor.py @@ -21,10 +21,6 @@ def _kill_group(proc: mp.Process) -> None: pid = proc.pid assert pid is not None if proc.is_alive(): - print( - f"Killing process group pid={pid}", - file=sys.stderr, - ) os.killpg(pid, signal.SIGTERM) else: print(f"Process {proc.name} with pid {pid} is already dead", file=sys.stderr) @@ -67,6 +63,7 @@ def _init_proc( in_file: Path, wait_stdin_connect: bool, proc_name: str, + on_except: Callable[[Exception, mp.process.BaseProcess], None], **kwargs: Any, ) -> None: # Create a new process group @@ -97,8 +94,9 @@ def _init_proc( print(f"Executing function {func.__name__} now", file=sys.stderr) try: func(**kwargs) - except Exception: + except Exception as ex: traceback.print_exc() + on_except(ex, mp.current_process()) finally: pid = os.getpid() gpid = os.getpgid(pid=pid) @@ -107,12 +105,16 @@ def _init_proc( def spawn( - *, wait_stdin_con: bool, log_path: Path, func: Callable, **kwargs: Any + *, + wait_stdin_con: bool, + log_path: Path, + on_except: Callable[[Exception, mp.process.BaseProcess], None], + func: Callable, + **kwargs: Any, ) -> MPProcess: # Decouple the process from the parent if mp.get_start_method(allow_none=True) is None: mp.set_start_method(method="forkserver") - print("Set mp start method to forkserver", file=sys.stderr) if not log_path.is_dir(): raise ClanError(f"Log path {log_path} is not a directory") @@ -132,7 +134,7 @@ def spawn( # Start the process proc = mp.Process( target=_init_proc, - args=(func, out_file, in_file, wait_stdin_con, proc_name), + args=(func, out_file, in_file, wait_stdin_con, proc_name, on_except), name=proc_name, kwargs=kwargs, ) @@ -140,8 +142,6 @@ def spawn( # Print some information assert proc.pid is not None - print(f"Started process '{proc_name}'") - print(f"Arguments: {kwargs}") if wait_stdin_con: cmd = f"cat - > {in_file}" @@ -166,17 +166,42 @@ class ProcessManager: self.procs: dict[str, MPProcess] = dict() self._finalizer = weakref.finalize(self, self.kill_all) + def by_pid(self, pid: int) -> tuple[str, MPProcess] | None: + for ident, proc in self.procs.items(): + if proc.proc.pid == pid: + return (ident, proc) + return None + + def by_proc(self, proc: mp.process.BaseProcess) -> tuple[str, MPProcess] | None: + if proc.pid is None: + return None + return self.by_pid(pid=proc.pid) + + def running_procs(self) -> list[str]: + res = [] + for ident, proc in self.procs.copy().items(): + if proc.proc.is_alive(): + res.append(ident) + else: + del self.procs[ident] + return res + def spawn( self, *, ident: str, wait_stdin_con: bool, log_path: Path, + on_except: Callable[[Exception, mp.process.BaseProcess], None], func: Callable, **kwargs: Any, ) -> MPProcess: proc = spawn( - wait_stdin_con=wait_stdin_con, log_path=log_path, func=func, **kwargs + wait_stdin_con=wait_stdin_con, + log_path=log_path, + on_except=on_except, + func=func, + **kwargs, ) if ident in self.procs: raise ClanError(f"Process with id {ident} already exists") diff --git a/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py b/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py index 5be8696f3..ddefe9d86 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/ui/clan_select_list.py @@ -123,32 +123,26 @@ class ClanList(Gtk.Box): ) def on_flash_clicked(self, widget: Gtk.Widget) -> None: - print("Flash clicked") self.cbs.show_flash() def on_double_click(self, vm: VMBase) -> None: - print(f"on_double_click: {vm.name}") self.on_start_clicked(self) def on_start_clicked(self, widget: Gtk.Widget) -> None: - print("Start clicked") if self.selected_vm: self.cbs.spawn_vm(self.selected_vm.url, self.selected_vm._flake_attr) # Call this to reload self.remount_list_view() def on_stop_clicked(self, widget: Gtk.Widget) -> None: - print("Stop clicked") if self.selected_vm: self.cbs.stop_vm(self.selected_vm.url, self.selected_vm._flake_attr) self.remount_list_view() def on_new_clicked(self, widget: Gtk.Widget) -> None: - print("New clicked") self.show_join() def on_edit_clicked(self, widget: Gtk.Widget) -> None: - print("Edit clicked") self.remount_edit_view() def on_select_vm(self, vm: VMBase) -> None: diff --git a/pkgs/clan-vm-manager/clan_vm_manager/windows/join.py b/pkgs/clan-vm-manager/clan_vm_manager/windows/join.py index 18f5499f2..123481e0d 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/windows/join.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/windows/join.py @@ -75,7 +75,6 @@ class Trust(Gtk.Box): self.set_center_widget(layout) def on_trust(self, widget: Gtk.Widget) -> None: - # TODO: @Johannes, replace image with real clan logo try: uri = self.url or ClanURI(self.entry.get_text()) print(f"trusted: {uri}") diff --git a/pkgs/clan-vm-manager/clan_vm_manager/windows/overview.py b/pkgs/clan-vm-manager/clan_vm_manager/windows/overview.py index 7d88b9d68..d4d844275 100644 --- a/pkgs/clan-vm-manager/clan_vm_manager/windows/overview.py +++ b/pkgs/clan-vm-manager/clan_vm_manager/windows/overview.py @@ -54,7 +54,6 @@ class OverviewWindow(Gtk.ApplicationWindow): def remount_list_view(self) -> None: widget = self.stack.get_child_by_name("list") - print("Remounting ClanListView") if widget: widget.destroy() @@ -71,7 +70,6 @@ class OverviewWindow(Gtk.ApplicationWindow): self.stack.set_visible_child_name("list") def remount_edit_view(self) -> None: - print("Remounting ClanEdit") widget = self.stack.get_child_by_name("edit") if widget: widget.destroy()