2 # -*- coding: utf-8 -*-
5 from functools import wraps
7 from HTMLParser import HTMLParser
9 from settings import default_settings, models
10 from handlers import swagger_handlers
15 class EpytextParser(HTMLParser):
18 def __init__(self, tag):
19 HTMLParser.__init__(self)
23 def handle_starttag(self, tag, attr):
27 def handle_endtag(self, tag):
31 def handle_data(self, data):
39 class DocParser(object):
43 self.responseClass = None
44 self.responseMessages = []
48 def parse_docstring(self, text):
53 doc = epydoc.markup.parse(text, markup='epytext', errors=errors)
54 _, fields = doc.split_fields(errors)
60 self._get_parser(tag)(arg=arg, body=body)
63 def _get_parser(self, tag):
65 'param': self._parse_param,
66 'type': self._parse_type,
68 'required': self._parse_required,
69 'rtype': self._parse_rtype,
70 'property': self._parse_property,
71 'ptype': self._parse_ptype,
72 'return': self._parse_return,
73 'raise': self._parse_return,
74 'notes': self._parse_notes,
75 'description': self._parse_description,
77 return parser.get(tag, self._not_supported)
79 def _parse_param(self, **kwargs):
80 arg = kwargs.get('arg', None)
81 body = self._get_body(**kwargs)
82 self.params.setdefault(arg, {}).update({
87 if 'paramType' not in self.params[arg]:
88 self.params[arg]['paramType'] = 'query'
90 def _parse_type(self, **kwargs):
91 arg = kwargs.get('arg', None)
92 body = self._get_body(**kwargs)
93 self.params.setdefault(arg, {}).update({
98 def _parse_in(self, **kwargs):
99 arg = kwargs.get('arg', None)
100 body = self._get_body(**kwargs)
101 self.params.setdefault(arg, {}).update({
106 def _parse_required(self, **kwargs):
107 arg = kwargs.get('arg', None)
108 body = self._get_body(**kwargs)
109 self.params.setdefault(arg, {}).update({
111 'required': False if body in ['False', 'false'] else True
114 def _parse_rtype(self, **kwargs):
115 body = self._get_body(**kwargs)
116 self.responseClass = body
118 def _parse_property(self, **kwargs):
119 arg = kwargs.get('arg', None)
120 self.properties.setdefault(arg, {}).update({
124 def _parse_ptype(self, **kwargs):
125 arg = kwargs.get('arg', None)
126 code = self._parse_epytext_para('code', **kwargs)
127 link = self._parse_epytext_para('link', **kwargs)
129 self.properties.setdefault(arg, {}).update({
133 self.properties.setdefault(arg, {}).update({
135 'items': {'type': link}
138 def _parse_return(self, **kwargs):
139 arg = kwargs.get('arg', None)
140 body = self._get_body(**kwargs)
141 self.responseMessages.append({
146 def _parse_notes(self, **kwargs):
147 body = self._get_body(**kwargs)
148 self.notes = self._sanitize_doc(body)
150 def _parse_description(self, **kwargs):
151 body = self._get_body(**kwargs)
152 self.summary = self._sanitize_doc(body)
154 def _not_supported(self, **kwargs):
158 def _sanitize_doc(comment):
159 return comment.replace('\n', '<br/>') if comment else comment
162 def _get_body(**kwargs):
163 body = kwargs.get('body', None)
164 return body.to_plaintext(None).strip() if body else body
167 def _parse_epytext_para(tag, **kwargs):
168 def _parse_epytext(tag, body):
169 epytextParser = EpytextParser(tag)
170 epytextParser.feed(str(body))
171 data = epytextParser.get_data()
172 epytextParser.close()
175 body = kwargs.get('body', None)
176 return _parse_epytext(tag, body) if body else body
179 class model(DocParser):
180 def __init__(self, *args, **kwargs):
181 super(model, self).__init__()
187 def __call__(self, *args, **kwargs):
192 self._parse_model(cls)
196 def _parse_model(self, cls):
197 self.id = cls.__name__
199 if '__init__' in dir(cls):
200 self._parse_args(cls.__init__)
201 self.parse_docstring(inspect.getdoc(cls))
204 def _parse_args(self, func):
205 argspec = inspect.getargspec(func)
206 argspec.args.remove("self")
209 defaults = list(zip(argspec.args[-len(argspec.defaults):], argspec.defaults))
210 required_args_count = len(argspec.args) - len(defaults)
211 for arg in argspec.args[:required_args_count]:
212 self.required.append(arg)
213 self.properties.setdefault(arg, {'type': 'string'})
214 for arg, default in defaults:
215 self.properties.setdefault(arg, {'type': 'string', "default": default})
218 class operation(DocParser):
219 def __init__(self, nickname=None, **kwds):
220 super(operation, self).__init__()
221 self.nickname = nickname
226 def __call__(self, *args, **kwds):
228 return self.func(*args, **kwds)
231 self._parse_operation(func)
234 def __wrapper__(*in_args, **in_kwds):
235 return self.func(*in_args, **in_kwds)
237 __wrapper__.rest_api = self
240 def _parse_operation(self, func):
243 self.__name__ = func.__name__
244 self._parse_args(func)
245 self.parse_docstring(inspect.getdoc(self.func))
247 def _parse_args(self, func):
248 argspec = inspect.getargspec(func)
249 argspec.args.remove("self")
253 defaults = argspec.args[-len(argspec.defaults):]
255 for arg in argspec.args:
260 self.params.setdefault(arg, {
262 'required': required,
266 self.func_args = argspec.args
270 default_settings.update(opts)
273 class Application(tornado.web.Application):
274 def __init__(self, handlers=None, default_host="", transforms=None, **settings):
275 super(Application, self).__init__(swagger_handlers() + handlers, default_host, transforms, **settings)