From 0f10e6e848e5de6324caba82ed3d7fb41c02d873 Mon Sep 17 00:00:00 2001 From: Mizaki Date: Thu, 26 Jan 2023 00:52:02 +0000 Subject: [PATCH] Create simple dict of talkers with objects. Moved thresh setting back to talkers (general) as it is called outside of talker. --- comictaggerlib/autotagstartwindow.py | 2 +- comictaggerlib/ctoptions/file.py | 6 ++- comictaggerlib/ctoptions/talker_plugins.py | 6 +-- comictaggerlib/main.py | 53 +++++++++++----------- comictaggerlib/seriesselectionwindow.py | 2 +- comictaggerlib/settingswindow.py | 4 +- comictalker/comictalkerapi.py | 41 ++++++----------- comictalker/talkerbase.py | 5 +- comictalker/talkers/comicvine.py | 18 ++------ tests/conftest.py | 5 -- 10 files changed, 59 insertions(+), 83 deletions(-) diff --git a/comictaggerlib/autotagstartwindow.py b/comictaggerlib/autotagstartwindow.py index b0be168..c7d6750 100644 --- a/comictaggerlib/autotagstartwindow.py +++ b/comictaggerlib/autotagstartwindow.py @@ -75,7 +75,7 @@ class AutoTagStartWindow(QtWidgets.QDialog): self.remove_after_success = False self.wait_and_retry_on_rate_limit = False self.search_string = "" - self.name_length_match_tolerance = self.options.comicvine_series_match_search_thresh + self.name_length_match_tolerance = self.options.talkers_series_match_search_thresh self.split_words = self.cbxSplitWords.isChecked() def search_string_toggle(self) -> None: diff --git a/comictaggerlib/ctoptions/file.py b/comictaggerlib/ctoptions/file.py index a6cb332..e2d87fe 100644 --- a/comictaggerlib/ctoptions/file.py +++ b/comictaggerlib/ctoptions/file.py @@ -96,7 +96,11 @@ def filename(parser: settngs.Manager) -> None: def talkers(parser: settngs.Manager) -> None: # General settings for all information talkers parser.add_setting("--source", default="comicvine", help="Use a specified source by source ID") - parser.add_setting("--use-series-start-as-volume", default=False, action=argparse.BooleanOptionalAction) + parser.add_setting( + "--series-match-search-thresh", + default=90, + type=int, + ) parser.add_setting( "--clear-metadata", default=True, diff --git a/comictaggerlib/ctoptions/talker_plugins.py b/comictaggerlib/ctoptions/talker_plugins.py index 31ea94b..73bdbc2 100644 --- a/comictaggerlib/ctoptions/talker_plugins.py +++ b/comictaggerlib/ctoptions/talker_plugins.py @@ -4,14 +4,14 @@ import logging import settngs -from comictalker.comictalkerapi import TalkerPlugin +from comictalker.talkerbase import ComicTalker logger = logging.getLogger(__name__) -def register_talker_settings(parser: settngs.Manager, plugins: dict[str, TalkerPlugin]) -> None: +def register_talker_settings(parser: settngs.Manager, plugins: dict[str, ComicTalker]) -> None: for talker_name, talker in plugins.items(): try: - parser.add_group(talker_name, talker["cls"].comic_settings, False) + parser.add_group(talker_name, talker.comic_settings, False) except Exception: logger.exception("Failed to register settings for %s", talker_name) diff --git a/comictaggerlib/main.py b/comictaggerlib/main.py index 18c5a13..ed6b62b 100644 --- a/comictaggerlib/main.py +++ b/comictaggerlib/main.py @@ -21,6 +21,7 @@ import logging.handlers import platform import signal import sys +from typing import Any import settngs @@ -29,7 +30,6 @@ from comicapi import utils from comictaggerlib import cli, ctoptions from comictaggerlib.ctversion import version from comictaggerlib.log import setup_logging -from comictalker.talkerbase import TalkerError if sys.version_info < (3, 10): import importlib_metadata @@ -66,14 +66,14 @@ class App: self.options = settngs.Config({}, {}) self.initial_arg_parser = ctoptions.initial_cmd_line_parser() self.config_load_success = False - # First get a list of classes - self.talker_plugins = ct_api.get_talkers() + self.talker_plugins: dict[str, Any] = {} def run(self) -> None: opts = self.initialize() + self.initialize_dirs(opts.config) + self.talker_plugins = ct_api.get_talkers(version, opts.config.user_cache_dir) self.register_options() self.parse_options(opts.config) - self.initialize_dirs() self.initialize_talkers() self.main() @@ -103,28 +103,27 @@ class App: self.options = ctoptions.validate_settings(self.options, self.manager) self.options = self.options - def initialize_dirs(self) -> None: - self.options[0].runtime_config.user_data_dir.mkdir(parents=True, exist_ok=True) - self.options[0].runtime_config.user_config_dir.mkdir(parents=True, exist_ok=True) - self.options[0].runtime_config.user_cache_dir.mkdir(parents=True, exist_ok=True) - self.options[0].runtime_config.user_state_dir.mkdir(parents=True, exist_ok=True) - self.options[0].runtime_config.user_log_dir.mkdir(parents=True, exist_ok=True) - logger.debug("user_data_dir: %s", self.options[0].runtime_config.user_data_dir) - logger.debug("user_config_dir: %s", self.options[0].runtime_config.user_config_dir) - logger.debug("user_cache_dir: %s", self.options[0].runtime_config.user_cache_dir) - logger.debug("user_state_dir: %s", self.options[0].runtime_config.user_state_dir) - logger.debug("user_log_dir: %s", self.options[0].runtime_config.user_log_dir) + def initialize_dirs(self, paths: ctoptions.ComicTaggerPaths) -> None: + paths.user_data_dir.mkdir(parents=True, exist_ok=True) + paths.user_config_dir.mkdir(parents=True, exist_ok=True) + paths.user_cache_dir.mkdir(parents=True, exist_ok=True) + paths.user_state_dir.mkdir(parents=True, exist_ok=True) + paths.user_log_dir.mkdir(parents=True, exist_ok=True) + logger.debug("user_data_dir: %s", paths.user_data_dir) + logger.debug("user_config_dir: %s", paths.user_config_dir) + logger.debug("user_cache_dir: %s", paths.user_cache_dir) + logger.debug("user_state_dir: %s", paths.user_state_dir) + logger.debug("user_log_dir: %s", paths.user_log_dir) def initialize_talkers(self) -> None: + # Apply talker settings from config file try: - self.talker_plugins = ct_api.get_talker_objects( - version=version, - cache_folder=self.options[0].runtime_config.user_cache_dir, - settings=self.options[0], - plugins=self.talker_plugins, - ) - except Exception: - logger.exception("Failed to initialize talkers. See log for full details.") + for talker_name, talker in self.talker_plugins.items(): + ct_api.set_talker_settings(talker, self.options[0]) + except Exception as e: + # Remove talker as we failed to apply the settings + del self.talker_plugins[e.source] # type: ignore[attr-defined] + logger.exception("Failed to initialize talker settings. Error %s", str(e)) def main(self) -> None: assert self.options is not None @@ -154,10 +153,12 @@ class App: # TODO Have option to save passed in config options and quit? try: - talker_api = self.talker_plugins[self.options[0].talkers_source]["obj"] - except TalkerError as e: + talker_api = self.talker_plugins[self.options[0].talkers_source] + except Exception as e: logger.exception(f"Unable to load talker {self.options[0].talkers_source}. Error: {str(e)}") - error = (str(e), True) + talker_api = None + # TODO error True can be changed to False after the talker settings menu generation is in + error = (f"Unable to load talker {self.options[0].talkers_source}. Error: {str(e)}", True) if not self.config_load_success: error = ( diff --git a/comictaggerlib/seriesselectionwindow.py b/comictaggerlib/seriesselectionwindow.py index ec6b053..088ed86 100644 --- a/comictaggerlib/seriesselectionwindow.py +++ b/comictaggerlib/seriesselectionwindow.py @@ -328,7 +328,7 @@ class SeriesSelectionWindow(QtWidgets.QDialog): def perform_query(self, refresh: bool = False) -> None: self.search_thread = SearchThread( - self.talker_api, self.series_name, refresh, self.literal, self.options.comicvine_series_match_search_thresh + self.talker_api, self.series_name, refresh, self.literal, self.options.talkers_series_match_search_thresh ) self.search_thread.searchComplete.connect(self.search_complete) self.search_thread.progressUpdate.connect(self.search_progress_update) diff --git a/comictaggerlib/settingswindow.py b/comictaggerlib/settingswindow.py index bdbd8a7..ec32e23 100644 --- a/comictaggerlib/settingswindow.py +++ b/comictaggerlib/settingswindow.py @@ -271,7 +271,7 @@ class SettingsWindow(QtWidgets.QDialog): # Copy values from settings to form self.leRarExePath.setText(self.options[0].general_rar_exe_path) self.sbNameMatchIdentifyThresh.setValue(self.options[0].identifier_series_match_identify_thresh) - self.sbNameMatchSearchThresh.setValue(self.options[0].comicvine_series_match_search_thresh) + self.sbNameMatchSearchThresh.setValue(self.options[0].talkers_series_match_search_thresh) self.tePublisherFilter.setPlainText("\n".join(self.options[0].identifier_publisher_filter)) self.cbxCheckForNewVersion.setChecked(self.options[0].general_check_for_new_version) @@ -386,7 +386,7 @@ class SettingsWindow(QtWidgets.QDialog): self.options[0].general_check_for_new_version = self.cbxCheckForNewVersion.isChecked() self.options[0].identifier_series_match_identify_thresh = self.sbNameMatchIdentifyThresh.value() - self.options[0].comicvine_series_match_search_thresh = self.sbNameMatchSearchThresh.value() + self.options[0].talkers_series_match_search_thresh = self.sbNameMatchSearchThresh.value() self.options[0].identifier_publisher_filter = [ x.strip() for x in str(self.tePublisherFilter.toPlainText()).splitlines() if x.strip() ] diff --git a/comictalker/comictalkerapi.py b/comictalker/comictalkerapi.py index c8fdd55..e78b973 100644 --- a/comictalker/comictalkerapi.py +++ b/comictalker/comictalkerapi.py @@ -15,9 +15,10 @@ # limitations under the License. from __future__ import annotations +import argparse import logging import pathlib -from typing import Any, TypedDict +from typing import Any import comictalker.talkers.comicvine from comictalker.talkerbase import ComicTalker, TalkerError @@ -25,37 +26,25 @@ from comictalker.talkerbase import ComicTalker, TalkerError logger = logging.getLogger(__name__) -class TalkerPlugin(TypedDict, total=False): - cls: type[ComicTalker] - obj: ComicTalker - - -def set_talker_settings(talker, settings) -> None: +def set_talker_settings(talker: ComicTalker, settings: argparse.Namespace) -> None: try: talker.set_settings(settings) except Exception as e: logger.exception( - f"Failed to set talker settings for {talker.talker_id}, will use defaults. Error: {str(e)}", + f"Failed to set talker settings for {talker.source_details.name}, will use defaults. Error: {str(e)}", ) - raise TalkerError(source=talker.talker_id, code=4, desc="Could not apply talker settings, will use defaults") + raise TalkerError(source=talker.source_details.name, code=4, desc="Could not apply talker settings") -def get_talker_objects( - version: str, cache_folder: pathlib.Path, settings: dict[str, Any], plugins: dict[str, TalkerPlugin] -) -> dict[str, TalkerPlugin]: - for talker_name, talker in plugins.items(): +def get_talkers(version: str, cache: pathlib.Path) -> dict[str, Any]: + """Returns all comic talker instances""" + # TODO separate PR will bring talkers in via entry points. TalkerError etc. source will then be a var + talkers = {} + for talker in [comictalker.talkers.comicvine.ComicVineTalker]: try: - obj = talker["cls"](version, cache_folder) - plugins[talker_name]["obj"] = obj + obj = talker(version, cache) + talkers[obj.source_details.id] = obj except Exception: - logger.exception("Failed to create talker object") - raise TalkerError(source=talker_name, code=4, desc="Failed to initialise talker object") - - # Run outside of try block so as to keep except separate - set_talker_settings(plugins[talker_name]["obj"], settings) - return plugins - - -def get_talkers() -> dict[str, TalkerPlugin]: - """Returns all comic talker modules NOT objects""" - return {"comicvine": TalkerPlugin(cls=comictalker.talkers.comicvine.ComicVineTalker)} + logger.exception("Failed to load talker: %s", "comicvine") + raise TalkerError(source="comicvine", code=4, desc="Failed to initialise talker") + return talkers diff --git a/comictalker/talkerbase.py b/comictalker/talkerbase.py index 4f73b70..1bf638f 100644 --- a/comictalker/talkerbase.py +++ b/comictalker/talkerbase.py @@ -139,8 +139,6 @@ class TalkerDataError(TalkerError): class ComicTalker: """The base class for all comic source talkers""" - talker_id = "" - def __init__(self, version: str, cache_folder: pathlib.Path) -> None: # Identity name for the information source etc. self.source_details = SourceDetails() @@ -150,8 +148,7 @@ class ComicTalker: self.api_key: str = "" self.api_url: str = "" - @classmethod - def comic_settings(cls, parser: settngs.Manager) -> None: + def comic_settings(self, parser: settngs.Manager) -> None: """Talker settings.""" def set_settings(self, settings: argparse.Namespace) -> None: diff --git a/comictalker/talkers/comicvine.py b/comictalker/talkers/comicvine.py index b23abfd..ef99d86 100644 --- a/comictalker/talkers/comicvine.py +++ b/comictalker/talkers/comicvine.py @@ -154,15 +154,13 @@ CV_RATE_LIMIT_STATUS = 107 class ComicVineTalker(ComicTalker): - talker_id = "comicvine" - def __init__( self, version: str, cache_folder: pathlib.Path, ): super().__init__(version, cache_folder) - self.source_details = SourceDetails(name="Comic Vine", ident=ComicVineTalker.talker_id) + self.source_details = SourceDetails(name="Comic Vine", ident="comicvine") self.static_options = SourceStaticOptions( website="https://comicvine.gamespot.com/", has_issues=True, @@ -194,17 +192,9 @@ class ComicVineTalker(ComicTalker): # NOTE: This was hardcoded before which is why it isn't in settings self.wait_for_rate_limit_time: int = 20 - @classmethod - def comic_settings(cls, parser: settngs.Manager) -> None: - # Might be general settings? - parser.add_setting( - "--series-match-search-thresh", - default=90, - type=int, - ) + def comic_settings(self, parser: settngs.Manager) -> None: + """Register settings""" parser.add_setting("--cv-use-series-start-as-volume", default=False, action=argparse.BooleanOptionalAction) - - # Comic Vine settings parser.add_setting("--cv-wait-on-ratelimit", default=False, action=argparse.BooleanOptionalAction) parser.add_setting( "--cv-remove-html-tables", @@ -238,7 +228,7 @@ class ComicVineTalker(ComicTalker): self.api_url = tmp_url.geturl() except Exception: - logger.exception("Failed to parse new talker URL for %s, will use default", self.talker_id) + logger.exception("Failed to parse new talker URL for %s, will use default", self.source_name) def check_api_key(self, key: str, url: str) -> bool: if not url: diff --git a/tests/conftest.py b/tests/conftest.py index 87e6bbd..f8f4a96 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -157,16 +157,11 @@ def seed_all_publishers(monkeypatch): monkeypatch.setattr(utils, "publishers", publisher_seed) -def get_plugins() -> dict: - return comictalker.comictalkerapi.get_talkers() - - @pytest.fixture def options(settings_manager, tmp_path): comictaggerlib.ctoptions.register_commandline(settings_manager) comictaggerlib.ctoptions.register_settings(settings_manager) - comictaggerlib.ctoptions.register_talker_settings(settings_manager, get_plugins()) defaults = settings_manager.get_namespace(settings_manager.defaults()) defaults[0].runtime_config = comictaggerlib.ctoptions.ComicTaggerPaths(tmp_path / "config") defaults[0].runtime_config.user_data_dir.mkdir(parents=True, exist_ok=True)