From d1cdc819080f014932a4a5f1fbd1e7fd3472020d Mon Sep 17 00:00:00 2001 From: Daniel McKnight <34697904+NeonDaniel@users.noreply.github.com> Date: Fri, 29 Dec 2023 11:47:01 -0800 Subject: [PATCH] Add backwards-compat imports with deprecation warnings (#209) * Add backwards-compat imports with deprecation warnings https://github.com/NeonGeckoCom/NeonCore/pull/599 * Fix `merge_dict` override * Specify post release version * 0.0.32post1 (#210) * less deprecation warning logs * restore EventContainer deprecation log --------- Co-authored-by: Daniel McKnight Co-authored-by: JarbasAI <33701864+JarbasAl@users.noreply.github.com> --- ovos_utils/__init__.py | 18 +- ovos_utils/enclosure/api.py | 2 +- ovos_utils/events.py | 12 +- ovos_utils/fakebus.py | 347 ++++++++++++++++ ovos_utils/file_utils.py | 14 +- ovos_utils/gui.py | 26 +- .../intents/intent_service_interface.py | 15 +- ovos_utils/messagebus.py | 382 ++---------------- ovos_utils/skills/__init__.py | 64 +-- ovos_utils/skills/api.py | 2 +- ovos_utils/skills/audioservice.py | 3 +- ovos_utils/version.py | 2 +- test/unittests/test_enclosure.py | 2 +- test/unittests/test_events.py | 2 +- test/unittests/test_gui.py | 4 +- test/unittests/test_intents.py | 2 +- test/unittests/test_skills.py | 2 +- 17 files changed, 478 insertions(+), 421 deletions(-) create mode 100644 ovos_utils/fakebus.py diff --git a/ovos_utils/__init__.py b/ovos_utils/__init__.py index c4f829f9..1fa3e1f7 100644 --- a/ovos_utils/__init__.py +++ b/ovos_utils/__init__.py @@ -183,19 +183,11 @@ def wait_for_exit_signal(): LOG.debug(f"Exiting on KeyboardInterrupt") -def get_handler_name(handler): - """Name (including class if available) of handler function. - - Arguments: - handler (function): Function to be named - - Returns: - string: handler name as string - """ - if '__self__' in dir(handler) and 'name' in dir(handler.__self__): - return handler.__self__.name + '.' + handler.__name__ - else: - return handler.__name__ +def get_handler_name(*args, **kwargs): + from ovos_utils.log import log_deprecation + log_deprecation("Import from `ovos_utils.events`", "0.1.0") + from ovos_utils.events import get_handler_name + return get_handler_name(*args, **kwargs) def camel_case_split(identifier: str) -> str: diff --git a/ovos_utils/enclosure/api.py b/ovos_utils/enclosure/api.py index c3c2f59a..3427bdcb 100644 --- a/ovos_utils/enclosure/api.py +++ b/ovos_utils/enclosure/api.py @@ -7,7 +7,7 @@ from ovos_bus_client.apis.enclosure import EnclosureApi except ImportError: - from ovos_utils.messagebus import FakeMessage as Message, dig_for_message + from ovos_utils.fakebus import Message, dig_for_message class EnclosureAPI: """ diff --git a/ovos_utils/events.py b/ovos_utils/events.py index b8d7ca68..8c603bf7 100644 --- a/ovos_utils/events.py +++ b/ovos_utils/events.py @@ -3,8 +3,8 @@ from inspect import signature from typing import Callable, Optional, Union -import ovos_utils.messagebus -from ovos_utils.intents.intent_service_interface import to_alnum +from ovos_utils.fakebus import Message, FakeBus, dig_for_message +from ovos_utils.file_utils import to_alnum from ovos_utils.log import LOG, log_deprecation, deprecated @@ -17,7 +17,7 @@ def unmunge_message(message, skill_id: str): Returns: Message without clear keywords """ - if isinstance(message, ovos_utils.messagebus.Message) and \ + if isinstance(message, Message) and \ isinstance(message.data, dict): skill_id = to_alnum(skill_id) for key in list(message.data.keys()): @@ -131,7 +131,7 @@ class EventContainer: """ def __init__(self, bus=None): - self.bus = bus or ovos_utils.messagebus.FakeBus() + self.bus = bus or FakeBus() self.events = [] def set_bus(self, bus): @@ -241,7 +241,7 @@ def set_id(self, sched_id: str): self.skill_id = sched_id def _get_source_message(self): - message = ovos_utils.messagebus.dig_for_message() or ovos_utils.messagebus.Message("") + message = dig_for_message() or Message("") message.context['skill_id'] = self.skill_id return message @@ -301,7 +301,7 @@ def on_error(e): message = self._get_source_message() context = context or message.context context["skill_id"] = self.skill_id - self.bus.emit(ovos_utils.messagebus.Message('mycroft.scheduler.schedule_event', + self.bus.emit(Message('mycroft.scheduler.schedule_event', data=event_data, context=context)) def schedule_event(self, handler: Callable[..., None], diff --git a/ovos_utils/fakebus.py b/ovos_utils/fakebus.py new file mode 100644 index 00000000..4e86ddca --- /dev/null +++ b/ovos_utils/fakebus.py @@ -0,0 +1,347 @@ +import json +from copy import deepcopy +from threading import Event + +from pyee import BaseEventEmitter + +from ovos_utils.log import LOG, log_deprecation + + +def dig_for_message(): + try: + from ovos_bus_client.message import dig_for_message as _dig + return _dig() + except ImportError: + pass + return None + + +class FakeBus: + def __init__(self, *args, **kwargs): + self.started_running = False + self.session_id = "default" + self.ee = kwargs.get("emitter") or BaseEventEmitter() + self.ee.on("error", self.on_error) + self.on_open() + try: + self.session_id = kwargs["session"].session_id + except: + pass # don't care + + self.on("ovos.session.update_default", + self.on_default_session_update) + + def on(self, msg_type, handler): + self.ee.on(msg_type, handler) + + def once(self, msg_type, handler): + self.ee.once(msg_type, handler) + + def emit(self, message): + if "session" not in message.context: + try: # replicate side effects + from ovos_bus_client.session import Session, SessionManager + sess = SessionManager.sessions.get(self.session_id) or \ + Session(self.session_id) + message.context["session"] = sess.serialize() + except ImportError: # don't care + message.context["session"] = {"session_id": self.session_id} + self.ee.emit("message", message.serialize()) + self.ee.emit(message.msg_type, message) + self.on_message(message.serialize()) + + def on_message(self, *args): + """ + Handle an incoming websocket message + @param args: + message (str): serialized Message + """ + if len(args) == 1: + message = args[0] + else: + message = args[1] + parsed_message = Message.deserialize(message) + try: # replicate side effects + from ovos_bus_client.session import Session, SessionManager + sess = Session.from_message(parsed_message) + if sess.session_id != "default": + # 'default' can only be updated by core + SessionManager.update(sess) + except ImportError: + pass # don't care + + def on_default_session_update(self, message): + try: # replicate side effects + from ovos_bus_client.session import Session, SessionManager + new_session = message.data["session_data"] + sess = Session.deserialize(new_session) + SessionManager.update(sess, make_default=True) + LOG.debug("synced default_session") + except ImportError: + pass # don't care + + def wait_for_message(self, message_type, timeout=3.0): + """Wait for a message of a specific type. + + Arguments: + message_type (str): the message type of the expected message + timeout: seconds to wait before timeout, defaults to 3 + + Returns: + The received message or None if the response timed out + """ + received_event = Event() + received_event.clear() + + msg = None + + def rcv(m): + nonlocal msg + msg = m + received_event.set() + + self.ee.once(message_type, rcv) + received_event.wait(timeout) + return msg + + def wait_for_response(self, message, reply_type=None, timeout=3.0): + """Send a message and wait for a response. + + Arguments: + message (Message): message to send + reply_type (str): the message type of the expected reply. + Defaults to ".response". + timeout: seconds to wait before timeout, defaults to 3 + + Returns: + The received message or None if the response timed out + """ + reply_type = reply_type or message.msg_type + ".response" + received_event = Event() + received_event.clear() + + msg = None + + def rcv(m): + nonlocal msg + msg = m + received_event.set() + + self.ee.once(reply_type, rcv) + self.emit(message) + received_event.wait(timeout) + return msg + + def remove(self, msg_type, handler): + try: + self.ee.remove_listener(msg_type, handler) + except: + pass + + def remove_all_listeners(self, event_name): + self.ee.remove_all_listeners(event_name) + + def create_client(self): + return self + + def on_error(self, error): + LOG.error(error) + + def on_open(self): + pass + + def on_close(self): + pass + + def run_forever(self): + self.started_running = True + + def run_in_thread(self): + self.run_forever() + + def close(self): + self.on_close() + + +class _MutableMessage(type): + """ To override isinstance checks we need to use a metaclass """ + + def __instancecheck__(self, instance): + try: + from ovos_bus_client.message import Message as _MycroftMessage + if isinstance(instance, _MycroftMessage): + return True + except ImportError: + pass + try: + from mycroft_bus_client.message import Message as _MycroftMessage + if isinstance(instance, _MycroftMessage): + return True + except ImportError: + pass + return super().__instancecheck__(instance) + + +# fake Message object to allow usage without ovos-bus-client installed +class FakeMessage(metaclass=_MutableMessage): + """ fake Message object to allow usage with FakeBus without ovos-bus-client installed""" + + def __new__(cls, *args, **kwargs): + try: # most common case + from ovos_bus_client import Message as _M + return _M(*args, **kwargs) + except ImportError: + pass + try: # some old install that upgraded during migration period + from mycroft_bus_client import Message as _M + return _M(*args, **kwargs) + except ImportError: # FakeMessage + return super().__new__(cls) + + def __init__(self, msg_type, data=None, context=None): + """Used to construct a message object + + Message objects will be used to send information back and forth + between processes of mycroft service, voice, skill and cli + """ + self.msg_type = msg_type + self.data = data or {} + self.context = context or {} + + def __eq__(self, other): + try: + return other.msg_type == self.msg_type and \ + other.data == self.data and \ + other.context == self.context + except: + return False + + def serialize(self): + """This returns a string of the message info. + + This makes it easy to send over a websocket. This uses + json dumps to generate the string with type, data and context + + Returns: + str: a json string representation of the message. + """ + return json.dumps({'type': self.msg_type, + 'data': self.data, + 'context': self.context}) + + @staticmethod + def deserialize(value): + """This takes a string and constructs a message object. + + This makes it easy to take strings from the websocket and create + a message object. This uses json loads to get the info and generate + the message object. + + Args: + value(str): This is the json string received from the websocket + + Returns: + FakeMessage: message object constructed from the json string passed + int the function. + value(str): This is the string received from the websocket + """ + obj = json.loads(value) + return FakeMessage(obj.get('type') or '', + obj.get('data') or {}, + obj.get('context') or {}) + + def forward(self, msg_type, data=None): + """ Keep context and forward message + + This will take the same parameters as a message object but use + the current message object as a reference. It will copy the context + from the existing message object. + + Args: + msg_type (str): type of message + data (dict): data for message + + Returns: + FakeMessage: Message object to be used on the reply to the message + """ + data = data or {} + return FakeMessage(msg_type, data, context=self.context) + + def reply(self, msg_type, data=None, context=None): + """Construct a reply message for a given message + + This will take the same parameters as a message object but use + the current message object as a reference. It will copy the context + from the existing message object and add any context passed in to + the function. Check for a destination passed in to the function from + the data object and add that to the context as a destination. If the + context has a source then that will be swapped with the destination + in the context. The new message will then have data passed in plus the + new context generated. + + Args: + msg_type (str): type of message + data (dict): data for message + context: intended context for new message + + Returns: + FakeMessage: Message object to be used on the reply to the message + """ + data = deepcopy(data) or {} + context = context or {} + + new_context = deepcopy(self.context) + for key in context: + new_context[key] = context[key] + if 'destination' in data: + new_context['destination'] = data['destination'] + if 'source' in new_context and 'destination' in new_context: + s = new_context['destination'] + new_context['destination'] = new_context['source'] + new_context['source'] = s + return FakeMessage(msg_type, data, context=new_context) + + def response(self, data=None, context=None): + """Construct a response message for the message + + Constructs a reply with the data and appends the expected + ".response" to the message + + Args: + data (dict): message data + context (dict): message context + Returns + (Message) message with the type modified to match default response + """ + return self.reply(self.msg_type + '.response', data, context) + + def publish(self, msg_type, data, context=None): + """ + Copy the original context and add passed in context. Delete + any target in the new context. Return a new message object with + passed in data and new context. Type remains unchanged. + + Args: + msg_type (str): type of message + data (dict): date to send with message + context: context added to existing context + + Returns: + FakeMessage: Message object to publish + """ + context = context or {} + new_context = self.context.copy() + for key in context: + new_context[key] = context[key] + + if 'target' in new_context: + del new_context['target'] + + return FakeMessage(msg_type, data, context=new_context) + + +class Message(FakeMessage): + def __int__(self, *args, **kwargs): + log_deprecation(f"Import from ovos_bus_client.message directly", + "0.1.0") + FakeMessage.__init__(self, *args, **kwargs) diff --git a/ovos_utils/file_utils.py b/ovos_utils/file_utils.py index 2a25c7b4..1ce41efd 100644 --- a/ovos_utils/file_utils.py +++ b/ovos_utils/file_utils.py @@ -18,6 +18,19 @@ from ovos_utils.system import search_mycroft_core_location +def to_alnum(skill_id: str) -> str: + """ + Convert a skill id to only alphanumeric characters + Non-alphanumeric characters are converted to "_" + + Args: + skill_id (str): identifier to be converted + Returns: + (str) String of letters + """ + return ''.join(c if c.isalnum() else '_' for c in str(skill_id)) + + def get_temp_path(*args) -> str: """ Generate a valid path in the system temp directory. @@ -244,7 +257,6 @@ def load_vocabulary(basedir: str, skill_id: str) -> dict: Returns: dict with intent_type as keys and list of list of lists as value. """ - from ovos_utils.intents.intent_service_interface import to_alnum vocabs = {} for path, _, files in walk(basedir): diff --git a/ovos_utils/gui.py b/ovos_utils/gui.py index 2af3ae99..7b34320a 100644 --- a/ovos_utils/gui.py +++ b/ovos_utils/gui.py @@ -8,8 +8,9 @@ from ovos_utils import resolve_ovos_resource_file, resolve_resource_file from ovos_utils.log import LOG, log_deprecation -from ovos_utils.messagebus import wait_for_reply, get_mycroft_bus, Message from ovos_utils.system import is_installed, has_screen, is_process_running +from ovos_utils.fakebus import Message + _default_gui_apps = ( "mycroft-gui-app", @@ -49,6 +50,10 @@ def is_gui_connected(bus=None) -> bool: @param bus: MessageBusClient to use for query @return: True if GUI is connected """ + try: + from ovos_bus_client.util import wait_for_reply + except: + from ovos_utils.messagebus import wait_for_reply response = wait_for_reply("gui.status.request", "gui.status.request.response", bus=bus) if response: @@ -105,7 +110,9 @@ def extend_about_data(about_data: Union[list, dict], @param about_data: list of dict key, val information to add to the GUI @param bus: MessageBusClient object to emit update on """ - bus = bus or get_mycroft_bus() + if not bus: + from ovos_bus_client.util import get_mycroft_bus + bus = get_mycroft_bus() if isinstance(about_data, list): bus.emit(Message("smartspeaker.extension.extend.about", {"display_list": about_data})) @@ -119,7 +126,10 @@ def extend_about_data(about_data: Union[list, dict], class GUIWidgets: def __init__(self, bus=None): - self.bus = bus or get_mycroft_bus() + if not bus: + from ovos_bus_client.util import get_mycroft_bus + bus = get_mycroft_bus() + self.bus = bus def show_widget(self, widget_type, widget_data): LOG.debug("Showing widget: " + widget_type) @@ -150,7 +160,10 @@ class GUITracker: def __init__(self, bus=None, host='0.0.0.0', port=8181, route='/core', ssl=False): - self.bus = bus or get_mycroft_bus(host, port, route, ssl) + if not bus: + from ovos_bus_client.util import get_mycroft_bus + bus = get_mycroft_bus(host, port, route, ssl) + self.bus = bus self._active_skill = None self._is_idle = False self.idle_ts = 0 @@ -568,7 +581,10 @@ def remote_url(self, val: str): self.config["remote-server"] = val def set_bus(self, bus=None): - self._bus = bus or get_mycroft_bus() + if not bus: + from ovos_bus_client.util import get_mycroft_bus + bus = get_mycroft_bus() + self._bus = bus self.setup_default_handlers() @property diff --git a/ovos_utils/intents/intent_service_interface.py b/ovos_utils/intents/intent_service_interface.py index 6fd52a2d..09b0cd67 100644 --- a/ovos_utils/intents/intent_service_interface.py +++ b/ovos_utils/intents/intent_service_interface.py @@ -5,23 +5,14 @@ import ovos_utils.messagebus from ovos_utils.log import LOG, log_deprecation +from ovos_utils.file_utils import to_alnum # backwards compat import + log_deprecation("ovos_utils.intents moved to ovos_workshop.intents", "0.1.0") + try: from ovos_workshop.intents import * except: - def to_alnum(skill_id: str) -> str: - """ - Convert a skill id to only alphanumeric characters - Non-alphanumeric characters are converted to "_" - - Args: - skill_id (str): identifier to be converted - Returns: - (str) String of letters - """ - return ''.join(c if c.isalnum() else '_' for c in str(skill_id)) - def munge_regex(regex: str, skill_id: str) -> str: """ diff --git a/ovos_utils/messagebus.py b/ovos_utils/messagebus.py index 5251ef93..924ab3c4 100644 --- a/ovos_utils/messagebus.py +++ b/ovos_utils/messagebus.py @@ -1,358 +1,39 @@ import json import time -from copy import deepcopy -from threading import Event - -# from ovos_utils.configuration import read_mycroft_config, get_default_lang -from pyee import BaseEventEmitter from ovos_utils import create_loop -from ovos_utils.json_helper import merge_dict +from ovos_utils.fakebus import dig_for_message, FakeMessage, Message, FakeBus from ovos_utils.log import LOG, log_deprecation, deprecated +from ovos_utils.events import EventContainer as _EC log_deprecation("decode_binary_message, send_binary_file_message, send_binary_data_message, \ send_message, wait_for_reply, listen_once_for_message, get_message_lang, get_websocket, get_mycroft_bus, \ listen_for_message have moved to ovos_bus_client.util", "0.1.0") +log_deprecation("dig_for_message, FakeMessage, FakeBus moved to ovos_utils.fakebus", "0.1.0") -def dig_for_message(): - try: - from ovos_bus_client.message import dig_for_message as _dig - return _dig() - except ImportError: - pass - return None - - -class FakeBus: - def __init__(self, *args, **kwargs): - self.started_running = False - self.session_id = "default" - self.ee = kwargs.get("emitter") or BaseEventEmitter() - self.ee.on("error", self.on_error) - self.on_open() - try: - self.session_id = kwargs["session"].session_id - except: - pass # don't care - - self.on("ovos.session.update_default", - self.on_default_session_update) - - def on(self, msg_type, handler): - self.ee.on(msg_type, handler) - - def once(self, msg_type, handler): - self.ee.once(msg_type, handler) - - def emit(self, message): - if "session" not in message.context: - try: # replicate side effects - from ovos_bus_client.session import Session, SessionManager - sess = SessionManager.sessions.get(self.session_id) or \ - Session(self.session_id) - message.context["session"] = sess.serialize() - except ImportError: # don't care - message.context["session"] = {"session_id": self.session_id} - self.ee.emit("message", message.serialize()) - self.ee.emit(message.msg_type, message) - self.on_message(message.serialize()) - - def on_message(self, *args): - """ - Handle an incoming websocket message - @param args: - message (str): serialized Message - """ - if len(args) == 1: - message = args[0] - else: - message = args[1] - parsed_message = Message.deserialize(message) - try: # replicate side effects - from ovos_bus_client.session import Session, SessionManager - sess = Session.from_message(parsed_message) - if sess.session_id != "default": - # 'default' can only be updated by core - SessionManager.update(sess) - except ImportError: - pass # don't care - - def on_default_session_update(self, message): - try: # replicate side effects - from ovos_bus_client.session import Session, SessionManager - new_session = message.data["session_data"] - sess = Session.deserialize(new_session) - SessionManager.update(sess, make_default=True) - LOG.debug("synced default_session") - except ImportError: - pass # don't care - - def wait_for_message(self, message_type, timeout=3.0): - """Wait for a message of a specific type. - - Arguments: - message_type (str): the message type of the expected message - timeout: seconds to wait before timeout, defaults to 3 - - Returns: - The received message or None if the response timed out - """ - received_event = Event() - received_event.clear() - - msg = None - - def rcv(m): - nonlocal msg - msg = m - received_event.set() - - self.ee.once(message_type, rcv) - received_event.wait(timeout) - return msg - - def wait_for_response(self, message, reply_type=None, timeout=3.0): - """Send a message and wait for a response. - - Arguments: - message (Message): message to send - reply_type (str): the message type of the expected reply. - Defaults to ".response". - timeout: seconds to wait before timeout, defaults to 3 - - Returns: - The received message or None if the response timed out - """ - reply_type = reply_type or message.msg_type + ".response" - received_event = Event() - received_event.clear() - - msg = None - - def rcv(m): - nonlocal msg - msg = m - received_event.set() - - self.ee.once(reply_type, rcv) - self.emit(message) - received_event.wait(timeout) - return msg - - def remove(self, msg_type, handler): - try: - self.ee.remove_listener(msg_type, handler) - except: - pass - - def remove_all_listeners(self, event_name): - self.ee.remove_all_listeners(event_name) - - def create_client(self): - return self - - def on_error(self, error): - LOG.error(error) - - def on_open(self): - pass - - def on_close(self): - pass - - def run_forever(self): - self.started_running = True - - def run_in_thread(self): - self.run_forever() - - def close(self): - self.on_close() - - -class _MutableMessage(type): - """ To override isinstance checks we need to use a metaclass """ - - def __instancecheck__(self, instance): - try: - from ovos_bus_client.message import Message as _MycroftMessage - if isinstance(instance, _MycroftMessage): - return True - except ImportError: - pass - try: - from mycroft_bus_client.message import Message as _MycroftMessage - if isinstance(instance, _MycroftMessage): - return True - except ImportError: - pass - return super().__instancecheck__(instance) - - -# fake Message object to allow usage without ovos-bus-client installed -class FakeMessage(metaclass=_MutableMessage): - """ fake Message object to allow usage with FakeBus without ovos-bus-client installed""" - - def __new__(cls, *args, **kwargs): - try: # most common case - from ovos_bus_client import Message as _M - return _M(*args, **kwargs) - except ImportError: - pass - try: # some old install that upgraded during migration period - from mycroft_bus_client import Message as _M - return _M(*args, **kwargs) - except ImportError: # FakeMessage - return super().__new__(cls) - - def __init__(self, msg_type, data=None, context=None): - """Used to construct a message object - - Message objects will be used to send information back and forth - between processes of mycroft service, voice, skill and cli - """ - self.msg_type = msg_type - self.data = data or {} - self.context = context or {} - - def __eq__(self, other): - try: - return other.msg_type == self.msg_type and \ - other.data == self.data and \ - other.context == self.context - except: - return False +class EventContainer(_EC): + def __init__(self, bus=None): + log_deprecation("Import from `ovos_utils.events`", "0.1.0") + _EC.__init__(self, bus=bus) - def serialize(self): - """This returns a string of the message info. - This makes it easy to send over a websocket. This uses - json dumps to generate the string with type, data and context +def create_wrapper(*args, **kwargs): + log_deprecation("Import from `ovos_utils.events`", "0.1.0") + from ovos_utils.events import create_wrapper + return create_wrapper(*args, **kwargs) - Returns: - str: a json string representation of the message. - """ - return json.dumps({'type': self.msg_type, - 'data': self.data, - 'context': self.context}) - @staticmethod - def deserialize(value): - """This takes a string and constructs a message object. +def get_handler_name(*args, **kwargs): + log_deprecation("Import from `ovos_utils.events`", "0.1.0") + from ovos_utils.events import get_handler_name + return get_handler_name(*args, **kwargs) - This makes it easy to take strings from the websocket and create - a message object. This uses json loads to get the info and generate - the message object. - Args: - value(str): This is the json string received from the websocket - - Returns: - FakeMessage: message object constructed from the json string passed - int the function. - value(str): This is the string received from the websocket - """ - obj = json.loads(value) - return FakeMessage(obj.get('type') or '', - obj.get('data') or {}, - obj.get('context') or {}) - - def forward(self, msg_type, data=None): - """ Keep context and forward message - - This will take the same parameters as a message object but use - the current message object as a reference. It will copy the context - from the existing message object. - - Args: - msg_type (str): type of message - data (dict): data for message - - Returns: - FakeMessage: Message object to be used on the reply to the message - """ - data = data or {} - return FakeMessage(msg_type, data, context=self.context) - - def reply(self, msg_type, data=None, context=None): - """Construct a reply message for a given message - - This will take the same parameters as a message object but use - the current message object as a reference. It will copy the context - from the existing message object and add any context passed in to - the function. Check for a destination passed in to the function from - the data object and add that to the context as a destination. If the - context has a source then that will be swapped with the destination - in the context. The new message will then have data passed in plus the - new context generated. - - Args: - msg_type (str): type of message - data (dict): data for message - context: intended context for new message - - Returns: - FakeMessage: Message object to be used on the reply to the message - """ - data = deepcopy(data) or {} - context = context or {} - - new_context = deepcopy(self.context) - for key in context: - new_context[key] = context[key] - if 'destination' in data: - new_context['destination'] = data['destination'] - if 'source' in new_context and 'destination' in new_context: - s = new_context['destination'] - new_context['destination'] = new_context['source'] - new_context['source'] = s - return FakeMessage(msg_type, data, context=new_context) - - def response(self, data=None, context=None): - """Construct a response message for the message - - Constructs a reply with the data and appends the expected - ".response" to the message - - Args: - data (dict): message data - context (dict): message context - Returns - (Message) message with the type modified to match default response - """ - return self.reply(self.msg_type + '.response', data, context) - - def publish(self, msg_type, data, context=None): - """ - Copy the original context and add passed in context. Delete - any target in the new context. Return a new message object with - passed in data and new context. Type remains unchanged. - - Args: - msg_type (str): type of message - data (dict): date to send with message - context: context added to existing context - - Returns: - FakeMessage: Message object to publish - """ - context = context or {} - new_context = self.context.copy() - for key in context: - new_context[key] = context[key] - - if 'target' in new_context: - del new_context['target'] - - return FakeMessage(msg_type, data, context=new_context) - - -class Message(FakeMessage): - def __int__(self, *args, **kwargs): - log_deprecation(f"Import from ovos_bus_client.message directly", - "0.1.0") - FakeMessage.__init__(self, *args, **kwargs) +def merge_dict(*args, **kwargs): + log_deprecation("Import from `ovos_utils.json_helper`", "0.1.0") + from ovos_utils.json_helper import merge_dict + return merge_dict(*args, **kwargs) try: @@ -366,6 +47,7 @@ def __int__(self, *args, **kwargs): "route": "/core", "ssl": False} + @deprecated("moved to ovos_bus_client.util", "0.1.0") def get_message_lang(message=None): """Get the language from the message or the default language. @@ -451,11 +133,11 @@ def _handler(message): @deprecated("moved to ovos_bus_client.util", "0.1.0") - def wait_for_reply(message, reply_type=None, timeout=3.0, bus=None): + def wait_for_reply(message: Message, reply_type=None, timeout=3.0, bus=None): """Send a message and wait for a response. Args: - message (FakeMessage or str or dict): message object or type to send + message (Message or str or dict): message object or type to send reply_type (str): the message type of the expected reply. Defaults to ".response". timeout: seconds to wait before timeout, defaults to 3 @@ -470,12 +152,12 @@ def wait_for_reply(message, reply_type=None, timeout=3.0, bus=None): except: pass if isinstance(message, str): - message = FakeMessage(message) + message = Message(message) elif isinstance(message, dict): - message = FakeMessage(message["type"], + message = Message(message["type"], message.get("data"), message.get("context")) - elif not isinstance(message, FakeMessage): + elif not isinstance(message, Message): raise ValueError response = bus.wait_for_response(message, reply_type, timeout) if auto_close: @@ -489,17 +171,17 @@ def send_message(message, data=None, context=None, bus=None): bus = bus or get_mycroft_bus() if isinstance(message, str): if isinstance(data, dict) or isinstance(context, dict): - message = FakeMessage(message, data, context) + message = Message(message, data, context) else: try: message = json.loads(message) except: - message = FakeMessage(message) + message = Message(message) if isinstance(message, dict): - message = FakeMessage(message["type"], + message = Message(message["type"], message.get("data"), message.get("context")) - if not isinstance(message, FakeMessage): + if not isinstance(message, Message): raise ValueError bus.emit(message) if auto_close: @@ -652,7 +334,7 @@ def set_data_gatherer(self, callback, default_data=None, daemonic=False, interva else: response_type = self.trigger_message + ".reply" - response = FakeMessage(response_type, default_data) + response = Message(response_type, default_data) self.service = BusService(response, bus=self.bus) self.callback = callback self.bus.on(self.trigger_message, self._respond) @@ -700,7 +382,7 @@ class BusQuery: def __init__(self, message, bus=None): self.bus = bus or get_mycroft_bus() self._waiting = False - self.response = FakeMessage(None, None, None) + self.response = Message(None, None, None) self.query = message self.valid_response_types = [] @@ -724,7 +406,7 @@ def _wait_response(self, timeout): self._waiting = False def send(self, response_type=None, timeout=10): - self.response = FakeMessage(None, None, None) + self.response = Message(None, None, None) if response_type is None: response_type = self.query.type + ".reply" self.add_response_type(response_type) diff --git a/ovos_utils/skills/__init__.py b/ovos_utils/skills/__init__.py index 27d559b2..e861e313 100644 --- a/ovos_utils/skills/__init__.py +++ b/ovos_utils/skills/__init__.py @@ -1,6 +1,4 @@ from ovos_config.config import read_mycroft_config, update_mycroft_config -from ovos_utils.messagebus import wait_for_reply -from ovos_utils.skills.locations import get_default_skills_directory, get_installed_skill_ids from ovos_utils.log import LOG, deprecated @@ -30,6 +28,10 @@ def check_class(cls): def skills_loaded(bus=None): + try: + from ovos_bus_client.util import wait_for_reply + except: + from ovos_utils.messagebus import wait_for_reply reply = wait_for_reply('mycroft.skills.all_loaded', 'mycroft.skills.all_loaded.response', bus=bus) @@ -38,35 +40,47 @@ def skills_loaded(bus=None): return False +@deprecated("This reference is deprecated, use " + "`ovos_workshop.permissions.blacklist_skill", "0.1.0") def blacklist_skill(skill, config=None): config = config or read_mycroft_config() - skills_config = config.get("skills", {}) - blacklisted_skills = skills_config.get("blacklisted_skills", []) - if skill not in blacklisted_skills: - blacklisted_skills.append(skill) - conf = { - "skills": { - "blacklisted_skills": blacklisted_skills + try: + from ovos_workshop.permissions import blacklist_skill as _wl + return _wl(skill, config) + except ImportError: + skills_config = config.get("skills", {}) + blacklisted_skills = skills_config.get("blacklisted_skills", []) + if skill not in blacklisted_skills: + blacklisted_skills.append(skill) + conf = { + "skills": { + "blacklisted_skills": blacklisted_skills + } } - } - update_mycroft_config(conf) - return True + update_mycroft_config(conf) + return True return False +@deprecated("This reference is deprecated, use " + "`ovos_workshop.permissions.whitelist_skill", "0.1.0") def whitelist_skill(skill, config=None): config = config or read_mycroft_config() - skills_config = config.get("skills", {}) - blacklisted_skills = skills_config.get("blacklisted_skills", []) - if skill in blacklisted_skills: - blacklisted_skills.pop(skill) - conf = { - "skills": { - "blacklisted_skills": blacklisted_skills + try: + from ovos_workshop.permissions import whitelist_skill as _wl + return _wl(skill, config) + except ImportError: + skills_config = config.get("skills", {}) + blacklisted_skills = skills_config.get("blacklisted_skills", []) + if skill in blacklisted_skills: + blacklisted_skills.pop(skill) + conf = { + "skills": { + "blacklisted_skills": blacklisted_skills + } } - } - update_mycroft_config(conf) - return True + update_mycroft_config(conf) + return True return False @@ -89,12 +103,14 @@ def make_priority_skill(skill, config=None): @deprecated("This reference is deprecated, use " - "`ovos_utils.skills.locations.get_default_skill_dir", "0.1.0") + "`ovos_plugin_manager.skills.get_default_skill_dir", "0.1.0") def get_skills_folder(config=None): + from ovos_utils.skills.locations import get_default_skills_directory return get_default_skills_directory(config) @deprecated("This reference is deprecated, use " - "`ovos_utils.skills.locations.get_installed_skill_ids", "0.1.0") + "`ovos_plugin_manager.skills.get_installed_skill_ids", "0.1.0") def get_installed_skills(config=None): + from ovos_utils.skills.locations import get_installed_skill_ids return get_installed_skill_ids(config) diff --git a/ovos_utils/skills/api.py b/ovos_utils/skills/api.py index ee91f08c..15186a1d 100644 --- a/ovos_utils/skills/api.py +++ b/ovos_utils/skills/api.py @@ -6,7 +6,7 @@ from ovos_workshop.skills.api import SkillApi except: from typing import Dict, Optional - from ovos_utils.messagebus import Message + from ovos_utils.fakebus import Message class SkillApi: diff --git a/ovos_utils/skills/audioservice.py b/ovos_utils/skills/audioservice.py index d583c95a..90ea7ecf 100644 --- a/ovos_utils/skills/audioservice.py +++ b/ovos_utils/skills/audioservice.py @@ -22,7 +22,8 @@ from ovos_bus_client.apis.ocp import OCPInterface, ClassicAudioServiceInterface, ensure_uri except ImportError: from os.path import abspath - from ovos_utils.messagebus import get_mycroft_bus, dig_for_message, FakeMessage as Message + from ovos_utils.messagebus import get_mycroft_bus + from ovos_utils.fakebus import Message, dig_for_message def ensure_uri(s: str): """ diff --git a/ovos_utils/version.py b/ovos_utils/version.py index 85700f73..d726f33d 100644 --- a/ovos_utils/version.py +++ b/ovos_utils/version.py @@ -2,6 +2,6 @@ # START_VERSION_BLOCK VERSION_MAJOR = 0 VERSION_MINOR = 0 -VERSION_BUILD = 37 +VERSION_BUILD = "37.post1" VERSION_ALPHA = 0 # END_VERSION_BLOCK diff --git a/test/unittests/test_enclosure.py b/test/unittests/test_enclosure.py index 88991851..28963c3e 100644 --- a/test/unittests/test_enclosure.py +++ b/test/unittests/test_enclosure.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import patch -from ovos_utils.messagebus import FakeBus, Message +from ovos_utils.fakebus import FakeBus, Message class TestEnclosureAPI(unittest.TestCase): diff --git a/test/unittests/test_events.py b/test/unittests/test_events.py index 2c005c30..c8757695 100644 --- a/test/unittests/test_events.py +++ b/test/unittests/test_events.py @@ -7,7 +7,7 @@ from time import time from unittest.mock import Mock -from ovos_utils.messagebus import FakeBus, Message +from ovos_utils.fakebus import FakeBus, Message class TestEvents(unittest.TestCase): diff --git a/test/unittests/test_gui.py b/test/unittests/test_gui.py index a10df316..6cae1f74 100644 --- a/test/unittests/test_gui.py +++ b/test/unittests/test_gui.py @@ -93,8 +93,8 @@ def test_get_ui_directories(self): class TestGuiInterface(unittest.TestCase): - from ovos_utils.messagebus import FakeBus - from ovos_utils.gui import GUIInterface + from ovos_utils.fakebus import FakeBus + from ovos_bus_client.apis.gui import GUIInterface bus = FakeBus() config = {"extension": "test"} ui_base_dir = join(dirname(__file__), "test_ui") diff --git a/test/unittests/test_intents.py b/test/unittests/test_intents.py index 2a6580d5..a98667ed 100644 --- a/test/unittests/test_intents.py +++ b/test/unittests/test_intents.py @@ -3,7 +3,7 @@ from time import sleep from unittest.mock import Mock -from ovos_utils.messagebus import FakeBus +from ovos_utils.fakebus import FakeBus class TestIntent(unittest.TestCase): diff --git a/test/unittests/test_skills.py b/test/unittests/test_skills.py index dadc6257..2b698f45 100644 --- a/test/unittests/test_skills.py +++ b/test/unittests/test_skills.py @@ -3,7 +3,7 @@ from os.path import isdir, join, dirname, basename from unittest.mock import patch -from ovos_utils.messagebus import FakeBus, Message +from ovos_utils.fakebus import FakeBus, Message from ovos_utils.skills.locations import get_skill_directories from ovos_utils.skills.locations import get_default_skills_directory from ovos_utils.skills.locations import get_installed_skill_ids