1 import inspect, handler
3 from piston.handler import typemapper
4 from piston.handler import handler_tracker
6 from django.core.urlresolvers import get_resolver, get_callable, get_script_prefix
7 from django.shortcuts import render_to_response
8 from django.template import RequestContext
10 def generate_doc(handler_cls):
12 Returns a `HandlerDocumentation` object
13 for the given handler. Use this to generate
14 documentation for your API.
16 if not type(handler_cls) is handler.HandlerMetaClass:
17 raise ValueError("Give me handler, not %s" % type(handler_cls))
19 return HandlerDocumentation(handler_cls)
21 class HandlerMethod(object):
22 def __init__(self, method, stale=False):
27 args, _, _, defaults = inspect.getargspec(self.method)
29 for idx, arg in enumerate(args):
30 if arg in ('self', 'request', 'form'):
35 if defaults and len(defaults) >= didx:
36 yield (arg, str(defaults[-didx]))
41 def signature(self, parse_optional=True):
44 for argn, argdef in self.iter_args():
48 spec += '=%s' % argdef
52 spec = spec.rstrip(", ")
55 return spec.replace("=None", "=<optional>")
61 return inspect.getdoc(self.method)
65 return self.method.__name__
69 if self.name == 'read':
71 elif self.name == 'create':
73 elif self.name == 'delete':
75 elif self.name == 'update':
79 return "<Method: %s>" % self.name
81 class HandlerDocumentation(object):
82 def __init__(self, handler):
83 self.handler = handler
85 def get_methods(self, include_default=False):
86 for method in "read create update delete".split():
87 met = getattr(self.handler, method, None)
92 stale = inspect.getmodule(met) is handler
94 if not self.handler.is_anonymous:
95 if met and (not stale or include_default):
96 yield HandlerMethod(met, stale)
98 if not stale or met.__name__ == "read" \
99 and 'GET' in self.allowed_methods:
101 yield HandlerMethod(met, stale)
103 def get_all_methods(self):
104 return self.get_methods(include_default=True)
107 def is_anonymous(self):
108 return handler.is_anonymous
111 return getattr(self, 'model', None)
114 def has_anonymous(self):
115 return self.handler.anonymous
119 if self.has_anonymous:
120 return HandlerDocumentation(self.handler.anonymous)
124 return self.handler.__doc__
128 return self.handler.__name__
131 def allowed_methods(self):
132 return self.handler.allowed_methods
134 def get_resource_uri_template(self):
136 URI template processor.
138 See http://bitworking.org/projects/URI-Templates/
140 def _convert(template, params=[]):
141 """URI template converter"""
142 paths = template % dict([p, "{%s}" % p] for p in params)
143 return u'%s%s' % (get_script_prefix(), paths)
146 resource_uri = self.handler.resource_uri()
148 components = [None, [], {}]
150 for i, value in enumerate(resource_uri):
151 components[i] = value
153 lookup_view, args, kwargs = components
154 lookup_view = get_callable(lookup_view, True)
156 possibilities = get_resolver(None).reverse_dict.getlist(lookup_view)
158 for possibility, pattern in possibilities:
159 for result, params in possibility:
161 if len(args) != len(params):
163 return _convert(result, params)
165 if set(kwargs.keys()) != set(params):
167 return _convert(result, params)
171 resource_uri_template = property(get_resource_uri_template)
174 return u'<Documentation for "%s">' % self.name
176 def documentation_view(request):
178 Generic documentation view. Generates documentation
179 from the handlers you've defined.
183 for handler in handler_tracker:
184 docs.append(generate_doc(handler))
186 def _compare(doc1, doc2):
187 #handlers and their anonymous counterparts are put next to each other.
188 name1 = doc1.name.replace("Anonymous", "")
189 name2 = doc2.name.replace("Anonymous", "")
190 return cmp(name1, name2)
194 return render_to_response('documentation.html',
195 { 'docs': docs }, RequestContext(request))