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

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