├── MANIFEST.in ├── examples ├── user_put.yml └── example.py ├── .gitignore ├── LICENSE ├── setup.py ├── README ├── flask_swagger.py └── README.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.md LICENSE 2 | -------------------------------------------------------------------------------- /examples/user_put.yml: -------------------------------------------------------------------------------- 1 | Update a user 2 | --- 3 | tags: 4 | - users 5 | parameters: 6 | - in: body 7 | name: body 8 | schema: 9 | id: User 10 | required: 11 | - email 12 | - name 13 | properties: 14 | email: 15 | type: string 16 | description: email for user 17 | name: 18 | type: string 19 | description: name for user 20 | responses: 21 | 204: 22 | description: User updated 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | *.egg-info/ 22 | .installed.cfg 23 | *.egg 24 | 25 | # PyInstaller 26 | # Usually these files are written by a python script from a template 27 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 28 | *.manifest 29 | *.spec 30 | 31 | # Installer logs 32 | pip-log.txt 33 | pip-delete-this-directory.txt 34 | 35 | # Unit test / coverage reports 36 | htmlcov/ 37 | .tox/ 38 | .coverage 39 | .cache 40 | nosetests.xml 41 | coverage.xml 42 | 43 | # Translations 44 | *.mo 45 | *.pot 46 | 47 | # Django stuff: 48 | *.log 49 | 50 | # Sphinx documentation 51 | docs/_build/ 52 | 53 | # PyBuilder 54 | target/ 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Atli Thorbjornsson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup 3 | except ImportError: 4 | from distutils.core import setup 5 | 6 | with open('README') as file: 7 | long_description = file.read() 8 | 9 | setup(name='flask-swagger', 10 | version='0.2.14', 11 | url='https://github.com/gangverk/flask-swagger', 12 | description='Extract swagger specs from your flask project', 13 | author='Atli Thorbjornsson', 14 | license='MIT', 15 | py_modules=['flask_swagger', 'build_swagger_spec'], 16 | long_description=long_description, 17 | install_requires=['Flask>=0.10', 'PyYAML>=5.1'], 18 | classifiers=[ 19 | 'Intended Audience :: Developers', 20 | 'Operating System :: OS Independent', 21 | 'Programming Language :: Python', 22 | 'Programming Language :: Python :: 2', 23 | 'Programming Language :: Python :: 2.7', 24 | 'Programming Language :: Python :: 3', 25 | 'Programming Language :: Python :: 3.4', 26 | 'Topic :: Software Development :: Libraries :: Python Modules', 27 | ], 28 | entry_points = """ 29 | [console_scripts] 30 | flaskswagger = build_swagger_spec:run 31 | """, 32 | options={ 33 | 'bdist_rpm':{ 34 | 'build_requires':[ 35 | 'python', 36 | 'python-setuptools', 37 | 'python-itsdangerous', 38 | 'python-flask', 39 | 'python-markupsafe', 40 | 'PyYAML', 41 | ], 42 | 'requires':[ 43 | 'python', 44 | 'python-setuptools', 45 | 'python-itsdangerous', 46 | 'python-flask', 47 | 'python-markupsafe', 48 | 'PyYAML', 49 | ], 50 | }, 51 | }, 52 | ) 53 | -------------------------------------------------------------------------------- /examples/example.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | from flask.views import MethodView 3 | from flask_swagger import swagger 4 | 5 | app = Flask(__name__) 6 | 7 | class UserAPI(MethodView): 8 | 9 | def get(self, team_id): 10 | """ 11 | Get a list of users 12 | First line is the summary 13 | All following lines until the hyphens is added to description 14 | --- 15 | tags: 16 | - users 17 | responses: 18 | 200: 19 | description: Returns a list of users 20 | """ 21 | return [] 22 | 23 | def post(self, team_id): 24 | """ 25 | Create a new user 26 | --- 27 | tags: 28 | - users 29 | parameters: 30 | - in: body 31 | name: body 32 | schema: 33 | id: User 34 | required: 35 | - email 36 | - name 37 | properties: 38 | email: 39 | type: string 40 | description: email for user 41 | name: 42 | type: string 43 | description: name for user 44 | responses: 45 | 201: 46 | description: User created 47 | """ 48 | return {} 49 | 50 | def put(self, user_id): 51 | """ 52 | Update a user 53 | 54 | swagger_from_file: user_put.yml 55 | """ 56 | return {} 57 | 58 | 59 | @app.after_request 60 | def after_request(response): 61 | response.headers.add('Access-Control-Allow-Origin','*') 62 | response.headers.add('Access-Control-Allow-Headers', "Authorization, Content-Type") 63 | response.headers.add('Access-Control-Expose-Headers', "Authorization") 64 | response.headers.add('Access-Control-Allow-Methods', "GET, POST, PUT, DELETE, OPTIONS") 65 | response.headers.add('Access-Control-Allow-Credentials', "true") 66 | response.headers.add('Access-Control-Max-Age', 60 * 60 * 24 * 20) 67 | return response 68 | 69 | view = UserAPI.as_view('users') 70 | app.add_url_rule('/users/', view_func=view, methods=["GET"]) 71 | app.add_url_rule('/testing/', view_func=view) 72 | 73 | @app.route("/hacky") 74 | def bla(): 75 | """ 76 | An endpoint that isn't using method view 77 | --- 78 | tags: 79 | - hacks 80 | responses: 81 | 200: 82 | description: Hacked some hacks 83 | schema: 84 | id: Hack 85 | properties: 86 | hack: 87 | type: string 88 | description: it's a hack 89 | subitems: 90 | type: array 91 | items: 92 | schema: 93 | id: SubItem 94 | properties: 95 | bla: 96 | type: string 97 | description: Bla 98 | blu: 99 | type: integer 100 | description: Blu 101 | 102 | """ 103 | return jsonify(['hacky']) 104 | 105 | class PetAPI(MethodView): 106 | 107 | def get(self, pet_id): 108 | """ 109 | Get a pet. 110 | 111 | This is an example of how to use references and factored out definitions 112 | --- 113 | tags: 114 | - pets 115 | parameters: 116 | - in: path 117 | name: pet_id 118 | definitions: 119 | - schema: 120 | id: Pet 121 | required: 122 | - name 123 | - owner 124 | properties: 125 | name: 126 | type: string 127 | description: the pet's name 128 | owner: 129 | $ref: '#/definitions/Owner' 130 | - schema: 131 | id: Owner 132 | required: 133 | - name 134 | properties: 135 | name: 136 | type: string 137 | description: the owner's name 138 | responses: 139 | 200: 140 | description: Returns the specified pet 141 | $ref: '#/definitions/Pet' 142 | """ 143 | return {} 144 | 145 | pet_view = PetAPI.as_view('pets') 146 | app.add_url_rule('/pets/', view_func=pet_view, methods=["GET"]) 147 | 148 | 149 | @app.route("/") 150 | def hello(): 151 | return "Hello World!" 152 | 153 | @app.route("/spec") 154 | def spec(): 155 | return jsonify(swagger(app, from_file_keyword='swagger_from_file')) 156 | 157 | if __name__ == "__main__": 158 | app.run(debug=True) 159 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | flask-swagger 2 | ============= 3 | 4 | A Swagger 2.0 spec extractor for Flask 5 | 6 | Install: 7 | 8 | :: 9 | 10 | pip install flask-swagger 11 | 12 | Flask-swagger provides a method (swagger) that inspects the Flask app 13 | for endpoints that contain YAML docstrings with Swagger 2.0 14 | `Operation `__ 15 | objects. 16 | 17 | :: 18 | 19 | class UserAPI(MethodView): 20 | 21 | def post(self): 22 | """ 23 | Create a new user 24 | --- 25 | tags: 26 | - users 27 | definitions: 28 | - schema: 29 | id: Group 30 | properties: 31 | name: 32 | type: string 33 | description: the group's name 34 | parameters: 35 | - in: body 36 | name: body 37 | schema: 38 | id: User 39 | required: 40 | - email 41 | - name 42 | properties: 43 | email: 44 | type: string 45 | description: email for user 46 | name: 47 | type: string 48 | description: name for user 49 | address: 50 | description: address for user 51 | schema: 52 | id: Address 53 | properties: 54 | street: 55 | type: string 56 | state: 57 | type: string 58 | country: 59 | type: string 60 | postalcode: 61 | type: string 62 | groups: 63 | type: array 64 | description: list of groups 65 | items: 66 | $ref: "#/definitions/Group" 67 | responses: 68 | 201: 69 | description: User created 70 | """ 71 | return {} 72 | 73 | Flask-swagger supports docstrings in methods of MethodView classes and 74 | regular Flask view functions. 75 | 76 | Following YAML conventions, flask-swagger searches for ``---``, 77 | everything preceding is provided as ``summary`` (first line) and 78 | ``description`` (following lines) for the endpoint while everything 79 | after is parsed as a swagger 80 | `Operation `__ 81 | object. 82 | 83 | In order to support inline definition of 84 | `Schema `__ 85 | objects in 86 | `Parameter `__ 87 | and 88 | `Response `__ 89 | objects, flask-swagger veers a little off from the standard. We require 90 | an ``id`` field for the inline Schema which is then used to correctly 91 | place the 92 | `Schema `__ 93 | object in the 94 | `Definitions `__ 95 | object. 96 | 97 | `Schema `__ 98 | objects can be defined in a definitions section within the docstrings (see group object above) or within responses or parameters (see user object above). We alo support schema objects nested within the properties of other 99 | `Schema `__ 100 | objects. An example is shown above with the address property of User. 101 | 102 | To expose your Swagger specification to the world you provide a Flask 103 | route that does something along these lines 104 | 105 | :: 106 | 107 | from flask import Flask, jsonify 108 | from flask_swagger import swagger 109 | 110 | app = Flask(__name__) 111 | 112 | @app.route("/spec") 113 | def spec(): 114 | return jsonify(swagger(app)) 115 | 116 | Note that the Swagger specification returned by ``swagger(app)`` is as 117 | minimal as it can be. It's your job to override and add to the 118 | specification as you see fit. 119 | 120 | :: 121 | 122 | @app.route("/spec") 123 | def spec(): 124 | swag = swagger(app) 125 | swag['info']['version'] = "1.0" 126 | swag['info']['title'] = "My API" 127 | return jsonify(swag) 128 | 129 | `Swagger-UI `__ 130 | 131 | Swagger-UI is the reason we embarked on this mission to begin with, 132 | flask-swagger does not however include Swagger-UI. Simply follow the 133 | awesome documentation over at https://github.com/swagger-api/swagger-ui 134 | and point your 135 | `swaggerUi.url `__ 136 | to your new flask-swagger endpoint and enjoy. 137 | 138 | Acknowledgments 139 | 140 | Flask-swagger builds on ideas and code from 141 | `flask-sillywalk `__ and 142 | `flask-restful-swagger `__ 143 | -------------------------------------------------------------------------------- /flask_swagger.py: -------------------------------------------------------------------------------- 1 | """ 2 | What's the big idea? 3 | 4 | An endpoint that traverses all restful endpoints producing a swagger 2.0 schema 5 | If a swagger yaml description is found in the docstrings for an endpoint 6 | we add the endpoint to swagger specification output 7 | 8 | - Added basic_path parameter 9 | """ 10 | import inspect 11 | import yaml 12 | import re 13 | import os 14 | 15 | from collections import defaultdict 16 | 17 | 18 | def _sanitize(comment): 19 | return comment.replace('\n', '
') if comment else comment 20 | 21 | 22 | def _find_from_file(full_doc, from_file_keyword, base_path): 23 | """ 24 | Finds a line in like 25 | 26 | 27 | 28 | and return path 29 | """ 30 | path = None 31 | 32 | for line in full_doc.splitlines(): 33 | if from_file_keyword in line: 34 | parts = line.strip().split(':') 35 | if len(parts) == 2 and parts[0].strip() == from_file_keyword: 36 | path = parts[1].strip() 37 | break 38 | path = path if not path else os.path.join(base_path, path) 39 | return path 40 | 41 | 42 | def _doc_from_file(path): 43 | doc = None 44 | with open(path) as f: 45 | doc = f.read() 46 | return doc 47 | 48 | 49 | def _parse_docstring(obj, process_doc, from_file_keyword, base_path): 50 | first_line, other_lines, swag = None, None, None 51 | full_doc = inspect.getdoc(obj) 52 | if full_doc: 53 | if from_file_keyword is not None: 54 | from_file = _find_from_file(full_doc, from_file_keyword, base_path) 55 | if from_file: 56 | full_doc_from_file = _doc_from_file(from_file) 57 | if full_doc_from_file: 58 | full_doc = full_doc_from_file 59 | line_feed = full_doc.find('\n') 60 | if line_feed != -1: 61 | first_line = process_doc(full_doc[:line_feed]) 62 | yaml_sep = full_doc[line_feed+1:].find('---') 63 | if yaml_sep != -1: 64 | other_lines = process_doc(full_doc[line_feed+1:line_feed+yaml_sep]) 65 | swag = yaml.full_load(full_doc[line_feed+yaml_sep:]) 66 | else: 67 | other_lines = process_doc(full_doc[line_feed+1:]) 68 | else: 69 | first_line = full_doc 70 | return first_line, other_lines, swag 71 | 72 | 73 | def _extract_definitions(alist, level=None): 74 | """ 75 | Since we couldn't be bothered to register models elsewhere 76 | our definitions need to be extracted from the parameters. 77 | We require an 'id' field for the schema to be correctly 78 | added to the definitions list. 79 | """ 80 | 81 | def _extract_array_defs(source): 82 | # extract any definitions that are within arrays 83 | # this occurs recursively 84 | ret = [] 85 | items = source.get('items') 86 | if items is not None and 'schema' in items: 87 | ret += _extract_definitions([items], level+1) 88 | return ret 89 | 90 | # for tracking level of recursion 91 | if level is None: 92 | level = 0 93 | 94 | defs = list() 95 | if alist is not None: 96 | for item in alist: 97 | schema = item.get("schema") 98 | if schema is not None: 99 | schema_id = schema.get("id") 100 | if schema_id is not None: 101 | defs.append(schema) 102 | ref = {"$ref": "#/definitions/{}".format(schema_id)} 103 | 104 | # only add the reference as a schema if we are in a response or 105 | # a parameter i.e. at the top level 106 | # directly ref if a definition is used within another definition 107 | if level == 0: 108 | item['schema'] = ref 109 | else: 110 | item.update(ref) 111 | del item['schema'] 112 | 113 | # extract any definitions that are within properties 114 | # this occurs recursively 115 | properties = schema.get('properties') 116 | if properties is not None: 117 | defs += _extract_definitions(properties.values(), level+1) 118 | 119 | defs += _extract_array_defs(schema) 120 | 121 | defs += _extract_array_defs(item) 122 | 123 | return defs 124 | 125 | 126 | def swagger(app, prefix=None, process_doc=_sanitize, 127 | from_file_keyword=None, template=None, base_path=""): 128 | """ 129 | Call this from an @app.route method like this 130 | @app.route('/spec.json') 131 | def spec(): 132 | return jsonify(swagger(app)) 133 | 134 | We go through all endpoints of the app searching for swagger endpoints 135 | We provide the minimum required data according to swagger specs 136 | Callers can and should add and override at will 137 | 138 | Arguments: 139 | app -- the flask app to inspect 140 | 141 | Keyword arguments: 142 | process_doc -- text sanitization method, the default simply replaces \n with
143 | from_file_keyword -- how to specify a file to load doc from 144 | template -- The spec to start with and update as flask-swagger finds paths. 145 | """ 146 | output = { 147 | "swagger": "2.0", 148 | "info": { 149 | "version": "0.0.0", 150 | "title": "Cool product name", 151 | } 152 | } 153 | paths = defaultdict(dict) 154 | definitions = defaultdict(dict) 155 | if template is not None: 156 | output.update(template) 157 | # check for template provided paths and definitions 158 | for k, v in output.get('paths', {}).items(): 159 | paths[k] = v 160 | for k, v in output.get('definitions', {}).items(): 161 | definitions[k] = v 162 | output["paths"] = paths 163 | output["definitions"] = definitions 164 | 165 | ignore_verbs = {"HEAD", "OPTIONS"} 166 | # technically only responses is non-optional 167 | optional_fields = ['tags', 'consumes', 'produces', 'schemes', 'security', 168 | 'deprecated', 'operationId', 'externalDocs'] 169 | 170 | for rule in app.url_map.iter_rules(): 171 | if prefix and rule.rule[:len(prefix)] != prefix: 172 | continue 173 | endpoint = app.view_functions[rule.endpoint] 174 | methods = dict() 175 | for verb in rule.methods.difference(ignore_verbs): 176 | verb = verb.lower() 177 | if hasattr(endpoint, 'methods') \ 178 | and verb in map(lambda m: m.lower(), endpoint.methods) \ 179 | and hasattr(endpoint.view_class, verb): 180 | methods[verb] = getattr(endpoint.view_class, verb) 181 | else: 182 | methods[verb] = endpoint 183 | operations = dict() 184 | for verb, method in methods.items(): 185 | summary, description, swag = _parse_docstring(method, process_doc, 186 | from_file_keyword, base_path) 187 | if swag is not None: # we only add endpoints with swagger data in the docstrings 188 | defs = swag.get('definitions', []) 189 | defs = _extract_definitions(defs) 190 | params = swag.get('parameters', []) 191 | defs += _extract_definitions(params) 192 | responses = swag.get('responses', {}) 193 | responses = { 194 | str(key): value 195 | for key, value in responses.items() 196 | } 197 | if responses is not None: 198 | defs = defs + _extract_definitions(responses.values()) 199 | for definition in defs: 200 | def_id = definition.pop('id') 201 | if def_id is not None: 202 | definitions[def_id].update(definition) 203 | operation = dict( 204 | summary=summary, 205 | description=description, 206 | responses=responses 207 | ) 208 | # parameters - swagger ui dislikes empty parameter lists 209 | if len(params) > 0: 210 | operation['parameters'] = params 211 | # other optionals 212 | for key in optional_fields: 213 | if key in swag: 214 | operation[key] = swag.get(key) 215 | operations[verb] = operation 216 | 217 | if len(operations): 218 | rule = str(rule) 219 | for arg in re.findall('(<([^<>]*:)?([^<>]*)>)', rule): 220 | rule = rule.replace(arg[0], '{%s}' % arg[2]) 221 | paths[rule].update(operations) 222 | return output 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flask-swagger 2 | A Swagger 2.0 spec extractor for Flask 3 | 4 | You can now specify base path for yml files: 5 | ```python 6 | app = Flask(__name__) 7 | 8 | @app.route("/spec") 9 | def spec(): 10 | base_path = os.path.join(app.root_path, 'docs') 11 | return jsonify(swagger(app), from_file_keyword="swagger_from_file", base_path=base_path) 12 | ``` 13 | and use relative paths: 14 | ```python 15 | @app.route('/test', methods=['POST']) 16 | def login(): 17 | """ 18 | swagger_from_file: test.yml 19 | """ 20 | ``` 21 | 22 | Install: 23 | 24 | ```shell 25 | pip install flask-swagger 26 | ``` 27 | Flask-swagger provides a method (swagger) that inspects the Flask app for endpoints that contain YAML docstrings with Swagger 2.0 [Operation](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#operation-object) objects. 28 | 29 | ```python 30 | class UserAPI(MethodView): 31 | 32 | def post(self): 33 | """ 34 | Create a new user 35 | --- 36 | tags: 37 | - users 38 | definitions: 39 | - schema: 40 | id: Group 41 | properties: 42 | name: 43 | type: string 44 | description: the group's name 45 | parameters: 46 | - in: body 47 | name: body 48 | schema: 49 | id: User 50 | required: 51 | - email 52 | - name 53 | properties: 54 | email: 55 | type: string 56 | description: email for user 57 | name: 58 | type: string 59 | description: name for user 60 | address: 61 | description: address for user 62 | schema: 63 | id: Address 64 | properties: 65 | street: 66 | type: string 67 | state: 68 | type: string 69 | country: 70 | type: string 71 | postalcode: 72 | type: string 73 | groups: 74 | type: array 75 | description: list of groups 76 | items: 77 | $ref: "#/definitions/Group" 78 | responses: 79 | 201: 80 | description: User created 81 | """ 82 | return {} 83 | ``` 84 | Flask-swagger supports docstrings in methods of MethodView classes (à la [Flask-RESTful](https://github.com/flask-restful/flask-restful)) and regular Flask view functions. 85 | 86 | Following YAML conventions, flask-swagger searches for `---`, everything preceding is provided as `summary` (first line) and `description` (following lines) for the endpoint while everything after is parsed as a swagger [Operation](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#operation-object) object. 87 | 88 | In order to support inline definition of [Schema ](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject) objects in [Parameter](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#parameterObject) and [Response](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#responsesObject) objects, flask-swagger veers a little off from the standard. We require an `id` field for the inline Schema which is then used to correctly place the [Schema](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject) object in the [Definitions](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#definitionsObject) object. 89 | 90 | 91 | [Schema ](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject) objects can be defined in a definitions section within the docstrings (see group object above) or within responses or parameters (see user object above). We also support schema objects nested within the properties of other [Schema ](https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#schemaObject) objects. An example is shown above with the address property of User. 92 | 93 | If you don't like to put YAML on docstrings you can put the same content in an external file. 94 | 95 | #### file.yml 96 | ```yaml 97 | Create a new user 98 | --- 99 | tags: 100 | - users 101 | definitions: 102 | - schema: 103 | id: Group 104 | properties: 105 | name: 106 | type: string 107 | description: the group's name 108 | parameters: 109 | - in: body 110 | name: body 111 | schema: 112 | id: User 113 | required: 114 | - email 115 | - name 116 | properties: 117 | email: 118 | type: string 119 | description: email for user 120 | name: 121 | type: string 122 | description: name for user 123 | address: 124 | description: address for user 125 | schema: 126 | id: Address 127 | properties: 128 | street: 129 | type: string 130 | state: 131 | type: string 132 | country: 133 | type: string 134 | postalcode: 135 | type: string 136 | groups: 137 | type: array 138 | description: list of groups 139 | items: 140 | $ref: "#/definitions/Group" 141 | responses: 142 | 201: 143 | description: User created 144 | ``` 145 | 146 | and point to it in your docstring. 147 | 148 | ```python 149 | class UserAPI(MethodView): 150 | 151 | def post(self): 152 | """ 153 | Create a new user 154 | 155 | blah blah 156 | 157 | swagger_from_file: path/to/file.yml 158 | 159 | blah blah 160 | """ 161 | return {} 162 | ``` 163 | 164 | Note that you can replace `swagger_from_file` by another keyword. Supply your chosen keyword as an argument to swagger. 165 | 166 | 167 | To expose your Swagger specification to the world you provide a Flask route that does something along these lines 168 | 169 | ```python 170 | from flask import Flask, jsonify 171 | from flask_swagger import swagger 172 | 173 | app = Flask(__name__) 174 | 175 | @app.route("/spec") 176 | def spec(): 177 | return jsonify(swagger(app)) 178 | ``` 179 | 180 | Note that the Swagger specification returned by `swagger(app)` is as minimal as it can be. It's your job to override and add to the specification as you see fit. 181 | 182 | ```python 183 | @app.route("/spec") 184 | def spec(): 185 | swag = swagger(app) 186 | swag['info']['version'] = "1.0" 187 | swag['info']['title'] = "My API" 188 | return jsonify(swag) 189 | ``` 190 | 191 | 192 | [Swagger-UI](https://github.com/swagger-api/swagger-ui) 193 | 194 | Swagger-UI is the reason we embarked on this mission to begin with, flask-swagger does not however include Swagger-UI. Simply follow the awesome documentation over at https://github.com/swagger-api/swagger-ui and point your [swaggerUi.url](https://github.com/swagger-api/swagger-ui#swaggerui) to your new flask-swagger endpoint and enjoy. 195 | 196 | ## flaskswagger Command 197 | This package now comes with a very simple command line interface: flaskswagger. This command can be used to build and update swagger specs for your flask apps from the command line or at build time. 198 | 199 | ```shell 200 | flaskswagger -h 201 | ``` 202 | 203 | ``` 204 | usage: flaskswagger [-h] [--template TEMPLATE] [--out-dir OUT_DIR] 205 | [--definitions DEFINITIONS] [--host HOST] 206 | [--base-path BASE_PATH] [--version VERSION] 207 | app 208 | 209 | positional arguments: 210 | app the flask app to swaggerify 211 | 212 | optional arguments: 213 | -h, --help show this help message and exit 214 | --template TEMPLATE template spec to start with, before any other options 215 | or processing 216 | --out-dir OUT_DIR the directory to output to 217 | --definitions DEFINITIONS 218 | json definitions file 219 | --host HOST 220 | --base-path BASE_PATH 221 | --version VERSION Specify a spec version 222 | 223 | ``` 224 | 225 | For example, this can be used to build a swagger spec which can be served from your static directory. In the example below, we use the manually created swagger.json.manual as a template, and output to the `static/` directory. 226 | 227 | ```shell 228 | flaskswagger server:app --template static/swagger.json.manual --out-dir static/ 229 | ``` 230 | Also, you can ask flaskswagger to add host and basePath to your swagger spec: 231 | 232 | ```shell 233 | flaskswagger server:app --host localhost:5000 --base-path /v1 234 | ``` 235 | 236 | Acknowledgements 237 | 238 | Flask-swagger builds on ideas and code from [flask-sillywalk](https://github.com/hobbeswalsh/flask-sillywalk) and [flask-restful-swagger](https://github.com/rantav/flask-restful-swagger) 239 | 240 | Notable forks 241 | 242 | [Flasgger](https://github.com/rochacbruno/flasgger) 243 | --------------------------------------------------------------------------------