umu and typing fixes

This commit is contained in:
Mathieu Comandon
2025-07-04 03:27:15 -07:00
parent 5d0a9d21e3
commit 1ae2fe1085
14 changed files with 149 additions and 147 deletions

View File

@@ -96,7 +96,7 @@ class LutrisApplication(Gtk.Application):
self.tray = None
self.quit_on_game_exit = False
self.style_manager = None
self.style_manager = StyleManager()
if os.geteuid() == 0:
NoticeDialog(_("Do not run Lutris as root."))
@@ -340,8 +340,6 @@ class LutrisApplication(Gtk.Application):
self.add_action(action)
self.add_accelerator("<Primary>q", "app.quit")
self.style_manager = StyleManager()
def do_activate(self): # pylint: disable=arguments-differ
if not self.window:
self.window = LutrisWindow(application=self)

View File

@@ -1,7 +1,7 @@
from gettext import gettext as _
from typing import Any, Dict, Optional
from gi.repository import Gio, Gtk
from gi.repository import Gio, Gtk # type: ignore
from lutris import settings
from lutris.gui.config.base_config_box import BaseConfigBox

View File

@@ -4,9 +4,9 @@ import os
from abc import ABC, abstractmethod
from gettext import gettext as _
from inspect import Parameter, signature
from typing import Any, Callable, Dict, List, Optional, Union
from typing import Any, Callable, Dict, List, Optional
from gi.repository import Gdk, Gtk
from gi.repository import Gdk, Gtk # type: ignore
from lutris.config import LutrisConfig
from lutris.gui.widgets import NotificationSource
@@ -50,7 +50,7 @@ class WidgetGenerator(ABC):
# These are outputs set by generate_widget() or generate_container()
# and they are reset on each call.
self.wrapper: Optional[Gtk.Widget] = None
self.wrapper: Optional[Gtk.Box] = None
self.default_value = None
self.tooltip_default: Optional[str] = None
self.options: Dict[str, Dict[str, Any]] = {}
@@ -59,9 +59,9 @@ class WidgetGenerator(ABC):
self.warning_messages: List[Gtk.Widget] = []
# These accumulate results across all widgets
self.wrappers: Dict[str, Gtk.Widget] = {}
self.wrappers: Dict[str, Gtk.Container] = {}
self.section_frames: List[SectionFrame] = []
self.option_containers: Dict[str, Gtk.Widget] = {}
self.option_containers: Dict[str, Gtk.Container] = {}
self._generators: Dict[str, WidgetGenerator.GeneratorFunction] = {
"label": self._generate_label,
@@ -98,7 +98,7 @@ class WidgetGenerator(ABC):
# Widget Construction
def add_container(self, option: Dict[str, Any], wrapper: Gtk.Box = None) -> Optional[Gtk.Widget]:
def add_container(self, option: Dict[str, Any], wrapper: Optional[Gtk.Box] = None) -> Optional[Gtk.Widget]:
"""Generates the option's widget, wrapper and container, and adds the container to the parent;
if the option uses 'section', then the container is actually placed inside a SectionFrame,
or in the previous frame if it is for the same section."""
@@ -122,7 +122,7 @@ class WidgetGenerator(ABC):
self._current_parent.pack_start(option_container, False, False, 0)
return option_container
def generate_container(self, option: Dict[str, Any], wrapper: Gtk.Box = None) -> Optional[Gtk.Widget]:
def generate_container(self, option: Dict[str, Any], wrapper: Optional[Gtk.Box] = None) -> Optional[Gtk.Widget]:
"""Creates the widget, wrapper, and container; this returns the container
(or the wrapper if there's no container)."""
option_widget = self.generate_widget(option, wrapper)
@@ -145,7 +145,7 @@ class WidgetGenerator(ABC):
else:
return None
def generate_widget(self, option: Dict[str, Any], wrapper: Gtk.Box = None) -> Optional[Gtk.Widget]:
def generate_widget(self, option: Dict[str, Any], wrapper: Optional[Gtk.Box] = None) -> Optional[Gtk.Widget]:
"""This creates a wrapper box and a label and widget within it according to the options dict
given. The option widget itself, is returned, but this method also sets attributes on the
generator. You get 'wrapper', 'default_value', 'tooltip_default' and 'option_widget' which restates
@@ -232,7 +232,7 @@ class WidgetGenerator(ABC):
return Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12, margin_bottom=6, visible=True)
def create_option_container(self, option: Dict[str, Any], wrapper: Gtk.Widget) -> Gtk.Widget:
def create_option_container(self, option: Dict[str, Any], wrapper: Gtk.Container) -> Gtk.Container:
"""This creates a wrapper box around the widget wrapper, to support additional controls. The
base implementation wraps 'wrapper' in a Box with the error and warning widgets; if
there are none it just returns 'wrapper'."""
@@ -292,15 +292,15 @@ class WidgetGenerator(ABC):
frame.set_visible(visible)
frame.set_no_show_all(not visible)
def update_option_container(self, option, container: Gtk.Box, wrapper: Gtk.Box):
def update_option_container(self, option, container: Gtk.Container, wrapper: Gtk.Container):
"""This method updates an option container and its wrapper; this re-evaluates the
relevant options in case they contain callables and those callables return different
results."""
# Update messages in message boxes that support it
for ch in container.get_children():
if hasattr(ch, "update_message"):
ch.update_message(option, self)
for child in container.get_children():
if hasattr(child, "update_message"):
child.update_message(option, self)
# Hide entire container if the option is not visible
visible = self.get_visibility(option)
@@ -308,7 +308,7 @@ class WidgetGenerator(ABC):
container.set_no_show_all(not visible)
# Grey out option if condition unmet, or if a second setting is False
condition = self.get_condition(option)
condition: bool = self.get_condition(option)
wrapper.set_sensitive(condition)
# Widget factories
@@ -600,7 +600,7 @@ class WidgetGenerator(ABC):
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
label = Label(label + ":")
label.set_halign(Gtk.Align.START)
button = Gtk.Button(_("Add Files"))
button = Gtk.Button(label=_("Add Files"))
button.connect("clicked", on_add_files_clicked)
button.set_margin_left(10)
vbox.pack_start(label, False, False, 5)
@@ -619,7 +619,7 @@ class WidgetGenerator(ABC):
for filename in files:
files_list_store.append([filename])
cell_renderer = Gtk.CellRendererText()
files_treeview = Gtk.TreeView(files_list_store)
files_treeview = Gtk.TreeView(model=files_list_store)
files_column = Gtk.TreeViewColumn(_("Files"), cell_renderer, text=0)
files_treeview.append_column(files_column)
files_treeview.connect("key-press-event", on_files_treeview_keypress)
@@ -709,7 +709,7 @@ class WidgetGenerator(ABC):
True, and if it is callable this calls it. Subclasses can add further conditions."""
return self._evaluate_flag_option("visible", option)
def get_condition(self, option: Dict[str, Any]) -> Union[None, bool, Callable]:
def get_condition(self, option: Dict[str, Any]) -> bool:
"""Extracts the 'condition' option; but also the 'conditional_on' option, and if both
are present, then if either indicates the control should be disabled this will be false.."""
condition = self._evaluate_flag_option("condition", option)
@@ -722,8 +722,8 @@ class WidgetGenerator(ABC):
container = self.option_containers[option["option"]]
for ch in container.get_children():
if hasattr(ch, "blocks_sensitivity") and ch.blocks_sensitivity:
for child in container.get_children():
if hasattr(child, "blocks_sensitivity") and child.blocks_sensitivity:
return False
return condition

View File

@@ -1,7 +1,7 @@
from gettext import gettext as _
from typing import Optional
from gi.repository import Gdk, Gtk
from gi.repository import Gdk, Gtk # type: ignore
from lutris.exceptions import UnavailableRunnerError
from lutris.game import Game
@@ -79,7 +79,7 @@ class InstallUIDelegate(Delegate):
"""
return None
def download_install_file(self, url: str, destination: str) -> Downloader:
def download_install_file(self, url: str, destination: str) -> bool:
"""Downloads a file from a URL to a destination, overwriting any
file at that path.
@@ -137,7 +137,7 @@ class DialogInstallUIDelegate(InstallUIDelegate):
dlg = dialogs.FileDialog(message)
return dlg.filename
def download_install_file(self, url, destination):
def download_install_file(self, url: str, destination: str) -> bool:
dialog = DownloadDialog(url, destination, parent=self)
dialog.run()
return dialog.downloader.state == Downloader.COMPLETED

View File

@@ -1,8 +1,8 @@
# pylint: disable=no-member
import os
from typing import Any, Callable, Dict, Iterable, List, Set
from typing import Any, Callable, Dict, Iterable, List, Optional, Set
from gi.repository import GObject, Gtk
from gi.repository import GObject, Gtk # type: ignore
from lutris.gui.widgets.gi_composites import GtkTemplate
from lutris.gui.widgets.progress_box import ProgressBox
@@ -22,7 +22,7 @@ class DownloadQueue(Gtk.ScrolledWindow):
__gtype_name__ = "DownloadQueue"
download_box: Gtk.Box = GtkTemplate.Child()
download_box: Gtk.Box = GtkTemplate.Child() # type: ignore
CompletionFunction = Callable[[Any], None]
ErrorFunction = Callable[[Exception], None]
@@ -99,9 +99,9 @@ class DownloadQueue(Gtk.ScrolledWindow):
self,
operation: Callable[[], Any],
progress_function: ProgressBox.ProgressFunction,
completion_function: CompletionFunction = None,
error_function: ErrorFunction = None,
operation_name: str = None,
completion_function: Optional[CompletionFunction] = None,
error_function: Optional[ErrorFunction] = None,
operation_name: Optional[str] = None,
) -> bool:
"""Runs 'operation' on a thread, while displaying a progress bar. The 'progress_function'
controls this progress bar, and it is removed when the 'operation' completes.
@@ -129,9 +129,9 @@ class DownloadQueue(Gtk.ScrolledWindow):
self,
operation: Callable[[], Any],
progress_functions: Iterable[ProgressBox.ProgressFunction],
completion_function: CompletionFunction = None,
error_function: ErrorFunction = None,
operation_names: List[str] = None,
completion_function: Optional[CompletionFunction] = None,
error_function: Optional[ErrorFunction] = None,
operation_names: Optional[List[str]] = None,
) -> bool:
"""Runs 'operation' on a thread, while displaying a set of progress bars. The
'progress_functions' control these progress bars, and they are removed when the
@@ -152,6 +152,8 @@ class DownloadQueue(Gtk.ScrolledWindow):
if not self.running_operation_names.isdisjoint(operation_names):
return False
self.running_operation_names.update(operation_names)
else:
operation_names = []
# Must capture the functions, since in earlier (<3.8) Pythons functions do not provide
# value equality, so we need to make sure we're always using what we started with.

View File

@@ -96,7 +96,7 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
version_notification_label: Gtk.Revealer = GtkTemplate.Child()
show_hidden_games_button: Gtk.ModelButton = GtkTemplate.Child()
def __init__(self, application, **kwargs) -> None:
def __init__(self, application=None, **kwargs) -> None:
width = int(settings.read_setting("width") or self.default_width)
height = int(settings.read_setting("height") or self.default_height)
super().__init__(
@@ -150,7 +150,7 @@ class LutrisWindow(Gtk.ApplicationWindow, DialogLaunchUIDelegate, DialogInstallU
# Since system-search-symbolic is already *right there* we'll try to pick some
# other icon for the button that shows the search popover.
fallback_filter_icons_names = ["filter-symbolic", "edit-find-replace-symbolic", "system-search-symbolic"]
filter_button_image = self.search_filters_button.get_child()
filter_button_image: Gtk.Image = self.search_filters_button.get_child()
for n in fallback_filter_icons_names:
if has_stock_icon(n):
filter_button_image.set_from_icon_name(n, Gtk.IconSize.BUTTON)

View File

@@ -27,15 +27,12 @@ See: https://gitlab.gnome.org/GNOME/pygobject/merge_requests/52
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
# USA
# Standard Library
import inspect
import os
import warnings
from os.path import abspath, join
# Third Party Libraries
from gi.repository import Gio, GLib, GObject, Gtk
from gi.repository import Gio, GLib, GObject, Gtk # type: ignore
# Lutris Modules
from lutris.gui.dialogs import ErrorDialog
__all__ = ["GtkTemplate"]
@@ -58,12 +55,13 @@ def _connect_func(builder, obj, signal_name, handler_name, connect_object, flags
template_inst = builder.get_object(cls.__gtype_name__)
if template_inst is None: # This should never happen
errmsg = (
warnings.warn(
"Internal error: cannot find template instance! obj: %s; "
"signal: %s; handler: %s; connect_obj: %s; class: %s"
% (obj, signal_name, handler_name, connect_object, cls)
% (obj, signal_name, handler_name, connect_object, cls),
GtkTemplateWarning,
stacklevel=2,
)
warnings.warn(errmsg, GtkTemplateWarning)
return
handler = getattr(template_inst, handler_name)
@@ -145,8 +143,11 @@ def _init_template(self, cls, base_init_template):
)
for name in self.__gtemplate_methods__.difference(connected_signals):
errmsg = ("Signal '%s' was declared with @GtkTemplate.Callback " + "but was not present in template") % name
warnings.warn(errmsg, GtkTemplateWarning)
warnings.warn(
"Signal '%s' was declared with @GtkTemplate.Callback but was not present in template" % name,
GtkTemplateWarning,
stacklevel=2,
)
# TODO: Make it easier for IDE to introspect this
@@ -242,7 +243,7 @@ class _GtkTemplate:
TODO: Alternatively, could wait until first class instantiation
before registering templates? Would need a metaclass...
"""
_GtkTemplate.__ui_path__ = abspath(join(*path)) # pylint: disable=no-value-for-parameter
_GtkTemplate.__ui_path__ = os.path.abspath(os.path.join(*path))
def __init__(self, ui):
self.ui = ui
@@ -260,13 +261,13 @@ class _GtkTemplate:
try:
template_bytes = Gio.resources_lookup_data(self.ui, Gio.ResourceLookupFlags.NONE)
except GLib.GError:
except GLib.GError: # type: ignore
ui = self.ui
if isinstance(ui, (list, tuple)):
ui = join(ui)
ui = " ".join(ui)
if _GtkTemplate.__ui_path__ is not None:
ui = join(_GtkTemplate.__ui_path__, ui)
ui = os.path.join(_GtkTemplate.__ui_path__, ui)
with open(ui, "rb") as fp:
template_bytes = GLib.Bytes.new(fp.read())

View File

@@ -4,8 +4,10 @@
import os
import shlex
import time
from typing import Dict, Optional, Tuple
from lutris import runtime, settings
from lutris.config import LutrisConfig
from lutris.monitored_command import MonitoredCommand
from lutris.runners import import_runner
from lutris.util import linux, system
@@ -63,7 +65,7 @@ def set_regedit_file(filename, wine_path=None, prefix=None, arch=WINE_DEFAULT_AR
# a bug in Wine. see: https://github.com/lutris/lutris/issues/804
wine_path = wine_path + "64"
if proton.is_proton_path(wine_path):
if not wine_path or proton.is_proton_path(wine_path):
proton_verb = "run"
wineexec(
@@ -80,7 +82,7 @@ def set_regedit_file(filename, wine_path=None, prefix=None, arch=WINE_DEFAULT_AR
def delete_registry_key(key, wine_path=None, prefix=None, arch=WINE_DEFAULT_ARCH, proton_verb=None):
"""Deletes a registry key from a Wine prefix"""
if proton.is_proton_path(wine_path):
if not wine_path or proton.is_proton_path(wine_path):
proton_verb = "run"
wineexec(
@@ -98,7 +100,6 @@ def create_prefix(
prefix, wine_path=None, arch=WINE_DEFAULT_ARCH, overrides=None, install_gecko=None, install_mono=None, runner=None
):
"""Create a new Wine prefix."""
# pylint: disable=too-many-locals
if overrides is None:
overrides = {}
if not prefix:
@@ -175,17 +176,21 @@ def create_prefix(
prefix_manager.setup_defaults()
def winekill(prefix, arch=WINE_DEFAULT_ARCH, wine_path=None, env=None, initial_pids=None, runner=None):
def winekill(prefix, arch=WINE_DEFAULT_ARCH, wine_path="", env=None, initial_pids=None, runner=None):
"""Kill processes in Wine prefix."""
initial_pids = initial_pids or []
if not env:
env = {"WINEARCH": arch, "WINEPREFIX": prefix}
if proton.is_proton_path(wine_path):
env = {
"WINEARCH": arch,
"WINEPREFIX": prefix,
"GAMEID": proton.DEFAULT_GAMEID,
"PROTON_VERB": "runinprefix",
}
if proton.is_umu_path(wine_path):
command = [wine_path, "wineboot", "-k"]
elif proton.is_proton_path(wine_path):
command = [proton.get_umu_path(), "wineboot", "-k"]
env["GAMEID"] = proton.DEFAULT_GAMEID
env["WINEPREFIX"] = prefix
env["PROTON_VERB"] = "runinprefix"
env["PROTONPATH"] = proton.get_proton_path_by_path(wine_path)
else:
if not wine_path:
@@ -232,33 +237,32 @@ def use_lutris_runtime(wine_path, force_disable=False):
return True
# pragma pylint: disable=too-many-locals
def wineexec(
executable,
args="",
wine_path=None,
prefix=None,
arch=None,
working_dir=None,
winetricks_wine="",
blocking=False,
config=None,
include_processes=None,
exclude_processes=None,
disable_runtime=False,
env=None,
executable: str,
prefix: str,
args: str = "",
wine_path: Optional[str] = None,
arch: str = WINE_DEFAULT_ARCH,
working_dir: Optional[str] = None,
winetricks_wine: str = "",
blocking: bool = False,
config: Optional[LutrisConfig] = None,
include_processes: Optional[list] = None,
exclude_processes: Optional[list] = None,
disable_runtime: bool = False,
env: Optional[dict] = None,
overrides=None,
runner=None,
proton_verb=None,
proton_verb: Optional[str] = None,
):
"""
Execute a Wine command.
Args:
executable (str): wine program to run, pass None to run wine itself
prefix (str): path to the wine prefix to use
args (str): program arguments
wine_path (str): path to the wine version to use
prefix (str): path to the wine prefix to use
arch (str): wine architecture of the prefix
working_dir (str): path to the working dir for the process
winetricks_wine (str): path to the wine version used by winetricks
@@ -271,12 +275,9 @@ def wineexec(
Process results if the process is running in blocking mode or
MonitoredCommand instance otherwise.
"""
if env is None:
env = {}
if exclude_processes is None:
exclude_processes = []
if include_processes is None:
include_processes = []
env = env or {}
exclude_processes = exclude_processes or []
include_processes = include_processes or []
executable = str(executable) if executable else ""
if isinstance(include_processes, str):
include_processes = shlex.split(include_processes)
@@ -300,7 +301,7 @@ def wineexec(
wineenv = {"WINEARCH": arch}
if winetricks_wine and winetricks_wine is not wine_path and not proton.is_proton_path(wine_path):
wineenv["WINE"] = winetricks_wine
else:
elif wine_path:
wineenv["WINE"] = wine_path
if prefix:
@@ -376,7 +377,9 @@ def wineexec(
# pragma pylint: enable=too-many-locals
def find_winetricks(env=None, system_winetricks=False):
def find_winetricks(
env: Optional[dict[str, str]] = None, system_winetricks=False
) -> Tuple[str, Optional[str], Dict[str, str]]:
"""Find winetricks path."""
winetricks_path = os.path.join(settings.RUNTIME_DIR, "winetricks/winetricks")
if system_winetricks or not system.path_exists(winetricks_path):
@@ -388,9 +391,7 @@ def find_winetricks(env=None, system_winetricks=False):
# working_dir, so it will find the data file.
working_dir = os.path.join(settings.RUNTIME_DIR, "winetricks")
if not env:
env = {}
env = env or {}
path = env.get("PATH", os.environ["PATH"])
env["PATH"] = "%s:%s" % (working_dir, path)
@@ -398,11 +399,11 @@ def find_winetricks(env=None, system_winetricks=False):
def winetricks(
app,
prefix=None,
arch=None,
silent=True,
wine_path=None,
app: Optional[str],
prefix: str,
arch: str = WINE_DEFAULT_ARCH,
silent: bool = True,
wine_path: Optional[str] = None,
config=None,
env=None,
disable_runtime=False,
@@ -411,39 +412,39 @@ def winetricks(
proton_verb=None,
):
"""Execute winetricks."""
winetricks_path, working_dir, env = find_winetricks(env, system_winetricks)
if not app:
silent = False
app = "--gui"
args = app
if not wine_path or proton.is_umu_path(wine_path):
winetricks_wine = proton.get_umu_path()
proton_verb = "waitforexitandrun"
if wine_path:
winetricks_wine = wine_path
if proton.is_proton_path(wine_path):
protonfixes_path = os.path.join(proton.get_proton_path_by_path(wine_path), "protonfixes")
if os.path.exists(protonfixes_path):
winetricks_wine = os.path.join(protonfixes_path, "winetricks")
winetricks_path = wine_path
if not app:
silent = False
app = "--gui"
else:
logger.info("winetricks: Valve official Proton builds do not support winetricks.")
return
elif proton.is_proton_path(wine_path):
proton_verb = "waitforexitandrun"
protonfixes_path = os.path.join(proton.get_proton_path_by_path(wine_path), "protonfixes")
if os.path.exists(protonfixes_path):
winetricks_wine = os.path.join(protonfixes_path, "winetricks")
winetricks_path = wine_path
if not app:
silent = False
app = "--gui"
else:
logger.error("winetricks: Valve official Proton builds do not support winetricks.")
return
else:
winetricks_path, working_dir, env = find_winetricks(env, system_winetricks)
if not runner:
runner = import_runner("wine")()
winetricks_wine = runner.get_executable()
if arch not in ("win32", "win64"):
arch = detect_arch(prefix, winetricks_wine)
args = app
if str(silent).lower() in ("yes", "on", "true") and not proton.is_proton_path(wine_path):
args = "-q " + args
else:
if proton.is_proton_path(wine_path):
proton_verb = "waitforexitandrun"
if arch not in ("win32", "win64"):
arch = detect_arch(prefix, winetricks_wine)
if str(silent).lower() in ("yes", "on", "true"):
args = "-q " + args
# Execute wineexec
return wineexec(
None,
"",
prefix=prefix,
winetricks_wine=winetricks_wine,
wine_path=winetricks_path,
@@ -483,7 +484,7 @@ def winecfg(wine_path=None, prefix=None, arch=WINE_DEFAULT_ARCH, config=None, en
)
def eject_disc(wine_path, prefix, proton_verb=None):
def eject_disc(wine_path: str, prefix: str, proton_verb=None):
"""Use Wine to eject a drive"""
if proton.is_proton_path(wine_path):
@@ -491,7 +492,7 @@ def eject_disc(wine_path, prefix, proton_verb=None):
wineexec("eject", prefix=prefix, wine_path=wine_path, args="-a", proton_verb=proton_verb)
def install_cab_component(cabfile, component, wine_path=None, prefix=None, arch=None, proton_verb=None):
def install_cab_component(cabfile, component, wine_path: str, prefix=None, arch=None, proton_verb=None):
"""Install a component from a cabfile in a prefix"""
if proton.is_proton_path(wine_path):
@@ -504,7 +505,9 @@ def install_cab_component(cabfile, component, wine_path=None, prefix=None, arch=
cab_installer.cleanup()
def open_wine_terminal(terminal, wine_path, prefix, env, system_winetricks):
def open_wine_terminal(
terminal: Optional[str], wine_path: str, prefix: str, env: Optional[Dict[str, str]], system_winetricks: bool
):
winetricks_path, _working_dir, env = find_winetricks(env, system_winetricks)
path_paths = [os.path.dirname(wine_path)]
if proton.is_proton_path(wine_path):

View File

@@ -913,7 +913,7 @@ class wine(Runner):
def run_wine_terminal(self, *args):
terminal = self.system_config.get("terminal_app")
system_winetricks = self.runner_config.get("system_winetricks")
system_winetricks: bool = self.runner_config.get("system_winetricks", False)
open_wine_terminal(
terminal=terminal,
wine_path=self.get_executable(),
@@ -1213,10 +1213,10 @@ class wine(Runner):
except Exception as ex:
logger.exception("Failed to setup desktop integration, the prefix may not be valid: %s", ex)
def play(self): # pylint: disable=too-many-return-statements # noqa: C901
def play(self): # pylint: disable=too-many-return-statements
game_exe = self.game_exe
arguments = self.game_config.get("args", "")
launch_info = {"env": self.get_env(os_env=False)}
arguments: str = self.game_config.get("args", "")
launch_info: dict = {"env": self.get_env(os_env=False)}
using_dxvk = self.runner_config.get("dxvk") and LINUX_SYSTEM.is_vulkan_supported
if using_dxvk:

View File

@@ -160,7 +160,10 @@ class SteamID:
match = COMMUNITY32_REGEX.match(url.path)
if match:
if match.group("path") not in TYPE_URL_PATH_MAP[LETTER_TYPE_MAP[match.group("type")]]:
warnings.warn("Community URL ({}) path doesn't match type character".format(url.path))
warnings.warn(
"Community URL ({}) path doesn't match type character".format(url.path),
stacklevel=2,
)
steamid = int(match.group("steamid"))
instance = steamid & 1
account_number = (steamid - instance) / 2

View File

@@ -13,7 +13,7 @@ from gettext import gettext as _
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from gi.repository import Gio, GLib
from gi.repository import Gio, GLib # type: ignore
from lutris import settings
from lutris.exceptions import MissingExecutableError
@@ -41,11 +41,11 @@ def get_environment():
def execute(
command: List[str],
env: Dict[str, str] = None,
cwd: str = None,
env: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
quiet: bool = False,
shell: bool = False,
timeout: float = None,
timeout: Optional[float] = None,
) -> str:
"""
Execute a system command and return its standard output; standard error is discarded.
@@ -66,11 +66,11 @@ def execute(
def execute_with_error(
command: List[str],
env: Dict[str, str] = None,
cwd: str = None,
env: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
quiet: bool = False,
shell: bool = False,
timeout: float = None,
timeout: Optional[float] = None,
) -> Tuple[str, str]:
"""
Execute a system command and return its standard output and; standard error in a tuple.
@@ -90,12 +90,12 @@ def execute_with_error(
def _execute(
command: List[str],
env: Dict[str, str] = None,
cwd: str = None,
env: Optional[Dict[str, str]] = None,
cwd: Optional[str] = None,
capture_stderr: bool = False,
quiet: bool = False,
shell: bool = False,
timeout: float = None,
timeout: Optional[float] = None,
) -> Tuple[str, str]:
# Check if the executable exists
if not command:
@@ -292,11 +292,8 @@ def kill_pid(pid):
logger.error("Could not kill process %s", pid)
def python_identifier(unsafe_string):
def python_identifier(unsafe_string: str):
"""Converts a string to something that can be used as a python variable"""
if not isinstance(unsafe_string, str):
logger.error("Cannot convert %s to a python identifier", type(unsafe_string))
return
def _dashrepl(matchobj):
return matchobj.group(0).replace("-", "_")
@@ -304,7 +301,7 @@ def python_identifier(unsafe_string):
return re.sub(r"(\${)([\w-]*)(})", _dashrepl, unsafe_string)
def substitute(string_template, variables):
def substitute(string_template: str, variables):
"""Expand variables on a string template
Args:
@@ -355,8 +352,8 @@ def merge_folders(source, destination):
def remove_folder(
path: str,
completion_function: TrashPortal.CompletionFunction = None,
error_function: TrashPortal.ErrorFunction = None,
completion_function: Optional[TrashPortal.CompletionFunction] = None,
error_function: Optional[TrashPortal.ErrorFunction] = None,
) -> None:
"""Trashes a folder specified by path, asynchronously. The folder
likely exists after this returns, since it's using DBus to ask

View File

@@ -34,7 +34,7 @@ except Exception as ex:
logger.exception("Unable to enumerate system Wine versions: %s", ex)
def detect_arch(prefix_path: str = "", wine_path: str = "") -> str:
def detect_arch(prefix_path: Optional[str] = None, wine_path: Optional[str] = None) -> str:
"""Given a Wine prefix path, return its architecture"""
if wine_path:
if proton.is_proton_path(wine_path) or system.path_exists(wine_path + "64"):
@@ -237,7 +237,7 @@ def get_system_wine_version(wine_path: str = "wine") -> str:
return version
def get_real_executable(windows_executable: str, working_dir: str) -> Tuple[str, List[str], str]:
def get_real_executable(windows_executable: str, working_dir: Optional[str]) -> Tuple[str, List[str], Optional[str]]:
"""Given a Windows executable, return the real program
capable of launching it along with necessary arguments."""

View File

@@ -1,5 +1,5 @@
[tool.mypy]
python_version = "3.8"
python_version = "3.12"
packages = [
"lutris",
"tests",
@@ -18,7 +18,6 @@ disable_error_code = [
allow_redefinition = true
follow_imports = "silent"
ignore_missing_imports = true
implicit_optional = true
[tool.mypy-baseline]
# --baseline-path: the file where the baseline should be stored

View File

@@ -5,7 +5,6 @@ select = ["A", "ARG", "B", "E", "F", "I", "W", "PERF", "RUF"]
ignore = [
# Ignores that is not worth/too hard to fix
"RUF001", # String contains ambiguous `` (FULLWIDTH EXCLAMATION MARK). Did you mean `!` (EXCLAMATION MARK)? - used ,mostly on tests
"B028", # No explicit `stacklevel` keyword argument found - not sure why this is needed
"E402", # Module level import not at top of file - gtk stuff
# Ignores that should be fixed and removed