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.
Files changed (53) hide show
  1. flask_openapi/__init__.py +44 -0
  2. flask_openapi/__version__.py +5 -0
  3. flask_openapi/blueprint.py +201 -0
  4. flask_openapi/commands.py +38 -0
  5. flask_openapi/models/__init__.py +91 -0
  6. flask_openapi/models/callback.py +18 -0
  7. flask_openapi/models/components.py +37 -0
  8. flask_openapi/models/contact.py +17 -0
  9. flask_openapi/models/data_type.py +18 -0
  10. flask_openapi/models/discriminator.py +16 -0
  11. flask_openapi/models/encoding.py +27 -0
  12. flask_openapi/models/example.py +19 -0
  13. flask_openapi/models/external_documentation.py +16 -0
  14. flask_openapi/models/file.py +27 -0
  15. flask_openapi/models/header.py +17 -0
  16. flask_openapi/models/info.py +24 -0
  17. flask_openapi/models/license.py +17 -0
  18. flask_openapi/models/link.py +23 -0
  19. flask_openapi/models/media_type.py +24 -0
  20. flask_openapi/models/oauth_flow.py +18 -0
  21. flask_openapi/models/oauth_flows.py +20 -0
  22. flask_openapi/models/operation.py +36 -0
  23. flask_openapi/models/parameter.py +34 -0
  24. flask_openapi/models/parameter_in_type.py +13 -0
  25. flask_openapi/models/path_item.py +36 -0
  26. flask_openapi/models/paths.py +9 -0
  27. flask_openapi/models/reference.py +14 -0
  28. flask_openapi/models/request_body.py +19 -0
  29. flask_openapi/models/response.py +23 -0
  30. flask_openapi/models/responses.py +11 -0
  31. flask_openapi/models/schema.py +59 -0
  32. flask_openapi/models/security_requirement.py +8 -0
  33. flask_openapi/models/security_scheme.py +25 -0
  34. flask_openapi/models/security_scheme_in_type.py +12 -0
  35. flask_openapi/models/server.py +19 -0
  36. flask_openapi/models/server_variable.py +17 -0
  37. flask_openapi/models/style_values.py +14 -0
  38. flask_openapi/models/tag.py +15 -0
  39. flask_openapi/models/validation_error.py +24 -0
  40. flask_openapi/models/xml.py +19 -0
  41. flask_openapi/openapi.py +449 -0
  42. flask_openapi/plugins.py +17 -0
  43. flask_openapi/py.typed +0 -0
  44. flask_openapi/request.py +250 -0
  45. flask_openapi/scaffold.py +547 -0
  46. flask_openapi/templates.py +113 -0
  47. flask_openapi/types.py +27 -0
  48. flask_openapi/utils.py +633 -0
  49. flask_openapi/view.py +230 -0
  50. flask_openapi-4.3.1.dist-info/METADATA +258 -0
  51. flask_openapi-4.3.1.dist-info/RECORD +53 -0
  52. flask_openapi-4.3.1.dist-info/WHEEL +4 -0
  53. flask_openapi-4.3.1.dist-info/licenses/LICENSE.rst +21 -0
@@ -0,0 +1,250 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Author : llc
3
+ # @Time : 2022/4/1 16:54
4
+ import inspect
5
+ import json
6
+ from functools import wraps
7
+ from json import JSONDecodeError
8
+ from typing import Any, Type
9
+
10
+ from flask import abort, current_app, request
11
+ from pydantic import BaseModel, ValidationError
12
+ from pydantic.fields import FieldInfo
13
+ from werkzeug.datastructures.structures import MultiDict
14
+
15
+ from .utils import parse_parameters
16
+
17
+
18
+ def _get_list_value(model: Type[BaseModel], args: MultiDict, model_field_key: str, model_field_value: FieldInfo):
19
+ if model_field_value.alias and model.model_config.get("populate_by_name"):
20
+ key = model_field_value.alias
21
+ value = args.getlist(model_field_value.alias) or args.getlist(model_field_key)
22
+ elif model_field_value.alias:
23
+ key = model_field_value.alias
24
+ value = args.getlist(model_field_value.alias)
25
+ else:
26
+ key = model_field_key
27
+ value = args.getlist(model_field_key)
28
+
29
+ return key, value
30
+
31
+
32
+ def _get_value(model: Type[BaseModel], args: MultiDict, model_field_key: str, model_field_value: FieldInfo):
33
+ if model_field_value.alias and model.model_config.get("populate_by_name"):
34
+ key = model_field_value.alias
35
+ value = args.get(model_field_value.alias) or args.get(model_field_key)
36
+ elif model_field_value.alias:
37
+ key = model_field_value.alias
38
+ value = args.get(model_field_value.alias)
39
+ else:
40
+ key = model_field_key
41
+ value = args.get(model_field_key)
42
+
43
+ return key, value
44
+
45
+
46
+ def _validate_header(header: Type[BaseModel], func_kwargs: dict):
47
+ request_headers = dict(request.headers)
48
+ header_dict = {}
49
+ model_properties = header.model_json_schema().get("properties", {})
50
+ for model_field_key, model_field_value in header.model_fields.items():
51
+ key_title = model_field_key.replace("_", "-").title()
52
+ model_field_schema = model_properties.get(model_field_value.alias or model_field_key)
53
+ if model_field_value.alias and header.model_config.get("populate_by_name"):
54
+ key = model_field_value.alias
55
+ key_alias_title = model_field_value.alias.replace("_", "-").title()
56
+ value = request_headers.get(key_alias_title) or request_headers.get(key_title)
57
+ elif model_field_value.alias:
58
+ key = model_field_value.alias
59
+ key_alias_title = model_field_value.alias.replace("_", "-").title()
60
+ value = request_headers.get(key_alias_title)
61
+ else:
62
+ key = model_field_key
63
+ value = request_headers[key_title]
64
+ if value is not None:
65
+ header_dict[key] = value
66
+ if model_field_schema.get("type") == "null":
67
+ header_dict[key] = value # type:ignore
68
+ # extra keys
69
+ for key, value in request_headers.items():
70
+ if key not in header_dict.keys():
71
+ header_dict[key] = value
72
+ func_kwargs["header"] = header.model_validate(obj=header_dict)
73
+
74
+
75
+ def _validate_cookie(cookie: Type[BaseModel], func_kwargs: dict):
76
+ request_cookies = dict(request.cookies)
77
+ func_kwargs["cookie"] = cookie.model_validate(obj=request_cookies)
78
+
79
+
80
+ def _validate_path(path: Type[BaseModel], path_kwargs: dict, func_kwargs: dict):
81
+ path_obj = path.model_validate(obj=path_kwargs)
82
+ func_kwargs["path"] = path_obj
83
+ # Consume path parameters to prevent from being passed to the function
84
+ for field_name, _ in path_obj:
85
+ path_kwargs.pop(field_name, None)
86
+
87
+
88
+ def _validate_query(query: Type[BaseModel], func_kwargs: dict):
89
+ request_args = request.args
90
+ query_dict = {}
91
+ model_properties = query.model_json_schema().get("properties", {})
92
+ for model_field_key, model_field_value in query.model_fields.items():
93
+ model_field_schema = model_properties.get(model_field_value.alias or model_field_key)
94
+ if model_field_schema.get("type") == "array":
95
+ key, value = _get_list_value(query, request_args, model_field_key, model_field_value)
96
+ # To handle Optional[list]
97
+ elif any(m.get("type") == "array" for m in model_field_schema.get("anyOf", [])):
98
+ key, value = _get_list_value(query, request_args, model_field_key, model_field_value)
99
+ else:
100
+ key, value = _get_value(query, request_args, model_field_key, model_field_value)
101
+ if value is not None and value != []:
102
+ query_dict[key] = value
103
+ if model_field_schema.get("type") == "null":
104
+ query_dict[key] = value
105
+ # extra keys
106
+ for key, value in request_args.items():
107
+ if key not in query_dict.keys():
108
+ query_dict[key] = value
109
+ func_kwargs["query"] = query.model_validate(obj=query_dict)
110
+
111
+
112
+ def _validate_form(form: Type[BaseModel], func_kwargs: dict):
113
+ request_form = request.form
114
+ request_files = request.files
115
+ form_dict = {}
116
+ model_properties = form.model_json_schema().get("properties", {})
117
+ for model_field_key, model_field_value in form.model_fields.items():
118
+ model_field_schema = model_properties.get(model_field_value.alias or model_field_key)
119
+ if model_field_schema.get("type") == "array":
120
+ if model_field_schema.get("items") == {"format": "binary", "type": "string"}:
121
+ # list[FileStorage]
122
+ key, value = _get_list_value(form, request_files, model_field_key, model_field_value)
123
+ else:
124
+ value = []
125
+ key, value_list = _get_list_value(form, request_form, model_field_key, model_field_value)
126
+ for _value in value_list:
127
+ try:
128
+ value.append(json.loads(_value))
129
+ except (JSONDecodeError, TypeError):
130
+ value.append(_value)
131
+ elif model_field_schema.get("type") == "string" and model_field_schema.get("format") == "binary":
132
+ # FileStorage
133
+ key, value = _get_value(form, request_files, model_field_key, model_field_value)
134
+ else:
135
+ key, _value = _get_value(form, request_form, model_field_key, model_field_value)
136
+ try:
137
+ value = json.loads(_value)
138
+ except (JSONDecodeError, TypeError):
139
+ value = _value
140
+ if value is not None and value != []:
141
+ form_dict[key] = value
142
+ if model_field_schema.get("type") == "null":
143
+ form_dict[key] = value
144
+ # extra keys
145
+ for key, value in {**dict(request_form), **dict(request_files)}.items():
146
+ if key not in form_dict.keys():
147
+ form_dict[key] = value
148
+ func_kwargs["form"] = form.model_validate(obj=form_dict)
149
+
150
+
151
+ def _validate_body(body: Type[BaseModel], func_kwargs: dict):
152
+ obj = request.get_json(silent=True)
153
+ if isinstance(obj, str):
154
+ body_model = body.model_validate_json(json_data=obj)
155
+ else:
156
+ body_model = body.model_validate(obj=obj)
157
+ func_kwargs["body"] = body_model
158
+
159
+
160
+ def _validate_request(
161
+ header: Type[BaseModel] | None = None,
162
+ cookie: Type[BaseModel] | None = None,
163
+ path: Type[BaseModel] | None = None,
164
+ query: Type[BaseModel] | None = None,
165
+ form: Type[BaseModel] | None = None,
166
+ body: Type[BaseModel] | None = None,
167
+ raw: Type[BaseModel] | None = None,
168
+ path_kwargs: dict[Any, Any] | None = None,
169
+ ) -> dict:
170
+ """
171
+ Validate requests and responses.
172
+
173
+ Args:
174
+ header: Header model.
175
+ cookie: Cookie model.
176
+ path: Path model.
177
+ query: Query model.
178
+ form: Form model.
179
+ body: Body model.
180
+ path_kwargs: Path parameters.
181
+
182
+ Returns:
183
+ dict: Request kwargs.
184
+
185
+ Raises:
186
+ ValidationError: If validation fails.
187
+ """
188
+
189
+ # Dictionary to store func kwargs
190
+ func_kwargs: dict = {}
191
+
192
+ try:
193
+ # Validate header, cookie, path, and query parameters
194
+ if header:
195
+ _validate_header(header, func_kwargs)
196
+ if cookie:
197
+ _validate_cookie(cookie, func_kwargs)
198
+ if path:
199
+ _validate_path(path, path_kwargs or {}, func_kwargs)
200
+ if query:
201
+ _validate_query(query, func_kwargs)
202
+ if form:
203
+ _validate_form(form, func_kwargs)
204
+ if body:
205
+ _validate_body(body, func_kwargs)
206
+ if raw:
207
+ func_kwargs["raw"] = request
208
+ except ValidationError as e:
209
+ # Create a response with validation error details
210
+ validation_error_callback = getattr(current_app, "validation_error_callback")
211
+ abort(validation_error_callback(e))
212
+
213
+ return func_kwargs
214
+
215
+
216
+ def validate_request():
217
+ """
218
+ Decorator to validate the annotated parts of the function and throw and error if applicable.
219
+ """
220
+
221
+ def decorator(func):
222
+ setattr(func, "__delay_validate_request__", True)
223
+
224
+ is_coroutine_function = inspect.iscoroutinefunction(func)
225
+
226
+ if is_coroutine_function:
227
+
228
+ @wraps(func)
229
+ async def wrapper(*args, **kwargs):
230
+ header, cookie, path, query, form, body, raw = parse_parameters(func)
231
+ func_kwargs = _validate_request(header, cookie, path, query, form, body, raw, path_kwargs=kwargs)
232
+ # Update func_kwargs with any additional keyword arguments passed from other decorators or calls.
233
+ func_kwargs.update(kwargs)
234
+
235
+ return await func(*args, **func_kwargs)
236
+
237
+ return wrapper
238
+ else:
239
+
240
+ @wraps(func)
241
+ def wrapper(*args, **kwargs):
242
+ header, cookie, path, query, form, body, raw = parse_parameters(func)
243
+ func_kwargs = _validate_request(header, cookie, path, query, form, body, raw, path_kwargs=kwargs)
244
+ # Update func_kwargs with any additional keyword arguments passed from other decorators or calls.
245
+ func_kwargs.update(kwargs)
246
+ return func(*args, **func_kwargs)
247
+
248
+ return wrapper
249
+
250
+ return decorator