Introduce a very basic RareCore to handle signletons and their cleanup.
This commit is contained in:
parent
7020141148
commit
1ebd0b18d8
23
rare/app.py
23
rare/app.py
|
@ -1,4 +1,3 @@
|
|||
import configparser
|
||||
import logging
|
||||
import os
|
||||
import platform
|
||||
|
@ -23,10 +22,8 @@ from rare.shared import (
|
|||
LegendaryCoreSingleton,
|
||||
GlobalSignalsSingleton,
|
||||
ArgumentsSingleton,
|
||||
ApiResultsSingleton,
|
||||
clear_singleton_instance
|
||||
)
|
||||
from rare.shared.image_manager import ImageManagerSingleton
|
||||
from rare.shared.rare_core import RareCore
|
||||
from rare.utils import legendary_utils, config_helper
|
||||
from rare.utils.paths import cache_dir, tmp_dir
|
||||
from rare.widgets.rare_app import RareApp
|
||||
|
@ -61,8 +58,12 @@ def excepthook(exc_type, exc_value, exc_tb):
|
|||
class App(RareApp):
|
||||
def __init__(self, args: Namespace):
|
||||
super(App, self).__init__()
|
||||
self.rare_core = RareCore(args=args)
|
||||
self.args = ArgumentsSingleton()
|
||||
self.signals = GlobalSignalsSingleton()
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.args = ArgumentsSingleton(args) # add some options
|
||||
|
||||
config_helper.init_config_handler(self.core)
|
||||
|
||||
lang = self.settings.value("language", self.core.language_code, type=str)
|
||||
self.load_translator(lang)
|
||||
|
@ -71,9 +72,6 @@ class App(RareApp):
|
|||
self.mainwindow: Optional[MainWindow] = None
|
||||
self.launch_dialog: Optional[LaunchDialog] = None
|
||||
|
||||
self.signals = GlobalSignalsSingleton(init=True)
|
||||
self.image_manager = ImageManagerSingleton(init=True)
|
||||
|
||||
# launch app
|
||||
self.launch_dialog = LaunchDialog(parent=None)
|
||||
self.launch_dialog.quit_app.connect(self.launch_dialog.close)
|
||||
|
@ -141,9 +139,8 @@ class App(RareApp):
|
|||
if self.mainwindow is not None:
|
||||
self.mainwindow.close()
|
||||
self.mainwindow = None
|
||||
clear_singleton_instance(self.signals)
|
||||
clear_singleton_instance(self.args)
|
||||
clear_singleton_instance(ApiResultsSingleton())
|
||||
self.rare_core.deleteLater()
|
||||
del self.rare_core
|
||||
self.processEvents()
|
||||
shutil.rmtree(tmp_dir)
|
||||
os.makedirs(tmp_dir)
|
||||
|
@ -182,15 +179,11 @@ def start(args):
|
|||
logger.info(f"Operating System: {platform.system()}")
|
||||
|
||||
while True:
|
||||
core = LegendaryCoreSingleton(init=True)
|
||||
config_helper.init_config_handler(core)
|
||||
app = App(args)
|
||||
exit_code = app.exec_()
|
||||
# if not restart
|
||||
# restart app
|
||||
del app
|
||||
core.exit()
|
||||
clear_singleton_instance(core)
|
||||
if exit_code != -133742:
|
||||
break
|
||||
|
||||
|
|
|
@ -7,12 +7,11 @@ from requests.exceptions import ConnectionError, HTTPError
|
|||
|
||||
from rare.components.dialogs.login import LoginDialog
|
||||
from rare.models.apiresults import ApiResults
|
||||
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton
|
||||
from rare.shared import LegendaryCoreSingleton, ArgumentsSingleton, ApiResultsSingleton, ImageManagerSingleton
|
||||
from rare.ui.components.dialogs.launch_dialog import Ui_LaunchDialog
|
||||
from rare.utils.misc import CloudWorker
|
||||
|
||||
logger = getLogger("Login")
|
||||
logger = getLogger("LoginDialog")
|
||||
|
||||
|
||||
class LaunchWorker(QRunnable):
|
||||
|
|
|
@ -11,7 +11,7 @@ from rare.shared import (
|
|||
ArgumentsSingleton,
|
||||
ApiResultsSingleton,
|
||||
)
|
||||
from rare.shared.image_manager import ImageManagerSingleton
|
||||
from rare.shared import ImageManagerSingleton
|
||||
from rare.widgets.library_layout import LibraryLayout
|
||||
from rare.widgets.sliding_stack import SlidingStackedWidget
|
||||
from .cloud_save_utils import CloudSaveUtils
|
||||
|
|
|
@ -3,8 +3,8 @@ from PyQt5.QtWidgets import QFrame, QWidget, QMessageBox
|
|||
from legendary.models.game import Game
|
||||
|
||||
from rare.components.tabs.games.game_utils import GameUtils
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.ui.components.tabs.games.game_info.game_dlc import Ui_GameDlc
|
||||
from rare.ui.components.tabs.games.game_info.game_dlc_widget import Ui_GameDlcWidget
|
||||
from rare.models.install import InstallOptionsModel
|
||||
|
|
|
@ -27,7 +27,8 @@ from rare.shared import (
|
|||
GlobalSignalsSingleton,
|
||||
ArgumentsSingleton,
|
||||
)
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
|
||||
from rare.utils.legendary_utils import VerifyWorker
|
||||
from rare.utils.misc import get_size
|
||||
|
|
|
@ -11,7 +11,8 @@ from rare.shared import (
|
|||
ArgumentsSingleton,
|
||||
ApiResultsSingleton,
|
||||
)
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.ui.components.tabs.games.game_info.game_info import Ui_GameInfo
|
||||
from rare.utils.extra_widgets import SideTabWidget
|
||||
from rare.utils.json_formatter import QJsonModel
|
||||
|
|
|
@ -7,8 +7,8 @@ from PyQt5.QtGui import QPixmap
|
|||
from PyQt5.QtWidgets import QFrame, QMessageBox, QAction
|
||||
|
||||
from rare.components.tabs.games.game_utils import GameUtils
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton, ArgumentsSingleton, ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.utils.misc import create_desktop_link
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
|
|
|
@ -4,7 +4,8 @@ from PyQt5.QtCore import pyqtSignal, Qt
|
|||
from PyQt5.QtWidgets import QFrame, QAction
|
||||
from legendary.models.game import Game
|
||||
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.widgets.image_widget import ImageWidget
|
||||
|
||||
logger = getLogger("Uninstalled")
|
||||
|
|
|
@ -3,8 +3,8 @@ from PyQt5.QtGui import QPixmap
|
|||
from PyQt5.QtWidgets import QVBoxLayout, QHBoxLayout, QWidget, QFrame
|
||||
from legendary.models.game import Game
|
||||
|
||||
from rare.shared import LegendaryCoreSingleton
|
||||
from rare.shared.image_manager import ImageManagerSingleton, ImageSize
|
||||
from rare.shared import LegendaryCoreSingleton, ImageManagerSingleton
|
||||
from rare.shared.image_manager import ImageSize
|
||||
from rare.widgets.elide_label import ElideLabel
|
||||
from .library_widget import LibraryWidget
|
||||
|
||||
|
|
|
@ -5,105 +5,34 @@ Each of the objects in this module should be instantiated ONCE
|
|||
and only ONCE!
|
||||
"""
|
||||
|
||||
import configparser
|
||||
import logging
|
||||
import os
|
||||
from argparse import Namespace
|
||||
from typing import Optional, Union
|
||||
from typing import Optional
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.models.apiresults import ApiResults
|
||||
from rare.models.signals import GlobalSignals
|
||||
from .image_manager import ImageManager
|
||||
from .rare_core import RareCore
|
||||
|
||||
logger = logging.getLogger("Singleton")
|
||||
|
||||
_legendary_core_singleton: Optional[LegendaryCore] = None
|
||||
_global_signals_singleton: Optional[GlobalSignals] = None
|
||||
_arguments_singleton: Optional[Namespace] = None
|
||||
_api_results_singleton: Optional[ApiResults] = None
|
||||
logger = logging.getLogger("Shared")
|
||||
|
||||
|
||||
def LegendaryCoreSingleton(init: bool = False) -> LegendaryCore:
|
||||
global _legendary_core_singleton
|
||||
if _legendary_core_singleton is None and not init:
|
||||
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
|
||||
if _legendary_core_singleton is not None and init:
|
||||
raise RuntimeError("LegendaryCore already initialized")
|
||||
if init:
|
||||
try:
|
||||
_legendary_core_singleton = LegendaryCore()
|
||||
except configparser.MissingSectionHeaderError as e:
|
||||
logger.warning(f"Config is corrupt: {e}")
|
||||
if config_path := os.environ.get("XDG_CONFIG_HOME"):
|
||||
path = os.path.join(config_path, "legendary")
|
||||
else:
|
||||
path = os.path.expanduser("~/.config/legendary")
|
||||
with open(os.path.join(path, "config.ini"), "w") as config_file:
|
||||
config_file.write("[Legendary]")
|
||||
_legendary_core_singleton = LegendaryCore()
|
||||
if "Legendary" not in _legendary_core_singleton.lgd.config.sections():
|
||||
_legendary_core_singleton.lgd.config.add_section("Legendary")
|
||||
_legendary_core_singleton.lgd.save_config()
|
||||
# workaround if egl sync enabled, but no programdata_path
|
||||
# programdata_path might be unset if logging in through the browser
|
||||
if _legendary_core_singleton.egl_sync_enabled:
|
||||
if _legendary_core_singleton.egl.programdata_path is None:
|
||||
_legendary_core_singleton.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
_legendary_core_singleton.lgd.save_config()
|
||||
else:
|
||||
if not os.path.exists(_legendary_core_singleton.egl.programdata_path):
|
||||
_legendary_core_singleton.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
_legendary_core_singleton.lgd.save_config()
|
||||
return _legendary_core_singleton
|
||||
def ArgumentsSingleton() -> Optional[Namespace]:
|
||||
return RareCore.instance().args()
|
||||
|
||||
|
||||
def GlobalSignalsSingleton(init: bool = False) -> GlobalSignals:
|
||||
global _global_signals_singleton
|
||||
if _global_signals_singleton is None and not init:
|
||||
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
|
||||
if _global_signals_singleton is not None and init:
|
||||
raise RuntimeError("GlobalSignals already initialized")
|
||||
if init:
|
||||
_global_signals_singleton = GlobalSignals()
|
||||
return _global_signals_singleton
|
||||
def GlobalSignalsSingleton() -> GlobalSignals:
|
||||
return RareCore.instance().signals()
|
||||
|
||||
|
||||
def ArgumentsSingleton(args: Namespace = None) -> Optional[Namespace]:
|
||||
global _arguments_singleton
|
||||
if _arguments_singleton is None and args is None:
|
||||
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
|
||||
if _arguments_singleton is not None and args is not None:
|
||||
raise RuntimeError("Arguments already initialized")
|
||||
if args is not None:
|
||||
_arguments_singleton = args
|
||||
return _arguments_singleton
|
||||
def LegendaryCoreSingleton() -> LegendaryCore:
|
||||
return RareCore.instance().core()
|
||||
|
||||
|
||||
def ImageManagerSingleton() -> ImageManager:
|
||||
return RareCore.instance().image_manager()
|
||||
|
||||
|
||||
def ApiResultsSingleton(res: ApiResults = None) -> Optional[ApiResults]:
|
||||
global _api_results_singleton
|
||||
if _api_results_singleton is None and res is None:
|
||||
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
|
||||
if _api_results_singleton is not None and res is not None:
|
||||
raise RuntimeError("ApiResults already initialized")
|
||||
if res is not None:
|
||||
_api_results_singleton = res
|
||||
return _api_results_singleton
|
||||
|
||||
|
||||
def clear_singleton_instance(instance: Union[LegendaryCore, GlobalSignals, Namespace, ApiResults]):
|
||||
global _legendary_core_singleton, _global_signals_singleton, _arguments_singleton, _api_results_singleton
|
||||
if isinstance(instance, LegendaryCore):
|
||||
del instance
|
||||
_legendary_core_singleton = None
|
||||
elif isinstance(instance, GlobalSignals):
|
||||
instance.deleteLater()
|
||||
del instance
|
||||
_global_signals_singleton = None
|
||||
elif isinstance(instance, Namespace):
|
||||
del instance
|
||||
_arguments_singleton = None
|
||||
elif isinstance(instance, ApiResults):
|
||||
del instance
|
||||
_api_results_singleton = None
|
||||
else:
|
||||
raise RuntimeError(f"Instance is of unknown type \"{type(instance)}\"")
|
||||
return RareCore.instance().api_results(res)
|
||||
|
|
|
@ -7,7 +7,7 @@ import zlib
|
|||
from logging import getLogger
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import Tuple, Dict, Union, Type, List, Callable, Optional
|
||||
from typing import Tuple, Dict, Union, Type, List, Callable
|
||||
|
||||
import requests
|
||||
from PyQt5.QtCore import (
|
||||
|
@ -26,7 +26,8 @@ from PyQt5.QtGui import (
|
|||
from PyQt5.QtWidgets import QApplication
|
||||
from legendary.models.game import Game
|
||||
|
||||
from rare.shared import LegendaryCoreSingleton, GlobalSignalsSingleton
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.models.signals import GlobalSignals
|
||||
from rare.utils.paths import image_dir, resources_path
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
@ -111,10 +112,10 @@ class ImageManager(QObject):
|
|||
__dl_retries = 1
|
||||
__worker_app_names: List[str] = list()
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, signals: GlobalSignals, core: LegendaryCore):
|
||||
super(QObject, self).__init__()
|
||||
self.core = LegendaryCoreSingleton()
|
||||
self.signals = GlobalSignalsSingleton()
|
||||
self.signals = signals
|
||||
self.core = core
|
||||
|
||||
self.image_dir = Path(image_dir)
|
||||
if not self.image_dir.is_dir():
|
||||
|
@ -357,15 +358,3 @@ class ImageManager(QObject):
|
|||
"""
|
||||
image: QImage = self.__get_cover(QImage, app_name, color)
|
||||
return image
|
||||
|
||||
|
||||
_image_manager_singleton: Optional[ImageManager] = None
|
||||
|
||||
|
||||
def ImageManagerSingleton(init: bool = False) -> ImageManager:
|
||||
global _image_manager_singleton
|
||||
if _image_manager_singleton is None and not init:
|
||||
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
|
||||
if _image_manager_singleton is None:
|
||||
_image_manager_singleton = ImageManager()
|
||||
return _image_manager_singleton
|
||||
|
|
133
rare/shared/rare_core.py
Normal file
133
rare/shared/rare_core.py
Normal file
|
@ -0,0 +1,133 @@
|
|||
import configparser
|
||||
import os
|
||||
from argparse import Namespace
|
||||
from logging import getLogger
|
||||
from typing import Optional
|
||||
|
||||
from PyQt5.QtCore import QObject
|
||||
|
||||
from rare.lgndr.core import LegendaryCore
|
||||
from rare.models.apiresults import ApiResults
|
||||
from rare.models.signals import GlobalSignals
|
||||
from .image_manager import ImageManager
|
||||
|
||||
logger = getLogger("RareCore")
|
||||
|
||||
|
||||
class RareCore(QObject):
|
||||
_instance: Optional['RareCore'] = None
|
||||
|
||||
def __init__(self, args: Namespace):
|
||||
if self._instance is not None:
|
||||
raise RuntimeError("RareCore already initialized")
|
||||
super(RareCore, self).__init__()
|
||||
self._args: Optional[Namespace] = None
|
||||
self._signals: Optional[GlobalSignals] = None
|
||||
self._core: Optional[LegendaryCore] = None
|
||||
self._image_manager: Optional[ImageManager] = None
|
||||
self._api_results: Optional[ApiResults] = None
|
||||
|
||||
self.args(args)
|
||||
self.signals(init=True)
|
||||
self.core(init=True)
|
||||
self.image_manager(init=True)
|
||||
|
||||
RareCore._instance = self
|
||||
|
||||
@staticmethod
|
||||
def instance() -> 'RareCore':
|
||||
if RareCore._instance is None:
|
||||
raise RuntimeError("Uninitialized use of RareCore")
|
||||
return RareCore._instance
|
||||
|
||||
def signals(self, init: bool = False) -> GlobalSignals:
|
||||
if self._signals is None and not init:
|
||||
raise RuntimeError("Uninitialized use of GlobalSignalsSingleton")
|
||||
if self._signals is not None and init:
|
||||
raise RuntimeError("GlobalSignals already initialized")
|
||||
if init:
|
||||
self._signals = GlobalSignals()
|
||||
return self._signals
|
||||
|
||||
def args(self, args: Namespace = None) -> Optional[Namespace]:
|
||||
if self._args is None and args is None:
|
||||
raise RuntimeError("Uninitialized use of ArgumentsSingleton")
|
||||
if self._args is not None and args is not None:
|
||||
raise RuntimeError("Arguments already initialized")
|
||||
if args is not None:
|
||||
self._args = args
|
||||
return self._args
|
||||
|
||||
def core(self, init: bool = False) -> LegendaryCore:
|
||||
if self._core is None and not init:
|
||||
raise RuntimeError("Uninitialized use of LegendaryCoreSingleton")
|
||||
if self._core is not None and init:
|
||||
raise RuntimeError("LegendaryCore already initialized")
|
||||
if init:
|
||||
try:
|
||||
self._core = LegendaryCore()
|
||||
except configparser.MissingSectionHeaderError as e:
|
||||
logger.warning(f"Config is corrupt: {e}")
|
||||
if config_path := os.environ.get("XDG_CONFIG_HOME"):
|
||||
path = os.path.join(config_path, "legendary")
|
||||
else:
|
||||
path = os.path.expanduser("~/.config/legendary")
|
||||
with open(os.path.join(path, "config.ini"), "w") as config_file:
|
||||
config_file.write("[Legendary]")
|
||||
self._core = LegendaryCore()
|
||||
if "Legendary" not in self._core.lgd.config.sections():
|
||||
self._core.lgd.config.add_section("Legendary")
|
||||
self._core.lgd.save_config()
|
||||
# workaround if egl sync enabled, but no programdata_path
|
||||
# programdata_path might be unset if logging in through the browser
|
||||
if self._core.egl_sync_enabled:
|
||||
if self._core.egl.programdata_path is None:
|
||||
self._core.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
self._core.lgd.save_config()
|
||||
else:
|
||||
if not os.path.exists(self._core.egl.programdata_path):
|
||||
self._core.lgd.config.remove_option("Legendary", "egl_sync")
|
||||
self._core.lgd.save_config()
|
||||
return self._core
|
||||
|
||||
def image_manager(self, init: bool = False) -> ImageManager:
|
||||
if self._image_manager is None and not init:
|
||||
raise RuntimeError("Uninitialized use of ImageManagerSingleton")
|
||||
if self._image_manager is not None and init:
|
||||
raise RuntimeError("ImageManager already initialized")
|
||||
if self._image_manager is None:
|
||||
self._image_manager = ImageManager(self.signals(), self.core())
|
||||
return self._image_manager
|
||||
|
||||
def api_results(self, res: ApiResults = None) -> Optional[ApiResults]:
|
||||
if self._api_results is None and res is None:
|
||||
raise RuntimeError("Uninitialized use of ApiResultsSingleton")
|
||||
if self._api_results is not None and res is not None:
|
||||
raise RuntimeError("ApiResults already initialized")
|
||||
if res is not None:
|
||||
self._api_results = res
|
||||
return self._api_results
|
||||
|
||||
def deleteLater(self) -> None:
|
||||
del self._api_results
|
||||
self._api_results = None
|
||||
|
||||
self._image_manager.deleteLater()
|
||||
del self._image_manager
|
||||
self._image_manager = None
|
||||
|
||||
self._core.exit()
|
||||
del self._core
|
||||
self._core = None
|
||||
|
||||
self._signals.deleteLater()
|
||||
del self._signals
|
||||
self._signals = None
|
||||
|
||||
del self._args
|
||||
self._args = None
|
||||
|
||||
RareCore._instance = None
|
||||
|
||||
super(RareCore, self).deleteLater()
|
||||
|
Loading…
Reference in a new issue