Blackened

This commit is contained in:
jtc42 2020-01-03 00:21:16 +00:00
parent 6be8b0044a
commit ab6fa9f0e5
11 changed files with 104 additions and 80 deletions

View file

@ -169,7 +169,7 @@ atexit.register(cleanup)
if __name__ == "__main__": if __name__ == "__main__":
app.run(host="0.0.0.0", port="5000", threaded=True, debug=True, use_reloader=False) app.run(host="0.0.0.0", port="5000", threaded=True, debug=True, use_reloader=False)
#from pprint import pprint # from pprint import pprint
#pprint(labthing.spec.to_dict()) # pprint(labthing.spec.to_dict())
#with open('spec.yaml', 'w') as f: # with open('spec.yaml', 'w') as f:
# f.write(labthing.spec.to_yaml()) # f.write(labthing.spec.to_yaml())

View file

@ -1,7 +1,12 @@
from openflexure_microscope.api.utilities import get_bool, JsonResponse from openflexure_microscope.api.utilities import get_bool, JsonResponse
from openflexure_microscope.common.flask_labthings.resource import Resource from openflexure_microscope.common.flask_labthings.resource import Resource
from openflexure_microscope.common.flask_labthings.find import find_device from openflexure_microscope.common.flask_labthings.find import find_device
from openflexure_microscope.common.flask_labthings.decorators import use_args, marshal_with, doc, doc_response from openflexure_microscope.common.flask_labthings.decorators import (
use_args,
marshal_with,
doc,
doc_response,
)
from openflexure_microscope.common.flask_labthings import fields from openflexure_microscope.common.flask_labthings import fields
from openflexure_microscope.utilities import filter_dict from openflexure_microscope.utilities import filter_dict
@ -10,6 +15,7 @@ from openflexure_microscope.api.v2.views.captures import capture_schema
import logging 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"]) @doc(tags=["actions"])
class CaptureAPI(Resource): class CaptureAPI(Resource):
""" """

View file

@ -18,7 +18,9 @@ from marshmallow import pre_dump
class CaptureSchema(Schema): class CaptureSchema(Schema):
id = fields.String() id = fields.String()
file = fields.String(data_key="path", description="Path of file on microscope device") file = fields.String(
data_key="path", description="Path of file on microscope device"
)
exists = fields.Bool(data_key="available") exists = fields.Bool(data_key="available")
filename = fields.String() filename = fields.String()
metadata = fields.Dict() metadata = fields.Dict()

View file

@ -5,6 +5,7 @@ from flask import make_response
from .utilities import rupdate from .utilities import rupdate
from .spec import update_spec from .spec import update_spec
def unpack(value): def unpack(value):
"""Return a three tuple of data, code, and headers""" """Return a three tuple of data, code, and headers"""
if not isinstance(value, tuple): if not isinstance(value, tuple):
@ -93,14 +94,7 @@ class doc_response(object):
def __call__(self, f): def __call__(self, f):
# Pass params to call function attribute for external access # Pass params to call function attribute for external access
f.__apispec__ = f.__dict__.get('__apispec__', {}) f.__apispec__ = f.__dict__.get("__apispec__", {})
d = { d = {"responses": {self.code: {"description": self.description, **self.kwargs}}}
"responses": {
self.code: {
"description": self.description,
**self.kwargs
}
}
}
rupdate(f.__apispec__, d) rupdate(f.__apispec__, d)
return f return f

View file

@ -17,6 +17,7 @@ from . import EXTENSION_NAME
import logging import logging
class LabThing(object): class LabThing(object):
def __init__( def __init__(
self, self,
@ -60,32 +61,31 @@ class LabThing(object):
self.init_app(app) self.init_app(app)
@property @property
def description(self, ): def description(self,):
return self._description return self._description
@description.setter @description.setter
def description(self, description: str): def description(self, description: str):
self._description = description self._description = description
self.spec.description = description self.spec.description = description
@property @property
def title(self, ): def title(self,):
return self._title return self._title
@title.setter @title.setter
def title(self, title: str): def title(self, title: str):
self._title = title self._title = title
self.spec.title = title self.spec.title = title
@property @property
def version(self, ): def version(self,):
return str(self._version) return str(self._version)
@version.setter @version.setter
def version(self, version: str): def version(self, version: str):
self._version = version self._version = version
self.spec.version = version self.spec.version = version
### Flask stuff ### Flask stuff
@ -117,7 +117,9 @@ class LabThing(object):
# Add thing description # Add thing description
self.app.add_url_rule(self._complete_url("/td", ""), "td", self.td) self.app.add_url_rule(self._complete_url("/td", ""), "td", self.td)
# Add swagger spec # Add swagger spec
self.app.add_url_rule(self._complete_url("/swagger", ""), "swagger", self.swagger) self.app.add_url_rule(
self._complete_url("/swagger", ""), "swagger", self.swagger
)
# Add plugin overview # Add plugin overview
self.add_resource(PluginListResource, "/plugins") self.add_resource(PluginListResource, "/plugins")
@ -212,7 +214,7 @@ class LabThing(object):
if self.app is not None: if self.app is not None:
self._register_view(self.app, resource, *urls, endpoint=endpoint, **kwargs) self._register_view(self.app, resource, *urls, endpoint=endpoint, **kwargs)
self.resources.append((resource, urls, endpoint, kwargs)) self.resources.append((resource, urls, endpoint, kwargs))
def resource(self, *urls, **kwargs): def resource(self, *urls, **kwargs):
@ -261,9 +263,7 @@ class LabThing(object):
# Add the url to the application or blueprint # Add the url to the application or blueprint
app.add_url_rule(rule, view_func=resource_func, **kwargs) app.add_url_rule(rule, view_func=resource_func, **kwargs)
# Add the resource to our API spec # Add the resource to our API spec
self.spec.path( self.spec.path(**view2path(rule, resource, self.spec))
**view2path(rule, resource, self.spec)
)
### Utilities ### Utilities
@ -334,8 +334,8 @@ class LabThing(object):
"tasks": { "tasks": {
"href": self.url_for(TaskList, _external=True), "href": self.url_for(TaskList, _external=True),
**description_from_view(TaskList), **description_from_view(TaskList),
} },
} },
} }
return jsonify(rr) return jsonify(rr)
@ -344,4 +344,4 @@ class LabThing(object):
""" """
OpenAPI v3 documentation OpenAPI v3 documentation
""" """
return jsonify(self.spec.to_dict()) return jsonify(self.spec.to_dict())

View file

@ -4,6 +4,7 @@ from flask.views import MethodView
class Resource(MethodView): class Resource(MethodView):
"""Currently identical to MethodView """Currently identical to MethodView
""" """
endpoint = None endpoint = None
methods = ["get", "post", "put", "delete"] methods = ["get", "post", "put", "delete"]
@ -15,7 +16,6 @@ class Resource(MethodView):
if hasattr(self, "__apispec__"): if hasattr(self, "__apispec__"):
docs.update(self.__apispec__) docs.update(self.__apispec__)
for meth in Resource.methods: for meth in Resource.methods:
if hasattr(self, meth) and hasattr(getattr(self, meth), "__apispec__"): if hasattr(self, meth) and hasattr(getattr(self, meth), "__apispec__"):
docs["operations"][meth] = {} docs["operations"][meth] = {}

View file

@ -1,27 +1,31 @@
from .resource import Resource from .resource import Resource
from .utilities import rupdate from .utilities import rupdate
from apispec import APISpec from apispec import APISpec
from apispec.ext.marshmallow import MarshmallowPlugin from apispec.ext.marshmallow import MarshmallowPlugin
from openflexure_microscope.common.labthings_core.utilities import get_docstring, get_summary from openflexure_microscope.common.labthings_core.utilities import (
get_docstring,
get_summary,
)
from .fields import Field from .fields import Field
from marshmallow import Schema as BaseSchema from marshmallow import Schema as BaseSchema
from collections import Mapping from collections import Mapping
def update_spec(obj, spec): def update_spec(obj, spec):
obj.__apispec__ = obj.__dict__.get('__apispec__', {}) obj.__apispec__ = obj.__dict__.get("__apispec__", {})
rupdate(obj.__apispec__, spec) rupdate(obj.__apispec__, spec)
return obj.__apispec__ return obj.__apispec__
def view2path(rule: str, view: Resource, spec: APISpec): def view2path(rule: str, view: Resource, spec: APISpec):
params = { params = {
"path": rule, # TODO: Validate this slightly (leading / etc) "path": rule, # TODO: Validate this slightly (leading / etc)
"operations": view2operations(view, spec), "operations": view2operations(view, spec),
"description": get_docstring(view), "description": get_docstring(view),
"summary": get_summary(view) "summary": get_summary(view),
} }
if hasattr(view, "__apispec__"): if hasattr(view, "__apispec__"):
@ -30,6 +34,7 @@ def view2path(rule: str, view: Resource, spec: APISpec):
return params return params
def view2operations(view: Resource, spec: APISpec, populate_default: bool = True): def view2operations(view: Resource, spec: APISpec, populate_default: bool = True):
ops = {} ops = {}
for method in Resource.methods: for method in Resource.methods:
@ -39,58 +44,67 @@ def view2operations(view: Resource, spec: APISpec, populate_default: bool = True
ops[method] = { ops[method] = {
"responses": { "responses": {
200: { 200: {
"description": get_summary(getattr(view, method)) or "Success" "description": get_summary(getattr(view, method))
or "Success"
}, },
404: { 404: {"description": "Resource not found"},
"description": "Resource not found" }
}
},
} }
else: else:
ops[method] = {} ops[method] = {}
rupdate(ops[method], { rupdate(
"description": get_docstring(getattr(view, method)), ops[method],
"summary": get_summary(getattr(view, method)) {
}) "description": get_docstring(getattr(view, method)),
"summary": get_summary(getattr(view, method)),
},
)
if hasattr(getattr(view, method), "__apispec__"): if hasattr(getattr(view, method), "__apispec__"):
rupdate(ops[method], doc2operation(getattr(view, method).__apispec__, spec)) rupdate(
ops[method], doc2operation(getattr(view, method).__apispec__, spec)
)
return ops return ops
def doc2operation(apispec: dict, spec: APISpec): def doc2operation(apispec: dict, spec: APISpec):
op = {} op = {}
if "_params" in apispec: if "_params" in apispec:
rupdate(op, { rupdate(
"requestBody": { op,
"content": { {
"application/json": { "requestBody": {
"schema": convert_schema(apispec.get("_params"), spec)
}
}
}
})
if "_schema" in apispec:
rupdate(op,
{
"responses": {
200: {
"content": { "content": {
"application/json": { "application/json": {
"schema": convert_schema(apispec.get("_schema"), spec) "schema": convert_schema(apispec.get("_params"), spec)
} }
}, }
} }
} },
}) )
if "_schema" in apispec:
rupdate(
op,
{
"responses": {
200: {
"content": {
"application/json": {
"schema": convert_schema(apispec.get("_schema"), spec)
}
}
}
}
},
)
for key, val in apispec.items(): for key, val in apispec.items():
if not key in ["_params", "_schema"]: if not key in ["_params", "_schema"]:
op[key] = val op[key] = val
return op return op
@ -100,12 +114,14 @@ def convert_schema(schema, spec: APISpec):
elif isinstance(schema, Mapping): elif isinstance(schema, Mapping):
return map2properties(schema, spec) return map2properties(schema, spec)
else: else:
raise TypeError("Unsupported schema type. Ensure schema is a Schema class, or dictionary of Field objects") raise TypeError(
"Unsupported schema type. Ensure schema is a Schema class, or dictionary of Field objects"
)
def map2properties(schema, spec: APISpec): def map2properties(schema, spec: APISpec):
marshmallow_plugin = next( marshmallow_plugin = next(
plugin for plugin in spec.plugins plugin for plugin in spec.plugins if isinstance(plugin, MarshmallowPlugin)
if isinstance(plugin, MarshmallowPlugin)
) )
converter = marshmallow_plugin.converter converter = marshmallow_plugin.converter
@ -118,4 +134,4 @@ def map2properties(schema, spec: APISpec):
else: else:
d[k] = v d[k] = v
return {"properties": d} return {"properties": d}

View file

@ -1,4 +1,7 @@
from openflexure_microscope.common.labthings_core.utilities import get_docstring, get_summary from openflexure_microscope.common.labthings_core.utilities import (
get_docstring,
get_summary,
)
from .schema import Schema, marshmallow, MARSHMALLOW_VERSION_INFO from .schema import Schema, marshmallow, MARSHMALLOW_VERSION_INFO
import collections.abc import collections.abc
@ -25,4 +28,4 @@ def rupdate(d, u):
d[k] = rupdate(d.get(k, {}), v) d[k] = rupdate(d.get(k, {}), v)
else: else:
d[k] = v d[k] = v
return d return d

View file

@ -48,6 +48,7 @@ class PluginListResource(Resource):
""" """
List and basic documentation for all enabled plugins List and basic documentation for all enabled plugins
""" """
@marshal_with(PluginSchema(many=True)) @marshal_with(PluginSchema(many=True))
def get(self): def get(self):
""" """

View file

@ -39,6 +39,7 @@ class TaskList(Resource):
""" """
List and basic documentation for all session tasks List and basic documentation for all session tasks
""" """
@marshal_with(TaskSchema(many=True)) @marshal_with(TaskSchema(many=True))
def get(self): def get(self):
return tasks.tasks() return tasks.tasks()

View file

@ -5,5 +5,6 @@ def get_docstring(obj):
else: else:
return "" return ""
def get_summary(obj): def get_summary(obj):
return get_docstring(obj).partition("\n")[0].strip() return get_docstring(obj).partition("\n")[0].strip()