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 ##############################################################################
9 from HTMLParser import HTMLParser
10 from functools import wraps
16 from opnfv_testapi.tornado_swagger import handlers
17 from opnfv_testapi.tornado_swagger import settings
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 code = self._parse_epytext_para('code', **kwargs)
98 link = self._parse_epytext_para('link', **kwargs)
100 self.params.setdefault(arg, {}).update({
105 self.params.setdefault(arg, {}).update({
107 'items': {'type': link}
110 def _parse_in(self, **kwargs):
111 arg = kwargs.get('arg', None)
112 body = self._get_body(**kwargs)
113 self.params.setdefault(arg, {}).update({
118 def _parse_required(self, **kwargs):
119 arg = kwargs.get('arg', None)
120 body = self._get_body(**kwargs)
121 self.params.setdefault(arg, {}).update({
123 'required': False if body in ['False', 'false'] else True
126 def _parse_rtype(self, **kwargs):
127 body = self._get_body(**kwargs)
128 self.responseClass = body
130 def _parse_property(self, **kwargs):
131 arg = kwargs.get('arg', None)
132 self.properties.setdefault(arg, {}).update({
136 def _parse_ptype(self, **kwargs):
137 arg = kwargs.get('arg', None)
138 code = self._parse_epytext_para('code', **kwargs)
139 link = self._parse_epytext_para('link', **kwargs)
141 self.properties.setdefault(arg, {}).update({
145 self.properties.setdefault(arg, {}).update({
147 'items': {'type': link}
150 def _parse_return(self, **kwargs):
151 arg = kwargs.get('arg', None)
152 body = self._get_body(**kwargs)
153 self.responseMessages.append({
158 def _parse_notes(self, **kwargs):
159 body = self._get_body(**kwargs)
160 self.notes = self._sanitize_doc(body)
162 def _parse_description(self, **kwargs):
163 body = self._get_body(**kwargs)
164 self.summary = self._sanitize_doc(body)
166 def _not_supported(self, **kwargs):
170 def _sanitize_doc(comment):
171 return comment.replace('\n', '<br/>') if comment else comment
174 def _get_body(**kwargs):
175 body = kwargs.get('body', None)
176 return body.to_plaintext(None).strip() if body else body
179 def _parse_epytext_para(tag, **kwargs):
180 def _parse_epytext(tag, body):
181 epytextParser = EpytextParser(tag)
182 epytextParser.feed(str(body))
183 data = epytextParser.get_data()
184 epytextParser.close()
187 body = kwargs.get('body', None)
188 return _parse_epytext(tag, body) if body else body
191 class model(DocParser):
192 def __init__(self, *args, **kwargs):
193 super(model, self).__init__()
199 def __call__(self, *args):
204 self._parse_model(cls)
208 def _parse_model(self, cls):
209 self.id = cls.__name__
211 if '__init__' in dir(cls):
212 self._parse_args(cls.__init__)
213 self.parse_docstring(inspect.getdoc(cls))
214 settings.models.append(self)
216 def _parse_args(self, func):
217 argspec = inspect.getargspec(func)
218 argspec.args.remove("self")
221 defaults = list(zip(argspec.args[-len(argspec.defaults):],
223 required_args_count = len(argspec.args) - len(defaults)
224 for arg in argspec.args[:required_args_count]:
225 self.required.append(arg)
226 self.properties.setdefault(arg, {'type': 'string'})
227 for arg, default in defaults:
228 self.properties.setdefault(arg, {
234 class operation(DocParser):
235 def __init__(self, nickname='apis', **kwds):
236 super(operation, self).__init__()
237 self.nickname = nickname
242 def __call__(self, *args, **kwds):
244 return self.func(*args, **kwds)
247 self._parse_operation(func)
250 def __wrapper__(*in_args, **in_kwds):
251 return self.func(*in_args, **in_kwds)
253 __wrapper__.rest_api = self
256 def _parse_operation(self, func):
259 self.__name__ = func.__name__
260 self._parse_args(func)
261 self.parse_docstring(inspect.getdoc(self.func))
263 def _parse_args(self, func):
264 argspec = inspect.getargspec(func)
265 argspec.args.remove("self")
269 defaults = argspec.args[-len(argspec.defaults):]
271 for arg in argspec.args:
276 self.params.setdefault(arg, {
278 'required': required,
282 self.func_args = argspec.args
286 settings.docs_settings.update(opts)
289 class Application(tornado.web.Application):
290 def __init__(self, app_handlers=None,
294 super(Application, self).__init__(
295 handlers.swagger_handlers() + app_handlers,