Merge pull request 'Added show_error_dialogue on exception' (#674) from Qubasa-main into main
This commit is contained in:
@@ -10,10 +10,13 @@ from clan_vm_manager.windows.flash import FlashUSBWindow
|
|||||||
|
|
||||||
gi.require_version("Gtk", "3.0")
|
gi.require_version("Gtk", "3.0")
|
||||||
|
|
||||||
|
import multiprocessing as mp
|
||||||
|
|
||||||
from clan_cli.clan_uri import ClanURI
|
from clan_cli.clan_uri import ClanURI
|
||||||
from gi.repository import Gio, Gtk
|
from gi.repository import Gio, Gtk
|
||||||
|
|
||||||
from .constants import constants
|
from .constants import constants
|
||||||
|
from .errors.show_error import show_error_dialog
|
||||||
from .executor import ProcessManager
|
from .executor import ProcessManager
|
||||||
from .interfaces import Callbacks, InitialFlashValues, InitialJoinValues
|
from .interfaces import Callbacks, InitialFlashValues, InitialJoinValues
|
||||||
from .windows.join import JoinWindow
|
from .windows.join import JoinWindow
|
||||||
@@ -33,6 +36,11 @@ class ClanConfig:
|
|||||||
url: ClanURI | None
|
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):
|
class Application(Gtk.Application):
|
||||||
def __init__(self, windows: ClanWindows, config: ClanConfig) -> None:
|
def __init__(self, windows: ClanWindows, config: ClanConfig) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -80,17 +88,17 @@ class Application(Gtk.Application):
|
|||||||
self.proc_manager.spawn(
|
self.proc_manager.spawn(
|
||||||
ident=url,
|
ident=url,
|
||||||
wait_stdin_con=False,
|
wait_stdin_con=False,
|
||||||
|
on_except=on_except,
|
||||||
log_path=log_path,
|
log_path=log_path,
|
||||||
func=vms.run.run_vm,
|
func=vms.run.run_vm,
|
||||||
vm=vm,
|
vm=vm,
|
||||||
)
|
)
|
||||||
|
|
||||||
def stop_vm(self, url: str, attr: str) -> None:
|
def stop_vm(self, url: str, attr: str) -> None:
|
||||||
print(f"stop_vm {url}")
|
|
||||||
self.proc_manager.kill(url)
|
self.proc_manager.kill(url)
|
||||||
|
|
||||||
def running_vms(self) -> list[str]:
|
def running_vms(self) -> list[str]:
|
||||||
return list(self.proc_manager.procs.keys())
|
return self.proc_manager.running_procs()
|
||||||
|
|
||||||
def show_list(self) -> None:
|
def show_list(self) -> None:
|
||||||
prev = self.window
|
prev = self.window
|
||||||
|
|||||||
@@ -21,10 +21,6 @@ def _kill_group(proc: mp.Process) -> None:
|
|||||||
pid = proc.pid
|
pid = proc.pid
|
||||||
assert pid is not None
|
assert pid is not None
|
||||||
if proc.is_alive():
|
if proc.is_alive():
|
||||||
print(
|
|
||||||
f"Killing process group pid={pid}",
|
|
||||||
file=sys.stderr,
|
|
||||||
)
|
|
||||||
os.killpg(pid, signal.SIGTERM)
|
os.killpg(pid, signal.SIGTERM)
|
||||||
else:
|
else:
|
||||||
print(f"Process {proc.name} with pid {pid} is already dead", file=sys.stderr)
|
print(f"Process {proc.name} with pid {pid} is already dead", file=sys.stderr)
|
||||||
@@ -67,6 +63,7 @@ def _init_proc(
|
|||||||
in_file: Path,
|
in_file: Path,
|
||||||
wait_stdin_connect: bool,
|
wait_stdin_connect: bool,
|
||||||
proc_name: str,
|
proc_name: str,
|
||||||
|
on_except: Callable[[Exception, mp.process.BaseProcess], None],
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
# Create a new process group
|
# Create a new process group
|
||||||
@@ -97,8 +94,9 @@ def _init_proc(
|
|||||||
print(f"Executing function {func.__name__} now", file=sys.stderr)
|
print(f"Executing function {func.__name__} now", file=sys.stderr)
|
||||||
try:
|
try:
|
||||||
func(**kwargs)
|
func(**kwargs)
|
||||||
except Exception:
|
except Exception as ex:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
on_except(ex, mp.current_process())
|
||||||
finally:
|
finally:
|
||||||
pid = os.getpid()
|
pid = os.getpid()
|
||||||
gpid = os.getpgid(pid=pid)
|
gpid = os.getpgid(pid=pid)
|
||||||
@@ -107,12 +105,16 @@ def _init_proc(
|
|||||||
|
|
||||||
|
|
||||||
def spawn(
|
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:
|
) -> MPProcess:
|
||||||
# Decouple the process from the parent
|
# Decouple the process from the parent
|
||||||
if mp.get_start_method(allow_none=True) is None:
|
if mp.get_start_method(allow_none=True) is None:
|
||||||
mp.set_start_method(method="forkserver")
|
mp.set_start_method(method="forkserver")
|
||||||
print("Set mp start method to forkserver", file=sys.stderr)
|
|
||||||
|
|
||||||
if not log_path.is_dir():
|
if not log_path.is_dir():
|
||||||
raise ClanError(f"Log path {log_path} is not a directory")
|
raise ClanError(f"Log path {log_path} is not a directory")
|
||||||
@@ -132,7 +134,7 @@ def spawn(
|
|||||||
# Start the process
|
# Start the process
|
||||||
proc = mp.Process(
|
proc = mp.Process(
|
||||||
target=_init_proc,
|
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,
|
name=proc_name,
|
||||||
kwargs=kwargs,
|
kwargs=kwargs,
|
||||||
)
|
)
|
||||||
@@ -140,8 +142,6 @@ def spawn(
|
|||||||
|
|
||||||
# Print some information
|
# Print some information
|
||||||
assert proc.pid is not None
|
assert proc.pid is not None
|
||||||
print(f"Started process '{proc_name}'")
|
|
||||||
print(f"Arguments: {kwargs}")
|
|
||||||
|
|
||||||
if wait_stdin_con:
|
if wait_stdin_con:
|
||||||
cmd = f"cat - > {in_file}"
|
cmd = f"cat - > {in_file}"
|
||||||
@@ -166,17 +166,42 @@ class ProcessManager:
|
|||||||
self.procs: dict[str, MPProcess] = dict()
|
self.procs: dict[str, MPProcess] = dict()
|
||||||
self._finalizer = weakref.finalize(self, self.kill_all)
|
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(
|
def spawn(
|
||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
ident: str,
|
ident: str,
|
||||||
wait_stdin_con: bool,
|
wait_stdin_con: bool,
|
||||||
log_path: Path,
|
log_path: Path,
|
||||||
|
on_except: Callable[[Exception, mp.process.BaseProcess], None],
|
||||||
func: Callable,
|
func: Callable,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> MPProcess:
|
) -> MPProcess:
|
||||||
proc = spawn(
|
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:
|
if ident in self.procs:
|
||||||
raise ClanError(f"Process with id {ident} already exists")
|
raise ClanError(f"Process with id {ident} already exists")
|
||||||
|
|||||||
@@ -123,32 +123,26 @@ class ClanList(Gtk.Box):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def on_flash_clicked(self, widget: Gtk.Widget) -> None:
|
def on_flash_clicked(self, widget: Gtk.Widget) -> None:
|
||||||
print("Flash clicked")
|
|
||||||
self.cbs.show_flash()
|
self.cbs.show_flash()
|
||||||
|
|
||||||
def on_double_click(self, vm: VMBase) -> None:
|
def on_double_click(self, vm: VMBase) -> None:
|
||||||
print(f"on_double_click: {vm.name}")
|
|
||||||
self.on_start_clicked(self)
|
self.on_start_clicked(self)
|
||||||
|
|
||||||
def on_start_clicked(self, widget: Gtk.Widget) -> None:
|
def on_start_clicked(self, widget: Gtk.Widget) -> None:
|
||||||
print("Start clicked")
|
|
||||||
if self.selected_vm:
|
if self.selected_vm:
|
||||||
self.cbs.spawn_vm(self.selected_vm.url, self.selected_vm._flake_attr)
|
self.cbs.spawn_vm(self.selected_vm.url, self.selected_vm._flake_attr)
|
||||||
# Call this to reload
|
# Call this to reload
|
||||||
self.remount_list_view()
|
self.remount_list_view()
|
||||||
|
|
||||||
def on_stop_clicked(self, widget: Gtk.Widget) -> None:
|
def on_stop_clicked(self, widget: Gtk.Widget) -> None:
|
||||||
print("Stop clicked")
|
|
||||||
if self.selected_vm:
|
if self.selected_vm:
|
||||||
self.cbs.stop_vm(self.selected_vm.url, self.selected_vm._flake_attr)
|
self.cbs.stop_vm(self.selected_vm.url, self.selected_vm._flake_attr)
|
||||||
self.remount_list_view()
|
self.remount_list_view()
|
||||||
|
|
||||||
def on_new_clicked(self, widget: Gtk.Widget) -> None:
|
def on_new_clicked(self, widget: Gtk.Widget) -> None:
|
||||||
print("New clicked")
|
|
||||||
self.show_join()
|
self.show_join()
|
||||||
|
|
||||||
def on_edit_clicked(self, widget: Gtk.Widget) -> None:
|
def on_edit_clicked(self, widget: Gtk.Widget) -> None:
|
||||||
print("Edit clicked")
|
|
||||||
self.remount_edit_view()
|
self.remount_edit_view()
|
||||||
|
|
||||||
def on_select_vm(self, vm: VMBase) -> None:
|
def on_select_vm(self, vm: VMBase) -> None:
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ class Trust(Gtk.Box):
|
|||||||
self.set_center_widget(layout)
|
self.set_center_widget(layout)
|
||||||
|
|
||||||
def on_trust(self, widget: Gtk.Widget) -> None:
|
def on_trust(self, widget: Gtk.Widget) -> None:
|
||||||
# TODO: @Johannes, replace image with real clan logo
|
|
||||||
try:
|
try:
|
||||||
uri = self.url or ClanURI(self.entry.get_text())
|
uri = self.url or ClanURI(self.entry.get_text())
|
||||||
print(f"trusted: {uri}")
|
print(f"trusted: {uri}")
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ class OverviewWindow(Gtk.ApplicationWindow):
|
|||||||
|
|
||||||
def remount_list_view(self) -> None:
|
def remount_list_view(self) -> None:
|
||||||
widget = self.stack.get_child_by_name("list")
|
widget = self.stack.get_child_by_name("list")
|
||||||
print("Remounting ClanListView")
|
|
||||||
if widget:
|
if widget:
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
|
|
||||||
@@ -71,7 +70,6 @@ class OverviewWindow(Gtk.ApplicationWindow):
|
|||||||
self.stack.set_visible_child_name("list")
|
self.stack.set_visible_child_name("list")
|
||||||
|
|
||||||
def remount_edit_view(self) -> None:
|
def remount_edit_view(self) -> None:
|
||||||
print("Remounting ClanEdit")
|
|
||||||
widget = self.stack.get_child_by_name("edit")
|
widget = self.stack.get_child_by_name("edit")
|
||||||
if widget:
|
if widget:
|
||||||
widget.destroy()
|
widget.destroy()
|
||||||
|
|||||||
Reference in New Issue
Block a user