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

refactor/move_from_utils #196

Merged
merged 1 commit into from
Dec 28, 2023
Merged
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
154 changes: 146 additions & 8 deletions ovos_plugin_manager/skills.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,159 @@
from os import makedirs, listdir
from os.path import join, isdir, dirname, expanduser, isfile

import importlib.util
from ovos_config.config import read_mycroft_config
from ovos_config.locations import get_xdg_data_save_path, get_xdg_data_dirs
from typing import List, Optional

from ovos_plugin_manager.utils import PluginTypes
from ovos_utils.log import LOG
from ovos_plugin_manager.utils import find_plugins
from ovos_utils.log import LOG, log_deprecation


def get_installed_skill_ids(conf: Optional[dict] = None) -> List[str]:
"""
Gets a list of `skill_id`s for all installed skills
Args:
conf: Configuration, else loads from ovos_config.config.Configuration
Returns:
list of `skill_id` strings for all installed skills
"""
_, skill_ids = get_plugin_skills()
for d in get_skill_directories(conf):
for skill_dir in listdir(d):
if isdir(join(d, skill_dir)) and isfile(join(d, skill_dir,
"__init__.py")):
if skill_dir in skill_ids:
LOG.info(f"{skill_dir} installed as plugin and local dir")
continue
skill_ids.append(skill_dir)
return skill_ids


def get_skill_directories(conf: Optional[dict] = None) -> List[str]:
""" returns list of skill directories ordered by expected loading order
This corresponds to:
- XDG_DATA_DIRS
- default directory (see get_default_skills_directory method for details)
- user defined extra directories
Each directory contains individual skill folders to be loaded
If a skill exists in more than one directory (same folder name) previous instances will be ignored
ie. directories at the end of the list have priority over earlier directories
NOTE: empty folders are interpreted as disabled skills
new directories can be defined in mycroft.conf by specifying a full path
each extra directory is expected to contain individual skill folders to be loaded
the xdg folder name can also be changed, it defaults to "skills"
eg. ~/.local/share/mycroft/FOLDER_NAME
{
"skills": {
"directory": "skills",
"extra_directories": ["path/to/extra/dir/to/scan/for/skills"]
}
}
Args:
conf: Configuration, else loads from ovos_config.config.Configuration
Returns:
list of fully-qualified directories containing non-plugin skills
"""
# the contents of each skills directory must be individual skill folders
# we are still dependent on the mycroft-core structure of skill_id/__init__.py

conf = conf or read_mycroft_config()
folder = conf["skills"].get("directory") or "skills"
# load all valid XDG paths
# NOTE: skills are actually code, but treated as user data!
# they should be considered applets rather than full applications
skill_locations = list(reversed(
[join(p, folder) for p in get_xdg_data_dirs() if
isdir(join(p, folder))]
))

# load the default skills folder
# only meaningful if xdg support is disabled
default = get_default_skills_directory(conf)
if default not in skill_locations:
skill_locations.append(default)

# load additional explicitly configured directories
conf = conf.get("skills") or {}
# extra_directories is a list of directories containing skill subdirectories
# NOT a list of individual skill folders
# preserve order while removing any duplicate entries
extra_dirs = (expanduser(d) for d in conf.get("extra_directories") or [])
for d in extra_dirs:
if isdir(d) and d not in skill_locations:
skill_locations.append(d)
return skill_locations

def find_plugins(*args, **kwargs):
# TODO: Deprecate in 0.1.0
LOG.warning("This reference is deprecated. "
"Import from ovos_plugin_manager.utils directly")
from ovos_plugin_manager.utils import find_plugins
return find_plugins(*args, **kwargs)

def get_default_skills_directory(conf: Optional[dict] = None) -> str:
""" return default directory to scan for skills
This is only meaningful if xdg is disabled in ovos.conf
If xdg is enabled then data_dir is always XDG_DATA_DIR
If xdg is disabled then data_dir by default corresponds to /opt/mycroft
users can define the data directory in mycroft.conf
the skills folder name (relative to data_dir) can also be defined there
NOTE: folder name also impacts all XDG skill directories!
{
"data_dir": "/opt/mycroft",
"skills": {
"directory": "skills"
}
}
Args:
conf: Configuration, else loads from ovos_config.config.Configuration
Returns:
Absolute path to default skills directory
"""
conf = conf or read_mycroft_config()
path_override = conf["skills"].get("directory_override")
folder = conf["skills"].get("directory") or "skills"

# if .conf wants to use a specific path, use it!
if path_override:
log_deprecation("'directory_override' is deprecated!"
"add the new path to 'extra_directories' instead",
"0.1.0")
skills_folder = expanduser(path_override)
elif conf["skills"].get("extra_directories") and \
len(conf["skills"].get("extra_directories")) > 0:
skills_folder = expanduser(conf["skills"]["extra_directories"][0])
else:
skills_folder = join(get_xdg_data_save_path(), folder)
# create folder if needed
try:
makedirs(skills_folder, exist_ok=True)
except PermissionError: # old style /opt/mycroft/skills not available
skills_folder = join(get_xdg_data_save_path(), folder)
makedirs(skills_folder, exist_ok=True)

return skills_folder


def get_plugin_skills() -> (list, list):
"""
Get the package directories for any pip installed skill plugins
Returns:
lists of skill directories and plugin skill IDs
"""

skill_dirs = list()
plugins = find_skill_plugins()
skill_ids = list(plugins.keys())
for skill_class in plugins.values():
skill_dir = dirname(importlib.util.find_spec(
skill_class.__module__).origin)
skill_dirs.append(skill_dir)
LOG.info(f"Located plugin skills: {skill_ids}")
return skill_dirs, skill_ids


def find_skill_plugins() -> dict:
"""
Find all installed plugins
@return: dict plugin names to entrypoints
"""
from ovos_plugin_manager.utils import find_plugins
return find_plugins(PluginTypes.SKILL)


Expand Down
Loading