Created @ltaction and @ltproperty decorators

This commit is contained in:
Joel Collins 2020-01-08 18:38:52 +00:00
parent d12fd3e02c
commit 3cbd0416d0
4 changed files with 47 additions and 26 deletions

View file

@ -5,6 +5,8 @@ from openflexure_microscope.common.flask_labthings.decorators import (
use_args, use_args,
marshal_with, marshal_with,
doc, doc,
tag,
ltaction,
doc_response, doc_response,
) )
@ -17,7 +19,7 @@ import logging
from flask import jsonify, request, abort, url_for, redirect, send_file from flask import jsonify, request, abort, url_for, redirect, send_file
@doc(tags=["actions"]) @ltaction
class CaptureAPI(Resource): class CaptureAPI(Resource):
""" """
Create a new image capture. Create a new image capture.
@ -42,7 +44,7 @@ class CaptureAPI(Resource):
) )
@marshal_with(capture_schema) @marshal_with(capture_schema)
@doc_response(200, description="Capture successful") @doc_response(200, description="Capture successful")
@doc(tags=["foo"]) @tag("foo")
def post(self, args): def post(self, args):
""" """
Create a new capture Create a new capture

View file

@ -71,6 +71,18 @@ def marshal_task(f):
return wrapper 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): class use_args(object):
def __init__(self, schema, **kwargs): def __init__(self, schema, **kwargs):
""" """
@ -106,6 +118,21 @@ class doc(object):
return f 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): class doc_response(object):
def __init__(self, code, description=None, **kwargs): def __init__(self, code, description=None, **kwargs):
self.code = code self.code = code

View file

@ -6,7 +6,7 @@ from . import EXTENSION_NAME # TODO: Move into .names
from .names import TASK_ENDPOINT, TASK_LIST_ENDPOINT, EXTENSION_LIST_ENDPOINT from .names import TASK_ENDPOINT, TASK_LIST_ENDPOINT, EXTENSION_LIST_ENDPOINT
from .extensions import BaseExtension from .extensions import BaseExtension
from .utilities import description_from_view from .utilities import description_from_view
from .spec import view2path from .spec import view2path, get_spec
from .views.extensions import ExtensionList from .views.extensions import ExtensionList
from .views.tasks import TaskList, TaskResource from .views.tasks import TaskList, TaskResource
@ -159,20 +159,12 @@ class LabThing(object):
return "".join([part for part in parts if part]) return "".join([part for part in parts if part])
def register_property(self, resource): def register_property(self, resource):
if hasattr(resource, "endpoint"): logging.warning("register_property is deprecated. Use the @ltproperty class decorator instead.")
self.properties[resource.endpoint] = resource pass
else:
raise RuntimeError(
f"Resource {resource} has not yet been added. Cannot set as a property."
)
def register_action(self, resource): def register_action(self, resource):
if hasattr(resource, "endpoint"): logging.warning("register_action is deprecated. Use the @ltaction class decorator instead.")
self.actions[resource.endpoint] = resource pass
else:
raise RuntimeError(
f"Resource {resource} has not yet been added. Cannot set as an action."
)
def add_resource(self, resource, *urls, endpoint=None, **kwargs): def add_resource(self, resource, *urls, endpoint=None, **kwargs):
"""Adds a resource to the api. """Adds a resource to the api.
@ -208,17 +200,6 @@ class LabThing(object):
self.resources.append((resource, urls, endpoint, kwargs)) self.resources.append((resource, urls, endpoint, kwargs))
def resource(self, *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): def decorator(cls):
self.add_resource(cls, *urls, **kwargs) self.add_resource(cls, *urls, **kwargs)
return cls return cls
@ -255,6 +236,14 @@ class LabThing(object):
# Add the resource to our API spec # Add the resource to our API spec
self.spec.path(**view2path(rule, resource, self.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 ### Utilities
def url_for(self, resource, **values): def url_for(self, resource, **values):

View file

@ -20,6 +20,9 @@ def update_spec(obj, spec):
rupdate(obj.__apispec__, spec) rupdate(obj.__apispec__, spec)
return obj.__apispec__ return obj.__apispec__
def get_spec(obj):
obj.__apispec__ = obj.__dict__.get("__apispec__", {})
return obj.__apispec__
def view2path(rule: str, view: Resource, spec: APISpec): def view2path(rule: str, view: Resource, spec: APISpec):
params = { params = {