1 ##############################################################################
2 # Copyright (c) 2016 ZTE Corporation
3 # feng.xiaowei@zte.com.cn
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
10 from functools import wraps
11 from HTMLParser import HTMLParser
16 from settings import default_settings, models
17 from handlers import swagger_handlers
20 class EpytextParser(HTMLParser):
23 def __init__(self, tag):
24 HTMLParser.__init__(self)
28 def handle_starttag(self, tag, attr):
32 def handle_endtag(self, tag):
36 def handle_data(self, data):
44 class DocParser(object):
48 self.responseClass = None
49 self.responseMessages = []
53 def parse_docstring(self, text):
58 doc = epydoc.markup.parse(text, markup='epytext', errors=errors)
59 _, fields = doc.split_fields(errors)
65 self._get_parser(tag)(arg=arg, body=body)
68 def _get_parser(self, tag):
70 'param': self._parse_param,
71 'type': self._parse_type,
73 'required': self._parse_required,
74 'rtype': self._parse_rtype,
75 'property': self._parse_property,
76 'ptype': self._parse_ptype,
77 'return': self._parse_return,
78 'raise': self._parse_return,
79 'notes': self._parse_notes,
80 'description': self._parse_description,
82 return parser.get(tag, self._not_supported)
84 def _parse_param(self, **kwargs):
85 arg = kwargs.get('arg', None)
86 body = self._get_body(**kwargs)
87 self.params.setdefault(arg, {}).update({
92 if 'paramType' not in self.params[arg]:
93 self.params[arg]['paramType'] = 'query'
95 def _parse_type(self, **kwargs):
96 arg = kwargs.get('arg', None)
97 body = self._get_body(**kwargs)
98 self.params.setdefault(arg, {}).update({
103 def _parse_in(self, **kwargs):
104 arg = kwargs.get('arg', None)
105 body = self._get_body(**kwargs)
106 self.params.setdefault(arg, {}).update({
111 def _parse_required(self, **kwargs):
112 arg = kwargs.get('arg', None)
113 body = self._get_body(**kwargs)
114 self.params.setdefault(arg, {}).update({
116 'required': False if body in ['False', 'false'] else True
119 def _parse_rtype(self, **kwargs):
120 body = self._get_body(**kwargs)
121 self.responseClass = body
123 def _parse_property(self, **kwargs):
124 arg = kwargs.get('arg', None)
125 self.properties.setdefault(arg, {}).update({
129 def _parse_ptype(self, **kwargs):
130 arg = kwargs.get('arg', None)
131 code = self._parse_epytext_para('code', **kwargs)
132 link = self._parse_epytext_para('link', **kwargs)
134 self.properties.setdefault(arg, {}).update({
138 self.properties.setdefault(arg, {}).update({
140 'items': {'type': link}
143 def _parse_return(self, **kwargs):
144 arg = kwargs.get('arg', None)
145 body = self._get_body(**kwargs)
146 self.responseMessages.append({
151 def _parse_notes(self, **kwargs):
152 body = self._get_body(**kwargs)
153 self.notes = self._sanitize_doc(body)
155 def _parse_description(self, **kwargs):
156 body = self._get_body(**kwargs)
157 self.summary = self._sanitize_doc(body)
159 def _not_supported(self, **kwargs):
163 def _sanitize_doc(comment):
164 return comment.replace('\n', '<br/>') if comment else comment
167 def _get_body(**kwargs):
168 body = kwargs.get('body', None)
169 return body.to_plaintext(None).strip() if body else body
172 def _parse_epytext_para(tag, **kwargs):
173 def _parse_epytext(tag, body):
174 epytextParser = EpytextParser(tag)
175 epytextParser.feed(str(body))
176 data = epytextParser.get_data()
177 epytextParser.close()
180 body = kwargs.get('body', None)
181 return _parse_epytext(tag, body) if body else body
184 class model(DocParser):
185 def __init__(self, *args, **kwargs):
186 super(model, self).__init__()
192 def __call__(self, *args):
197 self._parse_model(cls)
201 def _parse_model(self, cls):
202 self.id = cls.__name__
204 if '__init__' in dir(cls):
205 self._parse_args(cls.__init__)
206 self.parse_docstring(inspect.getdoc(cls))
209 def _parse_args(self, func):
210 argspec = inspect.getargspec(func)
211 argspec.args.remove("self")
214 defaults = list(zip(argspec.args[-len(argspec.defaults):],
216 required_args_count = len(argspec.args) - len(defaults)
217 for arg in argspec.args[:required_args_count]:
218 self.required.append(arg)
219 self.properties.setdefault(arg, {'type': 'string'})
220 for arg, default in defaults:
221 self.properties.setdefault(arg, {
227 class operation(DocParser):
228 def __init__(self, nickname='apis', **kwds):
229 super(operation, self).__init__()
230 self.nickname = nickname
235 def __call__(self, *args, **kwds):
237 return self.func(*args, **kwds)
240 self._parse_operation(func)
243 def __wrapper__(*in_args, **in_kwds):
244 return self.func(*in_args, **in_kwds)
246 __wrapper__.rest_api = self
249 def _parse_operation(self, func):
252 self.__name__ = func.__name__
253 self._parse_args(func)
254 self.parse_docstring(inspect.getdoc(self.func))
256 def _parse_args(self, func):
257 argspec = inspect.getargspec(func)
258 argspec.args.remove("self")
262 defaults = argspec.args[-len(argspec.defaults):]
264 for arg in argspec.args:
269 self.params.setdefault(arg, {
271 'required': required,
275 self.func_args = argspec.args
279 default_settings.update(opts)
282 class Application(tornado.web.Application):
283 def __init__(self, handlers=None,
287 super(Application, self).__init__(swagger_handlers() + handlers,