1
0
Fork 0
mirror of synced 2024-06-17 10:04:43 +12:00

Introduce a very basic RareCore to handle signletons and their cleanup.

This commit is contained in:
loathingKernel 2022-09-04 20:38:24 +03:00
parent 7020141148
commit 1ebd0b18d8
12 changed files with 177 additions and 131 deletions

View file

@ -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

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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")

View file

@ -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

View file

@ -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)

View file

@ -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
View 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()