chore: clear typehinting errors

This commit is contained in:
aclist 2026-05-03 02:14:08 +09:00
parent 99083d207a
commit 9bfa39c17e
14 changed files with 160 additions and 142 deletions

View File

@ -101,8 +101,8 @@ def get_delimited_mods(steam_path: Path) -> list[Any]:
continue
size = get_mod_size(mod)
# NOTE: final col is cell renderer highlight toggle
clean.append([meta.name, symlink, mod_dir, size, False])
clean.sort(key=lambda row: row[0].casefold())
clean.append([meta.name.casefold(), symlink, mod_dir, size, False])
clean.sort(key=lambda row: row[0])
return clean

View File

@ -9,7 +9,7 @@ import subprocess
import threading
from dataclasses import dataclass
from typing import Optional, TYPE_CHECKING, Union
from typing import Any, Optional, TYPE_CHECKING, Union
from dzgui.api.bm import map_id_to_record
from dzgui.const.constants import APP_NAME, REQUEST_TIMEOUT
@ -228,7 +228,7 @@ def query_direct(ip: str, qport: int, timeout: float = 3.0) -> dict | None:
class Res:
status: int
parsed: bool
json: Union[str, None]
json: Union[dict[str, Any], None]
@dataclass(slots=True, frozen=True)
@ -393,13 +393,13 @@ def query_api(key: str, appid: int, param: str) -> Res:
res.raise_for_status()
parsed = True
status = 200
data = res.json()
json = res.json()
except Exception:
parsed = False
data = None
json = None
status = 403
finally:
return Res(status, parsed, data)
return Res(status, parsed, json)
def validate_ip(addr: str) -> Record:
@ -453,7 +453,7 @@ def query_by_id(addr: str, key: str) -> Optional[dict]:
return None
def query_playercount(record: Record) -> Optional[tuple[int]]:
def query_playercount(record: Record) -> tuple[int, int] | None:
try:
res = query_direct(record.ip, record.qport)
players = int(res["players"])
@ -462,7 +462,7 @@ def query_playercount(record: Record) -> Optional[tuple[int]]:
queue = int(r[1].split(",")[0])
except IndexError:
queue = 0
return (players, queue)
return players, queue
except Exception as e:
logger.critical(e)
return None

View File

@ -72,7 +72,7 @@ class ConfigManager:
# TODO: do this on demand if/when in options page
# self.mediator.grid.notebook.settings.populate_settings()
def get_config(self) -> dict:
def get_config(self) -> Any:
# TODO: is this being called twice?
try:
return read_json(self.config)

View File

@ -33,61 +33,55 @@ class ContextMenuManager:
self.treeview = treeview
def process(self, action: ContextMenu) -> None:
match action:
# UNTHREADED
case ContextMenu.ADD_NOTE:
dialog = NoteDialog(self.controller)
dialog.run()
case ContextMenu.COPY_LOG_CLIPBOARD:
self.copy_log()
case ContextMenu.COPY_SERVER_IP:
self.copy_server_ip()
case ContextMenu.COPY_SERVER_NAME:
self.copy_server_name()
case ContextMenu.DELETE_MOD:
self.controller.delete_mods()
case ContextMenu.OPEN_WORKSHOP:
self.open_mod_page()
case ContextMenu.SET_FAV:
name = self.treeview.get_value_at_index(0)
record_str = self.treeview.get_record_string()
simple = self.treeview.get_simplified_ip()
self.controller.set_fav(name, record_str, simple)
if isinstance(self.treeview, ServerTreeView):
record = self.treeview.get_record()
if record is None:
return
# THREADED
case ContextMenu.ADD_SERVER:
record = self.treeview.get_record()
if record is None:
return
self.controller.add_by_record(record)
case ContextMenu.CONNECT:
record = self.treeview.get_record()
if record is None:
return
self.controller.connect_by_record(record)
case ContextMenu.REFRESH_PLAYERS:
record = self.treeview.get_record()
if record is None:
return
self.controller.refresh_players(record)
case ContextMenu.REMOVE_HISTORY:
record = self.treeview.get_record()
if record is None:
return
self.controller.remove_from_history(record)
case ContextMenu.REMOVE_SERVER:
record = self.treeview.get_record()
if record is None:
return
self.controller.remove_by_record(record)
case ContextMenu.SHOW_DETAILS:
record = self.treeview.get_record()
if record is None:
return
self.controller.get_details(record)
case ContextMenu.SHOW_MODS:
record = self.treeview.get_record()
self.controller.get_modlist(record)
match action:
# UNTHREADED
case ContextMenu.ADD_NOTE:
dialog = NoteDialog(self.controller)
dialog.run()
case ContextMenu.COPY_SERVER_IP:
self.copy_server_ip()
case ContextMenu.COPY_SERVER_NAME:
self.copy_server_name()
case ContextMenu.SET_FAV:
name = self.treeview.get_value_at_index(0)
# NOTE: fully qualified ip
fqip = self.treeview.get_record_string()
# NOTE: short ip for display purposes
simple = self.treeview.get_simplified_ip()
self.controller.set_fav(name, fqip, simple)
# THREADED
case ContextMenu.ADD_SERVER:
self.controller.add_by_record(record)
case ContextMenu.CONNECT:
self.controller.connect_by_record(record)
case ContextMenu.REFRESH_PLAYERS:
self.controller.refresh_players(record)
case ContextMenu.REMOVE_HISTORY:
self.controller.remove_from_history(record)
case ContextMenu.REMOVE_SERVER:
self.controller.remove_by_record(record)
case ContextMenu.SHOW_DETAILS:
self.controller.get_details(record)
case ContextMenu.SHOW_MODS:
self.controller.get_modlist(record)
if isinstance(self.treeview, ModTreeView):
match action:
case ContextMenu.DELETE_MOD:
self.controller.delete_mods()
case ContextMenu.OPEN_WORKSHOP:
self.open_mod_page()
if isinstance(self.treeview, LogTreeView):
match action:
case ContextMenu.COPY_LOG_CLIPBOARD:
self.copy_log()
def copy_server_ip(self) -> None:
if not isinstance(self.treeview, ServerTreeView):

View File

@ -76,7 +76,7 @@ class FilterManager:
def append_map(self, row: list[str]) -> None:
self.map_store.append(row)
def set_unique_maps(self, maps: list) -> None:
def set_unique_maps(self, maps: list[str]) -> None:
if maps is None:
return
if len(maps) < 1:

View File

@ -2,7 +2,7 @@ import logging
import shutil
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any, TYPE_CHECKING
from dzgui.api.mods import (
get_delimited_mods,
@ -60,7 +60,7 @@ class ModManager:
func = StoredFunc(self._on_mods_loaded, mods)
self.thread_man.set_cleanup_func(func)
def _on_mods_loaded(self, mods: list[list[str, str, str, float, bool]]) -> None:
def _on_mods_loaded(self, mods: list[list[Any]]) -> None:
self.store = ModelFactory().make_mod_store()
self.store.extend(mods)
self.treeview.set_model(self.store)
@ -83,14 +83,20 @@ class ModManager:
def get_mod_from_tree_path(
self, tree_path: Gtk.TreePath
) -> tuple[str, Gtk.TreeIter]:
) -> tuple[str, Gtk.TreeIter] | None:
model = self.treeview.get_model()
if model is None:
return None
tree_iter = model.get_iter(tree_path)
mod = model.get(tree_iter, 2)[0]
mod = model.get_value(tree_iter, 2)[0]
print(mod)
return mod, tree_iter
def delete_single_mod(self, tree_path: Gtk.TreePath) -> Gtk.TreeIter:
mod, _iter = self.get_mod_from_tree_path(tree_path)
def delete_single_mod(self, tree_path: Gtk.TreePath) -> Gtk.TreeIter | None:
res = self.get_mod_from_tree_path(tree_path)
if res is None:
return None
mod, _iter = res
steam_path = Path(self.path)
mods_path = get_local_mod_path(steam_path)
@ -118,12 +124,17 @@ class ModManager:
#if model:
# model.remove(_iter) # FIXME: do not remove from model interface directly
remove_stale_signatures(self.prefs.paths.config, self.prefs.paths.version)
mods = len(self.treeview.get_model())
model = self.treeview.get_model()
if model is None:
return
mods = len(model)
msg = self.format_mod_statusbar()
self.emitter.emit("mods_updated", msg, mods)
def uncolorize_mods(self) -> None:
model = self.treeview.get_model()
if model is None:
return
for mod in model:
_iter = mod.iter
path = model.get_path(_iter)
@ -175,6 +186,8 @@ class ModManager:
def select_colorized(self) -> None:
model = self.treeview.get_model()
if model is None:
return
sel = self.treeview.get_selection()
for mod in model:
it = mod.iter

View File

@ -32,8 +32,9 @@ class ProxyModelManager:
self.proxy_model: "FastInsertListStore" = None
self.filter_man = filter_man
self.control_model: list = None
self.filtered: list = None
# TODO: list typehints
self.control_model: list | None = None
self.filtered: list | None = None
self.success = True
def has_control_model(self) -> bool:
@ -61,9 +62,9 @@ class ProxyModelManager:
def remove_row_from_control(self, record: "Record") -> None:
addr = f"{record.ip}:{record.gameport}"
qport = record.qport
for record in self.control_model:
if addr == record[7] and qport == record[8]:
self.control_model.remove(record)
for row in self.control_model:
if addr == row[7] and qport == row[8]:
self.control_model.remove(row)
break
self.wipe_cache()
self.filter(FilterMode.INITIAL, skip_cache=True)

View File

@ -86,7 +86,7 @@ class ServerModelManager:
key = config_man.lookup(Preferences.STEAM)
job = Servers.query_api
params = Servers.params
servers: list[dict[Any, Any]] = []
servers = []
with ThreadPoolExecutor() as executor:
futures = [executor.submit(job, key, APPID_DAYZ, param) for param in params]
for future in as_completed(futures):
@ -105,7 +105,8 @@ class ServerModelManager:
)
return
j = res.json
servers += j["response"]["servers"]
if j is not None:
servers.extend(j["response"]["servers"])
except Exception as e:
logger.critical(e)
self.thread_man.set_cleanup_func(
@ -117,7 +118,8 @@ class ServerModelManager:
res = Servers.query_api(key, APPID_DAYZ_EXP, "")
if res.status == 200 and res.parsed is True:
j = res.json
servers += j["response"]["servers"]
if j is not None:
servers.extend(j["response"]["servers"])
parsed = Servers.parse_json(servers)
self._push_data(parsed)
@ -364,14 +366,15 @@ class ServerModelManager:
if self.first_iteration:
self._update_maps()
def _cleanup_on_failure(self, show_dialog=True) -> None:
def _cleanup_on_failure(self, show_dialog: bool = True) -> None:
# TODO: disable map, keyword, and filter widgets if model is None
# -> signal driven (servers_empty, servers_failed_to_load)
if self.preserve_on_fail is False:
# CHORE: test if maps are cleared on failure
self.tv.set_model(None)
filter_man = self.tv.get_filter_man()
filter_man.set_unique_maps(None)
#filter_man = self.tv.get_filter_man()
#filter_man.set_unique_maps([])
# TODO: emit signal to not disable widget sensitivity
# TODO: distinguish signals, e.g. "servers_failed_to_load", "servers_loaded_empty"

View File

@ -13,7 +13,7 @@ connect_entry_placeholder="Enter IP (IP:Query port) or Battlemetrics ID (numeric
fav_heading="Favorite server"
favs_empty="None set. Right click a server and select 'Set favorite' to set."
lan_heading="LAN query port",
lan_heading="LAN query port"
lan_default_button="Default port (27016)"
lan_custom_button="Custom port"
lan_scan_button="Scan"

View File

@ -59,7 +59,7 @@ class ServerDialog(GenericDialog):
class ServerDetailsDialog(ServerDialog):
def __init__(self, controller, details: "Details"):
def __init__(self, controller: "Controller", details: "Details"):
name = controller.get_server_name()
super().__init__(controller, strings.server_details, name)
@ -96,6 +96,9 @@ class ServerDetailsDialog(ServerDialog):
self.pack(scrollable_message)
if details.data is None:
return
for row in details.data:
self.store.append(row + [Pango.Weight.BOLD])
self.view.set_model(self.store)
@ -115,7 +118,7 @@ class ServerDetailsDialog(ServerDialog):
class ServerModDialog(ServerDialog):
def __init__(self, controller, mods: list[str]):
def __init__(self, controller: "Controller", mods: list[list[str]]):
name = controller.get_server_name()
super().__init__(controller, strings.modlist, name)

View File

@ -76,15 +76,20 @@ class ServerNotebook(Gtk.ScrolledWindow):
if cur_page == saved:
return
page = self.notebook.get_nth_page(saved)
if not page:
return
label = self.notebook.get_tab_label(page)
if label is None:
return
label.set_text(f"{server_labels.saved}*")
if hasattr(label, "set_text"):
label.set_text(f"{server_labels.saved}*")
def _on_servers_loaded(self, emitter: "Emitter", tab: "ServerTab") -> None:
# NOTE: workaround for GTK bug where fullscreen causes headers to vanish when model is None
# TODO: this should be internal to servers page
tv = self.get_active_treeview()
if tv is None:
return
state = False if tv.get_model() is None else True
tv.set_headers_visible(state)
tv.set_loaded(True)
@ -114,14 +119,19 @@ class ServerNotebook(Gtk.ScrolledWindow):
return False
def grab_content_area(self) -> None:
self.get_active_treeview().grab_focus()
tv = self.get_active_treeview()
if tv is not None:
tv.grab_focus()
def get_current_tab_text(self) -> str:
ind = self.notebook.get_current_page()
child = self.notebook.get_nth_page(ind)
if child is None:
return ""
return self.notebook.get_tab_label_text(child)
text = self.notebook.get_tab_label_text(child)
if text is None:
return ""
return text
def _on_page_changed(
self, notebook: Gtk.Notebook, child: ScrollableTree, index: int
@ -130,21 +140,27 @@ class ServerNotebook(Gtk.ScrolledWindow):
return
label = self.notebook.get_tab_label_text(child)
# TODO: strings
text = label.strip("*")
self.notebook.set_tab_label_text(child, text)
if label is not None:
# TODO: strings
text = label.strip("*")
self.notebook.set_tab_label_text(child, text)
tree = child.get_tree()
self.emitter.emit("server_page_changed", tree)
# NOTE: spawns a thread
self.controller.populate_model(self.get_active_treeview())
tv = self.get_active_treeview()
if tv is not None:
self.controller.populate_model(tv)
def get_active_treeview(self) -> ServerTreeView:
def get_active_treeview(self) -> ServerTreeView | None:
index = self.notebook.get_current_page()
scrollable = self.notebook.get_nth_page(index)
treeview = scrollable.get_children()[0]
return treeview
if scrollable is not None and hasattr(scrollable, "get_children"):
tv = scrollable.get_children()[0]
if isinstance(tv, ServerTreeView):
return tv
return None
def add_notification(self) -> None:
saved = self.notebook.get_nth_page(1)

View File

@ -21,7 +21,7 @@ if TYPE_CHECKING:
logger = logging.getLogger(APP_NAME)
class LogTreeView(ContextMixin, TreeView):
class LogTreeView(ContextMixin, TreeView): # type: ignore
def __init__(self, controller: "Controller") -> None:
super().__init__(controller, menu=ContextMenuGroup.LOG)
@ -56,7 +56,8 @@ class LogTreeView(ContextMixin, TreeView):
sortable = Gtk.TreeModelSort(_filter)
self.set_model(sortable)
_filter.refilter()
self.set_cursor(0)
path = Gtk.TreePath.new_from_indices([0])
self.set_cursor(path)
def toggle_filter(self, _filter: str) -> None:
if _filter in self.filters:
@ -64,19 +65,20 @@ class LogTreeView(ContextMixin, TreeView):
else:
self.filters.append(_filter)
# NOTE: unwrap TreeModelSort and TreeModelFilter
self.get_model().get_model().refilter()
self.get_model().get_model().refilter() # type: ignore
def _filter_rows(
self, model: Gtk.ListStore, _iter: Gtk.TreeIter, data: Any
) -> None:
self, filter_model: Gtk.TreeModelFilter, _iter: Gtk.TreeIter, data: Any
) -> bool:
if len(self.filters) < 1:
return False
return model[_iter][1] in self.filters
return filter_model[_iter][1] in self.filters
def _on_log_buttonpress(self, widget: Gtk.Widget, event: Gdk.EventButton) -> None:
def _on_log_buttonpress(self, widget: Gtk.Widget, event: Gdk.EventButton) -> bool:
if event.button == Gdk.BUTTON_SECONDARY:
self.present_menu(widget, event)
return True
return False
def _on_log_keypress(self, widget: Gtk.Widget, event: Gdk.EventKey) -> None:
self.present_menu(widget, event)

View File

@ -21,7 +21,7 @@ if TYPE_CHECKING:
logger = logging.getLogger(APP_NAME)
class ModTreeView(ModsMixin, ContextMixin, TreeView):
class ModTreeView(ModsMixin, ContextMixin, TreeView): # type: ignore
def __init__(self, controller: "Controller") -> None:
super().__init__(controller, menu=ContextMenuGroup.MOD)
self.controller = controller
@ -64,31 +64,17 @@ class ModTreeView(ModsMixin, ContextMixin, TreeView):
def _on_mods_updated(self, emitter: "Emitter", msg: str, mods: int) -> None:
if mods < 1:
return
self.set_cursor(0)
path = Gtk.TreePath.new_from_indices([0])
self.set_cursor(path)
def get_selected_mod(self) -> str:
path = self.get_focused_row_path()
model = self.get_model()
tree_iter = model.get_iter(path)
mod = model.get(tree_iter, 2)[0]
return mod
# return mod, tree_iter
return str(mod)
# def _on_mods_keypress(self, widget: Gtk.Widget, event: Gdk.EventKey) -> None:
# # TODO: multiselect
# # if event.keyval is Gdk.KEY_space:
# # it = self.get_focused_row_iter()
# # self.get_selection().select_iter(it)
# # path = self.get_focused_row_path()
# # self.set_cursor(path)
# # return False
# self.present_menu(widget, event)
# def _on_mods_button_press(self, widget: Gtk.Widget, event: Gdk.EventButton) -> None:
# if event.button == 3:
# self.present_menu(widget, event)
def _parent_selection_changed(self, base_class: TreeView, sel: Gtk.TreeSelection):
def _parent_selection_changed(self, base_class: TreeView, sel: Gtk.TreeSelection) -> None:
pass
def _format_color(

View File

@ -35,7 +35,7 @@ if TYPE_CHECKING:
QUEUE_CHECK_DELAY = 200
class ServerTreeView(ContextMixin, TreeView):
class ServerTreeView(ContextMixin, TreeView): # type: ignore
def __init__(
self, controller: "Controller", enum: ServerTab, menu: ContextMenuGroup
) -> None:
@ -58,9 +58,9 @@ class ServerTreeView(ContextMixin, TreeView):
self.queue_id: int
self.handler_id: int
self.queue = Queue()
self.queue: Queue = Queue()
self.seen_cache = []
self.seen_cache: list[str] = []
prefs = self.controller.get_prefs()
columns = prefs.paths.columns
@ -119,7 +119,7 @@ class ServerTreeView(ContextMixin, TreeView):
def _on_tooltip(
self,
widget: Gtk.Widget,
treeview: Self,
x: int,
y: int,
keyboard_mode: bool,
@ -128,12 +128,13 @@ class ServerTreeView(ContextMixin, TreeView):
"""
Present record data for the hovered row even if it is unfocused
"""
coords = widget.convert_widget_to_bin_window_coords(x, y)
path = self.get_path_at_pos(coords.bx, coords.by)
if path is None:
coords_x, coords_y = treeview.convert_widget_to_bin_window_coords(x, y)
path = self.get_path_at_pos(coords_x, coords_y)
if path is None or path[0] is None:
return False
model = self.get_model()
if model is None:
return False
tree_iter = model.get_iter(path[0])
ip = model.get_value(tree_iter, 7)
qport = model.get_value(tree_iter, 8)
@ -188,7 +189,7 @@ class ServerTreeView(ContextMixin, TreeView):
# NOTE: get final width after drag action completes
GLib.idle_add(self.controller.propagate_column_width, col)
def start_distcalc(self, emitter: Optional["Emitter"] = None):
def start_distcalc(self, emitter: Union["Emitter", None] = None) -> None:
self.emitter.emit("distcalc_started")
record = self.get_record()
if record is None:
@ -248,14 +249,10 @@ class ServerTreeView(ContextMixin, TreeView):
case Gdk.KEY_r:
# TODO: unimplemented, needs threading
self.controller.menu_action(ContextMenu.REFRESH_PLAYERS, self)
case _:
return False
return True
else:
match event.keyval:
case Gdk.KEY_l | Gdk.KEY_Right:
self.emitter.emit("request_button_box_focus")
return True
case _:
self.emitter.emit("check_button_pressed", event.keyval)
@ -266,12 +263,13 @@ class ServerTreeView(ContextMixin, TreeView):
path = pathlist[0]
tree_iter = model.get_iter(path)
has_mods = model.get_value(tree_iter, 11)
return has_mods
return bool(has_mods)
def get_selected_row(self) -> Gtk.TreeModelRow:
sel = self.get_selection()
sels = sel.get_selected_rows()
return sels[0]
#def get_selected_row(self) -> Gtk.TreeModelRow:
# sel = self.get_selection()
# sels = sel.get_selected_rows()
# print(type(sels[0]))
# return sels[0]
def is_in_favs(self) -> bool:
record = self.get_record_string()
@ -283,6 +281,8 @@ class ServerTreeView(ContextMixin, TreeView):
self, tree: TreeView, path: Gtk.TreePath, column: Gtk.TreeViewColumn
) -> None:
record = self.get_record()
if record is None:
return
self.controller.connect_by_record(record)
def _parent_selection_changed(
@ -321,14 +321,14 @@ class ServerTreeView(ContextMixin, TreeView):
def set_loaded(self, status: bool) -> None:
self.loaded = status
def get_model_and_control_model(self) -> tuple[Union["FastInsertListStore", None], list[Any]]:
def get_model_and_control_model(self) -> tuple[Union[Gtk.TreeModel, None], list[Any]]:
model = self.get_model()
control = self.proxy_man.get_control()
return model, control
@staticmethod
def ping_server(
model, _iter: Gtk.TreeIter, ip: str, qport: int, ping_column: int
model: "FastInsertListStore", _iter: Gtk.TreeIter, ip: str, qport: int, ping_column: int
) -> None:
_ping = ping(ip, qport)
GLib.idle_add(lambda: model.set(_iter, ping_column, _ping))