Commits

Hong Minhee committed 05274cb Merge

Comments (0)

Files changed (7)

httpdomain/doc/autobottle_sampleapp.py

+from bottle import route, default_app
+
+
+@route('/')
+def home():
+    """Home page."""
+    return 'home'
+
+
+@route('/<user>')
+def user(user):
+    """User profile page.
+
+    :param user: user login name
+    :status 200: when user exists
+    :status 404: when user doesn't exist
+
+    """
+    return 'hi, ' + user
+
+
+@route('/<user>/posts/<post_id:int>')
+def post(user, post_id):
+    """User's post.
+
+    :param user: user login name
+    :param post_id: post unique id
+    :status 200: when user and post exists
+    :status 404: when user and post doesn't exist
+
+    """
+    return str(post_id), 'by', user
+
+
+app = default_app()

httpdomain/doc/conf.py

 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = ['sphinxcontrib.httpdomain', 'sphinxcontrib.autohttp.flask',
+              'sphinxcontrib.autohttp.bottle',
               'sphinx.ext.extlinks']
 
 # Add any paths that contain templates here, relative to this directory.

httpdomain/doc/index.rst

    If your webapp is powered by Flask_? See :mod:`sphinxcontrib.autohttp.flask`
    also.
 
+   Or
+
+   If your webapp is powered by Bottle_? See :mod:`sphinxcontrib.autohttp.bottle`
+   also.
+
 In order to use it, add :mod:`sphinxcontrib.httpdomain` into
 :data:`extensions` list of your Sphinx configuration file (:file:`conf.py`)::
 
 .. _Flask: http://flask.pocoo.org/
 
 
+
+.. module:: sphinxcontrib.autohttp.bottle
+
+:mod:`sphinxcontrib.autohttp.bottle` --- Exporting API reference from Bottle app
+--------------------------------------------------------------------------------
+
+It generates RESTful HTTP API reference documentation from a Bottle_
+application's routing table, similar to :mod:`sphinx.ext.autodoc`.
+
+In order to use it, add :mod:`sphinxcontrib.autohttp.bottle` into
+:data:`extensions` list of your Sphinx configuration (:file:`conf.py`) file::
+
+    extensions = ['sphinxcontrib.autohttp.bottle']
+
+For example:
+
+.. sourcecode:: rst
+
+   .. autobottle:: autobottle_sampleapp:app
+
+will be rendered as:
+
+    .. autobottle:: autobottle_sampleapp:app
+
+.. rst:directive:: .. autobottle:: module:app
+
+   Generates HTTP API references from a Bottle application. It takes an
+   import name, like::
+
+       your.webapplication.module:app
+
+   Colon character (``:``) separates module path and application variable.
+   Latter part can be more complex::
+
+       your.webapplication.module:create_app(config='default.cfg')
+
+   It's useful when a Bottle application is created from the factory function
+   (:func:`create_app`, in the above example).
+
+   It takes several flag options as well.
+
+   ``endpoints``
+      Endpoints to generate a reference.
+
+      .. sourcecode:: rst
+
+         .. autobottle:: yourwebapp:app
+            :endpoints: user, post, friends
+
+      will document :func:`user`, :func:`post`, and :func:`friends`
+      view functions, and
+
+      .. sourcecode:: rst
+
+         .. autobottle:: yourwebapp:app
+            :endpoints:
+
+      will document all endpoints in the flask app.
+
+      For compatibility, omitting this option will produce the same effect
+      like above.
+
+   ``undoc-endpoints``
+      Excludes specified endpoints from generated references.
+
+      For example:
+
+      .. sourcecode:: rst
+
+         .. autobottle:: yourwebapp:app
+            :undoc-endpoints: admin, admin_login
+
+      will exclude :func:`admin`, :func:`admin_login` view functions.
+
+      .. note::
+
+         While the `undoc-members`_ flag of :mod:`sphinx.ext.autodoc` extension
+         includes members without docstrings, ``undoc-endpoints`` option has
+         nothing to do with docstrings. It just excludes specified endpoints.
+
+         .. _undoc-members: http://sphinx.pocoo.org/ext/autodoc.html#directive-automodule
+
+   ``include-empty-docstring``
+      View functions that don't have docstring (:attr:`__doc__`) are excluded
+      by default. If this flag option has given, they become included also.
+
+.. _Bottle: http://bottlepy.org/
+
+
 Author and License
 ------------------
 

httpdomain/setup.py

File contents unchanged.

httpdomain/sphinxcontrib/autohttp/__init__.py

 
 """
 
+
+def import_object(import_name):
+    module_name, expr = import_name.split(':', 1)
+    mod = __import__(module_name)
+    mod = reduce(getattr, module_name.split('.')[1:], mod)
+    globals = __builtins__
+    if not isinstance(globals, dict):
+        globals = globals.__dict__
+    return eval(expr, globals, mod.__dict__)
+
+
+def http_directive(method, path, content):
+    method = method.lower().strip()
+    if isinstance(content, basestring):
+        content = content.splitlines()
+    yield ''
+    yield '.. http:{method}:: {path}'.format(**locals())
+    yield ''
+    for line in content:
+        yield '   ' + line
+    yield ''

httpdomain/sphinxcontrib/autohttp/bottle.py

+"""
+    sphinxcontrib.autohttp.bottle
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    The sphinx.ext.autodoc-style HTTP API reference builder (from Bottle)
+    for sphinxcontrib.httpdomain.
+
+    :copyright: Copyright 2012 by Jameel Al-Aziz
+    :license: BSD, see LICENSE for details.
+
+"""
+
+import re
+try:
+    import cStringIO as StringIO
+except ImportError:
+    import StringIO
+
+from docutils import nodes
+from docutils.statemachine import ViewList
+
+from sphinx.util import force_decode
+from sphinx.util.compat import Directive
+from sphinx.util.nodes import nested_parse_with_titles
+from sphinx.util.docstrings import prepare_docstring
+from sphinx.pycode import ModuleAnalyzer
+
+from sphinxcontrib import httpdomain
+from sphinxcontrib import autohttp
+
+
+def translate_bottle_rule(app, rule):
+    buf = StringIO.StringIO()
+    for name, filter, conf in app.router.parse_rule(rule):
+        if filter:
+            buf.write('(')
+            buf.write(name)
+            if filter != app.router.default_filter or conf:
+                buf.write(':')
+                buf.write(filter)
+            if conf:
+                buf.write(':')
+                buf.write(conf)
+            buf.write(')')
+        else:
+            buf.write(name)
+    return buf.getvalue()
+
+
+def get_routes(app):
+    for rule, methods in app.router.rules.iteritems():
+        for method, target in methods.iteritems():
+            if method in ('OPTIONS', 'HEAD'):
+                continue
+            path = translate_bottle_rule(app, rule)
+            yield method, path, target
+
+
+class AutobottleDirective(Directive):
+
+    has_content = True
+    required_arguments = 1
+    option_spec = {'endpoints': str,
+                   'undoc-endpoints': str,
+                   'include-empty-docstring': str}
+
+    @property
+    def endpoints(self):
+        try:
+            endpoints = re.split(r'\s*,\s*', self.options['endpoints'])
+        except KeyError:
+            # means 'endpoints' option was missing
+            return None
+        return frozenset(endpoints)
+
+    @property
+    def undoc_endpoints(self):
+        try:
+            endpoints = re.split(r'\s*,\s*', self.options['undoc-endpoints'])
+        except KeyError:
+            return frozenset()
+        return frozenset(endpoints)
+
+    def make_rst(self):
+        app = autohttp.import_object(self.arguments[0])
+        for method, path, target in get_routes(app):
+            endpoint = target.name or target.callback.__name__
+            if self.endpoints and endpoint not in self.endpoints:
+                continue
+            if endpoint in self.undoc_endpoints:
+                continue
+            view = target.callback
+            docstring = view.__doc__ or ''
+            if not isinstance(docstring, unicode):
+                analyzer = ModuleAnalyzer.for_module(view.__module__)
+                docstring = force_decode(docstring, analyzer.encoding)
+            if not docstring and 'include-empty-docstring' not in self.options:
+                continue
+            docstring = prepare_docstring(docstring)
+            for line in autohttp.http_directive(method, path, docstring):
+                yield line
+
+    def run(self):
+        node = nodes.section()
+        node.document = self.state.document
+        result = ViewList()
+        for line in self.make_rst():
+            result.append(line, '<autobottle>')
+        nested_parse_with_titles(self.state, result, node)
+        return node.children
+
+
+def setup(app):
+    if 'http' not in app.domains:
+        httpdomain.setup(app)
+    app.add_directive('autobottle', AutobottleDirective)
+

httpdomain/sphinxcontrib/autohttp/flask.py

 from sphinx.pycode import ModuleAnalyzer
 
 from sphinxcontrib import httpdomain
-
-
-def import_object(import_name):
-    module_name, expr = import_name.split(':', 1)
-    mod = __import__(module_name)
-    mod = reduce(getattr, module_name.split('.')[1:], mod)
-    globals = __builtins__
-    if not isinstance(globals, dict):
-        globals = globals.__dict__
-    return eval(expr, globals, mod.__dict__)
-
-
-def http_directive(method, path, content):
-    method = method.lower().strip()
-    if isinstance(content, basestring):
-        content = content.splitlines()
-    yield ''
-    yield '.. http:{method}:: {path}'.format(**locals())
-    yield ''
-    for line in content:
-        yield '   ' + line
-    yield ''
+from sphinxcontrib import autohttp
 
 
 def translate_werkzeug_rule(rule):
         return frozenset(blueprints)
 
     def make_rst(self):
-        app = import_object(self.arguments[0])
+        app = autohttp.import_object(self.arguments[0])
         for method, path, endpoint in get_routes(app):
             try:
                 blueprint, endpoint_internal = endpoint.split('.')
             if not docstring and 'include-empty-docstring' not in self.options:
                 continue
             docstring = prepare_docstring(docstring)
-            for line in http_directive(method, path, docstring):
+            for line in autohttp.http_directive(method, path, docstring):
                 yield line
 
     def run(self):