"""
mark
====
Mark flask view with swagger spec.
"""
from marshmallow import Schema as MaSchema
from . import core, ext
from .utils import merge, normalize_indent, compose, get_type_base
[docs]class Mark(object):
"""
Utility that marks flask view with swagger spec.
You can add fields to view's operation object using this class. ::
mark = Mark()
@app.route('/users/')
@mark({
'summary': "List users.",
'produces': ['application/json'],
})
def index():
return jsonify(get_users())
The mark will be saved to `_swag` property of view function.
So the `@mark` decorator should be located right under the `@app.route`.
You can also use convinience methods like :meth:`responses`,
:meth:`summary`, ... ::
@app.route('/users/', methods=['POST'])
@mark.summary("Create a new user")
def create():
pass
You can remove some field from spec by using :meth:`unmark` ::
@app.route('/users/<int:user_id')
@mark.unmark('description')
@mark({
'summary': "Read a user",
'description': "Wooah"
})
def read(user_id):
pass
"""
def __init__(self):
super().__init__()
def __call__(self, spec):
return self.swag(spec)
[docs] def get_swag(self, fn):
if not hasattr(fn, '_swag'):
fn._swag = {}
return fn._swag
[docs] def set_swag(self, fn, swag):
fn._swag = swag
[docs] def update_swag(self, fn, swag):
self.get_swag(fn).update(swag)
return self.get_swag(fn)
[docs] def merge_swag(self, fn, swag):
self.set_swag(fn, merge(self.get_swag(fn), swag))
[docs] def swag(self, spec):
def decorator(fn):
self.update_swag(fn, spec)
return fn
return decorator
[docs] def summary(self, summary: str):
"""Mark summary to view."""
return self.swag({
'summary': summary
})
[docs] def description(self, description: str):
"""Mark description to view."""
return self.swag({
'description': normalize_indent(description),
})
[docs] def description_from_docstring(self, fn):
"""Mark description from docstring,"""
docstring = getattr(fn, '__doc__', None) or ''
description = normalize_indent(docstring)
return self.description(description)(fn)
[docs] def summary_from_docstring(self, fn):
"""Mark summary from docstring."""
docstring = getattr(fn, '__doc__', None) or ''
description = normalize_indent(docstring)
summary = description.split('\n', 1)[0].strip()[:120]
return self.summary(summary)(fn)
[docs] def parameter(self, parameter):
"""Mark parameter to view"""
def decorator(fn):
swag = self.get_swag(fn)
swag.setdefault('parameters', []).append(
core.Parameter(**parameter))
self.set_swag(fn, swag)
return fn
return decorator
[docs] def schema(self, schema, in_='formData'):
"""Convert json schema to parameter and mark it to view."""
parameters = core.parameters_from_object_schema(schema, in_=in_)
return compose(*map(self.parameter, parameters))
[docs] def response(self, status, response_or_description, schema=None,
headers=None):
"""Mark response field for view to view.
There are two ways to use this decorator.
First, you can pass response object directly ::
@app.route('/users/<user_id>')
@mark.response(200, {
'description': "Target user.",
'schema': {
'properties': {
'name': {'type': 'string'},
},
}
})
def user_read(user_id):
...
Or you can pass each field of response. ::
@app.route('/post/<post_id>')
@mark.response(200, "Target post", post_schema, read_headers)
def post_read(post_id):
...
`schema` can be marshmallow's :class:`~marshmallow.Schema` if
`marshmallow_jsonschema` is installed.
"""
if isinstance(response_or_description, str):
description = response_or_description
response = {
'description': description,
}
if schema is not None:
response['schema'] = schema
if headers is not None:
response['headers'] = headers
else:
response = response_or_description
if isinstance(response.get('schema', None), MaSchema):
response['schema'] = ext.dump_marshmallow(response['schema'])
def decorator(fn):
swag = self.get_swag(fn)
swag.setdefault('responses', {})[status] = core.Response(
**response)
self.set_swag(fn, swag)
return fn
return decorator
[docs] def simple_param(self, in_, name, python_type, optional=False, **kwargs):
"""
Mark parameter with python type that can be converted by
:func:`~.utils.get_type_base`
"""
required = not optional
params = (get_type_base(python_type) or {}).copy()
params.update(
name=name,
in_=in_,
required=required,
)
params.update(kwargs)
return self.parameter(core.Parameter(**params))
[docs] def query(self, name, python_type, optional=False, **kwargs):
"""Mark simple query parameter."""
return self.simple_param('query', name, python_type, optional=optional,
**kwargs)
[docs] def marshmallow(self, formencode_schema, in_='formData'):
"""Mark marshmallow schema as parameter."""
return self.schema(ext.dump_marshmallow(formencode_schema), in_=in_)