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 29 -def get_properties(obj):
30 if not hasattr(obj, '_wsgiwapi_props'): 31 return {} 32 return obj._wsgiwapi_props
33
34 -def make_doc(appurls, base_doc_url):
35 def build_link_tree(result, urls, leadingcomponents, thiscomponent): 36 components = urls.keys() 37 components.sort() 38 for component in components: 39 value = urls[component] 40 fullcomponents = leadingcomponents + (component, ) 41 componentpath = thiscomponent + '/' + component 42 result.append('<li><a href="%(component)s">/%(fullpath)s</a>' % 43 {'component': componentpath, 44 'fullpath': '/'.join(fullcomponents)}) 45 if hasattr(value, '__call__'): 46 doc = inspect.getdoc(value) 47 if doc is None: 48 doc = '' 49 else: 50 doc = doc.partition('\n')[0] 51 result.append(' ') 52 props = get_properties(value) 53 rettype = props.get('return_type', '') 54 if rettype: 55 result.append("[%s] " % rettype) 56 result.append(doc) 57 else: 58 result.append('<ul>') 59 build_link_tree(result, value, fullcomponents, componentpath) 60 result.append('</ul>') 61 result.append('</li>')
62 63 def path_description(callable): 64 """Generate a description of the subsequent path items. 65 66 """ 67 args, varargs, varkw, defaults = inspect.getargspec(callable) 68 args = args[1:] 69 if len(args) == 0 and varargs is None: 70 return "<li>No extra path components allowed.</li>\n" 71 result = [] 72 for arg in args: 73 result.append("<li>%s</li>" % arg) 74 if varargs is not None: 75 result.append("<li>...</li>") 76 return '\n'.join(result) 77 78 def param_descriptions(constraints): 79 """Generate a description of the parameters allowed. 80 81 """ 82 if len(constraints) == 0: 83 return "No parameters allowed" 84 result = [] 85 for name, values in constraints.iteritems(): 86 min, max, pattern, compiled_pattern, default, doc = values 87 required = (default is None and min > 0) 88 desc = [] 89 desc += name 90 if required: 91 desc += " (required)" 92 else: 93 desc += " (optional)" 94 95 if doc is not None: 96 desc += " - " + doc 97 98 desc += "<br/>\n" 99 100 if min is not None: 101 desc += "Minimum occurrences: %d<br/>\n" % min 102 if max is not None: 103 desc += "Maximum occurrences: %d<br/>\n" % max 104 if pattern is not None: 105 desc += "Must match %s<br/>\n" % pattern 106 107 result.append("<li>" + ''.join(desc) + "</li>") 108 return '\n'.join(result) 109 110 @allow_GET 111 @pathinfo(tail=()) 112 def doc(request): 113 """Display documentation about the API. 114 115 """ 116 urls = appurls 117 for arg in request.pathinfo.tail: 118 if arg not in urls: 119 raise HTTPNotFound(request.path) 120 if hasattr(urls, '__call__'): 121 raise HTTPNotFound(request.path) 122 urls = urls[arg] 123 124 if hasattr(urls, '__call__'): 125 body = '<pre>%s</pre>' % inspect.getdoc(urls) 126 props = get_properties(urls) 127 rettype = props.get('return_type', '') 128 if rettype: 129 body += "<b>Return type: %s</b><br/>\n" % rettype 130 body += "<b>Path items</b>\n<ul>" 131 body += path_description(urls) 132 body += "</ul><br/>\n" 133 constraints = props.get('valid_params', None) 134 if constraints != None: 135 body += "<b>Parameters:</b>\n<ul>" 136 body += param_descriptions(constraints) 137 body += "</ul><br/>\n" 138 methods = props.get('allowed_methods', None) 139 if methods != None: 140 methods = list(methods) 141 methods.sort() 142 body += "<b>Allowed methods:</b><ul>\n<li>" 143 body += '</li><li>'.join(methods) 144 body += "</li></ul><br/>\n" 145 else: 146 body = ['<ul>'] 147 thiscomponent = base_doc_url 148 if len(request.pathinfo.tail): 149 thiscomponent = request.pathinfo.tail[-1] 150 build_link_tree(body, urls, request.pathinfo.tail, thiscomponent) 151 body.append('</ul>') 152 body = ''.join(body) 153 return Response( 154 """<html><head><title>%(reqpath)s</title></head><body>%(body)s</body></html>""" % 155 { 156 'reqpath': request.path, 157 'body': body, 158 }, content_type='text/html' 159 ) 160 return doc 161 162 # vim: set fileencoding=utf-8 : 163