ruff: apply automatic fixes
This commit is contained in:
@@ -22,8 +22,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MainApplication(Adw.Application):
|
||||
"""
|
||||
This class is initialized every time the app is started
|
||||
"""This class is initialized every time the app is started
|
||||
Only the Adw.ApplicationWindow is a singleton.
|
||||
So don't use any singletons in the Adw.Application class.
|
||||
"""
|
||||
@@ -78,7 +77,8 @@ class MainApplication(Adw.Application):
|
||||
|
||||
if "debug" in options:
|
||||
ToastOverlay.use().add_toast_unique(
|
||||
InfoToast("Debug logging enabled").toast, "info.debugging.enabled"
|
||||
InfoToast("Debug logging enabled").toast,
|
||||
"info.debugging.enabled",
|
||||
)
|
||||
|
||||
args = command_line.get_arguments()
|
||||
|
||||
@@ -33,8 +33,7 @@ class ClanURI:
|
||||
# Check if the URI starts with clan://
|
||||
# If it does, remove the clan:// prefix
|
||||
prefix = "clan://"
|
||||
if uri.startswith(prefix):
|
||||
uri = uri[len(prefix) :]
|
||||
uri = uri.removeprefix(prefix)
|
||||
|
||||
# Fix missing colon (caused by browsers like Firefox)
|
||||
if "//" in uri and ":" not in uri.split("//", 1)[0]:
|
||||
|
||||
@@ -14,7 +14,8 @@ log = logging.getLogger(__name__)
|
||||
# Define type variables for key and value types
|
||||
K = TypeVar("K") # Key type
|
||||
V = TypeVar(
|
||||
"V", bound=GObject.Object
|
||||
"V",
|
||||
bound=GObject.Object,
|
||||
) # Value type, bound to GObject.GObject or its subclasses
|
||||
|
||||
|
||||
@@ -23,8 +24,7 @@ V = TypeVar(
|
||||
# clan_vm_manager/components/gkvstore.py:21: error: Definition of "install_properties" in base class "Object" is incompatible with definition in base class "GInterface" [misc]
|
||||
# clan_vm_manager/components/gkvstore.py:21: error: Definition of "getv" in base class "Object" is incompatible with definition in base class "GInterface" [misc]
|
||||
class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: ignore[misc]
|
||||
"""
|
||||
A simple key-value store that implements the Gio.ListModel interface, with generic types for keys and values.
|
||||
"""A simple key-value store that implements the Gio.ListModel interface, with generic types for keys and values.
|
||||
Only use self[key] and del self[key] for accessing the items for better performance.
|
||||
This class could be optimized by having the objects remember their position in the list.
|
||||
"""
|
||||
@@ -57,7 +57,9 @@ class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: i
|
||||
return False, -1
|
||||
|
||||
def find_with_equal_func(
|
||||
self, item: V, equal_func: Callable[[V, V], bool]
|
||||
self,
|
||||
item: V,
|
||||
equal_func: Callable[[V, V], bool],
|
||||
) -> tuple[bool, int]:
|
||||
log.warning("Finding is O(n) in GKVStore. Better use indexing")
|
||||
for i, v in enumerate(self.values()):
|
||||
@@ -66,7 +68,10 @@ class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: i
|
||||
return False, -1
|
||||
|
||||
def find_with_equal_func_full(
|
||||
self, item: V, equal_func: Callable[[V, V, Any], bool], user_data: Any
|
||||
self,
|
||||
item: V,
|
||||
equal_func: Callable[[V, V, Any], bool],
|
||||
user_data: Any,
|
||||
) -> tuple[bool, int]:
|
||||
log.warning("Finding is O(n) in GKVStore. Better use indexing")
|
||||
for i, v in enumerate(self.values()):
|
||||
@@ -77,7 +82,7 @@ class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: i
|
||||
def insert(self, position: int, item: V) -> None:
|
||||
log.warning("Inserting is O(n) in GKVStore. Better use append")
|
||||
log.warning(
|
||||
"This functions may have incorrect items_changed signal behavior. Please test it"
|
||||
"This functions may have incorrect items_changed signal behavior. Please test it",
|
||||
)
|
||||
key = self.key_gen(item)
|
||||
if key in self._items:
|
||||
@@ -105,7 +110,10 @@ class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: i
|
||||
self.items_changed(position, 0, 1)
|
||||
|
||||
def insert_sorted(
|
||||
self, item: V, compare_func: Callable[[V, V, Any], int], user_data: Any
|
||||
self,
|
||||
item: V,
|
||||
compare_func: Callable[[V, V, Any], int],
|
||||
user_data: Any,
|
||||
) -> None:
|
||||
msg = "insert_sorted is not implemented in GKVStore"
|
||||
raise NotImplementedError(msg)
|
||||
@@ -225,6 +233,7 @@ class GKVStore[K, V: GObject.Object](GObject.GObject, Gio.ListModel): # type: i
|
||||
return self.values()[-1]
|
||||
|
||||
def register_on_change(
|
||||
self, callback: Callable[["GKVStore[K,V]", int, int, int], None]
|
||||
self,
|
||||
callback: Callable[["GKVStore[K,V]", int, int, int], None],
|
||||
) -> None:
|
||||
self.connect("items-changed", callback)
|
||||
|
||||
@@ -67,8 +67,7 @@ class EmptySplash(Gtk.Box):
|
||||
return pixbuf
|
||||
|
||||
def _on_join(self, button: Gtk.Button, entry: Gtk.Entry) -> None:
|
||||
"""
|
||||
Callback for the join button
|
||||
"""Callback for the join button
|
||||
Extracts the text from the entry and calls the on_join callback
|
||||
"""
|
||||
log.info(f"Splash screen: Joining {entry.get_text()}")
|
||||
|
||||
@@ -128,7 +128,6 @@ def encode_path(path: str, prefix: bool = True) -> bytes:
|
||||
|
||||
On Windows, also append prefix to enable extended-length path.
|
||||
"""
|
||||
|
||||
if sys.platform == "win32" and prefix:
|
||||
path = path.replace("/", "\\")
|
||||
|
||||
@@ -142,10 +141,12 @@ def encode_path(path: str, prefix: bool = True) -> bytes:
|
||||
|
||||
# from pynicotine.utils import truncate_string_byte
|
||||
def truncate_string_byte(
|
||||
string: str, byte_limit: int, encoding: str = "utf-8", ellipsize: bool = False
|
||||
string: str,
|
||||
byte_limit: int,
|
||||
encoding: str = "utf-8",
|
||||
ellipsize: bool = False,
|
||||
) -> str:
|
||||
"""Truncates a string to fit inside a byte limit."""
|
||||
|
||||
string_bytes = string.encode(encoding)
|
||||
|
||||
if len(string_bytes) <= byte_limit:
|
||||
@@ -217,7 +218,8 @@ class BaseImplementation:
|
||||
|
||||
def create_menu(self) -> None:
|
||||
self.show_hide_item = self.create_item(
|
||||
"default", self.application.on_window_hide_unhide
|
||||
"default",
|
||||
self.application.on_window_hide_unhide,
|
||||
)
|
||||
|
||||
# self.create_item()
|
||||
@@ -365,17 +367,26 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
|
||||
def add_property(self, name: str, signature: Any, value: Any) -> None:
|
||||
self.properties[name] = StatusNotifierImplementation.DBusProperty(
|
||||
name, signature, value
|
||||
name,
|
||||
signature,
|
||||
value,
|
||||
)
|
||||
|
||||
def add_signal(self, name: str, args: Any) -> None:
|
||||
self.signals[name] = StatusNotifierImplementation.DBusSignal(name, args)
|
||||
|
||||
def add_method(
|
||||
self, name: str, in_args: Any, out_args: Any, callback: Any
|
||||
self,
|
||||
name: str,
|
||||
in_args: Any,
|
||||
out_args: Any,
|
||||
callback: Any,
|
||||
) -> None:
|
||||
self.methods[name] = StatusNotifierImplementation.DBusMethod(
|
||||
name, in_args, out_args, callback
|
||||
name,
|
||||
in_args,
|
||||
out_args,
|
||||
callback,
|
||||
)
|
||||
|
||||
def emit_signal(self, name: str, *args: Any) -> None:
|
||||
@@ -411,7 +422,12 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
invocation.return_value(return_value)
|
||||
|
||||
def on_get_property(
|
||||
self, _connection, _sender, _path, _interface_name, property_name
|
||||
self,
|
||||
_connection,
|
||||
_sender,
|
||||
_path,
|
||||
_interface_name,
|
||||
property_name,
|
||||
):
|
||||
prop = self.properties[property_name]
|
||||
return GLib.Variant(prop.signature, prop.value)
|
||||
@@ -483,7 +499,8 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
|
||||
for idx, item in self._items.items():
|
||||
serialized_item = GLib.Variant(
|
||||
"(ia{sv}av)", (idx, self._serialize_item(item), [])
|
||||
"(ia{sv}av)",
|
||||
(idx, self._serialize_item(item), []),
|
||||
)
|
||||
serialized_items.append(serialized_item)
|
||||
|
||||
@@ -558,7 +575,7 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
try:
|
||||
self.bus = Gio.bus_get_sync(bus_type=Gio.BusType.SESSION)
|
||||
self.tray_icon = self.StatusNotifierItemService(
|
||||
activate_callback=self.activate_callback
|
||||
activate_callback=self.activate_callback,
|
||||
)
|
||||
self.tray_icon.register()
|
||||
|
||||
@@ -573,7 +590,8 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
interface_name="org.kde.StatusNotifierWatcher",
|
||||
method_name="RegisterStatusNotifierItem",
|
||||
parameters=GLib.Variant(
|
||||
"(s)", ("/org/ayatana/NotificationItem/Nicotine",)
|
||||
"(s)",
|
||||
("/org/ayatana/NotificationItem/Nicotine",),
|
||||
),
|
||||
reply_type=None,
|
||||
flags=Gio.DBusCallFlags.NONE,
|
||||
@@ -590,7 +608,6 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
@staticmethod
|
||||
def check_icon_path(icon_name, icon_path) -> bool:
|
||||
"""Check if tray icons exist in the specified icon path."""
|
||||
|
||||
if not icon_path:
|
||||
return False
|
||||
|
||||
@@ -600,7 +617,8 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
with os.scandir(encode_path(icon_path)) as entries:
|
||||
for entry in entries:
|
||||
if entry.is_file() and entry.name.decode(
|
||||
"utf-8", "replace"
|
||||
"utf-8",
|
||||
"replace",
|
||||
).startswith(icon_scheme):
|
||||
return True
|
||||
|
||||
@@ -611,8 +629,8 @@ class StatusNotifierImplementation(BaseImplementation):
|
||||
|
||||
def get_icon_path(self):
|
||||
"""Returns an icon path to use for tray icons, or None to fall back to
|
||||
system-wide icons."""
|
||||
|
||||
system-wide icons.
|
||||
"""
|
||||
# icon_path = self.application.get_application_icon_path()
|
||||
|
||||
return ""
|
||||
@@ -811,7 +829,8 @@ class Win32Implementation(BaseImplementation):
|
||||
from ctypes import windll
|
||||
|
||||
windll.user32.UnregisterClassW(
|
||||
self.WINDOW_CLASS_NAME, self._window_class.h_instance
|
||||
self.WINDOW_CLASS_NAME,
|
||||
self._window_class.h_instance,
|
||||
)
|
||||
self._window_class = None
|
||||
|
||||
@@ -850,7 +869,12 @@ class Win32Implementation(BaseImplementation):
|
||||
|
||||
if GTK_API_VERSION >= 4:
|
||||
icon = ICON_THEME.lookup_icon(
|
||||
icon_name, fallbacks=None, size=icon_size, scale=1, direction=0, flags=0
|
||||
icon_name,
|
||||
fallbacks=None,
|
||||
size=icon_size,
|
||||
scale=1,
|
||||
direction=0,
|
||||
flags=0,
|
||||
)
|
||||
icon_path = icon.get_file().get_path()
|
||||
|
||||
@@ -858,7 +882,9 @@ class Win32Implementation(BaseImplementation):
|
||||
return ico_buffer
|
||||
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
|
||||
icon_path, icon_size, icon_size
|
||||
icon_path,
|
||||
icon_size,
|
||||
icon_size,
|
||||
)
|
||||
else:
|
||||
icon = ICON_THEME.lookup_icon(icon_name, size=icon_size, flags=0)
|
||||
@@ -931,7 +957,8 @@ class Win32Implementation(BaseImplementation):
|
||||
),
|
||||
u_callback_message=self.WM_TRAYICON,
|
||||
sz_tip=truncate_string_byte(
|
||||
pynicotine.__application_name__, byte_limit=127
|
||||
pynicotine.__application_name__,
|
||||
byte_limit=127,
|
||||
),
|
||||
)
|
||||
action = self.NIM_ADD
|
||||
@@ -943,10 +970,14 @@ class Win32Implementation(BaseImplementation):
|
||||
|
||||
self._notify_id.h_icon = self._h_icon
|
||||
self._notify_id.sz_info_title = truncate_string_byte(
|
||||
title, byte_limit=63, ellipsize=True
|
||||
title,
|
||||
byte_limit=63,
|
||||
ellipsize=True,
|
||||
)
|
||||
self._notify_id.sz_info = truncate_string_byte(
|
||||
message, byte_limit=255, ellipsize=True
|
||||
message,
|
||||
byte_limit=255,
|
||||
ellipsize=True,
|
||||
)
|
||||
|
||||
windll.shell32.Shell_NotifyIconW(action, byref(self._notify_id))
|
||||
@@ -1019,10 +1050,16 @@ class Win32Implementation(BaseImplementation):
|
||||
item_info = self._serialize_menu_item(item)
|
||||
|
||||
if not windll.user32.SetMenuItemInfoW(
|
||||
self._menu, item_id, False, byref(item_info)
|
||||
self._menu,
|
||||
item_id,
|
||||
False,
|
||||
byref(item_info),
|
||||
):
|
||||
windll.user32.InsertMenuItemW(
|
||||
self._menu, item_id, False, byref(item_info)
|
||||
self._menu,
|
||||
item_id,
|
||||
False,
|
||||
byref(item_info),
|
||||
)
|
||||
|
||||
def set_icon(self, icon_name):
|
||||
|
||||
@@ -56,10 +56,14 @@ class VMObject(GObject.Object):
|
||||
|
||||
# Create a process object to store the VM process
|
||||
self.vm_process: MPProcess = MPProcess(
|
||||
"vm_dummy", mp.Process(), Path("./dummy")
|
||||
"vm_dummy",
|
||||
mp.Process(),
|
||||
Path("./dummy"),
|
||||
)
|
||||
self.build_process: MPProcess = MPProcess(
|
||||
"build_dummy", mp.Process(), Path("./dummy")
|
||||
"build_dummy",
|
||||
mp.Process(),
|
||||
Path("./dummy"),
|
||||
)
|
||||
self._start_thread: threading.Thread = threading.Thread()
|
||||
self.machine: Machine | None = None
|
||||
@@ -77,7 +81,8 @@ class VMObject(GObject.Object):
|
||||
|
||||
# Create a temporary directory to store the logs
|
||||
self.log_dir: tempfile.TemporaryDirectory = tempfile.TemporaryDirectory(
|
||||
prefix="clan_vm-", suffix=f"-{self.data.flake.flake_attr}"
|
||||
prefix="clan_vm-",
|
||||
suffix=f"-{self.data.flake.flake_attr}",
|
||||
)
|
||||
self._logs_id: int = 0
|
||||
self._log_file: IO[str] | None = None
|
||||
@@ -87,7 +92,8 @@ class VMObject(GObject.Object):
|
||||
# and block the signal while we change the state. This is cursed.
|
||||
self.switch: Gtk.Switch = Gtk.Switch()
|
||||
self.switch_handler_id: int = self.switch.connect(
|
||||
"notify::active", self._on_switch_toggle
|
||||
"notify::active",
|
||||
self._on_switch_toggle,
|
||||
)
|
||||
self.connect("vm_status_changed", self._on_vm_status_changed)
|
||||
|
||||
@@ -145,7 +151,8 @@ class VMObject(GObject.Object):
|
||||
@contextmanager
|
||||
def _create_machine(self) -> Generator[Machine]:
|
||||
uri = ClanURI.from_str(
|
||||
url=str(self.data.flake.flake_url), machine_name=self.data.flake.flake_attr
|
||||
url=str(self.data.flake.flake_url),
|
||||
machine_name=self.data.flake.flake_attr,
|
||||
)
|
||||
self.machine = Machine(
|
||||
name=self.data.flake.flake_attr,
|
||||
@@ -154,7 +161,8 @@ class VMObject(GObject.Object):
|
||||
assert self.machine is not None
|
||||
|
||||
state_dir = vm_state_dir(
|
||||
flake_url=self.machine.flake.identifier, vm_name=self.machine.name
|
||||
flake_url=self.machine.flake.identifier,
|
||||
vm_name=self.machine.name,
|
||||
)
|
||||
self.qmp_wrap = QMPWrapper(state_dir)
|
||||
assert self.machine is not None
|
||||
@@ -194,7 +202,9 @@ class VMObject(GObject.Object):
|
||||
self.switch.set_sensitive(True)
|
||||
# Start the logs watcher
|
||||
self._logs_id = GLib.timeout_add(
|
||||
50, self._get_logs_task, self.build_process
|
||||
50,
|
||||
self._get_logs_task,
|
||||
self.build_process,
|
||||
)
|
||||
if self._logs_id == 0:
|
||||
log.error("Failed to start VM log watcher")
|
||||
@@ -307,7 +317,7 @@ class VMObject(GObject.Object):
|
||||
diff = datetime.datetime.now(tz=datetime.UTC) - start_time
|
||||
if diff.seconds > self.KILL_TIMEOUT:
|
||||
log.error(
|
||||
f"VM {self.get_id()} has not stopped after {self.KILL_TIMEOUT}s. Killing it"
|
||||
f"VM {self.get_id()} has not stopped after {self.KILL_TIMEOUT}s. Killing it",
|
||||
)
|
||||
self.vm_process.kill_group()
|
||||
break
|
||||
@@ -334,7 +344,8 @@ class VMObject(GObject.Object):
|
||||
log.debug(f"VM {self.get_id()} has stopped")
|
||||
|
||||
ToastOverlay.use().add_toast_unique(
|
||||
InfoToast(f"Stopped {self.get_id()}").toast, "info.vm.exit"
|
||||
InfoToast(f"Stopped {self.get_id()}").toast,
|
||||
"info.vm.exit",
|
||||
)
|
||||
|
||||
def shutdown(self) -> None:
|
||||
|
||||
@@ -92,7 +92,9 @@ def add_history(uri: ClanURI) -> HistoryEntry:
|
||||
|
||||
|
||||
def _add_maschine_to_history_list(
|
||||
uri_path: str, uri_machine: str, entries: list[HistoryEntry]
|
||||
uri_path: str,
|
||||
uri_machine: str,
|
||||
entries: list[HistoryEntry],
|
||||
) -> HistoryEntry:
|
||||
for new_entry in entries:
|
||||
if (
|
||||
@@ -143,10 +145,16 @@ def parse_args() -> argparse.Namespace:
|
||||
)
|
||||
add_parser = subparser.add_parser("add", help="Add a clan flake")
|
||||
add_parser.add_argument(
|
||||
"uri", type=ClanURI.from_str, help="Path to the flake", default="."
|
||||
"uri",
|
||||
type=ClanURI.from_str,
|
||||
help="Path to the flake",
|
||||
default=".",
|
||||
)
|
||||
add_parser.add_argument(
|
||||
"--all", help="Add all machines", default=False, action="store_true"
|
||||
"--all",
|
||||
help="Add all machines",
|
||||
default=False,
|
||||
action="store_true",
|
||||
)
|
||||
add_parser.set_defaults(func=add_history_command)
|
||||
list_parser = subparser.add_parser("list", help="List recently used flakes")
|
||||
|
||||
@@ -16,8 +16,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ToastOverlay:
|
||||
"""
|
||||
The ToastOverlay is a class that manages the display of toasts
|
||||
"""The ToastOverlay is a class that manages the display of toasts
|
||||
It should be used as a singleton in your application to prevent duplicate toasts
|
||||
Usage
|
||||
"""
|
||||
@@ -53,11 +52,14 @@ class ErrorToast:
|
||||
toast: Adw.Toast
|
||||
|
||||
def __init__(
|
||||
self, message: str, persistent: bool = False, details: str = ""
|
||||
self,
|
||||
message: str,
|
||||
persistent: bool = False,
|
||||
details: str = "",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.toast = Adw.Toast.new(
|
||||
f"""<span foreground='red'>❌ Error </span> {message}"""
|
||||
f"""<span foreground='red'>❌ Error </span> {message}""",
|
||||
)
|
||||
self.toast.set_use_markup(True)
|
||||
|
||||
@@ -85,7 +87,7 @@ class WarningToast:
|
||||
def __init__(self, message: str, persistent: bool = False) -> None:
|
||||
super().__init__()
|
||||
self.toast = Adw.Toast.new(
|
||||
f"<span foreground='orange'>⚠ Warning </span> {message}"
|
||||
f"<span foreground='orange'>⚠ Warning </span> {message}",
|
||||
)
|
||||
self.toast.set_use_markup(True)
|
||||
|
||||
@@ -135,7 +137,7 @@ class LogToast:
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.toast = Adw.Toast.new(
|
||||
f"""Logs are available <span weight="regular">{message}</span>"""
|
||||
f"""Logs are available <span weight="regular">{message}</span>""",
|
||||
)
|
||||
self.toast.set_use_markup(True)
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ class JoinValue(GObject.Object):
|
||||
|
||||
|
||||
class JoinList:
|
||||
"""
|
||||
This is a singleton.
|
||||
"""This is a singleton.
|
||||
It is initialized with the first call of use()
|
||||
"""
|
||||
|
||||
@@ -69,28 +68,32 @@ class JoinList:
|
||||
return cls._instance
|
||||
|
||||
def rerender_join_list(
|
||||
self, source: GKVStore, position: int, removed: int, added: int
|
||||
self,
|
||||
source: GKVStore,
|
||||
position: int,
|
||||
removed: int,
|
||||
added: int,
|
||||
) -> None:
|
||||
self.list_store.items_changed(
|
||||
0, self.list_store.get_n_items(), self.list_store.get_n_items()
|
||||
0,
|
||||
self.list_store.get_n_items(),
|
||||
self.list_store.get_n_items(),
|
||||
)
|
||||
|
||||
def is_empty(self) -> bool:
|
||||
return self.list_store.get_n_items() == 0
|
||||
|
||||
def push(self, uri: ClanURI, after_join: Callable[[JoinValue], None]) -> None:
|
||||
"""
|
||||
Add a join request.
|
||||
"""Add a join request.
|
||||
This method can add multiple join requests if called subsequently for each request.
|
||||
"""
|
||||
|
||||
value = JoinValue(uri)
|
||||
|
||||
machine_id = Machine(uri.machine_name, uri.flake)
|
||||
machine_id_list = []
|
||||
|
||||
for machine_obj in self.list_store:
|
||||
mvalue: ClanURI = cast(JoinValue, machine_obj).url
|
||||
mvalue: ClanURI = cast("JoinValue", machine_obj).url
|
||||
machine = Machine(mvalue.machine_name, mvalue.flake)
|
||||
machine_id_list.append(machine.get_id())
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@ from gi.repository import Adw
|
||||
|
||||
|
||||
class ViewStack:
|
||||
"""
|
||||
This is a singleton.
|
||||
"""This is a singleton.
|
||||
It is initialized with the first call of use()
|
||||
|
||||
Usage:
|
||||
|
||||
@@ -53,7 +53,8 @@ class ClanStore:
|
||||
if cls._instance is None:
|
||||
cls._instance = cls.__new__(cls)
|
||||
cls._clan_store = GKVStore(
|
||||
VMStore, lambda store: store.first().data.flake.flake_url
|
||||
VMStore,
|
||||
lambda store: store.first().data.flake.flake_url,
|
||||
)
|
||||
cls._emitter = Emitter()
|
||||
|
||||
@@ -74,19 +75,24 @@ class ClanStore:
|
||||
return self._logging_vm
|
||||
|
||||
def register_on_deep_change(
|
||||
self, callback: Callable[[GKVStore, int, int, int], None]
|
||||
self,
|
||||
callback: Callable[[GKVStore, int, int, int], None],
|
||||
) -> None:
|
||||
"""
|
||||
Register a callback that is called when a clan_store or one of the included VMStores changes
|
||||
"""
|
||||
"""Register a callback that is called when a clan_store or one of the included VMStores changes"""
|
||||
|
||||
def on_vmstore_change(
|
||||
store: VMStore, position: int, removed: int, added: int
|
||||
store: VMStore,
|
||||
position: int,
|
||||
removed: int,
|
||||
added: int,
|
||||
) -> None:
|
||||
callback(store, position, removed, added)
|
||||
|
||||
def on_clanstore_change(
|
||||
store: "GKVStore", position: int, removed: int, added: int
|
||||
store: "GKVStore",
|
||||
position: int,
|
||||
removed: int,
|
||||
added: int,
|
||||
) -> None:
|
||||
if added > 0:
|
||||
store.values()[position].register_on_change(on_vmstore_change)
|
||||
@@ -120,7 +126,9 @@ class ClanStore:
|
||||
logs_view: Logs = views.get_child_by_name("logs") # type: ignore
|
||||
|
||||
def file_read_callback(
|
||||
source_object: Gio.File, result: Gio.AsyncResult, _user_data: Any
|
||||
source_object: Gio.File,
|
||||
result: Gio.AsyncResult,
|
||||
_user_data: Any,
|
||||
) -> None:
|
||||
try:
|
||||
# Finish the asynchronous read operation
|
||||
@@ -155,7 +163,7 @@ class ClanStore:
|
||||
|
||||
if old_vm:
|
||||
log.info(
|
||||
f"VM {vm.data.flake.flake_attr} already exists in store. Updating data field."
|
||||
f"VM {vm.data.flake.flake_attr} already exists in store. Updating data field.",
|
||||
)
|
||||
old_vm.update(vm.data)
|
||||
else:
|
||||
|
||||
@@ -13,7 +13,8 @@ ListItem = TypeVar("ListItem", bound=GObject.Object)
|
||||
|
||||
|
||||
def create_details_list[ListItem: GObject.Object](
|
||||
model: Gio.ListStore, render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget]
|
||||
model: Gio.ListStore,
|
||||
render_row: Callable[[Gtk.ListBox, ListItem], Gtk.Widget],
|
||||
) -> Gtk.ListBox:
|
||||
boxed_list = Gtk.ListBox()
|
||||
boxed_list.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||
@@ -28,7 +29,10 @@ class PreferencesValue(GObject.Object):
|
||||
data: Any
|
||||
|
||||
def __init__(
|
||||
self, variant: Literal["CPU", "MEMORY"], editable: bool, data: Any
|
||||
self,
|
||||
variant: Literal["CPU", "MEMORY"],
|
||||
editable: bool,
|
||||
data: Any,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.variant = variant
|
||||
@@ -44,13 +48,16 @@ class Details(Gtk.Box):
|
||||
preferences_store.append(PreferencesValue("CPU", True, 1))
|
||||
|
||||
self.details_list = create_details_list(
|
||||
model=preferences_store, render_row=self.render_entry_row
|
||||
model=preferences_store,
|
||||
render_row=self.render_entry_row,
|
||||
)
|
||||
|
||||
self.append(self.details_list)
|
||||
|
||||
def render_entry_row(
|
||||
self, boxed_list: Gtk.ListBox, item: PreferencesValue
|
||||
self,
|
||||
boxed_list: Gtk.ListBox,
|
||||
item: PreferencesValue,
|
||||
) -> Gtk.Widget:
|
||||
cores: int | None = os.cpu_count()
|
||||
fcores = float(cores) if cores else 1.0
|
||||
|
||||
@@ -46,8 +46,7 @@ def create_boxed_list[CustomStore: Gio.ListModel, ListItem: GObject.Object](
|
||||
|
||||
|
||||
class ClanList(Gtk.Box):
|
||||
"""
|
||||
The ClanList
|
||||
"""The ClanList
|
||||
Is the composition of
|
||||
the ClanListToolbar
|
||||
the clanListView
|
||||
@@ -70,7 +69,8 @@ class ClanList(Gtk.Box):
|
||||
|
||||
# Add join list
|
||||
self.join_boxed_list = create_boxed_list(
|
||||
model=JoinList.use().list_store, render_row=self.render_join_row
|
||||
model=JoinList.use().list_store,
|
||||
render_row=self.render_join_row,
|
||||
)
|
||||
self.join_boxed_list.add_css_class("join-list")
|
||||
self.append(self.join_boxed_list)
|
||||
@@ -79,7 +79,8 @@ class ClanList(Gtk.Box):
|
||||
clan_store.connect("is_ready", self.display_splash)
|
||||
|
||||
self.group_list = create_boxed_list(
|
||||
model=clan_store.clan_store, render_row=self.render_group_row
|
||||
model=clan_store.clan_store,
|
||||
render_row=self.render_group_row,
|
||||
)
|
||||
self.group_list.add_css_class("group-list")
|
||||
self.append(self.group_list)
|
||||
@@ -95,7 +96,9 @@ class ClanList(Gtk.Box):
|
||||
self.append(self.splash)
|
||||
|
||||
def render_group_row(
|
||||
self, boxed_list: Gtk.ListBox, vm_store: VMStore
|
||||
self,
|
||||
boxed_list: Gtk.ListBox,
|
||||
vm_store: VMStore,
|
||||
) -> Gtk.Widget:
|
||||
self.remove(self.splash)
|
||||
|
||||
@@ -199,7 +202,8 @@ class ClanList(Gtk.Box):
|
||||
action_id = base64.b64encode(vm.get_id().encode("utf-8")).decode("utf-8")
|
||||
|
||||
build_logs_action = Gio.SimpleAction.new(
|
||||
f"logs.{action_id}", GLib.VariantType.new("s")
|
||||
f"logs.{action_id}",
|
||||
GLib.VariantType.new("s"),
|
||||
)
|
||||
|
||||
build_logs_action.connect("activate", self.on_show_build_logs)
|
||||
@@ -213,7 +217,9 @@ class ClanList(Gtk.Box):
|
||||
|
||||
# set a callback function for conditionally enabling the build_logs action
|
||||
def on_vm_build_notify(
|
||||
vm: VMObject, is_building: bool, is_running: bool
|
||||
vm: VMObject,
|
||||
is_building: bool,
|
||||
is_running: bool,
|
||||
) -> None:
|
||||
build_logs_action.set_enabled(is_building or is_running)
|
||||
app.add_action(build_logs_action)
|
||||
@@ -279,7 +285,9 @@ class ClanList(Gtk.Box):
|
||||
views.set_visible_child_name("logs")
|
||||
|
||||
def render_join_row(
|
||||
self, boxed_list: Gtk.ListBox, join_val: JoinValue
|
||||
self,
|
||||
boxed_list: Gtk.ListBox,
|
||||
join_val: JoinValue,
|
||||
) -> Gtk.Widget:
|
||||
if boxed_list.has_css_class("no-shadow"):
|
||||
boxed_list.remove_css_class("no-shadow")
|
||||
@@ -300,13 +308,13 @@ class ClanList(Gtk.Box):
|
||||
|
||||
ToastOverlay.use().add_toast_unique(
|
||||
WarningToast(
|
||||
f"""<span weight="regular">{join_val.url.machine_name!s}</span> Already exists. Joining again will update it"""
|
||||
f"""<span weight="regular">{join_val.url.machine_name!s}</span> Already exists. Joining again will update it""",
|
||||
).toast,
|
||||
"warning.duplicate.join",
|
||||
)
|
||||
|
||||
row.set_subtitle(
|
||||
sub + "\nClan already exists. Joining again will update it"
|
||||
sub + "\nClan already exists. Joining again will update it",
|
||||
)
|
||||
|
||||
avatar = Adw.Avatar()
|
||||
|
||||
@@ -11,8 +11,7 @@ log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Logs(Gtk.Box):
|
||||
"""
|
||||
Simple log view
|
||||
"""Simple log view
|
||||
This includes a banner and a text view and a button to close the log and navigate back to the overview
|
||||
"""
|
||||
|
||||
@@ -44,9 +43,7 @@ class Logs(Gtk.Box):
|
||||
self.banner.set_title(title)
|
||||
|
||||
def set_message(self, message: str) -> None:
|
||||
"""
|
||||
Set the log message. This will delete any previous message
|
||||
"""
|
||||
"""Set the log message. This will delete any previous message"""
|
||||
buffer = self.text_view.get_buffer()
|
||||
buffer.set_text(message)
|
||||
|
||||
@@ -54,9 +51,7 @@ class Logs(Gtk.Box):
|
||||
self.text_view.scroll_to_mark(mark, 0.05, True, 0.0, 1.0)
|
||||
|
||||
def append_message(self, message: str) -> None:
|
||||
"""
|
||||
Append to the end of a potentially existent log message
|
||||
"""
|
||||
"""Append to the end of a potentially existent log message"""
|
||||
buffer = self.text_view.get_buffer()
|
||||
end_iter = buffer.get_end_iter()
|
||||
buffer.insert(end_iter, message) # type: ignore
|
||||
|
||||
Reference in New Issue
Block a user