-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix/do not require mycroft * fix/decouple_decorators_from_skills being in the skills module was causing cyclic imports due to helper skill classes importing mycroft * build tests Co-authored-by: jarbasai <[email protected]>
- Loading branch information
1 parent
b6fbe4f
commit 9ca2018
Showing
18 changed files
with
378 additions
and
366 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
from ovos_workshop.app import OVOSAbstractApplication | ||
from ovos_workshop.skills.decorators import * | ||
from ovos_workshop.skills.decorators.killable import killable_event, \ | ||
from ovos_workshop.decorators import * | ||
from ovos_workshop.decorators.killable import killable_event, \ | ||
AbortEvent, AbortQuestion | ||
from ovos_workshop.skills.layers import IntentLayers |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from ovos_workshop.decorators.killable import \ | ||
killable_intent, killable_event | ||
from ovos_workshop.decorators.layers import enables_layer, \ | ||
disables_layer, layer_intent, removes_layer, resets_layers, replaces_layer | ||
from ovos_workshop.decorators.converse import converse_handler | ||
from ovos_workshop.decorators.fallback_handler import fallback_handler | ||
|
||
|
||
def resting_screen_handler(name): | ||
"""Decorator for adding a method as an resting screen handler. | ||
If selected will be shown on screen when device enters idle mode. | ||
""" | ||
|
||
def real_decorator(func): | ||
# Store the resting information inside the function | ||
# This will be used later in register_resting_screen | ||
if not hasattr(func, 'resting_handler'): | ||
func.resting_handler = name | ||
return func | ||
|
||
return real_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
|
||
|
||
def converse_handler(): | ||
"""Decorator for aliasing a method as the converse method""" | ||
|
||
def real_decorator(func): | ||
if not hasattr(func, 'converse'): | ||
func.converse = True | ||
return func | ||
|
||
return real_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
|
||
def fallback_handler(priority=50): | ||
def real_decorator(func): | ||
if not hasattr(func, 'fallback_priority'): | ||
func.fallback_priority = priority | ||
return func | ||
|
||
return real_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import time | ||
from ovos_utils import create_killable_daemon | ||
from ovos_utils.messagebus import Message | ||
import threading | ||
from inspect import signature | ||
from functools import wraps | ||
|
||
|
||
class AbortEvent(StopIteration): | ||
""" abort bus event handler """ | ||
|
||
|
||
class AbortIntent(AbortEvent): | ||
""" abort intent parsing """ | ||
|
||
|
||
class AbortQuestion(AbortEvent): | ||
""" gracefully abort get_response queries """ | ||
|
||
|
||
def killable_intent(msg="mycroft.skills.abort_execution", | ||
callback=None, react_to_stop=True, call_stop=True, | ||
stop_tts=True): | ||
return killable_event(msg, AbortIntent, callback, react_to_stop, | ||
call_stop, stop_tts) | ||
|
||
|
||
def killable_event(msg="mycroft.skills.abort_execution", exc=AbortEvent, | ||
callback=None, react_to_stop=False, call_stop=False, | ||
stop_tts=False): | ||
# Begin wrapper | ||
def create_killable(func): | ||
|
||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = args[0] | ||
t = create_killable_daemon(func, args, kwargs, autostart=False) | ||
|
||
def abort(_): | ||
if not t.is_alive(): | ||
return | ||
if stop_tts: | ||
skill.bus.emit(Message("mycroft.audio.speech.stop")) | ||
if call_stop: | ||
# call stop on parent skill | ||
skill.stop() | ||
|
||
# ensure no orphan get_response daemons | ||
# this is the only killable daemon that core itself will | ||
# create, users should also account for this condition with | ||
# callbacks if using the decorator for other purposes | ||
skill._handle_killed_wait_response() | ||
|
||
try: | ||
while t.is_alive(): | ||
t.raise_exc(exc) | ||
time.sleep(0.1) | ||
except threading.ThreadError: | ||
pass # already killed | ||
except AssertionError: | ||
pass # could not determine thread id ? | ||
if callback is not None: | ||
if len(signature(callback).parameters) == 1: | ||
# class method, needs self | ||
callback(args[0]) | ||
else: | ||
callback() | ||
|
||
# save reference to threads so they can be killed later | ||
skill._threads.append(t) | ||
skill.bus.once(msg, abort) | ||
if react_to_stop: | ||
skill.bus.once(args[0].skill_id + ".stop", abort) | ||
t.start() | ||
return t | ||
|
||
return call_function | ||
|
||
return create_killable | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
import inspect | ||
from functools import wraps | ||
|
||
from ovos_workshop.skills.layers import IntentLayers | ||
|
||
|
||
def dig_for_skill(max_records: int = 10): | ||
from ovos_workshop.app import OVOSAbstractApplication | ||
from ovos_workshop.skills import MycroftSkill | ||
stack = inspect.stack()[1:] # First frame will be this function call | ||
stack = stack if len(stack) <= max_records else stack[:max_records] | ||
for record in stack: | ||
args = inspect.getargvalues(record.frame) | ||
if args.locals.get("self"): | ||
obj = args.locals["self"] | ||
if isinstance(obj, MycroftSkill) or \ | ||
isinstance(obj, OVOSAbstractApplication): | ||
return obj | ||
elif args.locals.get("args"): | ||
for obj in args.locals["args"]: | ||
if isinstance(obj, MycroftSkill) or \ | ||
isinstance(obj, OVOSAbstractApplication): | ||
return obj | ||
return None | ||
|
||
|
||
def enables_layer(layer_name): | ||
def layer_handler(func): | ||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = dig_for_skill() | ||
skill.intent_layers = skill.intent_layers or \ | ||
IntentLayers().bind(skill) | ||
func(*args, **kwargs) | ||
skill.intent_layers.activate_layer(layer_name) | ||
|
||
return call_function | ||
|
||
return layer_handler | ||
|
||
|
||
def disables_layer(layer_name): | ||
def layer_handler(func): | ||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = dig_for_skill() | ||
skill.intent_layers = skill.intent_layers or \ | ||
IntentLayers().bind(skill) | ||
func(*args, **kwargs) | ||
skill.intent_layers.deactivate_layer(layer_name) | ||
|
||
return call_function | ||
|
||
return layer_handler | ||
|
||
|
||
def replaces_layer(layer_name, intent_list): | ||
def layer_handler(func): | ||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = dig_for_skill() | ||
skill.intent_layers = skill.intent_layers or \ | ||
IntentLayers().bind(skill) | ||
func(*args, **kwargs) | ||
skill.intent_layers.replace_layer(layer_name, intent_list) | ||
|
||
return call_function | ||
|
||
return layer_handler | ||
|
||
|
||
def removes_layer(layer_name, intent_list): | ||
def layer_handler(func): | ||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = dig_for_skill() | ||
skill.intent_layers = skill.intent_layers or \ | ||
IntentLayers().bind(skill) | ||
func(*args, **kwargs) | ||
skill.intent_layers.replace_layer(layer_name, intent_list) | ||
|
||
return call_function | ||
|
||
return layer_handler | ||
|
||
|
||
def resets_layers(): | ||
def layer_handler(func): | ||
@wraps(func) | ||
def call_function(*args, **kwargs): | ||
skill = dig_for_skill() | ||
skill.intent_layers = skill.intent_layers or \ | ||
IntentLayers().bind(skill) | ||
func(*args, **kwargs) | ||
skill.intent_layers.disable() | ||
|
||
return call_function | ||
|
||
return layer_handler | ||
|
||
|
||
def layer_intent(intent_parser, layer_name): | ||
"""Decorator for adding a method as an intent handler belonging to an | ||
intent layer.""" | ||
|
||
def real_decorator(func): | ||
# Store the intent_parser inside the function | ||
# This will be used later to call register_intent | ||
if not hasattr(func, 'intents'): | ||
func.intents = [] | ||
if not hasattr(func, 'intent_layers'): | ||
func.intent_layers = {} | ||
|
||
func.intents.append(intent_parser) | ||
if layer_name not in func.intent_layers: | ||
func.intent_layers[layer_name] = [] | ||
|
||
# get intent_name | ||
if hasattr(intent_parser, "build"): | ||
intent = intent_parser.build() | ||
intent_name = intent.name or func.__name__ | ||
elif hasattr(intent_parser, "name"): | ||
intent_name = intent_parser.name | ||
else: | ||
intent_name = intent_parser | ||
|
||
func.intent_layers[layer_name].append(intent_name) | ||
return func | ||
|
||
return real_decorator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
from functools import wraps | ||
from ovos_workshop.skills.layers import IntentLayers | ||
from ovos_plugin_common_play.ocp import * | ||
from ovos_plugin_common_play.ocp.status import * | ||
|
||
|
||
def ocp_search(): | ||
"""Decorator for adding a method as an common play search handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_search_handler'): | ||
func.is_ocp_search_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_play(): | ||
"""Decorator for adding a method as an common play search handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_playback_handler'): | ||
func.is_ocp_playback_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_previous(): | ||
"""Decorator for adding a method as an common play prev handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_prev_handler'): | ||
func.is_ocp_prev_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_next(): | ||
"""Decorator for adding a method as an common play next handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_next_handler'): | ||
func.is_ocp_next_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_pause(): | ||
"""Decorator for adding a method as an common play pause handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_pause_handler'): | ||
func.is_ocp_pause_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_resume(): | ||
"""Decorator for adding a method as an common play resume handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_resume_handler'): | ||
func.is_ocp_resume_handler = True | ||
|
||
return func | ||
|
||
return real_decorator | ||
|
||
|
||
def ocp_featured_media(): | ||
"""Decorator for adding a method as an common play search handler.""" | ||
|
||
def real_decorator(func): | ||
# Store the flag inside the function | ||
# This will be used later to identify the method | ||
if not hasattr(func, 'is_ocp_featured_handler'): | ||
func.is_ocp_featured_handler = True | ||
|
||
return func | ||
|
||
return real_decorator |
Oops, something went wrong.