🎨 SourceIterator can yield addtitional data

SourceIterator-s can yield a game and a tuple of additional data.
This data will be passed to the Store, Pipeline and Managers.
This commit is contained in:
GeoffreyCoulaud
2023-06-07 14:01:06 +02:00
parent 7eef050a64
commit 98f02da36c
12 changed files with 68 additions and 37 deletions

View File

@@ -26,16 +26,18 @@ class AsyncManager(Manager):
Already scheduled Tasks will no longer be cancellable."""
self.cancellable = Gio.Cancellable()
def process_game(self, game: Game, callback: Callable[["Manager"], Any]) -> None:
def process_game(
self, game: Game, additional_data: tuple, 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,))
task.set_task_data((game, additional_data))
task.run_in_thread(self._task_thread_func)
def _task_thread_func(self, _task, _source_object, data, cancellable):
"""Task thread entry point"""
game, *_rest = data
self.execute_resilient_manager_logic(game)
game, additional_data, *_rest = data
self.execute_resilient_manager_logic(game, additional_data)
def _task_callback(self, _source_object, _result, data):
"""Method run after the task is done"""

View File

@@ -10,7 +10,7 @@ class DisplayManager(Manager):
run_after = set((SteamAPIManager, SGDBManager))
def manager_logic(self, game: Game) -> None:
def manager_logic(self, game: Game, _additional_data: tuple) -> None:
# TODO decouple a game from its widget
shared.win.games[game.game_id] = game
game.update()

View File

@@ -8,5 +8,5 @@ class FileManager(AsyncManager):
run_after = set((SteamAPIManager,))
def manager_logic(self, game: Game) -> None:
def manager_logic(self, game: Game, _additional_data: tuple) -> None:
game.save()

View File

@@ -37,7 +37,7 @@ class Manager:
self.errors_lock = Lock()
def report_error(self, error: Exception):
"""Report an error that happened in Manager.run"""
"""Report an error that happened in Manager.process_game"""
with self.errors_lock:
self.errors.append(error)
@@ -49,7 +49,7 @@ class Manager:
return errors
@abstractmethod
def manager_logic(self, game: Game) -> None:
def manager_logic(self, game: Game, additional_data: tuple) -> None:
"""
Manager specific logic triggered by the run method
* Implemented by final child classes
@@ -58,10 +58,12 @@ class Manager:
* May raise other exceptions that will be reported
"""
def execute_resilient_manager_logic(self, game: Game, try_index: int = 0) -> None:
def execute_resilient_manager_logic(
self, game: Game, additional_data: tuple, try_index: int = 0
) -> None:
"""Execute the manager logic and handle its errors by reporting them or retrying"""
try:
self.manager_logic(game)
self.manager_logic(game, additional_data)
except Exception as error:
if error in self.continue_on:
# Handle skippable errors (skip silently)
@@ -71,7 +73,9 @@ class Manager:
# Handle retryable errors
logging_format = "Retrying %s in %s for %s"
sleep(self.retry_delay)
self.execute_resilient_manager_logic(game, try_index + 1)
self.execute_resilient_manager_logic(
game, additional_data, try_index + 1
)
else:
# Handle being out of retries
logging_format = "Out of retries dues to %s in %s for %s"
@@ -88,7 +92,9 @@ class Manager:
f"{game.name} ({game.game_id})",
)
def process_game(self, game: Game, callback: Callable[["Manager"], Any]) -> None:
def process_game(
self, game: Game, additional_data: tuple, callback: Callable[["Manager"], Any]
) -> None:
"""Pass the game through the manager"""
self.execute_resilient_manager_logic(game)
self.execute_resilient_manager_logic(game, additional_data)
callback(self)

View File

@@ -1,3 +1,5 @@
from urllib3.exceptions import SSLError
from src.game import Game
from src.store.managers.async_manager import AsyncManager
from src.store.managers.steam_api_manager import SteamAPIManager
@@ -8,9 +10,9 @@ class SGDBManager(AsyncManager):
"""Manager in charge of downloading a game's cover from steamgriddb"""
run_after = set((SteamAPIManager,))
retryable_on = set((HTTPError,))
retryable_on = set((HTTPError, SSLError))
def manager_logic(self, game: Game) -> None:
def manager_logic(self, game: Game, _additional_data: tuple) -> None:
try:
sgdb = SGDBHelper()
sgdb.conditionaly_update_cover(game)

View File

@@ -15,7 +15,7 @@ class SteamAPIManager(AsyncManager):
retryable_on = set((HTTPError, SSLError))
def manager_logic(self, game: Game) -> None:
def manager_logic(self, game: Game, _additional_data: tuple) -> None:
# Skip non-steam games
if not game.source.startswith("steam_"):
return

View File

@@ -11,14 +11,18 @@ class Pipeline(GObject.Object):
"""Class representing a set of managers for a game"""
game: Game
additional_data: tuple
waiting: set[Manager]
running: set[Manager]
done: set[Manager]
def __init__(self, game: Game, managers: Iterable[Manager]) -> None:
def __init__(
self, game: Game, additional_data: tuple, managers: Iterable[Manager]
) -> None:
super().__init__()
self.game = game
self.additional_data = additional_data
self.waiting = set(managers)
self.running = set()
self.done = set()
@@ -72,7 +76,7 @@ class Pipeline(GObject.Object):
for manager in (*parallel, *blocking):
self.waiting.remove(manager)
self.running.add(manager)
manager.process_game(self.game, self.manager_callback)
manager.process_game(self.game, self.additional_data, self.manager_callback)
def manager_callback(self, manager: Manager) -> None:
"""Method called by a manager when it's done"""

View File

@@ -20,7 +20,9 @@ class Store:
"""Add a manager class that will run when games are added"""
self.managers.add(manager)
def add_game(self, game: Game, replace=False) -> Pipeline | None:
def add_game(
self, game: Game, additional_data: tuple, replace=False
) -> Pipeline | None:
"""Add a game to the app if not already there
:param replace bool: Replace the game if it already exists
@@ -49,7 +51,7 @@ class Store:
return None
# Run the pipeline for the game
pipeline = Pipeline(game, self.managers)
pipeline = Pipeline(game, additional_data, self.managers)
self.games[game.game_id] = game
self.pipelines[game.game_id] = pipeline
pipeline.advance()