frosix

Multiparty signature service (experimental)
Log | Files | Refs | README | LICENSE

tornado.py (4083B)


      1 """
      2     sphinxcontrib.autohttp.tornado
      3     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      4 
      5     The sphinx.ext.autodoc-style HTTP API reference builder (from Tornado)
      6     for sphinxcontrib.httpdomain.
      7 
      8     :copyright: Copyright 2013 by Rodrigo Machado
      9     :license: BSD, see LICENSE for details.
     10 
     11 """
     12 
     13 import inspect
     14 import re
     15 import six
     16 
     17 from docutils import nodes
     18 from docutils.parsers.rst import directives
     19 from docutils.statemachine import ViewList
     20 
     21 from sphinx.util import force_decode
     22 from sphinx.util.compat import Directive
     23 from sphinx.util.nodes import nested_parse_with_titles
     24 from sphinx.util.docstrings import prepare_docstring
     25 from sphinx.pycode import ModuleAnalyzer
     26 
     27 from sphinxcontrib import httpdomain
     28 from sphinxcontrib.autohttp.common import http_directive, import_object
     29 
     30 
     31 def translate_tornado_rule(app, rule):
     32     buf = six.StringIO()
     33     for name, filter, conf in app.router.parse_rule(rule):
     34         if filter:
     35             buf.write('(')
     36             buf.write(name)
     37             if filter != app.router.default_filter or conf:
     38                 buf.write(':')
     39                 buf.write(filter)
     40             if conf:
     41                 buf.write(':')
     42                 buf.write(conf)
     43             buf.write(')')
     44         else:
     45             buf.write(name)
     46     return buf.getvalue()
     47 
     48 
     49 def get_routes(app):
     50     for spec in app.handlers[0][1]:
     51         handler = spec.handler_class
     52         doc_methods = list(handler.SUPPORTED_METHODS)
     53         if 'HEAD' in doc_methods:
     54             doc_methods.remove('HEAD')
     55         if 'OPTIONS' in doc_methods:
     56             doc_methods.remove('OPTIONS')
     57 
     58         for method in doc_methods:
     59             maybe_method = getattr(handler, method.lower(), None)
     60             if (inspect.isfunction(maybe_method) or
     61                     inspect.ismethod(maybe_method)):
     62                 yield method.lower(), spec.regex.pattern, handler
     63 
     64 
     65 def normalize_path(path):
     66     if path.endswith('$'):
     67         path = path[:-1]
     68     return path
     69 
     70 
     71 class AutoTornadoDirective(Directive):
     72 
     73     has_content = True
     74     required_arguments = 1
     75     option_spec = {'endpoints': directives.unchanged,
     76                    'undoc-endpoints': directives.unchanged,
     77                    'include-empty-docstring': directives.unchanged}
     78 
     79     @property
     80     def endpoints(self):
     81         endpoints = self.options.get('endpoints', None)
     82         if not endpoints:
     83             return None
     84         return frozenset(re.split(r'\s*,\s*', endpoints))
     85 
     86     @property
     87     def undoc_endpoints(self):
     88         undoc_endpoints = self.options.get('undoc-endpoints', None)
     89         if not undoc_endpoints:
     90             return frozenset()
     91         return frozenset(re.split(r'\s*,\s*', undoc_endpoints))
     92 
     93     def make_rst(self):
     94         app = import_object(self.arguments[0])
     95         for method, path, handler in get_routes(app):
     96             class_name = handler.__name__
     97             method_name = getattr(handler, method).__name__
     98             endpoint = '.'.join((class_name, method_name))
     99 
    100             if self.endpoints and endpoint not in self.endpoints:
    101                 continue
    102             if endpoint in self.undoc_endpoints:
    103                 continue
    104 
    105             docstring = getattr(handler, method).__doc__ or ''
    106             #if not isinstance(docstring, unicode):
    107             #    analyzer = ModuleAnalyzer.for_module(view.__module__)
    108             #    docstring = force_decode(docstring, analyzer.encoding)
    109             if not docstring and 'include-empty-docstring' not in self.options:
    110                 continue
    111             docstring = prepare_docstring(docstring)
    112             for line in http_directive(method, normalize_path(path), docstring):
    113                 yield line
    114 
    115     def run(self):
    116         node = nodes.section()
    117         node.document = self.state.document
    118         result = ViewList()
    119         for line in self.make_rst():
    120             result.append(line, '<autotornado>')
    121         nested_parse_with_titles(self.state, result, node)
    122         return node.children
    123 
    124 
    125 def setup(app):
    126     if 'http' not in app.domains:
    127         httpdomain.setup(app)
    128     app.add_directive('autotornado', AutoTornadoDirective)