🎨 Work on import error handling
- Generic ErrorProducer class - Importer and managers are error producers - SGDB Auth friendly error - Bad source location friendly errors (data, config, cache) - Removed unused decorators
This commit is contained in:
@@ -20,18 +20,21 @@
|
||||
|
||||
import logging
|
||||
|
||||
from gi.repository import Adw, Gtk, GLib
|
||||
from gi.repository import Adw, GLib, Gtk
|
||||
|
||||
from src import shared
|
||||
from src.errors.error_producer import ErrorProducer
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.importer.sources.source import Source
|
||||
from src.store.managers.async_manager import AsyncManager
|
||||
from src.store.pipeline import Pipeline
|
||||
from src.utils.create_dialog import create_dialog
|
||||
from src.utils.task import Task
|
||||
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
class Importer:
|
||||
class Importer(ErrorProducer):
|
||||
"""A class in charge of scanning sources for games"""
|
||||
|
||||
progressbar = None
|
||||
@@ -47,6 +50,7 @@ class Importer:
|
||||
game_pipelines: set[Pipeline] = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.game_pipelines = set()
|
||||
self.sources = set()
|
||||
|
||||
@@ -81,6 +85,15 @@ class Importer:
|
||||
|
||||
self.create_dialog()
|
||||
|
||||
# Collect all errors and reset the cancellables for the managers
|
||||
# - Only one importer exists at any given time
|
||||
# - Every import starts fresh
|
||||
self.collect_errors()
|
||||
for manager in shared.store.managers.values():
|
||||
manager.collect_errors()
|
||||
if isinstance(manager, AsyncManager):
|
||||
manager.reset_cancellable()
|
||||
|
||||
for source in self.sources:
|
||||
logging.debug("Importing games from source %s", source.id)
|
||||
task = Task.new(None, None, self.source_callback, (source,))
|
||||
@@ -129,10 +142,9 @@ class Importer:
|
||||
iteration_result = next(iterator)
|
||||
except StopIteration:
|
||||
break
|
||||
except Exception as exception: # pylint: disable=broad-exception-caught
|
||||
logging.exception(
|
||||
"Exception in source %s", source.id, exc_info=exception
|
||||
)
|
||||
except Exception as error: # pylint: disable=broad-exception-caught
|
||||
logging.exception("%s in %s", type(error).__name__, source.id)
|
||||
self.report_error(error)
|
||||
continue
|
||||
|
||||
# Handle the result depending on its type
|
||||
@@ -202,9 +214,16 @@ class Importer:
|
||||
string = _("The following errors occured during import:")
|
||||
errors = ""
|
||||
|
||||
# Collect all errors that happened in the importer and the managers
|
||||
collected_errors: list[Exception] = []
|
||||
collected_errors.extend(self.collect_errors())
|
||||
for manager in shared.store.managers.values():
|
||||
for error in manager.collect_errors():
|
||||
errors += "\n\n" + str(error)
|
||||
collected_errors.extend(manager.collect_errors())
|
||||
for error in collected_errors:
|
||||
# Only display friendly errors
|
||||
if not isinstance(error, FriendlyError):
|
||||
continue
|
||||
errors += "\n\n" + str(error)
|
||||
|
||||
if errors:
|
||||
create_dialog(
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import json
|
||||
import logging
|
||||
from json import JSONDecodeError
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
from typing import Generator
|
||||
|
||||
|
||||
@@ -20,10 +20,11 @@
|
||||
import sys
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Iterable, Iterator
|
||||
from typing import Generator, Any, Optional
|
||||
from typing import Any, Generator, Optional
|
||||
|
||||
from src.importer.sources.location import Location
|
||||
from src.errors.friendly_error import FriendlyError
|
||||
from src.game import Game
|
||||
from src.importer.sources.location import Location, UnresolvableLocationError
|
||||
|
||||
# Type of the data returned by iterating on a Source
|
||||
SourceIterationResult = None | Game | tuple[Game, tuple[Any]]
|
||||
@@ -100,9 +101,20 @@ class Source(Iterable):
|
||||
|
||||
def __iter__(self) -> SourceIterator:
|
||||
"""Get an iterator for the source"""
|
||||
for location in (self.data_location, self.cache_location, self.config_location):
|
||||
if location is not None:
|
||||
for location_name in ("data", "cache", "config"):
|
||||
location = getattr(self, f"{location_name}_location", None)
|
||||
if location is None:
|
||||
continue
|
||||
try:
|
||||
location.resolve()
|
||||
except UnresolvableLocationError as error:
|
||||
raise FriendlyError(
|
||||
# The variable is the source's name
|
||||
f"Invalid {location_name} location for {{}}",
|
||||
"Change it or disable the source in the preferences",
|
||||
(self.name,),
|
||||
(self.name,),
|
||||
) from error
|
||||
return self.iterator_class(self)
|
||||
|
||||
|
||||
|
||||
@@ -30,10 +30,6 @@ from src.importer.sources.source import (
|
||||
SourceIterator,
|
||||
URLExecutableSource,
|
||||
)
|
||||
from src.utils.decorators import (
|
||||
replaced_by_path,
|
||||
replaced_by_schema_key,
|
||||
)
|
||||
from src.utils.steam import SteamFileHelper, SteamInvalidManifestError
|
||||
from src.importer.sources.location import Location
|
||||
|
||||
|
||||
Reference in New Issue
Block a user