Blackened
This commit is contained in:
parent
6be8b0044a
commit
ab6fa9f0e5
11 changed files with 104 additions and 80 deletions
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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] = {}
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue