openflexure-microscope-server/openflexure_microscope/plugins/loader.py
2018-12-11 17:21:18 +00:00

181 lines
5.7 KiB
Python

import importlib
import os
import warnings
import logging
from openflexure_microscope.config import USER_CONFIG_DIR
MAIN_MODULE = '__init__'
HERE = os.path.abspath(os.path.dirname(__file__))
DEFAULT_PLUGIN_PATH = os.path.join(HERE, 'default')
USER_PLUGIN_DIR = os.path.join(USER_CONFIG_DIR, "plugins")
def search_plugin_dirs(plugin_paths, include_default=True):
"""
Search through, and load from, a list of plugin directories.
Args:
plugin_paths (list): List of strings of plugin directories.
include_default (bool): Also load plugins from the module default directory (DEFAULT_PLUGIN_PATH, USER_PLUGIN_DIR)
"""
global DEFAULT_PLUGIN_PATH, USER_PLUGIN_DIR
if not os.path.exists(USER_PLUGIN_DIR): # If user config file already exists
os.makedirs(USER_PLUGIN_DIR)
if include_default: # If including default plugins
plugin_paths.append(DEFAULT_PLUGIN_PATH) # Add default directory to the search paths
plugin_paths.append(USER_PLUGIN_DIR) # Add user directory to the search paths
logging.debug(plugin_paths)
plugins = [] # List of loaded plugins
for plugin_dir in plugin_paths: # For each plugin directory
logging.debug("Searching {}".format(plugin_dir))
plugins.extend(find_plugins(plugin_dir)) # Find plugin folders, and load into list
return plugins
def find_plugins_legacy(plugin_dir):
"""
Find all plugins residing within a directory
Args:
plugin_dir (str): String of directory to be searched
"""
plugins = []
plugins_folders = os.listdir(plugin_dir)
loader_details = (
importlib.machinery.SourceFileLoader,
importlib.machinery.SOURCE_SUFFIXES
)
for i in plugins_folders:
plugin_folder = os.path.join(plugin_dir, i)
logging.info(plugin_folder)
if not os.path.isdir(plugin_folder): # If plugin folder doesn't exist
continue # Skip this iteration
if not MAIN_MODULE + '.py' in os.listdir(plugin_folder): # If no __init__ file in plugin folder
continue # Skip this iteration
module_spec = importlib.machinery.FileFinder(plugin_folder, loader_details).find_spec(MAIN_MODULE)
plugins.append(module_spec)
return plugins
def find_plugins(plugin_dir):
"""
Find all plugins residing within a directory
Args:
plugin_dir (str): String of directory to be searched
"""
plugins = []
plugins_folders = os.listdir(plugin_dir)
loader_details = (
importlib.machinery.SourceFileLoader,
importlib.machinery.SOURCE_SUFFIXES
)
for i in plugins_folders:
plugin_folder = os.path.join(plugin_dir, i)
logging.info(plugin_folder)
if not os.path.isdir(plugin_folder): # If plugin folder doesn't exist
continue # Skip this iteration
if not MAIN_MODULE + '.py' in os.listdir(plugin_folder): # If no __init__ file in plugin folder
continue # Skip this iteration
module_spec = importlib.machinery.FileFinder(plugin_folder, loader_details).find_spec(MAIN_MODULE)
plugins.append(module_spec)
return plugins
def load_plugin(module_spec):
"""
Load a source file from a given spec.
Args:
module_spec: Module spec of module to be returned
"""
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
def load_plugin_legacy(module_spec):
"""
Load a source file from a given spec.
Args:
module_spec: Module spec of module to be returned
"""
module = importlib.util.module_from_spec(module_spec)
module_spec.loader.exec_module(module)
return module
class PluginMount(object):
"""
A mount-point for all loaded plugins. Attaches to a Microscope object.
Args:
parent (:py:class:`openflexure_microscope.microscope.Microscope`): The parent Microscope object to attach to.
"""
def __init__(self, parent):
self.parent = parent
print("Creating plugin mount")
def attach(self, plugin_module):
"""
Attach a MicroscopePlugin instance to the plugin mount.
Args:
plugin_module: A loaded module to be attached. Module can be loaded using :py:meth:`openflexure_microscope.plugins.load_plugin`
"""
print("LOADING MODULE {}".format(plugin_module.__name__))
if hasattr(plugin_module, 'PLUGINS') and isinstance(plugin_module.PLUGINS, dict):
for plugin_name, plugin_class in plugin_module.PLUGINS.items():
plugin_object = plugin_class()
if hasattr(self, plugin_name):
warnings.warn("A plugin named {} has already been loaded. Skipping {}.".format(plugin_name, plugin_class))
else:
setattr(self, plugin_name, plugin_object)
# Grant plugin access to the hardware
assert(isinstance(plugin_object, MicroscopePlugin))
plugin_object.microscope = self.parent
print("Adding plugin: {}".format(plugin_name))
else:
warnings.warn("No valid PLUGINS dictionary found in {}".format(plugin_module))
class PluginGroup():
"""
A class used to group plugin methods within a PluginMount.
Currently useless aside from creating a namespace in which plugin methods will reside.
"""
def __init__(self):
pass
class MicroscopePlugin():
"""
Parent class for all microscope plugins.
Initially only defines an empty object for microscope. All plugins
must be an instance of this class to successfully attach to PluginMount.
"""
def __init__(self):
self.microscope = None #: :py:class:`openflexure_microscope.microscope.Microscope`: Microscope object