Package wsgiwapi :: Module autodoc
[frames] | no frames]

Source Code for Module wsgiwapi.autodoc

  1  # Copyright (c) 2009 Richard Boulton 
  2  # 
  3  # Permission is hereby granted, free of charge, to any person obtaining a copy 
  4  # of this software and associated documentation files (the "Software"), to deal 
  5  # in the Software without restriction, including without limitation the rights 
  6  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
  7  # copies of the Software, and to permit persons to whom the Software is 
  8  # furnished to do so, subject to the following conditions: 
  9  # 
 10  # The above copyright notice and this permission notice shall be included in 
 11  # all copies or substantial portions of the Software. 
 12  # 
 13  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 14  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 15  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 16  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 17  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 18  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
 19  # SOFTWARE. 
 20  r"""Support for automatically documenting an API. 
 21   
 22  """ 
 23  __docformat__ = "restructuredtext en" 
 24   
 25  import inspect 
 26  from decorators import allow_GET, pathinfo 
 27  from wsgisupport import Response, HTTPNotFound 
 28  from application import MethodSwitch 
29 30 -def get_properties(obj):
31 if not hasattr(obj, '_wsgiwapi_props'): 32 return {} 33 return obj._wsgiwapi_props
34
35 -def make_doc(appurls, base_doc_url):
36 def build_link_tree(result, urls, leadingcomponents, thiscomponent): 37 components = urls.keys() 38 components.sort() 39 for component in components: 40 value = urls[component] 41 if component is None: 42 fullcomponents = leadingcomponents 43 componentpath = thiscomponent 44 else: 45 fullcomponents = leadingcomponents + (component, ) 46 componentpath = thiscomponent + '/' + component 47 result.append('<li><a href="%(component)s">/%(fullpath)s</a>' % 48 {'component': componentpath, 49 'fullpath': '/'.join(fullcomponents)}) 50 if hasattr(value, '__call__'): 51 doc = inspect.getdoc(value) 52 if doc is None: 53 doc = '' 54 else: 55 doc = doc.partition('\n')[0] 56 result.append(' ') 57 props = get_properties(value) 58 rettype = props.get('return_type', '') 59 if rettype: 60 result.append("[%s] " % rettype) 61 result.append(doc) 62 else: 63 result.append('<ul>') 64 build_link_tree(result, value, fullcomponents, componentpath) 65 result.append('</ul>') 66 result.append('</li>')
67 68 def path_description(callable): 69 """Generate a description of the subsequent path items. 70 71 """ 72 if isinstance(callable, MethodSwitch): 73 # Hack - this just returns the path description for the GET method. 74 args, varargs, varkw, defaults = inspect.getargspec(callable.methods['GET']) 75 else: 76 args, varargs, varkw, defaults = inspect.getargspec(callable) 77 78 args = args[1:] 79 if len(args) == 0 and varargs is None: 80 return "<li>No extra path components allowed.</li>\n" 81 result = [] 82 for arg in args: 83 result.append("<li>%s</li>" % arg) 84 if varargs is not None: 85 result.append("<li>...</li>") 86 return '\n'.join(result) 87 88 def param_descriptions(constraints): 89 """Generate a description of the parameters allowed. 90 91 """ 92 if len(constraints) == 0: 93 return "No parameters allowed" 94 result = [] 95 for name, values in constraints.iteritems(): 96 min, max, pattern, compiled_pattern, default, doc = values 97 required = (default is None and min > 0) 98 desc = [] 99 desc += name 100 if required: 101 desc += " (required)" 102 else: 103 desc += " (optional)" 104 105 if doc is not None: 106 desc += " - " + doc 107 108 desc += "<br/>\n" 109 110 if min is not None: 111 desc += "Minimum occurrences: %d<br/>\n" % min 112 if max is not None: 113 desc += "Maximum occurrences: %d<br/>\n" % max 114 if pattern is not None: 115 desc += "Must match %s<br/>\n" % pattern 116 117 result.append("<li>" + ''.join(desc) + "</li>") 118 return '\n'.join(result) 119 120 @allow_GET 121 @pathinfo(tail=()) 122 def doc(request): 123 """Display documentation about the API. 124 125 """ 126 urls = appurls 127 for arg in request.pathinfo.tail: 128 if arg not in urls: 129 raise HTTPNotFound(request.path) 130 if hasattr(urls, '__call__'): 131 raise HTTPNotFound(request.path) 132 urls = urls[arg] 133 134 if hasattr(urls, '__call__'): 135 body = '<pre>%s</pre>' % inspect.getdoc(urls) 136 props = get_properties(urls) 137 rettype = props.get('return_type', '') 138 if rettype: 139 body += "<b>Return type: %s</b><br/>\n" % rettype 140 body += "<b>Path items</b>\n<ul>" 141 body += path_description(urls) 142 body += "</ul><br/>\n" 143 constraints = props.get('valid_params', None) 144 if constraints != None: 145 body += "<b>Parameters:</b>\n<ul>" 146 body += param_descriptions(constraints) 147 body += "</ul><br/>\n" 148 methods = props.get('allowed_methods', None) 149 if methods != None: 150 methods = list(methods) 151 methods.sort() 152 body += "<b>Allowed methods:</b><ul>\n<li>" 153 body += '</li><li>'.join(methods) 154 body += "</li></ul><br/>\n" 155 else: 156 body = ['<ul>'] 157 thiscomponent = base_doc_url 158 if len(request.pathinfo.tail): 159 thiscomponent = request.pathinfo.tail[-1] 160 build_link_tree(body, urls, request.pathinfo.tail, thiscomponent) 161 body.append('</ul>') 162 body = ''.join(body) 163 return Response( 164 """<html><head><title>%(reqpath)s</title></head><body>%(body)s</body></html>""" % 165 { 166 'reqpath': request.path, 167 'body': body, 168 }, content_type='text/html' 169 ) 170 return doc 171 172 # vim: set fileencoding=utf-8 : 173