Commit 2b5b0b87 authored by Joel Collins's avatar Joel Collins
Browse files

Added base plugin resource

parent 469e8218
Loading
Loading
Loading
Loading
+12 −23
Original line number Diff line number Diff line
@@ -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

@@ -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
@@ -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)
+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
+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
+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)
+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