# Credit: https://github.com/hfaran/Tornado-JSON/blob/master/tornado_json/api_doc_gen.py import re import inspect import types import itertools import tornado.web from tornado.web import RequestHandler HTTP_METHODS = ["get", "put", "post", "patch", "delete", "head", "options"] def get_api_methods(route): """Return [(pattern, method, handler_class, handle_function)] from ``route`` :type route: tuple|tornado.web.URLSpec :rtype: tuple :raises TypeError: If ``route`` is not a tuple or URLSpec """ if isinstance(route, tuple): assert len(route) >= 2 pattern, handler_class = route[:2] elif isinstance(route, tornado.web.URLSpec): pattern, handler_class = route.regex.pattern, route.handler_class else: raise TypeError("Unknown route type '{}'" .format(type(route).__name__)) methods = [] route_re = re.compile(pattern) route_params = set(list(route_re.groupindex.keys()) + ['self']) for http_method in HTTP_METHODS: method = getattr(handler_class, http_method, None) if method: method_params = set(inspect.signature(method).parameters) if method_params == route_params: methods.append((pattern, http_method, handler_class, method)) return methods def _add_indent(lines, indent=4): if isinstance(lines, str): lines = lines.splitlines() indent_line = lambda l: " "*indent + l if l else '' return "\n".join([indent_line(l) for l in lines]) def generate_api_section(url, http_method, handle_fun): api_doc = inspect.getdoc(handle_fun) doc = (".. http:{http_method}:: {url}\n\n{api_doc}".format( url=url, http_method=http_method, api_doc=_add_indent(api_doc))) return doc def get_api_docs(routes): """ Generates Sphinx formatted API documentation using method's docstring :type routes: [(url, RequestHandler), ...] :param routes: List of routes (this is ideally all possible routes of the app) :rtype: str :returns: generated GFM-formatted documentation """ apis = list(itertools.chain(*[get_api_methods(r) for r in routes])) api_docs = [] for url, http_method, handle_cls, handle_fun in apis: if issubclass(handle_cls, RequestHandler): api_docs.append(generate_api_section(url, http_method, handle_fun)) return "\n\n\n".join(api_docs) def api_doc_gen(routes): """Get and write API documentation for ``routes`` to file""" docs = get_api_docs(routes) return docs