diff --git a/ovos_workshop/app.py b/ovos_workshop/app.py index 6e0a5208..d49e9fc1 100644 --- a/ovos_workshop/app.py +++ b/ovos_workshop/app.py @@ -1,16 +1,33 @@ from os.path import isdir, join - +from typing import Optional from ovos_config.locations import get_xdg_config_save_path from ovos_utils.messagebus import get_mycroft_bus -from ovos_utils.log import LOG - +from ovos_utils.log import log_deprecation +from ovos_utils.gui import GUIInterface +from ovos_bus_client.client.client import MessageBusClient from ovos_workshop.resource_files import locate_lang_directories from ovos_workshop.skills.ovos import OVOSSkill class OVOSAbstractApplication(OVOSSkill): - def __init__(self, skill_id, bus=None, resources_dir=None, - lang=None, settings=None, gui=None, enable_settings_manager=False): + def __init__(self, skill_id: str, bus: Optional[MessageBusClient] = None, + resources_dir: Optional[str] = None, + lang=None, settings: Optional[dict] = None, + gui: Optional[GUIInterface] = None, + enable_settings_manager: bool = False): + """ + Create an Application. An application is essentially a skill, but + designed such that it may be run without an intent service. + @param skill_id: Unique ID for this application + @param bus: MessageBusClient to bind to application + @param resources_dir: optional root resource directory (else defaults to + application `root_dir` + @param lang: DEPRECATED language of the application + @param settings: DEPRECATED settings object + @param gui: GUIInterface to bind (if `None`, one is created) + @param enable_settings_manager: if True, enables a SettingsManager for + this application to manage default settings and backend sync + """ super().__init__(bus=bus, gui=gui, resources_dir=resources_dir, enable_settings_manager=enable_settings_manager) self.skill_id = skill_id @@ -22,25 +39,38 @@ def __init__(self, skill_id, bus=None, resources_dir=None, bus = get_mycroft_bus() self._startup(bus, skill_id) if settings: - LOG.warning("settings arg is deprecated and will be removed " - "in a future release") + log_deprecation(f"Settings should be set in {self._settings_path}. " + f"Passing `settings` to __init__ is not supported.", + "0.1.0") self.settings.merge(settings) @property - def _settings_path(self): + def _settings_path(self) -> str: + """ + Overrides the default path to put settings in `apps` subdirectory. + """ return join(get_xdg_config_save_path(), 'apps', self.skill_id, 'settings.json') def default_shutdown(self): + """ + Shutdown this application. + """ self.clear_intents() super().default_shutdown() if self._dedicated_bus: self.bus.close() - def get_language_dir(self, base_path=None, lang=None): - """ checks for all language variations and returns best path - eg, if lang is set to pt-pt but only pt-br resources exist, - those will be loaded instead of failing, or en-gb vs en-us and so on + def get_language_dir(self, base_path: Optional[str] = None, + lang: Optional[str] = None) -> Optional[str]: + """ + Get the best matched language resource directory for the requested lang. + This will consider dialects for the requested language, i.e. if lang is + set to pt-pt but only pt-br resources exist, the `pt-br` resource path + will be returned. + @param base_path: root path to find resources (default res_dir) + @param lang: language to get resources for (default self.lang) + @return: path to language resources if they exist, else None """ base_path = base_path or self.res_dir @@ -56,13 +86,15 @@ def get_language_dir(self, base_path=None, lang=None): similar_dialect_directories = locate_lang_directories(lang, base_path) for directory in similar_dialect_directories: if directory.exists(): - return directory + return str(directory) def clear_intents(self): - # remove bus handlers, otherwise if re-registered we get multiple - # handler executions + """ + Remove bus event handlers and detach from the intent service to prevent + multiple registered handlers. + """ for intent_name, _ in self.intent_service: event_name = f'{self.skill_id}:{intent_name}' self.remove_event(event_name) - - self.intent_service.detach_all() # delete old intents before re-registering + # delete old intents before re-registering + self.intent_service.detach_all() diff --git a/test/unittests/test_abstract_app.py b/test/unittests/test_abstract_app.py index d743ce2b..38847a43 100644 --- a/test/unittests/test_abstract_app.py +++ b/test/unittests/test_abstract_app.py @@ -2,6 +2,7 @@ from os.path import join, dirname from os import remove +from unittest.mock import Mock, patch from ovos_utils.gui import GUIInterface from ovos_utils.messagebus import FakeBus @@ -84,6 +85,28 @@ def test_settings_path(self): remove(test_app._settings_path) remove(test_skill._settings_path) + @patch("ovos_workshop.app.OVOSSkill.default_shutdown") + def test_default_shutdown(self, skill_shutdown): + real_clear_intents = self.app.clear_intents + real_bus_close = self.app.bus.close + self.app.bus.close = Mock() + self.app.clear_intents = Mock() + self.app.default_shutdown() + self.app.clear_intents.assert_called_once() + self.app.bus.close.assert_called_once() + skill_shutdown.assert_called_once() + + self.app.bus.close = real_bus_close + self.app.clear_intents = real_clear_intents + + def test_get_language_dir(self): + # TODO + pass + + def test_clear_intents(self): + # TODO + pass + def test_class_inheritance(self): from ovos_workshop.skills.base import BaseSkill from ovos_workshop.skills.ovos import OVOSSkill