How to create swagger (json) specification file from python using annotations in code?

Reporting problems . I want to automate the creation of machine and human readable specifications for the JSON API so that everyone can visualize and interact with our API. One possible solution is to use OpenAPISpecification (fka swagger). I could not find an acceptable guide to use swagger, especially with a tornado, so my questions are:

  • How can I auto generate swagger spec file from annotations in python code?
  • I also use JSON schemes to validate input, how can I integrate them with the swagger specification.

My API is written in python 2.7.11 with tornado 4.3. Please suggest if you have any other suggestion besides using swagger.

Update: Apispec is an interesting start, but at the moment it cannot be used with JSON schemes, so it does not answer my entire question.

+7
source share
2 answers

we recently had this requirement at work. We created our own generator that generates OpenAPI 3.0 API specifications from Google-style documentation strings. You just need to decorate the handlers and class models. For more information: https://pypi.org/project/tornado-swirl/ - the work is still ongoing, but we are actively working on it.

import tornado.web import tornado_swirl as swirl @swirl.restapi('/item/(?P<itemid>\d+)') class ItemHandler(tornado.web.RequestHandler): def get(self, itemid): """Get Item data. Gets Item data from database. Path Parameter: itemid (int) -- The item id """ pass @swirl.schema class User(object): """This is the user class Your usual long description. Properties: name (string) -- required. Name of user age (int) -- Age of user """ pass def make_app(): return swirl.Application(swirl.api_routes()) if __name__ == "__main__": app = make_app() app.listen(8888) tornado.ioloop.IOLoop.current().start() 
0
source

I wrote a plugin that is compatible with python2.7. You can install it directly using pip install -U tornado-rest-swagger . It uses openapi3.0 syntax. Here is an example:

 import tornado.ioloop import tornado.options import tornado.web from tornado_swagger.components import components from tornado_swagger.setup import setup_swagger class BaseHandler(tornado.web.RequestHandler): def data_received(self, chunk): pass class PostsHandler(BaseHandler): def get(self): """ --- tags: - Posts summary: List posts description: List all posts in feed operationId: getPost responses: '200': description: A list of users content: application/json: schema: $ref: '#/components/schemas/ArrayOfPostModel' application/xml: schema: $ref: '#/components/schemas/ArrayOfPostModel' text/plain: schema: type: string """ def post(self): """ --- tags: - Posts summary: Add a new Post to the blog operationId: addPost requestBody: description: Post object that needs to be added to the blog content: application/json: schema: $ref: '#/components/schemas/PostModel' application/xml: schema: $ref: '#/components/schemas/PostModel' required: true responses: '405': description: Invalid input content: {} security: - petstore_auth: - 'write:pets' - 'read:pets' """ class PostsDetailsHandler(BaseHandler): def get(self, posts_id): """ --- tags: - Posts summary: Find Post by ID description: Returns a single post operationId: getPostById parameters: - name: post_id in: path description: ID of post to return required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/xml: schema: $ref: '#/components/schemas/PostModel' application/json: schema: $ref: '#/components/schemas/PostModel' '400': description: Invalid ID supplied content: {} '404': description: Pet not found content: {} security: - api_key: [] """ def patch(self, posts_id): """ --- tags: - Posts summary: Find Post by ID description: Returns a single post operationId: getPostById parameters: - name: post_id in: path description: ID of post to return required: true schema: type: integer format: int64 requestBody: description: Post object that needs to be added to the blog content: application/json: schema: $ref: '#/components/schemas/PostModel' application/xml: schema: $ref: '#/components/schemas/PostModel' required: true responses: '400': description: Invalid ID supplied content: {} '404': description: Pet not found content: {} security: - api_key: [] """ def delete(self, posts_id): """ --- tags: - Posts summary: Delete Post by ID description: Returns a single post operationId: getPostById parameters: - name: post_id in: path description: ID of post to return required: true schema: type: integer format: int64 responses: '200': description: successful operation content: application/json: schema: type: object description: Post model representation properties: id: type: integer format: int64 title: type: string text: type: string is_visible: type: boolean default: true '400': description: Invalid ID supplied content: {} '404': description: Pet not found content: {} """ @components.schemas.register class PostModel(object): """ --- type: object description: Post model representation properties: id: type: integer format: int64 title: type: string text: type: string is_visible: type: boolean default: true """ @components.schemas.register class ArrayOfPostModel(object): """ --- type: array description: Array of Post model representation items: $ref: '#/components/schemas/PostModel' """ @components.security_schemes.register class JWTToken(object): """ --- type: http scheme: bearer bearerFormat: JWT """ class Application(tornado.web.Application): _routes = [tornado.web.url(r"/api/posts", PostsHandler), tornado.web.url(r"/api/posts/(\w+)", PostsDetailsHandler)] def __init__(self): settings = {"debug": True} setup_swagger( self._routes, swagger_url="/doc", description="", api_version="1.0.0", title="Journal API", contact=dict(name="test", email=" test@domain.com ", url="https://www.cluas.me"), ) super(Application, self).__init__(self._routes, **settings) if __name__ == "__main__": tornado.options.define("port", default="8080", help="Port to listen on") tornado.options.parse_command_line() app = Application() app.listen(port=8080) tornado.ioloop.IOLoop.current().start() 

If your function is a decorator and uses python2.7, you can try wrapping your decorator with a decoder ( pip install decorator ) so that you can get the correct behavior.

0
source

Source: https://habr.com/ru/post/1262484/


All Articles