1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
Writing Plugins
===============
A plugins is a subclass of `apispec.plugin.BasePlugin`.
Helper Methods
--------------
Plugins provide "helper" methods that augment the behavior of `apispec.APISpec` methods.
There are five types of helper methods:
* Schema helpers
* Parameter helpers
* Response helpers
* Path helpers
* Operation helpers
Helper functions modify `apispec.APISpec` methods. For example, path helpers modify `apispec.APISpec.path`.
A plugin with a path helper function may look something like this:
.. code-block:: python
from apispec import BasePlugin
from apispec.yaml_utils import load_operations_from_docstring
class MyPlugin(BasePlugin):
def path_helper(self, path, operations, func, **kwargs):
"""Path helper that parses docstrings for operations. Adds a
``func`` parameter to `apispec.APISpec.path`.
"""
operations.update(load_operations_from_docstring(func.__doc__))
All plugin helpers must accept extra `**kwargs`, allowing custom plugins to define new arguments if required.
A plugin with an operation helper that adds `deprecated` flag may look like this
.. code-block:: python
# deprecated_plugin.py
from apispec import BasePlugin
from apispec.yaml_utils import load_operations_from_docstring
class DeprecatedPlugin(BasePlugin):
def operation_helper(self, path, operations, **kwargs):
"""Operation helper that add `deprecated` flag if in `kwargs`"""
if kwargs.pop("deprecated", False) is True:
for key, value in operations.items():
value["deprecated"] = True
Using this plugin
.. code-block:: python
import json
from apispec import APISpec
from deprecated_plugin import DeprecatedPlugin
spec = APISpec(
title="Gisty",
version="1.0.0",
openapi_version="3.0.2",
plugins=[DeprecatedPlugin()],
)
# path will call operation_helper on operations
spec.path(
path="/gists/{gist_id}",
operations={"get": {"responses": {"200": {"description": "standard response"}}}},
deprecated=True,
)
print(json.dumps(spec.to_dict()["paths"]))
# {"/gists/{gist_id}": {"get": {"responses": {"200": {"description": "standard response"}}, "deprecated": true}}}
The ``init_spec`` Method
------------------------
`BasePlugin` has an `init_spec` method that `APISpec` calls on each plugin at initialization with the spec object itself as parameter. It is no-op by default, but a plugin may override it to access and store useful information on the spec object.
A typical use case is conditional code depending on the OpenAPI version, which is stored as ``openapi_version`` on the `spec` object. See source code for `apispec.ext.marshmallow.MarshmallowPlugin </_modules/apispec/ext/marshmallow.html>`_ for an example.
Example: Docstring-parsing Plugin
---------------------------------
Here's a plugin example involving conditional processing depending on the OpenAPI version:
.. code-block:: python
# docplugin.py
from apispec import BasePlugin
from apispec.yaml_utils import load_operations_from_docstring
class DocPlugin(BasePlugin):
def init_spec(self, spec):
super(DocPlugin, self).init_spec(spec)
self.openapi_major_version = spec.openapi_version.major
def operation_helper(self, operations, func, **kwargs):
"""Operation helper that parses docstrings for operations. Adds a
``func`` parameter to `apispec.APISpec.path`.
"""
doc_operations = load_operations_from_docstring(func.__doc__)
# Apply conditional processing
if self.openapi_major_version < 3:
"...Mutating doc_operations for OpenAPI v2..."
else:
"...Mutating doc_operations for OpenAPI v3+..."
operations.update(doc_operations)
To use the plugin:
.. code-block:: python
from apispec import APISpec
from docplugin import DocPlugin
spec = APISpec(
title="Gisty", version="1.0.0", openapi_version="3.0.2", plugins=[DocPlugin()]
)
def gist_detail(gist_id):
"""Gist detail view.
---
get:
responses:
200:
content:
application/json:
schema: '#/definitions/Gist'
"""
pass
spec.path(path="/gists/{gist_id}", func=gist_detail)
print(dict(spec.to_dict()["paths"]))
# {'/gists/{gist_id}': {'get': {'responses': {200: {'content': {'application/json': {'schema': '#/definitions/Gist'}}}}}}}
Next Steps
----------
To learn more about how to write plugins:
* Consult the :doc:`Core API docs <api_core>` for `BasePlugin <apispec.BasePlugin>`
* View the source for an existing apispec plugin, e.g. `FlaskPlugin <https://github.com/marshmallow-code/apispec-webframeworks/blob/master/src/apispec_webframeworks/flask.py>`_.
* Check out some projects using apispec: https://github.com/marshmallow-code/apispec/wiki/Ecosystem
|