From 3cbd0416d0408286beac8d21fba353d1aefaa312 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Wed, 8 Jan 2020 18:38:52 +0000 Subject: [PATCH] Created @ltaction and @ltproperty decorators --- .../api/v2/views/actions/camera.py | 6 ++- .../common/flask_labthings/decorators.py | 27 ++++++++++++++ .../common/flask_labthings/labthing.py | 37 +++++++------------ .../common/flask_labthings/spec.py | 3 ++ 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/openflexure_microscope/api/v2/views/actions/camera.py b/openflexure_microscope/api/v2/views/actions/camera.py index 69e6016b..4169e042 100644 --- a/openflexure_microscope/api/v2/views/actions/camera.py +++ b/openflexure_microscope/api/v2/views/actions/camera.py @@ -5,6 +5,8 @@ from openflexure_microscope.common.flask_labthings.decorators import ( use_args, marshal_with, doc, + tag, + ltaction, doc_response, ) @@ -17,7 +19,7 @@ import logging from flask import jsonify, request, abort, url_for, redirect, send_file -@doc(tags=["actions"]) +@ltaction class CaptureAPI(Resource): """ Create a new image capture. @@ -42,7 +44,7 @@ class CaptureAPI(Resource): ) @marshal_with(capture_schema) @doc_response(200, description="Capture successful") - @doc(tags=["foo"]) + @tag("foo") def post(self, args): """ Create a new capture diff --git a/openflexure_microscope/common/flask_labthings/decorators.py b/openflexure_microscope/common/flask_labthings/decorators.py index d3aafa16..67e56eee 100644 --- a/openflexure_microscope/common/flask_labthings/decorators.py +++ b/openflexure_microscope/common/flask_labthings/decorators.py @@ -71,6 +71,18 @@ def marshal_task(f): return wrapper +def ltaction(f): + # Pass params to call function attribute for external access + update_spec(f, {"tags": ["actions"]}) + update_spec(f, {"_groups": ["actions"]}) + return f + +def ltproperty(f): + # Pass params to call function attribute for external access + update_spec(f, {"tags": ["properties"]}) + update_spec(f, {"_groups": ["properties"]}) + return f + class use_args(object): def __init__(self, schema, **kwargs): """ @@ -106,6 +118,21 @@ class doc(object): return f +class tag(object): + def __init__(self, tags): + if isinstance(tags, str): + self.tags = [tags] + elif isinstance(tags, list) and all([isinstance(e, str) for e in tags]): + self.tags = tags + else: + raise TypeError("Tags must be a string or list of strings") + + def __call__(self, f): + # Pass params to call function attribute for external access + update_spec(f, {"tags": self.tags}) + return f + + class doc_response(object): def __init__(self, code, description=None, **kwargs): self.code = code diff --git a/openflexure_microscope/common/flask_labthings/labthing.py b/openflexure_microscope/common/flask_labthings/labthing.py index 8e3dc0e0..58bbe574 100644 --- a/openflexure_microscope/common/flask_labthings/labthing.py +++ b/openflexure_microscope/common/flask_labthings/labthing.py @@ -6,7 +6,7 @@ from . import EXTENSION_NAME # TODO: Move into .names from .names import TASK_ENDPOINT, TASK_LIST_ENDPOINT, EXTENSION_LIST_ENDPOINT from .extensions import BaseExtension from .utilities import description_from_view -from .spec import view2path +from .spec import view2path, get_spec from .views.extensions import ExtensionList from .views.tasks import TaskList, TaskResource @@ -159,20 +159,12 @@ class LabThing(object): return "".join([part for part in parts if part]) def register_property(self, resource): - if hasattr(resource, "endpoint"): - self.properties[resource.endpoint] = resource - else: - raise RuntimeError( - f"Resource {resource} has not yet been added. Cannot set as a property." - ) + logging.warning("register_property is deprecated. Use the @ltproperty class decorator instead.") + pass def register_action(self, resource): - if hasattr(resource, "endpoint"): - self.actions[resource.endpoint] = resource - else: - raise RuntimeError( - f"Resource {resource} has not yet been added. Cannot set as an action." - ) + logging.warning("register_action is deprecated. Use the @ltaction class decorator instead.") + pass def add_resource(self, resource, *urls, endpoint=None, **kwargs): """Adds a resource to the api. @@ -208,17 +200,6 @@ class LabThing(object): self.resources.append((resource, urls, endpoint, 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 @@ -255,6 +236,14 @@ class LabThing(object): # Add the resource to our API spec self.spec.path(**view2path(rule, resource, self.spec)) + # Handle resource groups listed in API spec + view_spec = get_spec(resource) + view_groups = view_spec.get("_groups", {}) + if "actions" in view_groups: + self.actions[resource.endpoint] = resource + if "properties" in view_groups: + self.properties[resource.endpoint] = resource + ### Utilities def url_for(self, resource, **values): diff --git a/openflexure_microscope/common/flask_labthings/spec.py b/openflexure_microscope/common/flask_labthings/spec.py index 7a85c55f..d21687a5 100644 --- a/openflexure_microscope/common/flask_labthings/spec.py +++ b/openflexure_microscope/common/flask_labthings/spec.py @@ -20,6 +20,9 @@ def update_spec(obj, spec): rupdate(obj.__apispec__, spec) return obj.__apispec__ +def get_spec(obj): + obj.__apispec__ = obj.__dict__.get("__apispec__", {}) + return obj.__apispec__ def view2path(rule: str, view: Resource, spec: APISpec): params = {