Skip to content

Commit cd39df0

Browse files
authored
Merge pull request #86 from legenove/tlp_for_tornado
Tlp for tornado
2 parents 24395a7 + 84dd8e3 commit cd39df0

File tree

13 files changed

+873
-12
lines changed

13 files changed

+873
-12
lines changed

README.md

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Flask RESTful Application Code Generator
1+
# Flask/Tornado RESTful Application Code Generator
22

33
[![Build Status][travis-image]][travis-url] [![PyPi Version][pypi-image]][pypi-url]
44

55
## Overview
66

7-
Generate Flask-RESTful application code from a Swagger Specification doc.
7+
Generate Flask/Tornado-RESTful application code from a Swagger Specification doc.
88

99
**Alpha version for now, it can not handle all validation properly.**
1010

@@ -25,13 +25,16 @@ swagger_py_codegen --swagger-doc api.yml example-app
2525

2626
Command Options:
2727

28-
-s, --swagger, --swagger-doc Swagger doc file. [required]
29-
-f, --force Force overwrite.
30-
-p, --package Package name / application name.
31-
-t, --template-dir Path of your custom templates directory.
32-
--spec, --specification Generate online specification json response.
33-
--ui Generate swagger ui.
34-
--help Show this message and exit.
28+
-s, --swagger, --swagger-doc Swagger doc file. [required]
29+
-f, --force Force overwrite.
30+
-p, --package Package name / application name.
31+
-t, --template-dir Path of your custom templates directory.
32+
--spec, --specification Generate online specification json response.
33+
--ui Generate swagger ui.
34+
-j, --jobs INTEGER Parallel jobs for processing.
35+
-tlp, --templates gen flask/tornado templates,default flask.
36+
--version Show current version.
37+
--help Show this message and exit.
3538

3639
## Examples:
3740

@@ -42,7 +45,7 @@ Generate example-app from [apis.yml](https://github.com/guokr/swagger-py-codegen
4245
|__ api.yml
4346

4447
$ swagger_py_codegen -s api.yml example-app -p demo
45-
$ tree
48+
$ tree (flask-demo)
4649
.
4750
|__ api.yml
4851
|__ example-app
@@ -62,6 +65,29 @@ Generate example-app from [apis.yml](https://github.com/guokr/swagger-py-codegen
6265
| |__ validators.py
6366
|__ requirements.txt
6467

68+
$ swagger_py_codegen -s docs/panel.yml example-app -p demo -tlp=tornado
69+
$ tree (tornado-demo)
70+
.
71+
|__ api.yml
72+
|__ example-app
73+
|__ demo
74+
| |__ __init__.py
75+
| |__ core
76+
| |__ __init.py
77+
| |__ v1
78+
| |__ api
79+
| | |__ __init__.py
80+
| | |__ oauth_auth_approach_approach.py
81+
| | |__ oauth_auth_approach.py
82+
| | |__ users_token.py
83+
| | |__ users_current.py
84+
| | |__ users.py
85+
| |__ __init__.py
86+
| |__ routes.py
87+
| |__ schemas.py
88+
| |__ validators.py
89+
|__ requirements.txt
90+
6591
Install example-app requirements:
6692

6793
$ cd example-app

swagger_py_codegen/command.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from ._version import __version__
1515
from .flask import FlaskGenerator
16+
from .tornado import TornadoGenerator
1617
from .parser import Swagger
1718
from .base import Template
1819

@@ -81,16 +82,22 @@ def print_version(ctx, param, value):
8182
help='Generate swagger ui.')
8283
@click.option('-j', '--jobs',
8384
default=4, help='Parallel jobs for processing.')
85+
@click.option('-tlp', '--templates',
86+
default='flask', help='gen flask/tornado templates,default flask.')
8487
@click.option('--version', is_flag=True, callback=print_version,
8588
expose_value=False, is_eager=True,
8689
help='Show current version.')
8790
def generate(destination, swagger_doc, force=False, package=None,
88-
template_dir=None, specification=False, ui=False, jobs=4):
91+
template_dir=None, templates='flask',
92+
specification=False, ui=False, jobs=4):
8993
pool = Pool(processes=int(jobs))
9094
package = package or destination.replace('-', '_')
9195
data = spec_load(swagger_doc)
9296
swagger = Swagger(data, pool)
93-
generator = FlaskGenerator(swagger)
97+
if templates == 'tornado':
98+
generator = TornadoGenerator(swagger)
99+
else:
100+
generator = FlaskGenerator(swagger)
94101
generator.with_spec = specification
95102
generator.with_ui = ui
96103
template = Template()
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
from core import RequestHandler
5+
from .. import UserInfo
6+
from ..validators import validate_filter
7+
8+
class ApiHandler(RequestHandler):
9+
on_initialize_decorators = [validate_filter]
10+
11+
def get_current_user(self):
12+
authorization = self.request.headers.get('Authorization', '')
13+
user_id = self.request.headers.get('user_id')
14+
15+
return UserInfo(user_id, authorization, self.blueprint)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# -*- coding: utf-8 -*-
2+
import os
3+
import tornado.httpserver
4+
import tornado.ioloop
5+
import tornado.web
6+
import tornado.options
7+
8+
from core import load_tornado_settings
9+
10+
modules = ['{{ blueprint }}']
11+
config = load_tornado_settings(*modules)
12+
13+
class Application(tornado.web.Application):
14+
def __init__(self, url_list, **app_settings):
15+
tornado.web.Application.__init__(self, url_list, **app_settings)
16+
self.config = config
17+
18+
19+
class RestfulErrorHandler(tornado.web.ErrorHandler):
20+
def write_error(self, status_code, **kwargs):
21+
"""Override to implement custom error pages.
22+
23+
``write_error`` may call `write`, `render`, `set_header`, etc
24+
to produce output as usual.
25+
26+
If this error was caused by an uncaught exception (including
27+
HTTPError), an ``exc_info`` triple will be available as
28+
``kwargs["exc_info"]``. Note that this exception may not be
29+
the "current" exception for purposes of methods like
30+
``sys.exc_info()`` or ``traceback.format_exc``.
31+
"""
32+
self.finish({
33+
"code": status_code,
34+
"message": self._reason,
35+
})
36+
37+
38+
def main():
39+
import socket
40+
41+
socket.setdefaulttimeout(2)
42+
url_list = []
43+
url_list.extend(config.URIS)
44+
45+
app_settings = {
46+
"static_path": os.path.join(os.path.dirname(__file__), "static"),
47+
"default_handler_class": RestfulErrorHandler,
48+
"default_handler_args": dict(status_code=404)
49+
50+
}
51+
52+
app = Application(url_list,
53+
debug=config.DEBUG,
54+
**app_settings)
55+
56+
http_server = tornado.httpserver.HTTPServer(app)
57+
http_server.bind(config.PORT)
58+
http_server.start(config.WORKER)
59+
tornado.ioloop.IOLoop.current().start()
60+
61+
62+
if __name__ == "__main__":
63+
main()
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import absolute_import
3+
4+
class UserInfo(object):
5+
_scopes = None
6+
client_id = None
7+
_account = None
8+
9+
def __init__(self, user_id, authorization, blueprint):
10+
self.authorization = authorization
11+
self.user_id = user_id
12+
self.blueprint = blueprint
13+
self.valid = False
14+
15+
@property
16+
def scopes(self):
17+
if self._scopes is None:
18+
self._scopes = self._loader()
19+
return self._scopes
20+
21+
def _loader(self):
22+
return {{ scopes_supported }}
23+
24+
@property
25+
def account(self):
26+
if self._account is None:
27+
if self.valid and self.user_id:
28+
# TODO: test
29+
self._account = None
30+
return self._account
31+
32+
33+
def before_request(obj):
34+
pass
35+
36+
37+
def after_request(obj):
38+
pass
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -*- coding: utf-8 -*-
2+
from __future__ import unicode_literals, absolute_import
3+
4+
import six
5+
import importlib
6+
import tornado.web
7+
8+
settings = {}
9+
10+
def load_tornado_settings(*modules):
11+
settings.update({'MODULES': modules})
12+
kwargs = {}
13+
mods = []
14+
config = Config()
15+
config.update(**settings)
16+
try:
17+
setting_mod = importlib.import_module('my_settings')
18+
if hasattr(setting_mod, 'load_settings'):
19+
getattr(setting_mod, 'load_uris')(config, **kwargs)
20+
except ImportError:
21+
pass
22+
23+
for module in modules:
24+
try:
25+
mods.append(importlib.import_module('%s.routes' % module))
26+
except ImportError:
27+
raise ImportError(
28+
"Could not import routers '%s' (Is it on sys.path?)" % (
29+
module))
30+
31+
for mod in mods:
32+
if hasattr(mod, 'load_uris'):
33+
getattr(mod, 'load_uris')(config, **kwargs)
34+
35+
return config
36+
37+
class RequestHandler(tornado.web.RequestHandler):
38+
on_initialize_decorators = []
39+
40+
def initialize(self):
41+
request = self.request
42+
meth = getattr(self, self.request.method.lower(), None)
43+
if meth is None and self.request.method == 'HEAD':
44+
meth = getattr(self, 'get', None)
45+
assert meth is not None, 'Unimplemented method %r' % request.method
46+
47+
for decorator in self.on_initialize_decorators:
48+
meth = decorator(meth)
49+
50+
setattr(self, self.request.method.lower(), meth)
51+
52+
def set_headers(self, items):
53+
if items is None:
54+
return
55+
for k, v in items:
56+
self.set_header(k, v)
57+
58+
def write_error(self, status_code, **kwargs):
59+
"""Override to implement custom error pages.
60+
61+
``write_error`` may call `write`, `render`, `set_header`, etc
62+
to produce output as usual.
63+
64+
If this error was caused by an uncaught exception (including
65+
HTTPError), an ``exc_info`` triple will be available as
66+
``kwargs["exc_info"]``. Note that this exception may not be
67+
the "current" exception for purposes of methods like
68+
``sys.exc_info()`` or ``traceback.format_exc``.
69+
"""
70+
self.finish({
71+
"code": status_code,
72+
"message": self._reason,
73+
})
74+
75+
class Config(object):
76+
def __getitem__(self, item):
77+
return getattr(self, item)
78+
79+
def update(self, **kw):
80+
for name, value in kw.items():
81+
self.__setattr__(name, value)
82+
83+
def setdefault(self, key, default=None):
84+
try:
85+
return getattr(self, key)
86+
except AttributeError:
87+
setattr(self, key, default)
88+
return default
89+
90+
def uri_tuple(self, route, url_prefix):
91+
route['resource'].endpoint = route['endpoint']
92+
route['resource'].blueprint = url_prefix.replace('/', '_')
93+
if url_prefix:
94+
return r'/' + url_prefix + route['urls'][0], route['resource']
95+
return route['urls'][0], route['resource']
96+
97+
def update_uri(self, routes, url_prefix=r''):
98+
self.ROUTES.extend(routes)
99+
self.URIS.extend([self.uri_tuple(r, url_prefix) for r in routes])
100+
101+
URIS = []
102+
ROUTES = []
103+
DEBUG = True
104+
PORT = 5000
105+
WORKER = 1
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
tornado
2+
jsonschema
3+
six
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# -*- coding: utf-8 -*-
2+
3+
{% include '_do_not_change.tpl' %}
4+
from __future__ import absolute_import
5+
6+
{% for view in views -%}
7+
from .api.{{ view.endpoint }} import {{ view.name }}
8+
{% endfor %}
9+
10+
url_prefix = '{{ blueprint }}'
11+
12+
routes = [
13+
{%- for view in views %}
14+
dict(resource={{ view.name }}, urls=[r"{{ view.url }}"], endpoint='{{ view.endpoint }}'),
15+
{%- endfor %}
16+
]
17+
18+
def load_uris(config):
19+
try:
20+
config.update_uri(routes, url_prefix)
21+
except:
22+
pass
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{ swagger }}

0 commit comments

Comments
 (0)