bottle.py (3640B)
1 """ 2 sphinxcontrib.autohttp.bottle 3 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 4 5 The sphinx.ext.autodoc-style HTTP API reference builder (from Bottle) 6 for sphinxcontrib.httpdomain. 7 8 :copyright: Copyright 2012 by Jameel Al-Aziz 9 :license: BSD, see LICENSE for details. 10 11 """ 12 13 import re 14 import six 15 16 from docutils import nodes 17 from docutils.parsers.rst import directives 18 from docutils.statemachine import ViewList 19 20 from sphinx.util import force_decode 21 from sphinx.util.compat import Directive 22 from sphinx.util.nodes import nested_parse_with_titles 23 from sphinx.util.docstrings import prepare_docstring 24 from sphinx.pycode import ModuleAnalyzer 25 26 from sphinxcontrib import httpdomain 27 from sphinxcontrib.autohttp.common import http_directive, import_object 28 29 30 def translate_bottle_rule(app, rule): 31 buf = six.StringIO() 32 if hasattr(app.router, "parse_rule"): 33 iterator = app.router.parse_rule(rule) # bottle 0.11 34 else: 35 iterator = app.router._itertokens(rule) # bottle 0.12 36 for name, filter, conf in iterator: 37 if filter: 38 buf.write('(') 39 buf.write(name) 40 if (filter != app.router.default_filter and filter != 'default')\ 41 or conf: 42 buf.write(':') 43 buf.write(filter) 44 if conf: 45 buf.write(':') 46 buf.write(conf) 47 buf.write(')') 48 else: 49 buf.write(name) 50 return buf.getvalue() 51 52 53 def get_routes(app): 54 for route in app.routes: 55 path = translate_bottle_rule(app, route.rule) 56 yield route.method, path, route 57 58 59 class AutobottleDirective(Directive): 60 61 has_content = True 62 required_arguments = 1 63 option_spec = {'endpoints': directives.unchanged, 64 'undoc-endpoints': directives.unchanged, 65 'include-empty-docstring': directives.unchanged} 66 67 @property 68 def endpoints(self): 69 endpoints = self.options.get('endpoints', None) 70 if not endpoints: 71 return None 72 return frozenset(re.split(r'\s*,\s*', endpoints)) 73 74 @property 75 def undoc_endpoints(self): 76 undoc_endpoints = self.options.get('undoc-endpoints', None) 77 if not undoc_endpoints: 78 return frozenset() 79 return frozenset(re.split(r'\s*,\s*', undoc_endpoints)) 80 81 def make_rst(self): 82 app = import_object(self.arguments[0]) 83 for method, path, target in get_routes(app): 84 endpoint = target.name or target.callback.__name__ 85 if self.endpoints and endpoint not in self.endpoints: 86 continue 87 if endpoint in self.undoc_endpoints: 88 continue 89 view = target.callback 90 docstring = view.__doc__ or '' 91 if not isinstance(docstring, six.text_type): 92 analyzer = ModuleAnalyzer.for_module(view.__module__) 93 docstring = force_decode(docstring, analyzer.encoding) 94 if not docstring and 'include-empty-docstring' not in self.options: 95 continue 96 docstring = prepare_docstring(docstring) 97 for line in http_directive(method, path, docstring): 98 yield line 99 100 def run(self): 101 node = nodes.section() 102 node.document = self.state.document 103 result = ViewList() 104 for line in self.make_rst(): 105 result.append(line, '<autobottle>') 106 nested_parse_with_titles(self.state, result, node) 107 return node.children 108 109 110 def setup(app): 111 if 'http' not in app.domains: 112 httpdomain.setup(app) 113 app.add_directive('autobottle', AutobottleDirective) 114