flask-openapi 4.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flask_openapi/__init__.py +44 -0
- flask_openapi/__version__.py +5 -0
- flask_openapi/blueprint.py +201 -0
- flask_openapi/commands.py +38 -0
- flask_openapi/models/__init__.py +91 -0
- flask_openapi/models/callback.py +18 -0
- flask_openapi/models/components.py +37 -0
- flask_openapi/models/contact.py +17 -0
- flask_openapi/models/data_type.py +18 -0
- flask_openapi/models/discriminator.py +16 -0
- flask_openapi/models/encoding.py +27 -0
- flask_openapi/models/example.py +19 -0
- flask_openapi/models/external_documentation.py +16 -0
- flask_openapi/models/file.py +27 -0
- flask_openapi/models/header.py +17 -0
- flask_openapi/models/info.py +24 -0
- flask_openapi/models/license.py +17 -0
- flask_openapi/models/link.py +23 -0
- flask_openapi/models/media_type.py +24 -0
- flask_openapi/models/oauth_flow.py +18 -0
- flask_openapi/models/oauth_flows.py +20 -0
- flask_openapi/models/operation.py +36 -0
- flask_openapi/models/parameter.py +34 -0
- flask_openapi/models/parameter_in_type.py +13 -0
- flask_openapi/models/path_item.py +36 -0
- flask_openapi/models/paths.py +9 -0
- flask_openapi/models/reference.py +14 -0
- flask_openapi/models/request_body.py +19 -0
- flask_openapi/models/response.py +23 -0
- flask_openapi/models/responses.py +11 -0
- flask_openapi/models/schema.py +59 -0
- flask_openapi/models/security_requirement.py +8 -0
- flask_openapi/models/security_scheme.py +25 -0
- flask_openapi/models/security_scheme_in_type.py +12 -0
- flask_openapi/models/server.py +19 -0
- flask_openapi/models/server_variable.py +17 -0
- flask_openapi/models/style_values.py +14 -0
- flask_openapi/models/tag.py +15 -0
- flask_openapi/models/validation_error.py +24 -0
- flask_openapi/models/xml.py +19 -0
- flask_openapi/openapi.py +449 -0
- flask_openapi/plugins.py +17 -0
- flask_openapi/py.typed +0 -0
- flask_openapi/request.py +250 -0
- flask_openapi/scaffold.py +547 -0
- flask_openapi/templates.py +113 -0
- flask_openapi/types.py +27 -0
- flask_openapi/utils.py +633 -0
- flask_openapi/view.py +230 -0
- flask_openapi-4.3.1.dist-info/METADATA +258 -0
- flask_openapi-4.3.1.dist-info/RECORD +53 -0
- flask_openapi-4.3.1.dist-info/WHEEL +4 -0
- flask_openapi-4.3.1.dist-info/licenses/LICENSE.rst +21 -0
flask_openapi/view.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Author : llc
|
|
3
|
+
# @Time : 2022/10/14 16:09
|
|
4
|
+
import typing
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
from .models import ExternalDocumentation, Server, Tag
|
|
8
|
+
from .types import ResponseDict
|
|
9
|
+
from .utils import (
|
|
10
|
+
HTTPMethod,
|
|
11
|
+
convert_responses_key_to_string,
|
|
12
|
+
get_operation,
|
|
13
|
+
get_operation_id_for_path,
|
|
14
|
+
get_responses,
|
|
15
|
+
parse_and_store_tags,
|
|
16
|
+
parse_method,
|
|
17
|
+
parse_parameters,
|
|
18
|
+
parse_rule,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if typing.TYPE_CHECKING: # pragma: no cover
|
|
22
|
+
from .openapi import OpenAPI
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class APIView:
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
url_prefix: str | None = None,
|
|
29
|
+
view_tags: list[Tag] | None = None,
|
|
30
|
+
view_security: list[dict[str, list[str]]] | None = None,
|
|
31
|
+
view_responses: ResponseDict | None = None,
|
|
32
|
+
doc_ui: bool = True,
|
|
33
|
+
operation_id_callback: Callable = get_operation_id_for_path,
|
|
34
|
+
validate_response: bool | None = None,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
Create a class-based view
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
url_prefix: A path to prepend to all the APIView's urls
|
|
41
|
+
view_tags: APIView tags for every API.
|
|
42
|
+
view_security: APIView security for every API.
|
|
43
|
+
view_responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
44
|
+
doc_ui: Enable OpenAPI document UI (Swagger UI and Redoc). Defaults to True.
|
|
45
|
+
operation_id_callback: Callback function for custom operation_id generation.
|
|
46
|
+
Receives name (str), path (str) and method (str) parameters.
|
|
47
|
+
Defaults to `get_operation_id_for_path` from utils
|
|
48
|
+
validate_response: Verify the response body.
|
|
49
|
+
"""
|
|
50
|
+
self.url_prefix = url_prefix
|
|
51
|
+
self.view_tags = view_tags or []
|
|
52
|
+
self.view_security = view_security or []
|
|
53
|
+
|
|
54
|
+
# Convert key to string
|
|
55
|
+
self.view_responses = convert_responses_key_to_string(view_responses or {})
|
|
56
|
+
|
|
57
|
+
self.doc_ui = doc_ui
|
|
58
|
+
self.operation_id_callback: Callable = operation_id_callback
|
|
59
|
+
|
|
60
|
+
self.views: dict = dict()
|
|
61
|
+
self.paths: dict = dict()
|
|
62
|
+
self.components_schemas: dict = dict()
|
|
63
|
+
self.tags: list[Tag] = []
|
|
64
|
+
self.tag_names: list[str] = []
|
|
65
|
+
|
|
66
|
+
self.validate_response = validate_response
|
|
67
|
+
|
|
68
|
+
def route(self, rule: str):
|
|
69
|
+
"""Decorator for view class"""
|
|
70
|
+
|
|
71
|
+
def wrapper(cls):
|
|
72
|
+
if self.views.get(rule): # pragma: no cover
|
|
73
|
+
raise ValueError(f"malformed url rule: {rule!r}")
|
|
74
|
+
methods = []
|
|
75
|
+
|
|
76
|
+
# Parse rule: merge url_prefix and format rule from /pet/<petId> to /pet/{petId}
|
|
77
|
+
uri = parse_rule(rule, url_prefix=self.url_prefix)
|
|
78
|
+
|
|
79
|
+
for method in HTTPMethod:
|
|
80
|
+
cls_method = getattr(cls, method.lower(), None)
|
|
81
|
+
if not cls_method:
|
|
82
|
+
continue
|
|
83
|
+
methods.append(method)
|
|
84
|
+
if self.doc_ui is False:
|
|
85
|
+
continue
|
|
86
|
+
if not getattr(cls_method, "operation", None):
|
|
87
|
+
continue
|
|
88
|
+
# Parse method
|
|
89
|
+
parse_method(uri, method, self.paths, cls_method.operation)
|
|
90
|
+
# Update operation_id
|
|
91
|
+
if not cls_method.operation.operationId:
|
|
92
|
+
cls_method.operation.operationId = self.operation_id_callback(
|
|
93
|
+
name=cls_method.__qualname__, path=rule, method=method
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Convert route parameters from {param} to <param>
|
|
97
|
+
_rule = uri.replace("{", "<").replace("}", ">")
|
|
98
|
+
self.views[_rule] = (cls, methods)
|
|
99
|
+
|
|
100
|
+
return cls
|
|
101
|
+
|
|
102
|
+
return wrapper
|
|
103
|
+
|
|
104
|
+
def doc(
|
|
105
|
+
self,
|
|
106
|
+
*,
|
|
107
|
+
tags: list[Tag] | None = None,
|
|
108
|
+
summary: str | None = None,
|
|
109
|
+
description: str | None = None,
|
|
110
|
+
external_docs: ExternalDocumentation | None = None,
|
|
111
|
+
operation_id: str | None = None,
|
|
112
|
+
responses: ResponseDict | None = None,
|
|
113
|
+
deprecated: bool | None = None,
|
|
114
|
+
security: list[dict[str, list[Any]]] | None = None,
|
|
115
|
+
servers: list[Server] | None = None,
|
|
116
|
+
openapi_extensions: dict[str, Any] | None = None,
|
|
117
|
+
validate_response: bool | None = None,
|
|
118
|
+
doc_ui: bool = True,
|
|
119
|
+
) -> Callable:
|
|
120
|
+
"""
|
|
121
|
+
Decorator for view method.
|
|
122
|
+
More information goto https://spec.openapis.org/oas/v3.1.0#operation-object
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
tags: Adds metadata to a single tag.
|
|
126
|
+
summary: A short summary of what the operation does.
|
|
127
|
+
description: A verbose explanation of the operation behavior.
|
|
128
|
+
external_docs: Additional external documentation for this operation.
|
|
129
|
+
operation_id: Unique string used to identify the operation.
|
|
130
|
+
responses: API responses should be either a subclass of BaseModel, a dictionary, or None.
|
|
131
|
+
deprecated: Declares this operation to be deprecated.
|
|
132
|
+
security: A declaration of which security mechanisms can be used for this operation.
|
|
133
|
+
servers: An alternative server array to service this operation.
|
|
134
|
+
openapi_extensions: Allows extensions to the OpenAPI Schema.
|
|
135
|
+
doc_ui: Declares this operation to be shown. Default to True.
|
|
136
|
+
validate_response: Verify the response body.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
new_responses = convert_responses_key_to_string(responses or {})
|
|
140
|
+
security = security or []
|
|
141
|
+
tags = tags + self.view_tags if tags else self.view_tags
|
|
142
|
+
|
|
143
|
+
def decorator(func):
|
|
144
|
+
func.validate_response = validate_response
|
|
145
|
+
func.responses = responses
|
|
146
|
+
|
|
147
|
+
if self.doc_ui is False or doc_ui is False:
|
|
148
|
+
return func
|
|
149
|
+
|
|
150
|
+
# Global response combines API responses
|
|
151
|
+
combine_responses = {**self.view_responses, **new_responses}
|
|
152
|
+
|
|
153
|
+
# Create operation
|
|
154
|
+
operation = get_operation(
|
|
155
|
+
func, summary=summary, description=description, openapi_extensions=openapi_extensions
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Set external docs
|
|
159
|
+
if external_docs:
|
|
160
|
+
operation.externalDocs = external_docs
|
|
161
|
+
|
|
162
|
+
# Unique string used to identify the operation.
|
|
163
|
+
if operation_id:
|
|
164
|
+
operation.operationId = operation_id
|
|
165
|
+
|
|
166
|
+
# Only set `deprecated` if True, otherwise leave it as None
|
|
167
|
+
if deprecated is not None:
|
|
168
|
+
operation.deprecated = deprecated
|
|
169
|
+
|
|
170
|
+
# Add security
|
|
171
|
+
_security = (security or []) + self.view_security or None
|
|
172
|
+
if _security:
|
|
173
|
+
operation.security = _security
|
|
174
|
+
|
|
175
|
+
# Add servers
|
|
176
|
+
if servers:
|
|
177
|
+
operation.servers = servers
|
|
178
|
+
|
|
179
|
+
# Store tags
|
|
180
|
+
parse_and_store_tags(tags, self.tags, self.tag_names, operation)
|
|
181
|
+
|
|
182
|
+
# Parse parameters
|
|
183
|
+
parse_parameters(func, components_schemas=self.components_schemas, operation=operation)
|
|
184
|
+
|
|
185
|
+
# Parse response
|
|
186
|
+
get_responses(combine_responses, self.components_schemas, operation)
|
|
187
|
+
func.operation = operation
|
|
188
|
+
|
|
189
|
+
return func
|
|
190
|
+
|
|
191
|
+
return decorator
|
|
192
|
+
|
|
193
|
+
def register(
|
|
194
|
+
self, app: "OpenAPI", url_prefix: str | None = None, view_kwargs: dict[Any, Any] | None = None
|
|
195
|
+
) -> None:
|
|
196
|
+
"""
|
|
197
|
+
Register the API views with the given OpenAPI app.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
app: An instance of the OpenAPI app.
|
|
201
|
+
url_prefix: A path to prepend to all the APIView's urls
|
|
202
|
+
view_kwargs: Additional keyword arguments to pass to the API views.
|
|
203
|
+
"""
|
|
204
|
+
for rule, (cls, methods) in self.views.items():
|
|
205
|
+
for method in methods:
|
|
206
|
+
func = getattr(cls, method.lower())
|
|
207
|
+
_validate_response = getattr(func, "validate_response", None) or self.validate_response
|
|
208
|
+
header, cookie, path, query, form, body, raw = parse_parameters(func, doc_ui=False)
|
|
209
|
+
view_func = app.create_view_func(
|
|
210
|
+
func,
|
|
211
|
+
header,
|
|
212
|
+
cookie,
|
|
213
|
+
path,
|
|
214
|
+
query,
|
|
215
|
+
form,
|
|
216
|
+
body,
|
|
217
|
+
raw,
|
|
218
|
+
view_class=cls,
|
|
219
|
+
view_kwargs=view_kwargs,
|
|
220
|
+
responses=func.responses,
|
|
221
|
+
validate_response=_validate_response,
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
if url_prefix and self.url_prefix and url_prefix != self.url_prefix:
|
|
225
|
+
rule = url_prefix + rule.removeprefix(self.url_prefix)
|
|
226
|
+
elif url_prefix and not self.url_prefix:
|
|
227
|
+
rule = url_prefix.rstrip("/") + "/" + rule.lstrip("/")
|
|
228
|
+
|
|
229
|
+
options = {"endpoint": cls.__name__ + "." + method.lower(), "methods": [method.upper()]}
|
|
230
|
+
app.add_url_rule(rule, view_func=view_func, **options)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flask-openapi
|
|
3
|
+
Version: 4.3.1
|
|
4
|
+
Summary: Generate REST API and OpenAPI documentation for your Flask project.
|
|
5
|
+
Project-URL: Homepage, https://github.com/luolingchun/flask-openapi
|
|
6
|
+
Project-URL: Documentation, https://luolingchun.github.io/flask-openapi
|
|
7
|
+
Maintainer-email: llc <luolingchun@outlook.com>
|
|
8
|
+
License: MIT
|
|
9
|
+
License-File: LICENSE.rst
|
|
10
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Framework :: Flask
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: flask>=2.0
|
|
26
|
+
Requires-Dist: pydantic>=2.4
|
|
27
|
+
Provides-Extra: async
|
|
28
|
+
Requires-Dist: asgiref>=3.2; extra == 'async'
|
|
29
|
+
Provides-Extra: dotenv
|
|
30
|
+
Requires-Dist: python-dotenv; extra == 'dotenv'
|
|
31
|
+
Provides-Extra: elements
|
|
32
|
+
Requires-Dist: flask-openapi-elements; extra == 'elements'
|
|
33
|
+
Provides-Extra: email
|
|
34
|
+
Requires-Dist: email-validator; extra == 'email'
|
|
35
|
+
Provides-Extra: rapidoc
|
|
36
|
+
Requires-Dist: flask-openapi-rapidoc; extra == 'rapidoc'
|
|
37
|
+
Provides-Extra: rapipdf
|
|
38
|
+
Requires-Dist: flask-openapi-rapipdf; extra == 'rapipdf'
|
|
39
|
+
Provides-Extra: redoc
|
|
40
|
+
Requires-Dist: flask-openapi-redoc; extra == 'redoc'
|
|
41
|
+
Provides-Extra: scalar
|
|
42
|
+
Requires-Dist: flask-openapi-scalar; extra == 'scalar'
|
|
43
|
+
Provides-Extra: swagger
|
|
44
|
+
Requires-Dist: flask-openapi-swagger; extra == 'swagger'
|
|
45
|
+
Provides-Extra: yaml
|
|
46
|
+
Requires-Dist: pyyaml; extra == 'yaml'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
<div align="center">
|
|
50
|
+
<a href="https://luolingchun.github.io/flask-openapi/" target="_blank">
|
|
51
|
+
<img class="off-glb" src="https://raw.githubusercontent.com/luolingchun/flask-openapi/master/docs/images/logo-text.svg"
|
|
52
|
+
width="60%" height="auto" alt="logo">
|
|
53
|
+
</a>
|
|
54
|
+
</div>
|
|
55
|
+
<p align="center">
|
|
56
|
+
<em>Generate REST API and OpenAPI documentation for your Flask project.</em>
|
|
57
|
+
</p>
|
|
58
|
+
<p align="center">
|
|
59
|
+
<a href="https://github.com/luolingchun/flask-openapi/actions/workflows/tests.yml" target="_blank">
|
|
60
|
+
<img class="off-glb" src="https://img.shields.io/github/actions/workflow/status/luolingchun/flask-openapi/tests.yml?branch=master" alt="test">
|
|
61
|
+
</a>
|
|
62
|
+
<a href="https://pypi.org/project/flask-openapi/" target="_blank">
|
|
63
|
+
<img class="off-glb" src="https://img.shields.io/pypi/v/flask-openapi" alt="pypi">
|
|
64
|
+
</a>
|
|
65
|
+
<a href="https://pypistats.org/packages/flask-openapi" target="_blank">
|
|
66
|
+
<img class="off-glb" src="https://img.shields.io/pypi/dm/flask-openapi" alt="pypistats">
|
|
67
|
+
</a>
|
|
68
|
+
<a href="https://pypi.org/project/flask-openapi/" target="_blank">
|
|
69
|
+
<img class="off-glb" src="https://img.shields.io/pypi/pyversions/flask-openapi" alt="pypi versions">
|
|
70
|
+
</a>
|
|
71
|
+
</p>
|
|
72
|
+
|
|
73
|
+
**Flask openapi** is a web API framework based on **Flask**. It uses **Pydantic** to verify data and automatic
|
|
74
|
+
generation of interaction documentation.
|
|
75
|
+
|
|
76
|
+
The key features are:
|
|
77
|
+
|
|
78
|
+
- **Easy to code:** Easy to use and easy to learn
|
|
79
|
+
|
|
80
|
+
- **Standard document specification:** Based on [OpenAPI Specification](https://spec.openapis.org/oas/v3.1.0)
|
|
81
|
+
|
|
82
|
+
- **Interactive OpenAPI documentation:** [Swagger](https://github.com/swagger-api/swagger-ui), [Redoc](https://github.com/Redocly/redoc), [RapiDoc](https://github.com/rapi-doc/RapiDoc), [RapiPdf](https://mrin9.github.io/RapiPdf/), [Scalar](https://github.com/scalar/scalar), [Elements](https://github.com/stoplightio/elements)
|
|
83
|
+
|
|
84
|
+
- **Data validation:** Fast data verification based on [Pydantic](https://github.com/pydantic/pydantic)
|
|
85
|
+
|
|
86
|
+
## Requirements
|
|
87
|
+
|
|
88
|
+
Python 3.10+
|
|
89
|
+
|
|
90
|
+
flask-openapi is dependent on the following libraries:
|
|
91
|
+
|
|
92
|
+
- [Flask](https://github.com/pallets/flask) for the web app.
|
|
93
|
+
- [Pydantic](https://github.com/pydantic/pydantic) for the data validation.
|
|
94
|
+
|
|
95
|
+
## Installation
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install -U flask-openapi[swagger]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
or
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
conda install -c conda-forge flask-openapi[swagger]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
<details markdown="block">
|
|
108
|
+
<summary>Optional dependencies</summary>
|
|
109
|
+
|
|
110
|
+
- [python-email-validator](https://github.com/JoshData/python-email-validator) supports email verification.
|
|
111
|
+
- [python-dotenv](https://github.com/theskumar/python-dotenv#readme) enables support
|
|
112
|
+
for [Environment Variables From dotenv](https://flask.palletsprojects.com/en/latest/cli/#dotenv) when running `flask`
|
|
113
|
+
commands.
|
|
114
|
+
- [pyyaml](https://github.com/yaml/pyyaml) is used to output the OpenAPI document in yaml format.
|
|
115
|
+
- [asgiref](https://github.com/django/asgiref) allows views to be defined with `async def` and use `await`.
|
|
116
|
+
- [flask-openapi-plugins](https://github.com/luolingchun/flask-openapi-plugins) Provide OpenAPI UI for flask-openapi.
|
|
117
|
+
|
|
118
|
+
To install these dependencies with flask-openapi:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install flask-openapi[yaml]
|
|
122
|
+
# or
|
|
123
|
+
pip install flask-openapi[async]
|
|
124
|
+
# or
|
|
125
|
+
pip install flask-openapi[dotenv]
|
|
126
|
+
# or
|
|
127
|
+
pip install flask-openapi[email]
|
|
128
|
+
# or all
|
|
129
|
+
pip install flask-openapi[yaml,async,dotenv,email]
|
|
130
|
+
# or manually
|
|
131
|
+
pip install pyyaml asgiref python-dotenv email-validator
|
|
132
|
+
# OpenAPI UI plugins
|
|
133
|
+
pip install -U flask-openapi[swagger,redoc,rapidoc,rapipdf,scalar,elements]
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
</details>
|
|
137
|
+
|
|
138
|
+
## A Simple Example
|
|
139
|
+
|
|
140
|
+
Here's a simple example, further go to the [Example](https://luolingchun.github.io/flask-openapi/latest/Example/).
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from pydantic import BaseModel
|
|
144
|
+
|
|
145
|
+
from flask_openapi import Info, Tag
|
|
146
|
+
from flask_openapi import OpenAPI
|
|
147
|
+
|
|
148
|
+
info = Info(title="book API", version="1.0.0")
|
|
149
|
+
app = OpenAPI(__name__, info=info)
|
|
150
|
+
|
|
151
|
+
book_tag = Tag(name="book", description="Some Book")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class BookQuery(BaseModel):
|
|
155
|
+
age: int
|
|
156
|
+
author: str
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@app.get("/book", summary="get books", tags=[book_tag])
|
|
160
|
+
def get_book(query: BookQuery):
|
|
161
|
+
"""
|
|
162
|
+
to get all books
|
|
163
|
+
"""
|
|
164
|
+
return {
|
|
165
|
+
"code": 0,
|
|
166
|
+
"message": "ok",
|
|
167
|
+
"data": [
|
|
168
|
+
{"bid": 1, "age": query.age, "author": query.author},
|
|
169
|
+
{"bid": 2, "age": query.age, "author": query.author}
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
app.run(debug=True)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
<details>
|
|
179
|
+
<summary>Class-based API View Example</summary>
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from pydantic import BaseModel, Field
|
|
183
|
+
|
|
184
|
+
from flask_openapi import OpenAPI, Tag, Info, APIView
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
info = Info(title="book API", version="1.0.0")
|
|
188
|
+
app = OpenAPI(__name__, info=info)
|
|
189
|
+
|
|
190
|
+
api_view = APIView(url_prefix="/api/v1", view_tags=[Tag(name="book")])
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class BookPath(BaseModel):
|
|
194
|
+
id: int = Field(..., description="book ID")
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class BookQuery(BaseModel):
|
|
198
|
+
age: int | None = Field(None, description="Age")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class BookBody(BaseModel):
|
|
202
|
+
age: int | None = Field(..., ge=2, le=4, description="Age")
|
|
203
|
+
author: str = Field(None, min_length=2, max_length=4, description="Author")
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@api_view.route("/book")
|
|
207
|
+
class BookListAPIView:
|
|
208
|
+
a = 1
|
|
209
|
+
|
|
210
|
+
@api_view.doc(summary="get book list")
|
|
211
|
+
def get(self, query: BookQuery):
|
|
212
|
+
print(self.a)
|
|
213
|
+
return query.model_dump_json()
|
|
214
|
+
|
|
215
|
+
@api_view.doc(summary="create book")
|
|
216
|
+
def post(self, body: BookBody):
|
|
217
|
+
"""description for a created book"""
|
|
218
|
+
return body.model_dump_json()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@api_view.route("/book/<id>")
|
|
222
|
+
class BookAPIView:
|
|
223
|
+
@api_view.doc(summary="get book")
|
|
224
|
+
def get(self, path: BookPath):
|
|
225
|
+
print(path)
|
|
226
|
+
return "get"
|
|
227
|
+
|
|
228
|
+
@api_view.doc(summary="update book")
|
|
229
|
+
def put(self, path: BookPath):
|
|
230
|
+
print(path)
|
|
231
|
+
return "put"
|
|
232
|
+
|
|
233
|
+
@api_view.doc(summary="delete book", deprecated=True)
|
|
234
|
+
def delete(self, path: BookPath):
|
|
235
|
+
print(path)
|
|
236
|
+
return "delete"
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
app.register_api_view(api_view)
|
|
240
|
+
|
|
241
|
+
if __name__ == "__main__":
|
|
242
|
+
app.run(debug=True)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
</details>
|
|
246
|
+
|
|
247
|
+
## API Document
|
|
248
|
+
|
|
249
|
+
Run the [simple example](https://github.com/luolingchun/flask-openapi/blob/master/examples/simple_demo.py), and go to http://127.0.0.1:5000/openapi.
|
|
250
|
+
|
|
251
|
+
> OpenAPI UI plugins are optional dependencies that require manual installation.
|
|
252
|
+
>
|
|
253
|
+
> `pip install -U flask-openapi[swagger,redoc,rapidoc,rapipdf,scalar,elements]`
|
|
254
|
+
>
|
|
255
|
+
> More optional ui templates goto the document
|
|
256
|
+
> about [UI_Templates](https://luolingchun.github.io/flask-openapi/latest/Usage/UI_Templates/).
|
|
257
|
+
|
|
258
|
+

|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
flask_openapi/__init__.py,sha256=WAUy_LLjWVYdNYaY1Jij5_wIYDNZCKi3PTGGSFajHJE,776
|
|
2
|
+
flask_openapi/__version__.py,sha256=_AlqfMdduWaoR0bmwGi4snZGXO-TfHvU-UzOFDsRG8s,97
|
|
3
|
+
flask_openapi/blueprint.py,sha256=xIMmJUYgmhr0P_Su0e5V9KI5s6pibIVTxbyBrJlMwk4,8021
|
|
4
|
+
flask_openapi/commands.py,sha256=rPpmA-fYnBWVY52IV635Qtx-ldkhorMjiI0uXolvziU,1440
|
|
5
|
+
flask_openapi/openapi.py,sha256=4eAr8JIKLWzygzWTX8RcyBvpndfsWZn2n--I-zJ1lyQ,18335
|
|
6
|
+
flask_openapi/plugins.py,sha256=UlZYCMkEkXEy0lhgcKY99DC17RU6MbV5zSQMtqB7qUg,305
|
|
7
|
+
flask_openapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
flask_openapi/request.py,sha256=74P9Y6R5afPIO_UFP3elxMA_GDccCFCSC4a8ZWG5YUc,10079
|
|
9
|
+
flask_openapi/scaffold.py,sha256=wfKpu-hDGXK2WGGNeCBkdKFcfagjT9016F4XS8w5g_w,21527
|
|
10
|
+
flask_openapi/templates.py,sha256=s7VQ1rfAN-eWdG_PM5r5CMLMEzPYTLNONCVmM6Okbzc,3563
|
|
11
|
+
flask_openapi/types.py,sha256=rELO-SgmlOzPiGfnZoUGsUqjICZvdhN4NorrcUecQGQ,699
|
|
12
|
+
flask_openapi/utils.py,sha256=ouMW-nvz2YNzXGhy9c6t4R_o5EYLwed8IV2W3dn6Mr0,23962
|
|
13
|
+
flask_openapi/view.py,sha256=oY8nb_01POBqCzcwJlsjaF7r9wqtoUmhN4OjhKTflfU,9058
|
|
14
|
+
flask_openapi/models/__init__.py,sha256=x2aLJ4A4pwrtbx5YdX3KxzspoU8t0WGPCEk3dwAknqs,2926
|
|
15
|
+
flask_openapi/models/callback.py,sha256=5JnAtkszkqTrN8UI4YzabsBJF1ZjUui3SIKZn0jc3p0,636
|
|
16
|
+
flask_openapi/models/components.py,sha256=Sp4iwsc14iVNEXB9USrDjQ2uNcYdsGDtrIcAiOCztWg,1287
|
|
17
|
+
flask_openapi/models/contact.py,sha256=g0JzPAGNyVMkBSEdrp815HUC11rj6zlqC5E8lM4AFg0,341
|
|
18
|
+
flask_openapi/models/data_type.py,sha256=g7hgV1zrulAmlibXDWT5m8iD5vR2gmx2FYqDA56x3CU,340
|
|
19
|
+
flask_openapi/models/discriminator.py,sha256=eXR8Ah39LN3ukpPKbrcV26MZRA6VRsUqoSai0uBu9XI,332
|
|
20
|
+
flask_openapi/models/encoding.py,sha256=Y-AkRlbqM7yzcPCCwBM3TJO39N7YOuex9FcXr1cjBFE,632
|
|
21
|
+
flask_openapi/models/example.py,sha256=nkaTkDV1wY45QNfzCobBUlRD2GpF5YxGMZrhtYaEZVM,414
|
|
22
|
+
flask_openapi/models/external_documentation.py,sha256=dkSIc9b3T_Uk-xW3li8kY6Ojq1VGAe1taFMJYvum7y8,333
|
|
23
|
+
flask_openapi/models/file.py,sha256=O56tA73KU3r9T5b4OF--81o9K4B4p-BW0vtsVz2zytw,874
|
|
24
|
+
flask_openapi/models/header.py,sha256=F8JU5xT2FGcgPMYqsNCWt4hwi8Ik2wYRQXaLQNIdR-E,406
|
|
25
|
+
flask_openapi/models/info.py,sha256=VryN42q6hkUvY42yuZap7M2vo89Medyeq15rDPUT3lg,525
|
|
26
|
+
flask_openapi/models/license.py,sha256=TiWNtvirio4r0uN8MoWEbPw_RMGkHo4GMm2sagKqMFQ,332
|
|
27
|
+
flask_openapi/models/link.py,sha256=KKm1DJPARQjgV2MTh34R3Keg4Vbux7b9_WIHpHWk7vE,527
|
|
28
|
+
flask_openapi/models/media_type.py,sha256=sXyhdeA69viR4671MbiIhhVyf4BOUj_IglHWOZsecPM,671
|
|
29
|
+
flask_openapi/models/oauth_flow.py,sha256=2otdggLuk_5DfK_XASpDJXPqO5kcOenFK7hjm0m2tIY,396
|
|
30
|
+
flask_openapi/models/oauth_flows.py,sha256=YwI9KVtXvnPAQ3KSdnF3YgyMP4IGK1joAttdM1CkSz4,472
|
|
31
|
+
flask_openapi/models/operation.py,sha256=IwBzPKaLIbePsy_edwa-gMyYRTsqP9coif6qEMrh-pg,1105
|
|
32
|
+
flask_openapi/models/parameter.py,sha256=dtI0n5VCKmCdYC_5Yv0tO0xyQGfhRJSGlqVn__IUXYo,1031
|
|
33
|
+
flask_openapi/models/parameter_in_type.py,sha256=Mcoyzuzof6bLIDHk2ahZJoijxwTL2cIPgfX71I0cHnA,276
|
|
34
|
+
flask_openapi/models/path_item.py,sha256=cfeP_cpaFyfIVzw422XrEHI3WSZ91t5wPeg2bK176Zg,1068
|
|
35
|
+
flask_openapi/models/paths.py,sha256=gXZrjmqnU-i8B_j86iqwiA4NcHd-_btuqAvINdLQglQ,187
|
|
36
|
+
flask_openapi/models/reference.py,sha256=qQ_aIepIKpCW_EiyD6GkrP4Qmi_4DG6pr_nDJXMkSK0,316
|
|
37
|
+
flask_openapi/models/request_body.py,sha256=z3GsuZSCDsO_GLaQ2tbJhylzTed5UtYmFN4PYTpk6L8,405
|
|
38
|
+
flask_openapi/models/response.py,sha256=2rNtSYZBFHUtZQMr9PULC7BjHK0T-vcFwfu8_PMPhtg,539
|
|
39
|
+
flask_openapi/models/responses.py,sha256=uASTKpFXBtQ2VEG3JLIVQCEiSpxHg6Mda882L80H4wA,251
|
|
40
|
+
flask_openapi/models/schema.py,sha256=Bm30lJZ6gnopE7wdgA5auqG0sYLfszo8jpU-IygOGcQ,2348
|
|
41
|
+
flask_openapi/models/security_requirement.py,sha256=1BqLEDLVL0zlDsHL9U0lBBc8qnb6Mcfcy7tm9lqCT_E,185
|
|
42
|
+
flask_openapi/models/security_scheme.py,sha256=w-w_z56Hd4IMj23_If82HXaWLT2gBWuuQCz4_X600S0,713
|
|
43
|
+
flask_openapi/models/security_scheme_in_type.py,sha256=PXeWcskVysdKHwy3SRLzppnGkAxTJDFkCDf0JVfpC88,264
|
|
44
|
+
flask_openapi/models/server.py,sha256=gQ6SnzT2VlTpzpSosb3GXFzTMno1UiGal_yjUPdlwR0,407
|
|
45
|
+
flask_openapi/models/server_variable.py,sha256=t323bkY-5P55RPELs-v2q0gk_n-jiFrQA6Y6ujOEspA,386
|
|
46
|
+
flask_openapi/models/style_values.py,sha256=58u_rYCA5qdSymJM30eoXC-CxY2TD1tLmFEVnTBsRo0,309
|
|
47
|
+
flask_openapi/models/tag.py,sha256=pvK-oMTKWZF22IJpfuImSrwvuySjw9dYGFtW270-8ws,376
|
|
48
|
+
flask_openapi/models/validation_error.py,sha256=MuR4Ke2IIveucw7nnx8elCabS94ORqFpQYAocGQDInE,1062
|
|
49
|
+
flask_openapi/models/xml.py,sha256=cFCMeunP_Pk96dm5-3A_cArLYPX8BUba8JSt3w9A5m0,396
|
|
50
|
+
flask_openapi-4.3.1.dist-info/METADATA,sha256=pGgyT2q9ZLy9Qrcam_4G4VD45rMBYR10C-bcdY59S8E,8454
|
|
51
|
+
flask_openapi-4.3.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
52
|
+
flask_openapi-4.3.1.dist-info/licenses/LICENSE.rst,sha256=jr1BeOwli-7RPnwKmiSnwkURy-aBI25coRfimxgToyA,1059
|
|
53
|
+
flask_openapi-4.3.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 llc
|
|
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.
|