Sagi Dayan
278a39710a
to implement this generator in every Path you want to document add the following decorator: @auto.doc()
126 lines
No EOL
4.6 KiB
Python
126 lines
No EOL
4.6 KiB
Python
import os
|
|
import re
|
|
from collections import defaultdict
|
|
from flask import current_app, render_template, render_template_string
|
|
from jinja2 import evalcontextfilter
|
|
|
|
|
|
try:
|
|
from flask import _app_ctx_stack as stack
|
|
except ImportError:
|
|
from flask import _request_ctx_stack as stack
|
|
|
|
|
|
class Autodoc(object):
|
|
|
|
def __init__(self, app=None):
|
|
self.app = app
|
|
self.groups = defaultdict(set)
|
|
if app is not None:
|
|
self.init_app(app)
|
|
|
|
def init_app(self, app):
|
|
if hasattr(app, 'teardown_appcontext'):
|
|
app.teardown_appcontext(self.teardown)
|
|
else:
|
|
app.teardown_request(self.teardown)
|
|
self.add_custom_template_filters(app)
|
|
|
|
def teardown(self, exception):
|
|
ctx = stack.top
|
|
|
|
def add_custom_template_filters(self, app):
|
|
"""Add custom filters to jinja2 templating engine"""
|
|
self.add_custom_nl2br_filters(app)
|
|
|
|
def add_custom_nl2br_filters(self, app):
|
|
"""Add a custom filter nl2br to jinja2
|
|
Replaces all newline to <BR>
|
|
"""
|
|
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){3,}')
|
|
@app.template_filter()
|
|
@evalcontextfilter
|
|
def nl2br(eval_ctx, value):
|
|
result = u'\n\n'.join(u'%s' % p.replace('\n', '<br>\n') for p in _paragraph_re.split(value))
|
|
return result
|
|
|
|
def doc(self, group=None, aa=None, groups=None):
|
|
"""Decorator to add flask route to autodoc for automatic documentation\
|
|
|
|
Any route decorated with this method will be added to the list of routes to be documented by the generate() or
|
|
html() methods.
|
|
|
|
By default, the route is added to the 'all' group.
|
|
By specifying group or groups argument, the route can be added to one or multiple other groups as well, besides
|
|
the 'all' group.
|
|
"""
|
|
def decorator(f):
|
|
if groups:
|
|
groupset = set(groups)
|
|
else:
|
|
groupset = set()
|
|
if group:
|
|
groupset.add(group)
|
|
groupset.add("all")
|
|
for g in groupset:
|
|
self.groups[g].add(f)
|
|
return f
|
|
return decorator
|
|
|
|
def generate(self, group="all", groups=[], sort=None):
|
|
"""Returns a list of dict describing the routes specified by the doc() method
|
|
|
|
Each dict contains:
|
|
- methods: the set of allowed methods (ie ['GET', 'POST'])
|
|
- rule: relative url (ie '/user/<int:id>')
|
|
- endpoint: function name (ie 'show_user')
|
|
- doc: docstring of the function
|
|
- args: function arguments
|
|
- defaults: defaults values for the arguments
|
|
|
|
By specifying the group or groups arguments, only routes belonging to those groups will be returned.
|
|
|
|
Routes are sorted alphabetically based on the rule.
|
|
"""
|
|
links = []
|
|
for rule in current_app.url_map.iter_rules():
|
|
|
|
if rule.endpoint == 'static':
|
|
continue
|
|
|
|
func = current_app.view_functions[rule.endpoint]
|
|
|
|
if (groups and [True for g in groups if func in self.groups[g]]) or \
|
|
(not groups and func in self.groups[group]):
|
|
links.append(
|
|
dict(
|
|
methods = rule.methods,
|
|
rule = "%s" % rule,
|
|
endpoint = rule.endpoint,
|
|
docstring = func.__doc__,
|
|
args = list(func.func_code.co_varnames),
|
|
defaults = rule.defaults
|
|
)
|
|
)
|
|
if sort:
|
|
return sort(links)
|
|
else:
|
|
return sorted(links, cmp=lambda x,y: cmp(x['rule'], y['rule']))
|
|
|
|
def html(self, template=None, group="all", groups=None, **context):
|
|
"""Returns an html string of the routes specified by the doc() method
|
|
|
|
A template can be specified. A list of routes is available under the 'autodoc' value (refer to the documentation
|
|
for the generate() for a description of available values). If no template is specified, a default template is
|
|
used.
|
|
|
|
By specifying the group or groups arguments, only routes belonging to those groups will be returned.
|
|
"""
|
|
if template:
|
|
return render_template(template, autodoc=self.generate(group), **context)
|
|
else:
|
|
filename = os.path.dirname(__file__)+"/templates/autodoc_default.html"
|
|
with open(filename) as file:
|
|
content = file.read()
|
|
with current_app.app_context():
|
|
return render_template_string(content, autodoc=self.generate(group=group, groups=groups), **context) |