From 9b387985e3b3a4af43b2c27066d3cc533db00ad2 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Wed, 20 Nov 2019 12:08:25 +0000 Subject: [PATCH] Use more docstrings as descriptions --- openflexure_microscope/api/utilities.py | 8 +++- .../api/v2/blueprints/actions/__init__.py | 13 +++-- .../api/v2/blueprints/actions/camera.py | 9 ---- .../api/v2/blueprints/actions/stage.py | 3 -- .../api/v2/blueprints/actions/system.py | 6 --- .../api/v2/blueprints/captures.py | 8 +++- .../api/v2/blueprints/plugins.py | 17 ++++--- .../api/v2/blueprints/root.py | 48 +++++++++---------- .../api/v2/blueprints/settings.py | 7 ++- .../api/v2/blueprints/status.py | 8 +++- .../api/v2/blueprints/stream.py | 3 +- openflexure_microscope/utilities.py | 17 +++++++ 12 files changed, 88 insertions(+), 59 deletions(-) diff --git a/openflexure_microscope/api/utilities.py b/openflexure_microscope/api/utilities.py index 60d490db..ce8c1b1d 100644 --- a/openflexure_microscope/api/utilities.py +++ b/openflexure_microscope/api/utilities.py @@ -1,7 +1,13 @@ import logging from werkzeug.exceptions import BadRequest -from flask import url_for +from flask import url_for, Blueprint +def blueprint_for_module(module_name, api_ver=2, suffix=""): + return Blueprint(blueprint_name_for_module(module_name, api_ver=api_ver, suffix=suffix), module_name) + +def blueprint_name_for_module(module_name, api_ver=2, suffix=""): + bp_name = module_name.split('.')[-1] + return f"v{api_ver}_{bp_name}_blueprint{suffix}" class JsonResponse: def __init__(self, request): diff --git a/openflexure_microscope/api/v2/blueprints/actions/__init__.py b/openflexure_microscope/api/v2/blueprints/actions/__init__.py index 3d275734..e78f9d85 100644 --- a/openflexure_microscope/api/v2/blueprints/actions/__init__.py +++ b/openflexure_microscope/api/v2/blueprints/actions/__init__.py @@ -1,12 +1,16 @@ +""" +Top-level representation of enabled actions +""" + from flask import Blueprint, url_for, jsonify from sys import platform -from openflexure_microscope.utilities import get_docstring +from openflexure_microscope.api.utilities import blueprint_for_module +from openflexure_microscope.utilities import get_docstring, description_from_view from openflexure_microscope.api.views import MicroscopeView from . import camera, stage, system -# TODO: Could allowed methods be calculated automatically by looking at what methods exist? _actions = { "capture": { "rule": "/camera/capture/", @@ -53,11 +57,12 @@ def actions_representation(): for name, action in enabled_actions().items(): d = { "links": {"self": url_for(f".{name}")}, - "description": get_docstring(action["view_class"]), "rule": action["rule"], "view_class": str(action["view_class"]), } + d.update(description_from_view(action["view_class"])) + actions[name] = d return actions @@ -69,7 +74,7 @@ class ActionsAPI(MicroscopeView): def construct_blueprint(microscope_obj): global _actions - blueprint = Blueprint("v2_actions_blueprint", __name__) + blueprint = blueprint_for_module(__name__) # For each enabled action route defined in our dictionary above for name, action in enabled_actions().items(): diff --git a/openflexure_microscope/api/v2/blueprints/actions/camera.py b/openflexure_microscope/api/v2/blueprints/actions/camera.py index 80440e39..5e199eb1 100644 --- a/openflexure_microscope/api/v2/blueprints/actions/camera.py +++ b/openflexure_microscope/api/v2/blueprints/actions/camera.py @@ -9,9 +9,6 @@ from flask import jsonify, request, abort, url_for, redirect, send_file class CaptureAPI(MicroscopeView): """ Create a new image capture. - - Allowed methods: - POST """ def post(self): """ @@ -109,9 +106,6 @@ class CaptureAPI(MicroscopeView): class GPUPreviewStartAPI(MicroscopeView): """ Start the onboard GPU preview. - - Allowed methods: - POST """ def post(self, operation): """ @@ -157,9 +151,6 @@ class GPUPreviewStartAPI(MicroscopeView): class GPUPreviewStopAPI(MicroscopeView): """ Stop the onboard GPU preview. - - Allowed methods: - POST """ def post(self, operation): """ diff --git a/openflexure_microscope/api/v2/blueprints/actions/stage.py b/openflexure_microscope/api/v2/blueprints/actions/stage.py index 2afdd6b7..3294011d 100644 --- a/openflexure_microscope/api/v2/blueprints/actions/stage.py +++ b/openflexure_microscope/api/v2/blueprints/actions/stage.py @@ -10,9 +10,6 @@ import logging class MoveStageAPI(MicroscopeView): """ Handle stage movements. - - Allowed methods: - POST """ def post(self): """ diff --git a/openflexure_microscope/api/v2/blueprints/actions/system.py b/openflexure_microscope/api/v2/blueprints/actions/system.py index a82aeaec..596144d6 100644 --- a/openflexure_microscope/api/v2/blueprints/actions/system.py +++ b/openflexure_microscope/api/v2/blueprints/actions/system.py @@ -6,9 +6,6 @@ import subprocess class ShutdownAPI(MicroscopeView): """ Attempt to shutdown the device - - Allowed methods: - POST """ def post(self): """ @@ -25,9 +22,6 @@ class ShutdownAPI(MicroscopeView): class RebootAPI(MicroscopeView): """ Attempt to reboot the device - - Allowed methods: - POST """ def post(self): """ diff --git a/openflexure_microscope/api/v2/blueprints/captures.py b/openflexure_microscope/api/v2/blueprints/captures.py index 0a54e69a..e50ce2e2 100644 --- a/openflexure_microscope/api/v2/blueprints/captures.py +++ b/openflexure_microscope/api/v2/blueprints/captures.py @@ -1,6 +1,11 @@ +""" +Top-level representation of all acquired captures +""" + from openflexure_microscope.api.utilities import get_bool, JsonResponse from openflexure_microscope.api.views import MicroscopeView from openflexure_microscope.utilities import filter_dict +from openflexure_microscope.api.utilities import blueprint_for_module from flask import jsonify, request, abort, url_for, redirect, send_file, Blueprint @@ -378,7 +383,8 @@ class TagsAPI(MicroscopeView): def construct_blueprint(microscope_obj): - blueprint = Blueprint("v2_captures_blueprint", __name__) + + blueprint = blueprint_for_module(__name__) # Tag routes blueprint.add_url_rule( diff --git a/openflexure_microscope/api/v2/blueprints/plugins.py b/openflexure_microscope/api/v2/blueprints/plugins.py index 70e80e49..5b5b8ff6 100644 --- a/openflexure_microscope/api/v2/blueprints/plugins.py +++ b/openflexure_microscope/api/v2/blueprints/plugins.py @@ -1,6 +1,11 @@ +""" +Top-level representation of attached and enabled plugins +""" + from openflexure_microscope.plugins import PluginLoader, MicroscopePlugin from openflexure_microscope.api.views import MicroscopeViewPlugin -from openflexure_microscope.utilities import get_docstring +from openflexure_microscope.utilities import get_docstring, description_from_view +from openflexure_microscope.api.utilities import blueprint_for_module from flask import Blueprint, jsonify, url_for from openflexure_microscope.api.views import MicroscopeView @@ -9,7 +14,6 @@ import copy import logging import warnings - def plugins_representation(plugin_loader_object: PluginLoader): """ Generate a dictionary representation of all plugins, including Flask route URLs @@ -28,7 +32,7 @@ def plugins_representation(plugin_loader_object: PluginLoader): "python_name": plugin._name_python_safe, "plugin": str(plugin), "views": {}, - "form": plugin.form, + "gui": plugin.gui, "description": get_docstring(plugin) } @@ -37,10 +41,11 @@ def plugins_representation(plugin_loader_object: PluginLoader): uri = url_for(f"v2_plugins_blueprint.{view_id}") # Make links dictionary if it doesn't yet exist view_d = { - "description": get_docstring(view_data["view"]), "links": {"self": uri} } + view_d.update(description_from_view(view_data["view"])) + d["views"][view_id] = view_d plugins[plugin._name] = d @@ -67,15 +72,13 @@ class PluginFormAPI(MicroscopeView): def construct_blueprint(microscope_obj): - blueprint = Blueprint("v2_plugins_blueprint", __name__) + blueprint = blueprint_for_module(__name__) # Create a base route to return plugin API forms, if any exist blueprint.add_url_rule( "/", view_func=PluginFormAPI.as_view("plugins", microscope=microscope_obj) ) - all_routes = [] - # For each plugin attached to the microscope object for plugin in microscope_obj.plugins.active: diff --git a/openflexure_microscope/api/v2/blueprints/root.py b/openflexure_microscope/api/v2/blueprints/root.py index 6106ad86..17b2773c 100644 --- a/openflexure_microscope/api/v2/blueprints/root.py +++ b/openflexure_microscope/api/v2/blueprints/root.py @@ -2,29 +2,29 @@ from flask import Blueprint, jsonify, url_for from openflexure_microscope import Microscope from openflexure_microscope.api.views import MicroscopeView -def root_representation(microscope_obj: Microscope): - d = { - "settings": { - "description": "Writeable settings for the microscope, and attached hardware", - "links": {"self": url_for("v2_settings_blueprint.settings")} - }, - "status": { - "description": "Read-only status of the microscope, and attached hardware info", - "links": {"self": url_for("v2_status_blueprint.status")} - }, - "plugins": { - "description": "Top-level representation of attached and enabled plugins", - "links": {"self": url_for("v2_plugins_blueprint.plugins")} - }, - "captures": { - "description": "Top-level representation of all acquired captures", - "links": {"self": url_for("v2_captures_blueprint.captures")} - }, - "actions": { - "description": "Top-level representation of enabled actions", - "links": {"self": url_for("v2_actions_blueprint.actions")} - }, - } +from openflexure_microscope.utilities import get_docstring, bottom_level_name +from openflexure_microscope.api.utilities import blueprint_name_for_module +from openflexure_microscope.api.v2.blueprints import settings, status, plugins, captures, actions, stream + +# List of submodules containing create_blueprint methods using standard blueprint_for_module naming +_root_blueprint_modules = [settings, status, plugins, captures, actions, stream] + +def root_representation(): + """ + Generate a dictionar representation of all top-level blueprint rules + """ + global _root_blueprint_modules + d = {} + + for blueprint_module in _root_blueprint_modules: + module_short_name = bottom_level_name(blueprint_module) + blueprint_name = blueprint_name_for_module(blueprint_module.__name__) + + d[module_short_name] = { + "name": blueprint_module.__name__, + "description": get_docstring(blueprint_module), + "links": {"self": url_for(f"{blueprint_name}.{module_short_name}")} + } return d @@ -32,7 +32,7 @@ def root_representation(microscope_obj: Microscope): class RootAPI(MicroscopeView): def get(self): - return jsonify(root_representation(self.microscope)) + return jsonify(root_representation()) def construct_blueprint(microscope_obj): diff --git a/openflexure_microscope/api/v2/blueprints/settings.py b/openflexure_microscope/api/v2/blueprints/settings.py index 972785d3..4d14fd13 100644 --- a/openflexure_microscope/api/v2/blueprints/settings.py +++ b/openflexure_microscope/api/v2/blueprints/settings.py @@ -1,5 +1,10 @@ +""" +Writeable settings for the microscope, and attached hardware +""" + from openflexure_microscope.api.utilities import gen, JsonResponse from openflexure_microscope.api.views import MicroscopeView +from openflexure_microscope.api.utilities import blueprint_for_module from flask import Blueprint, jsonify, request import logging @@ -144,7 +149,7 @@ class SettingsAPI(MicroscopeView): def construct_blueprint(microscope_obj): - blueprint = Blueprint("v2_settings_blueprint", __name__) + blueprint = blueprint_for_module(__name__) blueprint.add_url_rule( "/", view_func=SettingsAPI.as_view("settings", microscope=microscope_obj) diff --git a/openflexure_microscope/api/v2/blueprints/status.py b/openflexure_microscope/api/v2/blueprints/status.py index 48e59602..ac63d35a 100644 --- a/openflexure_microscope/api/v2/blueprints/status.py +++ b/openflexure_microscope/api/v2/blueprints/status.py @@ -1,8 +1,12 @@ +""" +Read-only status of the microscope, and attached hardware info +""" + from openflexure_microscope.api.views import MicroscopeView +from openflexure_microscope.api.utilities import blueprint_for_module from flask import Blueprint, jsonify - class StatusAPI(MicroscopeView): def get(self): """ @@ -55,7 +59,7 @@ class StatusAPI(MicroscopeView): def construct_blueprint(microscope_obj): - blueprint = Blueprint("v2_status_blueprint", __name__) + blueprint = blueprint_for_module(__name__) blueprint.add_url_rule( "/", view_func=StatusAPI.as_view("status", microscope=microscope_obj) diff --git a/openflexure_microscope/api/v2/blueprints/stream.py b/openflexure_microscope/api/v2/blueprints/stream.py index a6f40a42..a79d2419 100644 --- a/openflexure_microscope/api/v2/blueprints/stream.py +++ b/openflexure_microscope/api/v2/blueprints/stream.py @@ -1,5 +1,6 @@ from openflexure_microscope.api.utilities import gen, JsonResponse from openflexure_microscope.api.views import MicroscopeView +from openflexure_microscope.api.utilities import blueprint_for_module from flask import Response, Blueprint, jsonify, request @@ -43,7 +44,7 @@ class SnapshotAPI(MicroscopeView): def construct_blueprint(microscope_obj): - blueprint = Blueprint("v2_stream_blueprint", __name__) + blueprint = blueprint_for_module(__name__) blueprint.add_url_rule( "/stream", view_func=StreamAPI.as_view("stream", microscope=microscope_obj) diff --git a/openflexure_microscope/utilities.py b/openflexure_microscope/utilities.py index c46494c4..fa194fc1 100644 --- a/openflexure_microscope/utilities.py +++ b/openflexure_microscope/utilities.py @@ -5,6 +5,23 @@ from collections import abc from functools import reduce from contextlib import contextmanager +def bottom_level_name(obj): + return obj.__name__.split('.')[-1] + +def description_from_view(view_class): + methods = [] + for method_key in ["get", "post", "put", "delete"]: + if hasattr(view_class, method_key): + methods.append(method_key.upper()) + brief_description = get_docstring(view_class).partition('\n')[0].strip() + + d = { + "methods": methods, + "description": brief_description + } + + return d + def get_docstring(obj): ds = obj.__doc__ if ds: