openflexure-microscope-server/openflexure_microscope/plugins/loader.py
2018-11-23 17:13:38 +00:00

116 lines
3.7 KiB
Python

import importlib
import os
import warnings
import logging
MAIN_MODULE = '__init__'
HERE = os.path.abspath(os.path.dirname(__file__))
DEFAULT_PLUGIN_PATH = os.path.join(HERE, 'default')
def search_plugin_dirs(plugin_dirs, include_default=True):
"""
Search through, and load from, a list of plugin directories.
Args:
plugin_dirs (list): List of strings of plugin directories.
include_default (bool): Also load plugins from the module default directory (DEFAULT_PLUGIN_PATH)
"""
global DEFAULT_PLUGIN_PATH
if include_default: # If including default plugins
plugin_dirs.append(DEFAULT_PLUGIN_PATH) # Add default directory to the search paths
logging.debug(plugin_dirs)
plugins = [] # List of loaded plugins
for plugin_dir in plugin_dirs: # 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(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):
continue
if not MAIN_MODULE + '.py' in os.listdir(plugin_folder):
continue
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
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):
if not hasattr(plugin_module, 'PLUGINS') or not isinstance(plugin_module.PLUGINS, dict):
raise Exception("No falid PLUGINS dictionary found in {}".format(plugin_module))
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.camera = self.parent.camera
plugin_object.stage = self.parent.stage
print("Adding plugin: {}".format(plugin_name))
class MicroscopePlugin():
"""
Parent class for all microscope plugins.
Initially only defines an empty object for camera and stage. All plugins
must be an instance of this class to successfully attach to PluginMount.
"""
def __init__(self):
self.camera = None #: :py:class:`openflexure_microscope.camera.pi.StreamingCamera`: Picamera object
self.stage = None #: :py:class:`openflexure_stage.stage.OpenFlexureStage`: OpenFlexure stage object