File: writing_plugins.rst

package info (click to toggle)
apispec 6.8.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 652 kB
  • sloc: python: 4,852; makefile: 150; sh: 10
file content (160 lines) | stat: -rw-r--r-- 5,073 bytes parent folder | download | duplicates (3)
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