Loading openflexure_microscope/api/app.py +12 −23 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ from openflexure_microscope.config import settings_file_path, JSONEncoder from openflexure_microscope.api.v1 import blueprints from openflexure_microscope.api import v2 from openflexure_microscope.common.labthings import LabThing from openflexure_microscope.common.labthings.labthing import LabThing from openflexure_microscope.common.labthings.find import registered_plugins from openflexure_microscope.common.labthings.plugins import find_plugins from openflexure_microscope.config import USER_PLUGINS_PATH from openflexure_microscope.api.microscope import default_microscope as api_microscope Loading Loading @@ -78,29 +81,15 @@ handler = JSONExceptionHandler(app) # Attach lab devices labthing = LabThing(app) labthing.register_device(api_microscope, "openflexure_microscope") # WEBAPP ROUTES # Base routes base_blueprint = blueprints.base.construct_blueprint(api_microscope) app.register_blueprint(base_blueprint, url_prefix=uri("", "v1")) labthing.url_prefix = "/api/v2b" labthing.description = "Test LabThing-based API for OpenFlexure Microscope" # Stage routes stage_blueprint = blueprints.stage.construct_blueprint(api_microscope) app.register_blueprint(stage_blueprint, url_prefix=uri("/stage", "v1")) # Camera routes camera_blueprint = blueprints.camera.construct_blueprint(api_microscope) app.register_blueprint(camera_blueprint, url_prefix=uri("/camera", "v1")) labthing.register_device(api_microscope, "openflexure_microscope") # Plugin routes plugin_blueprint = blueprints.plugins.construct_blueprint(api_microscope) app.register_blueprint(plugin_blueprint, url_prefix=uri("/plugin", "v1")) for _plugin in find_plugins(USER_PLUGINS_PATH): labthing.register_plugin(_plugin) # Task routes task_blueprint = blueprints.task.construct_blueprint(api_microscope) app.register_blueprint(task_blueprint, url_prefix=uri("/task", "v1")) # WEBAPP ROUTES ### V2 # Root routes Loading @@ -123,8 +112,8 @@ v2_status_blueprint = v2.blueprints.status.construct_blueprint(api_microscope) app.register_blueprint(v2_status_blueprint, url_prefix=uri("/status", "v2")) # Plugins routes v2_plugin_blueprint = v2.blueprints.plugins.construct_blueprint(api_microscope) app.register_blueprint(v2_plugin_blueprint, url_prefix=uri("/plugins", "v2")) # v2_plugin_blueprint = v2.blueprints.plugins.construct_blueprint(api_microscope) # app.register_blueprint(v2_plugin_blueprint, url_prefix=uri("/plugins", "v2")) # Tasks routes v2_tasks_blueprint = v2.blueprints.tasks.construct_blueprint(api_microscope) Loading openflexure_microscope/common/labthings/__init__.py +0 −36 Original line number Diff line number Diff line from flask import current_app, _app_ctx_stack EXTENSION_NAME = "flask-lab" class LabThing(object): def __init__(self, app=None): self.app = app self.devices = {} if app is not None: self.init_app(app) def init_app(self, app): app.teardown_appcontext(self.teardown) app.extensions = getattr(app, "extensions", {}) app.extensions[EXTENSION_NAME] = self def teardown(self, exception): print(f"Tearing down devices: {self.devices}") def register_device(self, device_object, device_name: str): self.devices[device_name] = device_object def find_device(device_name): app = current_app if not app: return None pylot_instance = app.extensions[EXTENSION_NAME] if device_name in pylot_instance.devices: return pylot_instance.devices[device_name] else: return None openflexure_microscope/common/labthings/find.py 0 → 100644 +32 −0 Original line number Diff line number Diff line from flask import current_app from . import EXTENSION_NAME def _current_labthing(): app = current_app if not app: return None return app.extensions[EXTENSION_NAME] def registered_plugins(labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() return labthing_instance.plugins def registered_devices(labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() return labthing_instance.devices def find_device(device_name, labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() if device_name in labthing_instance.devices: return labthing_instance.devices[device_name] else: return None openflexure_microscope/common/labthings/labthing.py 0 → 100644 +139 −0 Original line number Diff line number Diff line from flask import current_app, _app_ctx_stack, request from .plugins import BasePlugin from .views.plugins import PluginListResource from . import EXTENSION_NAME class LabThing(object): def __init__(self, app=None, url_prefix="", description=""): self.app = app self.devices = {} self.plugins = {} self.resources = [] self.endpoints = set() self.url_prefix = url_prefix self.description = description if app is not None: self.init_app(app) ### Flask stuff def init_app(self, app): app.teardown_appcontext(self.teardown) app.extensions = getattr(app, "extensions", {}) app.extensions[EXTENSION_NAME] = self self._create_base_routes() def teardown(self, exception): print(f"Tearing down devices: {self.devices}") def _create_base_routes(self): self.add_resource(PluginListResource, "/plugins") ### Device stuff def register_device(self, device_object, device_name: str): self.devices[device_name] = device_object ### Plugin stuff def register_plugin(self, plugin_object): if isinstance(plugin_object, BasePlugin): self.plugins[plugin_object.name] = plugin_object else: raise TypeError("Plugin object must be an instance of BasePlugin") ### Resource stuff def _complete_url(self, url_part, registration_prefix): """This method is used to defer the construction of the final url in the case that the Api is created with a Blueprint. :param url_part: The part of the url the endpoint is registered with :param registration_prefix: The part of the url contributed by the blueprint. Generally speaking, BlueprintSetupState.url_prefix """ parts = [registration_prefix, self.url_prefix, url_part] return "".join([part for part in parts if part]) def add_resource(self, resource, *urls, **kwargs): """Adds a resource to the api. :param resource: the class name of your resource :type resource: :class:`Type[Resource]` :param urls: one or more url routes to match for the resource, standard flask routing rules apply. Any url variables will be passed to the resource method as args. :type urls: str :param endpoint: endpoint name (defaults to :meth:`Resource.__name__.lower` Can be used to reference this route in :class:`fields.Url` fields :type endpoint: str :param resource_class_args: args to be forwarded to the constructor of the resource. :type resource_class_args: tuple :param resource_class_kwargs: kwargs to be forwarded to the constructor of the resource. :type resource_class_kwargs: dict Additional keyword arguments not specified above will be passed as-is to :meth:`flask.Flask.add_url_rule`. Examples:: api.add_resource(HelloWorld, '/', '/hello') api.add_resource(Foo, '/foo', endpoint="foo") api.add_resource(FooSpecial, '/special/foo', endpoint="foo") """ if self.app is not None: self._register_view(self.app, resource, *urls, **kwargs) else: self.resources.append((resource, urls, kwargs)) def resource(self, *urls, **kwargs): """Wraps a :class:`~flask_restful.Resource` class, adding it to the api. Parameters are the same as :meth:`~flask_restful.Api.add_resource`. Example:: app = Flask(__name__) api = restful.Api(app) @api.resource('/foo') class Foo(Resource): def get(self): return 'Hello, World!' """ def decorator(cls): self.add_resource(cls, *urls, **kwargs) return cls return decorator def _register_view(self, app, resource, *urls, **kwargs): endpoint = kwargs.pop("endpoint", None) or resource.__name__.lower() self.endpoints.add(endpoint) resource_class_args = kwargs.pop("resource_class_args", ()) resource_class_kwargs = kwargs.pop("resource_class_kwargs", {}) # NOTE: 'view_functions' is cleaned up from Blueprint class in Flask 1.0 if endpoint in getattr(app, "view_functions", {}): previous_view_class = app.view_functions[endpoint].__dict__["view_class"] # if you override the endpoint with a different class, avoid the collision by raising an exception if previous_view_class != resource: raise ValueError( "This endpoint (%s) is already set to the class %s." % (endpoint, previous_view_class.__name__) ) resource.endpoint = endpoint resource_func = resource.as_view( endpoint, *resource_class_args, **resource_class_kwargs ) for url in urls: # If we've got no Blueprint, just build a url with no prefix rule = self._complete_url(url, "") # Add the url to the application or blueprint app.add_url_rule(rule, view_func=resource_func, **kwargs) openflexure_microscope/common/labthings/resource.py 0 → 100644 +9 −0 Original line number Diff line number Diff line from flask.views import MethodView class Resource(MethodView): """Currently identical to MethodView """ def __init__(self, *args, **kwargs): MethodView.__init__(self, *args, **kwargs) Loading
openflexure_microscope/api/app.py +12 −23 Original line number Diff line number Diff line Loading @@ -20,7 +20,10 @@ from openflexure_microscope.config import settings_file_path, JSONEncoder from openflexure_microscope.api.v1 import blueprints from openflexure_microscope.api import v2 from openflexure_microscope.common.labthings import LabThing from openflexure_microscope.common.labthings.labthing import LabThing from openflexure_microscope.common.labthings.find import registered_plugins from openflexure_microscope.common.labthings.plugins import find_plugins from openflexure_microscope.config import USER_PLUGINS_PATH from openflexure_microscope.api.microscope import default_microscope as api_microscope Loading Loading @@ -78,29 +81,15 @@ handler = JSONExceptionHandler(app) # Attach lab devices labthing = LabThing(app) labthing.register_device(api_microscope, "openflexure_microscope") # WEBAPP ROUTES # Base routes base_blueprint = blueprints.base.construct_blueprint(api_microscope) app.register_blueprint(base_blueprint, url_prefix=uri("", "v1")) labthing.url_prefix = "/api/v2b" labthing.description = "Test LabThing-based API for OpenFlexure Microscope" # Stage routes stage_blueprint = blueprints.stage.construct_blueprint(api_microscope) app.register_blueprint(stage_blueprint, url_prefix=uri("/stage", "v1")) # Camera routes camera_blueprint = blueprints.camera.construct_blueprint(api_microscope) app.register_blueprint(camera_blueprint, url_prefix=uri("/camera", "v1")) labthing.register_device(api_microscope, "openflexure_microscope") # Plugin routes plugin_blueprint = blueprints.plugins.construct_blueprint(api_microscope) app.register_blueprint(plugin_blueprint, url_prefix=uri("/plugin", "v1")) for _plugin in find_plugins(USER_PLUGINS_PATH): labthing.register_plugin(_plugin) # Task routes task_blueprint = blueprints.task.construct_blueprint(api_microscope) app.register_blueprint(task_blueprint, url_prefix=uri("/task", "v1")) # WEBAPP ROUTES ### V2 # Root routes Loading @@ -123,8 +112,8 @@ v2_status_blueprint = v2.blueprints.status.construct_blueprint(api_microscope) app.register_blueprint(v2_status_blueprint, url_prefix=uri("/status", "v2")) # Plugins routes v2_plugin_blueprint = v2.blueprints.plugins.construct_blueprint(api_microscope) app.register_blueprint(v2_plugin_blueprint, url_prefix=uri("/plugins", "v2")) # v2_plugin_blueprint = v2.blueprints.plugins.construct_blueprint(api_microscope) # app.register_blueprint(v2_plugin_blueprint, url_prefix=uri("/plugins", "v2")) # Tasks routes v2_tasks_blueprint = v2.blueprints.tasks.construct_blueprint(api_microscope) Loading
openflexure_microscope/common/labthings/__init__.py +0 −36 Original line number Diff line number Diff line from flask import current_app, _app_ctx_stack EXTENSION_NAME = "flask-lab" class LabThing(object): def __init__(self, app=None): self.app = app self.devices = {} if app is not None: self.init_app(app) def init_app(self, app): app.teardown_appcontext(self.teardown) app.extensions = getattr(app, "extensions", {}) app.extensions[EXTENSION_NAME] = self def teardown(self, exception): print(f"Tearing down devices: {self.devices}") def register_device(self, device_object, device_name: str): self.devices[device_name] = device_object def find_device(device_name): app = current_app if not app: return None pylot_instance = app.extensions[EXTENSION_NAME] if device_name in pylot_instance.devices: return pylot_instance.devices[device_name] else: return None
openflexure_microscope/common/labthings/find.py 0 → 100644 +32 −0 Original line number Diff line number Diff line from flask import current_app from . import EXTENSION_NAME def _current_labthing(): app = current_app if not app: return None return app.extensions[EXTENSION_NAME] def registered_plugins(labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() return labthing_instance.plugins def registered_devices(labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() return labthing_instance.devices def find_device(device_name, labthing_instance=None): if not labthing_instance: labthing_instance = _current_labthing() if device_name in labthing_instance.devices: return labthing_instance.devices[device_name] else: return None
openflexure_microscope/common/labthings/labthing.py 0 → 100644 +139 −0 Original line number Diff line number Diff line from flask import current_app, _app_ctx_stack, request from .plugins import BasePlugin from .views.plugins import PluginListResource from . import EXTENSION_NAME class LabThing(object): def __init__(self, app=None, url_prefix="", description=""): self.app = app self.devices = {} self.plugins = {} self.resources = [] self.endpoints = set() self.url_prefix = url_prefix self.description = description if app is not None: self.init_app(app) ### Flask stuff def init_app(self, app): app.teardown_appcontext(self.teardown) app.extensions = getattr(app, "extensions", {}) app.extensions[EXTENSION_NAME] = self self._create_base_routes() def teardown(self, exception): print(f"Tearing down devices: {self.devices}") def _create_base_routes(self): self.add_resource(PluginListResource, "/plugins") ### Device stuff def register_device(self, device_object, device_name: str): self.devices[device_name] = device_object ### Plugin stuff def register_plugin(self, plugin_object): if isinstance(plugin_object, BasePlugin): self.plugins[plugin_object.name] = plugin_object else: raise TypeError("Plugin object must be an instance of BasePlugin") ### Resource stuff def _complete_url(self, url_part, registration_prefix): """This method is used to defer the construction of the final url in the case that the Api is created with a Blueprint. :param url_part: The part of the url the endpoint is registered with :param registration_prefix: The part of the url contributed by the blueprint. Generally speaking, BlueprintSetupState.url_prefix """ parts = [registration_prefix, self.url_prefix, url_part] return "".join([part for part in parts if part]) def add_resource(self, resource, *urls, **kwargs): """Adds a resource to the api. :param resource: the class name of your resource :type resource: :class:`Type[Resource]` :param urls: one or more url routes to match for the resource, standard flask routing rules apply. Any url variables will be passed to the resource method as args. :type urls: str :param endpoint: endpoint name (defaults to :meth:`Resource.__name__.lower` Can be used to reference this route in :class:`fields.Url` fields :type endpoint: str :param resource_class_args: args to be forwarded to the constructor of the resource. :type resource_class_args: tuple :param resource_class_kwargs: kwargs to be forwarded to the constructor of the resource. :type resource_class_kwargs: dict Additional keyword arguments not specified above will be passed as-is to :meth:`flask.Flask.add_url_rule`. Examples:: api.add_resource(HelloWorld, '/', '/hello') api.add_resource(Foo, '/foo', endpoint="foo") api.add_resource(FooSpecial, '/special/foo', endpoint="foo") """ if self.app is not None: self._register_view(self.app, resource, *urls, **kwargs) else: self.resources.append((resource, urls, kwargs)) def resource(self, *urls, **kwargs): """Wraps a :class:`~flask_restful.Resource` class, adding it to the api. Parameters are the same as :meth:`~flask_restful.Api.add_resource`. Example:: app = Flask(__name__) api = restful.Api(app) @api.resource('/foo') class Foo(Resource): def get(self): return 'Hello, World!' """ def decorator(cls): self.add_resource(cls, *urls, **kwargs) return cls return decorator def _register_view(self, app, resource, *urls, **kwargs): endpoint = kwargs.pop("endpoint", None) or resource.__name__.lower() self.endpoints.add(endpoint) resource_class_args = kwargs.pop("resource_class_args", ()) resource_class_kwargs = kwargs.pop("resource_class_kwargs", {}) # NOTE: 'view_functions' is cleaned up from Blueprint class in Flask 1.0 if endpoint in getattr(app, "view_functions", {}): previous_view_class = app.view_functions[endpoint].__dict__["view_class"] # if you override the endpoint with a different class, avoid the collision by raising an exception if previous_view_class != resource: raise ValueError( "This endpoint (%s) is already set to the class %s." % (endpoint, previous_view_class.__name__) ) resource.endpoint = endpoint resource_func = resource.as_view( endpoint, *resource_class_args, **resource_class_kwargs ) for url in urls: # If we've got no Blueprint, just build a url with no prefix rule = self._complete_url(url, "") # Add the url to the application or blueprint app.add_url_rule(rule, view_func=resource_func, **kwargs)
openflexure_microscope/common/labthings/resource.py 0 → 100644 +9 −0 Original line number Diff line number Diff line from flask.views import MethodView class Resource(MethodView): """Currently identical to MethodView """ def __init__(self, *args, **kwargs): MethodView.__init__(self, *args, **kwargs)