From bc3bc2dac65006e008701ca4d93ea0b6a7141bf2 Mon Sep 17 00:00:00 2001 From: kramo Date: Sun, 20 Aug 2023 14:32:23 +0200 Subject: [PATCH] =?UTF-8?q?=CE=BB=20my=20beloved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/game_cover.py | 16 ++--- src/importer/importer.py | 16 ++--- src/store/managers/async_manager.py | 10 ++- src/utils/task.py | 95 ----------------------------- 4 files changed, 17 insertions(+), 120 deletions(-) delete mode 100644 src/utils/task.py diff --git a/src/game_cover.py b/src/game_cover.py index 221075d..006af33 100644 --- a/src/game_cover.py +++ b/src/game_cover.py @@ -45,16 +45,6 @@ class GameCover: self.pictures = pictures self.new_cover(path) - # Wrap the function in another one as Gio.Task.run_in_thread does not allow for passing args - def create_func(self, path: Optional[Path]) -> Callable: - self.animation = GdkPixbuf.PixbufAnimation.new_from_file(str(path)) - self.anim_iter = self.animation.get_iter() - - def wrapper(task: Gio.Task, *_args: Any) -> None: - self.update_animation((task, self.animation)) - - return wrapper - def new_cover(self, path: Optional[Path] = None) -> None: self.animation = None self.texture = None @@ -64,8 +54,12 @@ class GameCover: if path: if path.suffix == ".gif": + self.animation = GdkPixbuf.PixbufAnimation.new_from_file(str(path)) + self.anim_iter = self.animation.get_iter() self.task = Gio.Task.new() - self.task.run_in_thread(self.create_func(self.path)) + self.task.run_in_thread( + lambda *_: self.update_animation((self.task, self.animation)) + ) else: self.texture = Gdk.Texture.new_from_filename(str(path)) diff --git a/src/importer/importer.py b/src/importer/importer.py index f6c07fc..c18e912 100644 --- a/src/importer/importer.py +++ b/src/importer/importer.py @@ -21,7 +21,7 @@ import logging from typing import Any, Optional -from gi.repository import Adw, GLib, Gtk +from gi.repository import Adw, Gio, GLib, Gtk from src import shared from src.errors.error_producer import ErrorProducer @@ -31,7 +31,6 @@ from src.importer.sources.location import UnresolvableLocationError from src.importer.sources.source import Source from src.store.managers.async_manager import AsyncManager from src.store.pipeline import Pipeline -from src.utils.task import Task # pylint: disable=too-many-instance-attributes @@ -121,10 +120,13 @@ class Importer(ErrorProducer): for source in self.sources: logging.debug("Importing games from source %s", source.source_id) - task = Task.new(None, None, self.source_callback, (source,)) + task = Gio.Task.new(None, None, self.source_callback, (source,)) self.n_source_tasks_created += 1 - task.set_task_data((source,)) - task.run_in_thread(self.source_task_thread_func) + task.run_in_thread( + lambda _task, _obj, _data, _cancellable, src=source: self.source_task_thread_func( + (src,) + ) + ) self.progress_changed_callback() @@ -145,9 +147,7 @@ class Importer(ErrorProducer): ) self.import_dialog.present() - def source_task_thread_func( - self, _task: Any, _obj: Any, data: tuple, _cancellable: Any - ) -> None: + def source_task_thread_func(self, data: tuple) -> None: """Source import task code""" source: Source diff --git a/src/store/managers/async_manager.py b/src/store/managers/async_manager.py index c7f2ff8..332bf29 100644 --- a/src/store/managers/async_manager.py +++ b/src/store/managers/async_manager.py @@ -17,13 +17,12 @@ # # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Callable, Any +from typing import Any, Callable from gi.repository import Gio from src.game import Game from src.store.managers.manager import Manager -from src.utils.task import Task class AsyncManager(Manager): @@ -49,11 +48,10 @@ class AsyncManager(Manager): self, game: Game, additional_data: dict, callback: Callable[["Manager"], Any] ) -> None: """Create a task to process the game in a separate thread""" - task = Task.new(None, self.cancellable, self._task_callback, (callback,)) - task.set_task_data((game, additional_data)) - task.run_in_thread(self._task_thread_func) + task = Gio.Task.new(None, self.cancellable, self._task_callback, (callback,)) + task.run_in_thread(lambda *_: self._task_thread_func((game, additional_data))) - def _task_thread_func(self, _task, _source_object, data, _cancellable): + def _task_thread_func(self, data): """Task thread entry point""" game, additional_data, *_rest = data self.run(game, additional_data) diff --git a/src/utils/task.py b/src/utils/task.py deleted file mode 100644 index 30ef68f..0000000 --- a/src/utils/task.py +++ /dev/null @@ -1,95 +0,0 @@ -# task.py -# -# Copyright 2023 Geoffrey Coulaud -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# SPDX-License-Identifier: GPL-3.0-or-later - -from functools import wraps -from typing import Any, Callable - -from gi.repository import Gio - - -def create_task_thread_func_closure(func: Callable, data: Any) -> Callable: - """Wrap a Gio.TaskThreadFunc with the given data in a closure""" - - def closure( - task: Gio.Task, source_object: object, _data: Any, cancellable: Gio.Cancellable - ) -> Any: - func(task, source_object, data, cancellable) - - return closure - - -def decorate_set_task_data(task: Gio.Task) -> Callable: - """Decorate Gio.Task.set_task_data to replace it""" - - def decorator(original_method: Callable) -> Callable: - @wraps(original_method) - def new_method(task_data: Any) -> None: - task.task_data = task_data - - return new_method - - return decorator - - -def decorate_run_in_thread(task: Gio.Task) -> Callable: - """Decorate Gio.Task.run_in_thread to pass the task data correctly - Creates a closure around task_thread_func with the task data available.""" - - def decorator(original_method: Callable) -> Callable: - @wraps(original_method) - def new_method(task_thread_func: Callable) -> None: - closure = create_task_thread_func_closure(task_thread_func, task.task_data) - original_method(closure) - - return new_method - - return decorator - - -# pylint: disable=too-few-public-methods -class Task: - """Wrapper around Gio.Task to patch task data not being passed""" - - @classmethod - def new( - cls, - source_object: object, - cancellable: Gio.Cancellable, - callback: Callable, - callback_data: Any, - ) -> Gio.Task: - """Create a new, monkey-patched Gio.Task. - The `set_task_data` and `run_in_thread` methods are decorated. - - As of 2023-05-19, PyGObject does not work well with Gio.Task, so to pass data - the only viable way it to create a closure with the thread function and its data. - This class is supposed to make Gio.Task comply with its expected behaviour - per the docs: - - http://lazka.github.io/pgi-docs/#Gio-2.0/classes/Task.html#Gio.Task.set_task_data - - This code may break if pygobject overrides change in the future. - We need to manually pass `self` to the decorators since it's otherwise bound but - not accessible from Python's side. - """ - - task = Gio.Task.new(source_object, cancellable, callback, callback_data) - task.set_task_data = decorate_set_task_data(task)(task.set_task_data) - task.run_in_thread = decorate_run_in_thread(task)(task.run_in_thread) - return task