From 1b0afe92dec918edacefe1951d03c46f5f44ebce Mon Sep 17 00:00:00 2001 From: Augusto Monteiro Date: Tue, 21 Feb 2017 02:43:50 -0300 Subject: [PATCH] #521 - Skill auto reload --- mycroft/skills/main.py | 102 +++++++++++++++++++++++++++++++---------- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/mycroft/skills/main.py b/mycroft/skills/main.py index 81f2e38e1c..50769a11de 100644 --- a/mycroft/skills/main.py +++ b/mycroft/skills/main.py @@ -17,13 +17,17 @@ import json - import sys +import time +from threading import Timer + +import os from os.path import expanduser, exists from mycroft.configuration import ConfigurationManager from mycroft.messagebus.client.ws import WebsocketClient -from mycroft.skills.core import load_skills, THIRD_PARTY_SKILLS_DIR +from mycroft.skills.core import load_skills, THIRD_PARTY_SKILLS_DIR, \ + load_skill, create_skill_descriptor, MainModule from mycroft.util.log import getLogger logger = getLogger("Skills") @@ -31,26 +35,9 @@ logger = getLogger("Skills") __author__ = 'seanfitz' ws = None -skills = [] - - -def load_skills_callback(): - global ws - global skills - skills += load_skills(ws) - config = ConfigurationManager.get().get("skills") - - try: - ini_third_party_skills_dir = expanduser(config.get("directory")) - except AttributeError as e: - logger.warning(e.message) - - for loc in THIRD_PARTY_SKILLS_DIR: - if exists(loc): - skills += load_skills(ws, loc) - - if ini_third_party_skills_dir and exists(ini_third_party_skills_dir): - skills += load_skills(ws, ini_third_party_skills_dir) +loaded_skills = {} +last_modified_skill = 0 +skills_directories = [] def connect(): @@ -58,6 +45,73 @@ def connect(): ws.run_forever() +def load_watch_skills(): + global ws, loaded_skills, last_modified_skill, skills_directories + + skills_directories = [os.path.dirname(os.path.abspath(__file__))] + skills_directories = skills_directories + THIRD_PARTY_SKILLS_DIR + + try: + config = ConfigurationManager.get().get("skills") + ini_third_party_skills_dir = expanduser(config.get("directory")) + if ini_third_party_skills_dir and exists(ini_third_party_skills_dir): + skills_directories.append(ini_third_party_skills_dir) + except AttributeError as e: + logger.warning(e.message) + + timer = Timer(0, load_skills) + timer.daemon = True + timer.start() + + # last_modified_skill = max(skills.values()) + + +def clear_skill_events(instance): + global ws + events = ws.emitter._events + instance_events = [] + for event in events: + e = ws.emitter._events[event] + if len(e) > 0 and e[0].func_closure and isinstance( + e[0].func_closure[1].cell_contents, instance.__class__): + instance_events.append(event) + + for event in instance_events: + del events[event] + + +def load_skills(): + global ws, loaded_skills, last_modified_skill, skills_directories + for dir in skills_directories: + if exists(dir): + list = filter(lambda x: os.path.isdir(os.path.join(dir, x)), + os.listdir(dir)) + for skill_folder in list: + if not loaded_skills.has_key(skill_folder): + loaded_skills[skill_folder] = {} + skill = loaded_skills[skill_folder] + skill["path"] = os.path.join(dir, skill_folder) + if not MainModule + ".py" in os.listdir(skill["path"]): + continue + skill["last_modified"] = max( + os.path.getmtime(root) for root, _, _ in + os.walk(skill["path"])) + if skill.get("instance") and skill.get("last_modified", + 0) <= last_modified_skill: + continue + elif skill.get("instance") and skill.get("last_modified", + 0) > last_modified_skill: + skill["instance"].shutdown() + clear_skill_events(skill["instance"]) + del skill["instance"] + skill["instance"] = load_skill( + create_skill_descriptor(skill["path"]), ws) + last_modified_skill = max( + map(lambda x: x.get("last_modified"), loaded_skills.values())) + time.sleep(2) + load_skills() + + def main(): global ws ws = WebsocketClient() @@ -76,7 +130,7 @@ def main(): logger.debug(message) ws.on('message', echo) - ws.once('open', load_skills_callback) + ws.once('open', load_watch_skills) ws.run_forever() @@ -84,7 +138,7 @@ if __name__ == "__main__": try: main() except KeyboardInterrupt: - for skill in skills: + for skill in loaded_skills: skill.shutdown() finally: sys.exit()