Loading openflexure_microscope/api/v1/blueprints/plugins.py +1 −4 Original line number Diff line number Diff line Loading @@ -39,10 +39,7 @@ def construct_blueprint(microscope_obj): all_routes = [] # For each plugin attached to the microscope object for plugin_representation in microscope_obj.plugins.active: plugin_obj = plugin_representation["plugin"] plugin_name = plugin_representation["name"] for plugin_name, plugin_obj in microscope_obj.plugins._legacy_plugins.items(): # If plugin contains valid endpoints if hasattr(plugin_obj, "api_views") and isinstance(plugin_obj.api_views, dict): Loading openflexure_microscope/api/v2/blueprints/plugins.py +36 −74 Original line number Diff line number Diff line Loading @@ -20,20 +20,34 @@ def plugins_representation(plugin_loader_object: PluginLoader): dict: Dictionary representation of all plugins """ plugins = [] print(plugin_loader_object.active) for plugin in plugin_loader_object.active: logging.info(f"Representing plugin {plugin._name}") d = { "name": plugin["name"], "plugin": str(plugin["plugin"]), "routes": plugin["routes"], "form": plugin["form"], "name": plugin._name, "plugin": str(plugin), "views": {}, "form": plugin.form, } for view_id, view in plugin.views.items(): logging.info(f"Representing view {view_id}") uri = url_for(f"v2_plugins_blueprint.{view_id}") # Make links dictionary if it doesn't yet exist view_d = { "links": {"self": uri} } for route in d["routes"]: route_id = route["id"] uri = url_for(f"v2_plugins_blueprint.{route_id}") route["links"]["self"] = uri d["views"][view_id] = view_d print("\n") print(d) print("\n") plugins.append(d) print(plugins) return plugins Loading Loading @@ -66,69 +80,20 @@ def construct_blueprint(microscope_obj): all_routes = [] # For each plugin attached to the microscope object for plugin_representation in microscope_obj.plugins.active: plugin_obj = plugin_representation["plugin"] plugin_name = plugin_representation["name"] # If plugin contains valid endpoints if hasattr(plugin_obj, "api_views") and isinstance(plugin_obj.api_views, dict): # We'll keep a record of how each route was expanded expanded_routes = {} # For each defined endpoint for view_route, view_class in plugin_obj.api_views.items(): # Remove all leading slashes from view route cleaned_route = view_route while cleaned_route[0] == "/": cleaned_route = cleaned_route[1:] # Construct a full view route from the plugin name full_view_route = "/{}/{}".format(plugin_name, cleaned_route) logging.debug(full_view_route) # Record how the view_route got expanded expanded_routes[view_route] = full_view_route # Check if endpoint name clashes if full_view_route not in all_routes and issubclass( view_class, MicroscopeViewPlugin ): # Add route to main route dictionary all_routes.append(full_view_route) # Create a Python-safe name for the route plugin_route_id = "plugin{}".format(full_view_route).replace( "/", "_" ) for plugin in microscope_obj.plugins.active: for plugin_view_id, plugin_view in plugin.views.items(): # Add route to the plugins blueprint blueprint.add_url_rule( full_view_route, view_func=view_class.as_view( plugin_route_id, plugin_view["rule"], view_func=plugin_view["view"].as_view( plugin_view_id, microscope=microscope_obj, plugin=plugin_obj, plugin=plugin, ), ) # Add route to the plugin representation dictionary route_representation = { "id": plugin_route_id, "route": full_view_route, "links": {}, } plugin_representation["routes"].append(route_representation) else: warnings.warn( "An endpoint /{} has already been loaded. Skipping {}.".format( full_view_route, view_class ) ) """ # If plugin includes an API form if hasattr(plugin_obj, "api_form") and isinstance( plugin_obj.api_form, dict Loading @@ -152,9 +117,6 @@ def construct_blueprint(microscope_obj): # Store the complete form in Microscope().plugin.form plugin_representation["form"] = api_form_info print(microscope_obj.plugins.forms) """ else: warnings.warn( "No valid 'api_views' dictionary found in {}".format(plugin_obj) ) return blueprint openflexure_microscope/plugins/default/scan/plugin.py +2 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,8 @@ class ScanPlugin(MicroscopePlugin): api_views = {"/tile": TileScanAPI} def __init__(self): MicroscopePlugin.__init__(self) self.images_to_be_captured: int = 1 update_task_data({"images_to_be_captured": self.images_to_be_captured}) Loading openflexure_microscope/plugins/loader.py +118 −58 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import os import inspect import logging from openflexure_microscope.utilities import camel_to_snake, camel_to_spine from openflexure_microscope.api.views import MicroscopeViewPlugin Loading Loading @@ -83,7 +84,6 @@ def name_from_module(plugin_path): def load_plugin_module(plugin_path): # If the loader was found (i.e. plugin probably exists) if check_module(plugin_path): try: Loading Loading @@ -140,50 +140,6 @@ def class_from_map(plugin_map): return load_plugin_class(*plugin_arr) def expand_views(plugin_object, plugin_name): routes = [] # If plugin contains valid endpoints if hasattr(plugin_object, "api_views") and isinstance(plugin_object.api_views, dict): # We'll keep a record of how each route was expanded expanded_routes = {} # For each defined endpoint for view_route, view_class in plugin_object.api_views.items(): # Remove all leading slashes from view route cleaned_route = view_route while cleaned_route[0] == "/": cleaned_route = cleaned_route[1:] # Construct a full view route from the plugin name full_view_route = "/{}/{}".format(plugin_name, cleaned_route) logging.debug(full_view_route) # Record how the view_route got expanded expanded_routes[view_route] = full_view_route # Check if endpoint name clashes if issubclass(view_class, MicroscopeViewPlugin): # Create a Python-safe name for the route plugin_route_id = "plugin{}".format(full_view_route).replace( "/", "_" ) # Add route to the plugin representation dictionary route_representation = { "id": plugin_route_id, "route": full_view_route, "links": {}, } routes.append(route_representation) return routes class PluginLoader(object): """ A mount-point for all loaded plugins. Attaches to a Microscope object. Loading @@ -195,6 +151,7 @@ class PluginLoader(object): def __init__(self, parent): self.parent = parent self._plugins = [] # List of plugin objects self._legacy_plugins = {} # DEPRECATED: Dictionary of plugins with old names self.forms = [] # List of plugin forms logging.info("Creating plugin mount") Loading @@ -221,6 +178,8 @@ class PluginLoader(object): if plugin_class is not None: logging.debug(f"Loading plugin class {plugin_class}, {plugin_name}") plugin_name_python_safe = plugin_name.replace("/", "_") if plugin_class and plugin_name: Loading @@ -238,23 +197,18 @@ class PluginLoader(object): ) elif isinstance( plugin_object, MicroscopePlugin plugin_object, BasePlugin ): # If plugin_object is an instance of MicroscopePlugin # Attach plugin_object to the plugin mount setattr(self, plugin_name_python_safe, plugin_object) # Store the plugin object, and it's properties self._plugins.append( { "name": plugin_name, "python_name": plugin_name_python_safe, "plugin": plugin_object, "routes": [], "form": None, } ) self._plugins.append(plugin_object) # DEPRECATED: Store the plugin with it's old name self._legacy_plugins[plugin_name_python_safe] = plugin_object # Grant plugin access to the hardware if isinstance(plugin_object, MicroscopePlugin): plugin_object.microscope = self.parent logging.info( Loading @@ -267,7 +221,112 @@ class PluginLoader(object): logging.warning(f"Error loading plugin {plugin_map}. Moving on.") class MicroscopePlugin: class BasePlugin: """ Parent class for all plugins. Handles binding route views and forms. """ def __init__(self): self._views = {} self._rules = {} self._form = None # If old api_views dictionary is found if hasattr(self, "api_views"): # Convert to new format self._convert_old_api_views() # If old api_form dictionary is found if hasattr(self, "api_form"): # Convert to new format self._convert_old_api_form() print("\n\n NAME: " + self._name + "\n\n") print(self.views) print(self.form) @property def views(self): return self._views def add_view(self, rule, view_class): # Remove all leading slashes from view route cleaned_rule = rule while cleaned_rule[0] == "/": cleaned_rule = cleaned_rule[1:] # Expand the rule to include plugin name full_rule = "/{}/{}".format(self._name_uri_safe, cleaned_rule) # Create a Python-safe route ID view_id = cleaned_rule.replace("/", "_") full_view_id = f"{self._name_python_safe}_{view_id}" # Store route information in a dictionary d = { "rule": full_rule, "view": view_class } # Add view to private views dictionary self._views[full_view_id] = d # Store the rule expansion information self._rules[rule] = self._views[full_view_id] @property def form(self): if not self._form: return None api_form_info = copy.deepcopy(self._form) api_form_info["id"] = self._name if "forms" in api_form_info and isinstance( api_form_info["forms"], list ): for form in api_form_info["forms"]: if "route" in form and form["route"] in self._rules.keys(): form["route"] = self._rules[form["route"]]["rule"] else: logging.warn( "No valid expandable route found for {}".format( form["route"] ) ) return api_form_info def set_form(self, form_dictionary: dict): self._form = form_dictionary def _convert_old_api_views(self): for view_route, view_class in self.api_views.items(): self.add_view(view_route, view_class) def _convert_old_api_form(self): self.set_form(self.api_form) @property def _name(self): return self.__class__.__name__ @property def _name_python_safe(self): return camel_to_snake(self._name) @property def _name_uri_safe(self): return camel_to_spine(self._name) def _full_name(self): module = self.__class__.__module__ if module is None or module == str.__class__.__module__: return self.__class__.__name__ # Avoid reporting __builtin__ else: return module + '.' + self.__class__.__name__ class MicroscopePlugin(BasePlugin): """ Parent class for all microscope plugins. Loading @@ -278,6 +337,7 @@ class MicroscopePlugin: api_views = {} # Initially empty dictionary of API views associated with the plugin def __init__(self): BasePlugin.__init__(self) self.microscope = ( None ) #: :py:class:`openflexure_microscope.microscope.Microscope`: Microscope object openflexure_microscope/plugins/testing/form_example/plugin.py +2 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ class ExamplePlugin(MicroscopePlugin): api_views = {"/do": DoAPI, "/task": TaskAPI} def __init__(self): MicroscopePlugin.__init__(self) self.val_int = 10 self.val_str = "Hello" self.val_radio = "First" Loading Loading
openflexure_microscope/api/v1/blueprints/plugins.py +1 −4 Original line number Diff line number Diff line Loading @@ -39,10 +39,7 @@ def construct_blueprint(microscope_obj): all_routes = [] # For each plugin attached to the microscope object for plugin_representation in microscope_obj.plugins.active: plugin_obj = plugin_representation["plugin"] plugin_name = plugin_representation["name"] for plugin_name, plugin_obj in microscope_obj.plugins._legacy_plugins.items(): # If plugin contains valid endpoints if hasattr(plugin_obj, "api_views") and isinstance(plugin_obj.api_views, dict): Loading
openflexure_microscope/api/v2/blueprints/plugins.py +36 −74 Original line number Diff line number Diff line Loading @@ -20,20 +20,34 @@ def plugins_representation(plugin_loader_object: PluginLoader): dict: Dictionary representation of all plugins """ plugins = [] print(plugin_loader_object.active) for plugin in plugin_loader_object.active: logging.info(f"Representing plugin {plugin._name}") d = { "name": plugin["name"], "plugin": str(plugin["plugin"]), "routes": plugin["routes"], "form": plugin["form"], "name": plugin._name, "plugin": str(plugin), "views": {}, "form": plugin.form, } for view_id, view in plugin.views.items(): logging.info(f"Representing view {view_id}") uri = url_for(f"v2_plugins_blueprint.{view_id}") # Make links dictionary if it doesn't yet exist view_d = { "links": {"self": uri} } for route in d["routes"]: route_id = route["id"] uri = url_for(f"v2_plugins_blueprint.{route_id}") route["links"]["self"] = uri d["views"][view_id] = view_d print("\n") print(d) print("\n") plugins.append(d) print(plugins) return plugins Loading Loading @@ -66,69 +80,20 @@ def construct_blueprint(microscope_obj): all_routes = [] # For each plugin attached to the microscope object for plugin_representation in microscope_obj.plugins.active: plugin_obj = plugin_representation["plugin"] plugin_name = plugin_representation["name"] # If plugin contains valid endpoints if hasattr(plugin_obj, "api_views") and isinstance(plugin_obj.api_views, dict): # We'll keep a record of how each route was expanded expanded_routes = {} # For each defined endpoint for view_route, view_class in plugin_obj.api_views.items(): # Remove all leading slashes from view route cleaned_route = view_route while cleaned_route[0] == "/": cleaned_route = cleaned_route[1:] # Construct a full view route from the plugin name full_view_route = "/{}/{}".format(plugin_name, cleaned_route) logging.debug(full_view_route) # Record how the view_route got expanded expanded_routes[view_route] = full_view_route # Check if endpoint name clashes if full_view_route not in all_routes and issubclass( view_class, MicroscopeViewPlugin ): # Add route to main route dictionary all_routes.append(full_view_route) # Create a Python-safe name for the route plugin_route_id = "plugin{}".format(full_view_route).replace( "/", "_" ) for plugin in microscope_obj.plugins.active: for plugin_view_id, plugin_view in plugin.views.items(): # Add route to the plugins blueprint blueprint.add_url_rule( full_view_route, view_func=view_class.as_view( plugin_route_id, plugin_view["rule"], view_func=plugin_view["view"].as_view( plugin_view_id, microscope=microscope_obj, plugin=plugin_obj, plugin=plugin, ), ) # Add route to the plugin representation dictionary route_representation = { "id": plugin_route_id, "route": full_view_route, "links": {}, } plugin_representation["routes"].append(route_representation) else: warnings.warn( "An endpoint /{} has already been loaded. Skipping {}.".format( full_view_route, view_class ) ) """ # If plugin includes an API form if hasattr(plugin_obj, "api_form") and isinstance( plugin_obj.api_form, dict Loading @@ -152,9 +117,6 @@ def construct_blueprint(microscope_obj): # Store the complete form in Microscope().plugin.form plugin_representation["form"] = api_form_info print(microscope_obj.plugins.forms) """ else: warnings.warn( "No valid 'api_views' dictionary found in {}".format(plugin_obj) ) return blueprint
openflexure_microscope/plugins/default/scan/plugin.py +2 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,8 @@ class ScanPlugin(MicroscopePlugin): api_views = {"/tile": TileScanAPI} def __init__(self): MicroscopePlugin.__init__(self) self.images_to_be_captured: int = 1 update_task_data({"images_to_be_captured": self.images_to_be_captured}) Loading
openflexure_microscope/plugins/loader.py +118 −58 Original line number Diff line number Diff line Loading @@ -4,6 +4,7 @@ import os import inspect import logging from openflexure_microscope.utilities import camel_to_snake, camel_to_spine from openflexure_microscope.api.views import MicroscopeViewPlugin Loading Loading @@ -83,7 +84,6 @@ def name_from_module(plugin_path): def load_plugin_module(plugin_path): # If the loader was found (i.e. plugin probably exists) if check_module(plugin_path): try: Loading Loading @@ -140,50 +140,6 @@ def class_from_map(plugin_map): return load_plugin_class(*plugin_arr) def expand_views(plugin_object, plugin_name): routes = [] # If plugin contains valid endpoints if hasattr(plugin_object, "api_views") and isinstance(plugin_object.api_views, dict): # We'll keep a record of how each route was expanded expanded_routes = {} # For each defined endpoint for view_route, view_class in plugin_object.api_views.items(): # Remove all leading slashes from view route cleaned_route = view_route while cleaned_route[0] == "/": cleaned_route = cleaned_route[1:] # Construct a full view route from the plugin name full_view_route = "/{}/{}".format(plugin_name, cleaned_route) logging.debug(full_view_route) # Record how the view_route got expanded expanded_routes[view_route] = full_view_route # Check if endpoint name clashes if issubclass(view_class, MicroscopeViewPlugin): # Create a Python-safe name for the route plugin_route_id = "plugin{}".format(full_view_route).replace( "/", "_" ) # Add route to the plugin representation dictionary route_representation = { "id": plugin_route_id, "route": full_view_route, "links": {}, } routes.append(route_representation) return routes class PluginLoader(object): """ A mount-point for all loaded plugins. Attaches to a Microscope object. Loading @@ -195,6 +151,7 @@ class PluginLoader(object): def __init__(self, parent): self.parent = parent self._plugins = [] # List of plugin objects self._legacy_plugins = {} # DEPRECATED: Dictionary of plugins with old names self.forms = [] # List of plugin forms logging.info("Creating plugin mount") Loading @@ -221,6 +178,8 @@ class PluginLoader(object): if plugin_class is not None: logging.debug(f"Loading plugin class {plugin_class}, {plugin_name}") plugin_name_python_safe = plugin_name.replace("/", "_") if plugin_class and plugin_name: Loading @@ -238,23 +197,18 @@ class PluginLoader(object): ) elif isinstance( plugin_object, MicroscopePlugin plugin_object, BasePlugin ): # If plugin_object is an instance of MicroscopePlugin # Attach plugin_object to the plugin mount setattr(self, plugin_name_python_safe, plugin_object) # Store the plugin object, and it's properties self._plugins.append( { "name": plugin_name, "python_name": plugin_name_python_safe, "plugin": plugin_object, "routes": [], "form": None, } ) self._plugins.append(plugin_object) # DEPRECATED: Store the plugin with it's old name self._legacy_plugins[plugin_name_python_safe] = plugin_object # Grant plugin access to the hardware if isinstance(plugin_object, MicroscopePlugin): plugin_object.microscope = self.parent logging.info( Loading @@ -267,7 +221,112 @@ class PluginLoader(object): logging.warning(f"Error loading plugin {plugin_map}. Moving on.") class MicroscopePlugin: class BasePlugin: """ Parent class for all plugins. Handles binding route views and forms. """ def __init__(self): self._views = {} self._rules = {} self._form = None # If old api_views dictionary is found if hasattr(self, "api_views"): # Convert to new format self._convert_old_api_views() # If old api_form dictionary is found if hasattr(self, "api_form"): # Convert to new format self._convert_old_api_form() print("\n\n NAME: " + self._name + "\n\n") print(self.views) print(self.form) @property def views(self): return self._views def add_view(self, rule, view_class): # Remove all leading slashes from view route cleaned_rule = rule while cleaned_rule[0] == "/": cleaned_rule = cleaned_rule[1:] # Expand the rule to include plugin name full_rule = "/{}/{}".format(self._name_uri_safe, cleaned_rule) # Create a Python-safe route ID view_id = cleaned_rule.replace("/", "_") full_view_id = f"{self._name_python_safe}_{view_id}" # Store route information in a dictionary d = { "rule": full_rule, "view": view_class } # Add view to private views dictionary self._views[full_view_id] = d # Store the rule expansion information self._rules[rule] = self._views[full_view_id] @property def form(self): if not self._form: return None api_form_info = copy.deepcopy(self._form) api_form_info["id"] = self._name if "forms" in api_form_info and isinstance( api_form_info["forms"], list ): for form in api_form_info["forms"]: if "route" in form and form["route"] in self._rules.keys(): form["route"] = self._rules[form["route"]]["rule"] else: logging.warn( "No valid expandable route found for {}".format( form["route"] ) ) return api_form_info def set_form(self, form_dictionary: dict): self._form = form_dictionary def _convert_old_api_views(self): for view_route, view_class in self.api_views.items(): self.add_view(view_route, view_class) def _convert_old_api_form(self): self.set_form(self.api_form) @property def _name(self): return self.__class__.__name__ @property def _name_python_safe(self): return camel_to_snake(self._name) @property def _name_uri_safe(self): return camel_to_spine(self._name) def _full_name(self): module = self.__class__.__module__ if module is None or module == str.__class__.__module__: return self.__class__.__name__ # Avoid reporting __builtin__ else: return module + '.' + self.__class__.__name__ class MicroscopePlugin(BasePlugin): """ Parent class for all microscope plugins. Loading @@ -278,6 +337,7 @@ class MicroscopePlugin: api_views = {} # Initially empty dictionary of API views associated with the plugin def __init__(self): BasePlugin.__init__(self) self.microscope = ( None ) #: :py:class:`openflexure_microscope.microscope.Microscope`: Microscope object
openflexure_microscope/plugins/testing/form_example/plugin.py +2 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ class ExamplePlugin(MicroscopePlugin): api_views = {"/do": DoAPI, "/task": TaskAPI} def __init__(self): MicroscopePlugin.__init__(self) self.val_int = 10 self.val_str = "Hello" self.val_radio = "First" Loading