taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

flask_base.py (7444B)


      1 """
      2     sphinxcontrib.autohttp.flask
      3     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      4 
      5     The sphinx.ext.autodoc-style HTTP API reference builder (from Flask)
      6     for sphinxcontrib.httpdomain.
      7 
      8     :copyright: Copyright 2011 by Hong Minhee
      9     :license: BSD, see LICENSE for details.
     10 
     11 """
     12 
     13 import re
     14 import itertools
     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_werkzeug_rule(rule):
     32     from werkzeug.routing import parse_rule
     33     buf = six.StringIO()
     34     for conv, arg, var in parse_rule(rule):
     35         if conv:
     36             buf.write('(')
     37             if conv != 'default':
     38                 buf.write(conv)
     39                 buf.write(':')
     40             buf.write(var)
     41             buf.write(')')
     42         else:
     43             buf.write(var)
     44     return buf.getvalue()
     45 
     46 
     47 def get_routes(app, endpoint=None, order=None):
     48     endpoints = []
     49     for rule in app.url_map.iter_rules(endpoint):
     50         url_with_endpoint = (
     51             six.text_type(next(app.url_map.iter_rules(rule.endpoint))),
     52             rule.endpoint
     53         )
     54         if url_with_endpoint not in endpoints:
     55             endpoints.append(url_with_endpoint)
     56     if order == 'path':
     57         endpoints.sort()
     58     endpoints = [e for _, e in endpoints]
     59     for endpoint in endpoints:
     60         methodrules = {}
     61         for rule in app.url_map.iter_rules(endpoint):
     62             methods = rule.methods.difference(['OPTIONS', 'HEAD'])
     63             path = translate_werkzeug_rule(rule.rule)
     64             for method in methods:
     65                 if method in methodrules:
     66                     methodrules[method].append(path)
     67                 else:
     68                     methodrules[method] = [path]
     69         for method, paths in methodrules.items():
     70             yield method, paths, endpoint
     71 
     72 
     73 def quickref_directive(method, path, content):
     74     rcomp = re.compile("^\s*.. :quickref:\s*(?P<quick>.*)$")
     75     method = method.lower().strip()
     76     if isinstance(content, six.string_types):
     77          content = content.splitlines()
     78     description=""
     79     name=""
     80     ref = path.replace("<","(").replace(">",")").replace("/","-").replace(":","-")
     81     for line in content:
     82          qref = rcomp.match(line)
     83          if qref:
     84             quickref = qref.group("quick")
     85             parts = quickref.split(";",1)
     86             if len(parts)>1:
     87                 name = parts[0]
     88                 description= parts[1]
     89             else:
     90                 description= quickref
     91             break
     92 
     93     row ={}
     94     row['name'] = name
     95     row['operation'] = '      - `%s %s <#%s-%s>`_' % (method.upper(), path, method.lower(), ref)
     96     row['description'] = description
     97 
     98     return row
     99 
    100 class AutoflaskBase(Directive):
    101 
    102     has_content = True
    103     required_arguments = 1
    104     option_spec = {'endpoints': directives.unchanged,
    105                    'blueprints': directives.unchanged,
    106                    'modules': directives.unchanged,
    107                    'order': directives.unchanged,
    108                    'undoc-endpoints': directives.unchanged,
    109                    'undoc-blueprints': directives.unchanged,
    110                    'undoc-modules': directives.unchanged,
    111                    'undoc-static': directives.unchanged,
    112                    'include-empty-docstring': directives.unchanged}
    113 
    114     @property
    115     def endpoints(self):
    116         endpoints = self.options.get('endpoints', None)
    117         if not endpoints:
    118             return None
    119         return re.split(r'\s*,\s*', endpoints)
    120 
    121     @property
    122     def undoc_endpoints(self):
    123         undoc_endpoints = self.options.get('undoc-endpoints', None)
    124         if not undoc_endpoints:
    125             return frozenset()
    126         return frozenset(re.split(r'\s*,\s*', undoc_endpoints))
    127 
    128     @property
    129     def blueprints(self):
    130         blueprints = self.options.get('blueprints', None)
    131         if not blueprints:
    132             return None
    133         return frozenset(re.split(r'\s*,\s*', blueprints))
    134 
    135     @property
    136     def undoc_blueprints(self):
    137         undoc_blueprints = self.options.get('undoc-blueprints', None)
    138         if not undoc_blueprints:
    139             return frozenset()
    140         return frozenset(re.split(r'\s*,\s*', undoc_blueprints))
    141 
    142     @property
    143     def modules(self):
    144         modules = self.options.get('modules', None)
    145         if not modules:
    146             return frozenset()
    147         return frozenset(re.split(r'\s*,\s*', modules))
    148 
    149     @property
    150     def undoc_modules(self):
    151         undoc_modules = self.options.get('undoc-modules', None)
    152         if not undoc_modules:
    153             return frozenset()
    154         return frozenset(re.split(r'\s*,\s*', undoc_modules))
    155 
    156     @property
    157     def order(self):
    158         order = self.options.get('order', None)
    159         if order not in (None, 'path'):
    160             raise ValueError('Invalid value for :order:')
    161         return order
    162 
    163     def make_rst(self, qref=False):
    164         app = import_object(self.arguments[0])
    165         if self.endpoints:
    166             routes = itertools.chain(*[get_routes(app, endpoint, self.order)
    167                     for endpoint in self.endpoints])
    168         else:
    169             routes = get_routes(app, order=self.order)
    170         for method, paths, endpoint in routes:
    171             try:
    172                 blueprint, _, endpoint_internal = endpoint.rpartition('.')
    173                 if self.blueprints and blueprint not in self.blueprints:
    174                     continue
    175                 if blueprint in self.undoc_blueprints:
    176                     continue
    177             except ValueError:
    178                 pass  # endpoint is not within a blueprint
    179 
    180             if endpoint in self.undoc_endpoints:
    181                 continue
    182             try:
    183                 static_url_path = app.static_url_path # Flask 0.7 or higher
    184             except AttributeError:
    185                 static_url_path = app.static_path # Flask 0.6 or under
    186             if ('undoc-static' in self.options and endpoint == 'static' and
    187                 static_url_path + '/(path:filename)' in paths):
    188                 continue
    189             view = app.view_functions[endpoint]
    190 
    191             if self.modules and view.__module__ not in self.modules:
    192                 continue
    193 
    194             if self.undoc_modules and view.__module__ in self.modules:
    195                 continue
    196 
    197             docstring = view.__doc__ or ''
    198             if hasattr(view, 'view_class'):
    199                 meth_func = getattr(view.view_class, method.lower(), None)
    200                 if meth_func and meth_func.__doc__:
    201                     docstring = meth_func.__doc__
    202             if not isinstance(docstring, six.text_type):
    203                 analyzer = ModuleAnalyzer.for_module(view.__module__)
    204                 docstring = force_decode(docstring, analyzer.encoding)
    205 
    206             if not docstring and 'include-empty-docstring' not in self.options:
    207                 continue
    208             docstring = prepare_docstring(docstring)
    209             if qref == True:
    210                 for path in paths:
    211                     row = quickref_directive(method, path, docstring)
    212                     yield row
    213             else:
    214                 for line in http_directive(method, paths, docstring):
    215                     yield line