Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecate!:backend client #295

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions ovos_workshop/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,15 @@
class OVOSAbstractApplication(OVOSSkill):
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, **kwargs):
gui: Optional[GUIInterface] = None, **kwargs):
"""
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
"""
self._dedicated_bus = False
if bus:
Expand All @@ -38,15 +32,7 @@ def __init__(self, skill_id: str, bus: Optional[MessageBusClient] = None,

super().__init__(skill_id=skill_id, bus=bus, gui=gui,
resources_dir=resources_dir,
enable_settings_manager=enable_settings_manager,
**kwargs)

if settings:
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) -> str:
"""
Expand Down
162 changes: 0 additions & 162 deletions ovos_workshop/settings.py
Original file line number Diff line number Diff line change
@@ -1,167 +1,5 @@
import json
from os.path import isfile
from threading import Timer
from typing import Optional

import yaml
from json_database import JsonStorageXDG

from ovos_backend_client.api import DeviceApi
from ovos_backend_client.pairing import is_paired, requires_backend
from ovos_backend_client.settings import RemoteSkillSettings, get_display_name
from ovos_bus_client import MessageBusClient
from ovos_bus_client.message import Message, dig_for_message
from ovos_utils.log import LOG


class SkillSettingsManager:
def __init__(self, skill):
self.download_timer: Optional[Timer] = None
self.skill = skill
self.api = DeviceApi()
self.remote_settings = \
RemoteSkillSettings(self.skill_id,
settings=dict(self.skill.settings),
meta=self.load_meta(), remote_id=self.skill_gid)
self.register_bus_handlers()

def start(self):
self._download()

def _download(self):
# If this method is called outside of the timer loop, ensure the
# existing timer is canceled before starting a new one.
if self.download_timer:
self.download_timer.cancel()

self.download()

# prepare to download again in 60 seconds
self.download_timer = Timer(60, self._download)
self.download_timer.daemon = True
self.download_timer.start()

def stop(self):
# If this method is called outside of the timer loop, ensure the
# existing timer is canceled
if self.download_timer:
self.download_timer.cancel()

@property
def bus(self) -> MessageBusClient:
return self.skill.bus

@property
def skill_id(self) -> str:
return self.skill.skill_id

@property
def display_name(self) -> str:
return get_display_name(self.skill_id)

@property
def skill_gid(self) -> str:
return f"@{self.api.uuid}|{self.skill_id}"

@property
def skill_meta(self) -> dict:
return self.remote_settings.meta

def register_bus_handlers(self):
self.skill.add_event('mycroft.skills.settings.update',
self.handle_download_remote) # backwards compat
self.skill.add_event('mycroft.skills.settings.download',
self.handle_download_remote)
self.skill.add_event('mycroft.skills.settings.upload',
self.handle_upload_local)
self.skill.add_event('mycroft.skills.settings.upload.meta',
self.handle_upload_meta)
self.skill.add_event('mycroft.paired',
self.handle_upload_local)

def load_meta(self) -> dict:
json_path = f"{self.skill.root_dir}/settingsmeta.json"
yaml_path = f"{self.skill.root_dir}/settingsmeta.yaml"
if isfile(yaml_path):
with open(yaml_path) as meta_file:
return yaml.safe_load(meta_file)
elif isfile(json_path):
with open(json_path) as meta_file:
return json.load(meta_file)
return {}

def save_meta(self, generate: bool = False):
# unset reload flag to avoid a reload on settingmeta change
# TODO - support for settingsmeta XDG paths
reload = self.skill.reload_skill
self.skill.reload_skill = False

# generate meta for missing fields
if generate:
self.remote_settings.generate_meta()

# write to disk
json_path = f"{self.skill.root_dir}/settingsmeta.json"
yaml_path = f"{self.skill.root_dir}/settingsmeta.yaml"
if isfile(yaml_path):
with open(yaml_path) as meta_file:
yaml.dump(self.remote_settings.meta, meta_file)
else:
with open(json_path, "w") as meta_file:
json.dump(self.remote_settings.meta, meta_file)

# reset reloading flag
self.skill.reload_skill = reload

@requires_backend
def upload(self, generate: bool = False):
if not is_paired():
LOG.debug("Device needs to be paired to upload settings")
return
self.remote_settings.settings = dict(self.skill.settings)
if generate:
self.remote_settings.generate_meta()
self.remote_settings.upload()

@requires_backend
def upload_meta(self, generate: bool = False):
if not is_paired():
LOG.debug("Device needs to be paired to upload settingsmeta")
return
if generate:
self.remote_settings.settings = dict(self.skill.settings)
self.remote_settings.generate_meta()
self.remote_settings.upload_meta()

@requires_backend
def download(self):
if not is_paired():
LOG.debug("Device needs to be paired to download remote settings")
return
self.remote_settings.download()
# we do not update skill object settings directly
# skill will handle the event and trigger a callback
if self.skill.settings != self.remote_settings.settings:
# dig old message to keep context
msg = dig_for_message() or Message("")
msg = msg.forward('mycroft.skills.settings.changed')

msg.data[self.skill_id] = self.remote_settings.settings
self.bus.emit(msg)

def handle_upload_meta(self, message: Message):
skill_id = message.data.get("skill_id")
if skill_id == self.skill_id:
self.upload_meta()

def handle_upload_local(self, message: Message):
skill_id = message.data.get("skill_id")
if skill_id == self.skill_id:
self.upload()

def handle_download_remote(self, message: Message):
self.download()


def settings2meta(settings, section_name="Skill Settings"):
""" generates basic settingsmeta """
Expand Down
32 changes: 0 additions & 32 deletions ovos_workshop/skills/ovos.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
from ovos_workshop.resource_files import ResourceFile, \
CoreResources, find_resource, SkillResources
from ovos_workshop.settings import PrivateSettings
from ovos_workshop.settings import SkillSettingsManager


def simple_trace(stack_trace: List[str]) -> str:
Expand Down Expand Up @@ -92,7 +91,6 @@ def __init__(self, name: Optional[str] = None,
resources_dir: Optional[str] = None,
settings: Optional[JsonStorage] = None,
gui: Optional[GUIInterface] = None,
enable_settings_manager: bool = True,
skill_id: str = ""):
"""
Create an OVOSSkill object.
Expand All @@ -103,18 +101,12 @@ def __init__(self, name: Optional[str] = None,
@param settings: Optional settings object, else defined in skill config
path
@param gui: Optional SkillGUI, else one is initialized
@param enable_settings_manager: if True, enables a SettingsManager for
this skill to manage default settings and backend sync
@param skill_id: Unique ID for this skill
"""

self.log = LOG # a dedicated namespace will be assigned in _startup
self._enable_settings_manager = enable_settings_manager
self._init_event = Event()
self.name = name or self.__class__.__name__
self.skill_id = skill_id # set by SkillLoader, guaranteed unique
self._settings_meta = None # DEPRECATED - backwards compat only
self.settings_manager = None
self.private_settings = None

# Get directory of skill source (__init__.py)
Expand Down Expand Up @@ -811,8 +803,6 @@ def _startup(self, bus: MessageBusClient, skill_id: str = ""):
self.status.set_alive()
if not self.gui:
self._init_skill_gui()
if self._enable_settings_manager:
self._init_settings_manager()
self.load_data_files()
self._register_skill_json()
self._register_decorated()
Expand Down Expand Up @@ -917,12 +907,6 @@ def _init_skill_gui(self):
self.gui = SkillGUI(self)
self.gui.setup_default_handlers()

def _init_settings_manager(self):
"""
Set up the SkillSettingsManager for this skill.
"""
self.settings_manager = SkillSettingsManager(self)

def register_homescreen_app(self, icon: str, name: str, event: str):
"""the icon file MUST be located under 'gui' subfolder"""
# this path is hardcoded in ovos_gui.constants and follows XDG spec
Expand Down Expand Up @@ -1025,19 +1009,6 @@ def _register_decorated(self):
for intent_file in getattr(method, 'converse_intents'):
self.register_converse_intent(intent_file, method)

def _upload_settings(self):
"""
Upload settings to a remote backend if configured.
"""
if self.settings_manager and self.config_core.get("skills", {}).get("sync2way"):
# upload new settings to backend
generate = self.config_core.get("skills", {}).get("autogen_meta", True)
# this will check global sync flag
self.settings_manager.upload(generate)
if generate:
# update settingsmeta file on disk
self.settings_manager.save_meta()

def bind(self, bus: MessageBusClient):
"""
Register MessageBusClient with skill.
Expand Down Expand Up @@ -1154,7 +1125,6 @@ def _handle_settings_file_change(self, path: str):
except Exception as e:
self.log.exception("settings change callback failed, "
f"file changes not handled!: {e}")
self._upload_settings()

def handle_settings_change(self, message: Message):
"""
Expand Down Expand Up @@ -1334,8 +1304,6 @@ def default_shutdown(self):
# Store settings
if self.settings != self._initial_settings:
self.settings.store()
if self._settings_meta:
self._settings_meta.stop()
if self._settings_watchdog:
self._settings_watchdog.shutdown()
except Exception as e:
Expand Down
1 change: 0 additions & 1 deletion requirements/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
ovos-utils>= 0.2.1,<1.0.0
ovos_bus_client>=0.0.8,<2.0.0
ovos-config>=0.0.12,<1.0.0
ovos-backend-client>=0.1.0,<2.0.0
ovos-solver-yes-no-plugin>=0.0.1,<1.0.0
ovos-number-parser>=0.0.1,<1.0.0
rapidfuzz
Expand Down
13 changes: 0 additions & 13 deletions test/unittests/skills/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def tearDownClass(cls) -> None:
shutil.rmtree(cls.test_config_path)

def test_00_skill_init(self):
from ovos_workshop.settings import SkillSettingsManager
from ovos_workshop.skills.ovos import SkillGUI
from ovos_utils.events import EventContainer, EventSchedulerInterface
from ovos_workshop.intents import IntentServiceInterface
Expand All @@ -36,10 +35,8 @@ def test_00_skill_init(self):
from ovos_workshop.resource_files import SkillResources

self.assertIsInstance(self.skill.log, Logger)
self.assertIsInstance(self.skill._enable_settings_manager, bool)
self.assertEqual(self.skill.name, self.skill.__class__.__name__)
self.assertEqual(self.skill.skill_id, self.skill_id)
self.assertIsInstance(self.skill.settings_manager, SkillSettingsManager)
self.assertTrue(isdir(self.skill.root_dir))
self.assertEqual(self.skill.res_dir, self.skill.root_dir)
self.assertIsInstance(self.skill.gui, SkillGUI)
Expand Down Expand Up @@ -170,28 +167,18 @@ def test_upload_settings(self):
pass

def test_handle_settings_file_change(self):
real_upload = self.skill._upload_settings
self.skill._upload_settings = Mock()
settings_file = self.skill.settings.path

# Handle change with no callback
self.skill._handle_settings_file_change(settings_file)
self.skill._upload_settings.assert_called_once()

# Handle change with callback
self.skill._upload_settings.reset_mock()
self.skill.settings_change_callback = Mock()
self.skill._handle_settings_file_change(settings_file)
self.skill._upload_settings.assert_called_once()
self.skill.settings_change_callback.assert_called_once()

# Handle non-settings file change
self.skill._handle_settings_file_change(join(dirname(settings_file),
"test.file"))
self.skill._upload_settings.assert_called_once()
self.skill.settings_change_callback.assert_called_once()

self.skill._upload_settings = real_upload

def test_load_lang(self):
# TODO
Expand Down
14 changes: 0 additions & 14 deletions test/unittests/skills/test_ovos.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from ovos_workshop.decorators.layers import IntentLayers
from ovos_workshop.resource_files import SkillResources

from ovos_workshop.settings import SkillSettingsManager
from ovos_workshop.skills.ovos import OVOSSkill


Expand Down Expand Up @@ -98,19 +97,6 @@ def test_send_stop_signal(self):
# TODO
pass

def test_settings_manager_init(self):
bus = FakeBus()
skill_default = MockSkill(bus=bus)
skill_default._startup(bus)

self.assertIsInstance(skill_default.settings_manager,
SkillSettingsManager)

skill_disabled_settings = MockSkill(bus=bus,
enable_settings_manager=False)
skill_disabled_settings._startup(bus)
self.assertIsNone(skill_disabled_settings.settings_manager)

def test_bus_setter(self):
bus = FakeBus()
skill = MockSkill()
Expand Down
Loading
Loading