fastapi 0.128.0__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. fastapi/__init__.py +25 -0
  2. fastapi/__main__.py +3 -0
  3. fastapi/_compat/__init__.py +41 -0
  4. fastapi/_compat/shared.py +206 -0
  5. fastapi/_compat/v2.py +568 -0
  6. fastapi/applications.py +4669 -0
  7. fastapi/background.py +60 -0
  8. fastapi/cli.py +13 -0
  9. fastapi/concurrency.py +41 -0
  10. fastapi/datastructures.py +183 -0
  11. fastapi/dependencies/__init__.py +0 -0
  12. fastapi/dependencies/models.py +193 -0
  13. fastapi/dependencies/utils.py +1021 -0
  14. fastapi/encoders.py +346 -0
  15. fastapi/exception_handlers.py +34 -0
  16. fastapi/exceptions.py +246 -0
  17. fastapi/logger.py +3 -0
  18. fastapi/middleware/__init__.py +1 -0
  19. fastapi/middleware/asyncexitstack.py +18 -0
  20. fastapi/middleware/cors.py +1 -0
  21. fastapi/middleware/gzip.py +1 -0
  22. fastapi/middleware/httpsredirect.py +3 -0
  23. fastapi/middleware/trustedhost.py +3 -0
  24. fastapi/middleware/wsgi.py +1 -0
  25. fastapi/openapi/__init__.py +0 -0
  26. fastapi/openapi/constants.py +3 -0
  27. fastapi/openapi/docs.py +344 -0
  28. fastapi/openapi/models.py +438 -0
  29. fastapi/openapi/utils.py +567 -0
  30. fastapi/param_functions.py +2369 -0
  31. fastapi/params.py +755 -0
  32. fastapi/py.typed +0 -0
  33. fastapi/requests.py +2 -0
  34. fastapi/responses.py +48 -0
  35. fastapi/routing.py +4508 -0
  36. fastapi/security/__init__.py +15 -0
  37. fastapi/security/api_key.py +318 -0
  38. fastapi/security/base.py +6 -0
  39. fastapi/security/http.py +423 -0
  40. fastapi/security/oauth2.py +663 -0
  41. fastapi/security/open_id_connect_url.py +94 -0
  42. fastapi/security/utils.py +10 -0
  43. fastapi/staticfiles.py +1 -0
  44. fastapi/templating.py +1 -0
  45. fastapi/testclient.py +1 -0
  46. fastapi/types.py +11 -0
  47. fastapi/utils.py +164 -0
  48. fastapi/websockets.py +3 -0
  49. fastapi-0.128.0.dist-info/METADATA +645 -0
  50. fastapi-0.128.0.dist-info/RECORD +53 -0
  51. fastapi-0.128.0.dist-info/WHEEL +4 -0
  52. fastapi-0.128.0.dist-info/entry_points.txt +5 -0
  53. fastapi-0.128.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,567 @@
1
+ import http.client
2
+ import inspect
3
+ import warnings
4
+ from collections.abc import Sequence
5
+ from typing import Any, Optional, Union, cast
6
+
7
+ from fastapi import routing
8
+ from fastapi._compat import (
9
+ ModelField,
10
+ Undefined,
11
+ get_compat_model_name_map,
12
+ get_definitions,
13
+ get_schema_from_model_field,
14
+ lenient_issubclass,
15
+ )
16
+ from fastapi.datastructures import DefaultPlaceholder
17
+ from fastapi.dependencies.models import Dependant
18
+ from fastapi.dependencies.utils import (
19
+ _get_flat_fields_from_params,
20
+ get_flat_dependant,
21
+ get_flat_params,
22
+ get_validation_alias,
23
+ )
24
+ from fastapi.encoders import jsonable_encoder
25
+ from fastapi.exceptions import FastAPIDeprecationWarning
26
+ from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
27
+ from fastapi.openapi.models import OpenAPI
28
+ from fastapi.params import Body, ParamTypes
29
+ from fastapi.responses import Response
30
+ from fastapi.types import ModelNameMap
31
+ from fastapi.utils import (
32
+ deep_dict_update,
33
+ generate_operation_id_for_path,
34
+ is_body_allowed_for_status_code,
35
+ )
36
+ from pydantic import BaseModel
37
+ from starlette.responses import JSONResponse
38
+ from starlette.routing import BaseRoute
39
+ from typing_extensions import Literal
40
+
41
+ validation_error_definition = {
42
+ "title": "ValidationError",
43
+ "type": "object",
44
+ "properties": {
45
+ "loc": {
46
+ "title": "Location",
47
+ "type": "array",
48
+ "items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
49
+ },
50
+ "msg": {"title": "Message", "type": "string"},
51
+ "type": {"title": "Error Type", "type": "string"},
52
+ },
53
+ "required": ["loc", "msg", "type"],
54
+ }
55
+
56
+ validation_error_response_definition = {
57
+ "title": "HTTPValidationError",
58
+ "type": "object",
59
+ "properties": {
60
+ "detail": {
61
+ "title": "Detail",
62
+ "type": "array",
63
+ "items": {"$ref": REF_PREFIX + "ValidationError"},
64
+ }
65
+ },
66
+ }
67
+
68
+ status_code_ranges: dict[str, str] = {
69
+ "1XX": "Information",
70
+ "2XX": "Success",
71
+ "3XX": "Redirection",
72
+ "4XX": "Client Error",
73
+ "5XX": "Server Error",
74
+ "DEFAULT": "Default Response",
75
+ }
76
+
77
+
78
+ def get_openapi_security_definitions(
79
+ flat_dependant: Dependant,
80
+ ) -> tuple[dict[str, Any], list[dict[str, Any]]]:
81
+ security_definitions = {}
82
+ # Use a dict to merge scopes for same security scheme
83
+ operation_security_dict: dict[str, list[str]] = {}
84
+ for security_dependency in flat_dependant._security_dependencies:
85
+ security_definition = jsonable_encoder(
86
+ security_dependency._security_scheme.model,
87
+ by_alias=True,
88
+ exclude_none=True,
89
+ )
90
+ security_name = security_dependency._security_scheme.scheme_name
91
+ security_definitions[security_name] = security_definition
92
+ # Merge scopes for the same security scheme
93
+ if security_name not in operation_security_dict:
94
+ operation_security_dict[security_name] = []
95
+ for scope in security_dependency.oauth_scopes or []:
96
+ if scope not in operation_security_dict[security_name]:
97
+ operation_security_dict[security_name].append(scope)
98
+ operation_security = [
99
+ {name: scopes} for name, scopes in operation_security_dict.items()
100
+ ]
101
+ return security_definitions, operation_security
102
+
103
+
104
+ def _get_openapi_operation_parameters(
105
+ *,
106
+ dependant: Dependant,
107
+ model_name_map: ModelNameMap,
108
+ field_mapping: dict[
109
+ tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
110
+ ],
111
+ separate_input_output_schemas: bool = True,
112
+ ) -> list[dict[str, Any]]:
113
+ parameters = []
114
+ flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
115
+ path_params = _get_flat_fields_from_params(flat_dependant.path_params)
116
+ query_params = _get_flat_fields_from_params(flat_dependant.query_params)
117
+ header_params = _get_flat_fields_from_params(flat_dependant.header_params)
118
+ cookie_params = _get_flat_fields_from_params(flat_dependant.cookie_params)
119
+ parameter_groups = [
120
+ (ParamTypes.path, path_params),
121
+ (ParamTypes.query, query_params),
122
+ (ParamTypes.header, header_params),
123
+ (ParamTypes.cookie, cookie_params),
124
+ ]
125
+ default_convert_underscores = True
126
+ if len(flat_dependant.header_params) == 1:
127
+ first_field = flat_dependant.header_params[0]
128
+ if lenient_issubclass(first_field.type_, BaseModel):
129
+ default_convert_underscores = getattr(
130
+ first_field.field_info, "convert_underscores", True
131
+ )
132
+ for param_type, param_group in parameter_groups:
133
+ for param in param_group:
134
+ field_info = param.field_info
135
+ # field_info = cast(Param, field_info)
136
+ if not getattr(field_info, "include_in_schema", True):
137
+ continue
138
+ param_schema = get_schema_from_model_field(
139
+ field=param,
140
+ model_name_map=model_name_map,
141
+ field_mapping=field_mapping,
142
+ separate_input_output_schemas=separate_input_output_schemas,
143
+ )
144
+ name = get_validation_alias(param)
145
+ convert_underscores = getattr(
146
+ param.field_info,
147
+ "convert_underscores",
148
+ default_convert_underscores,
149
+ )
150
+ if (
151
+ param_type == ParamTypes.header
152
+ and name == param.name
153
+ and convert_underscores
154
+ ):
155
+ name = param.name.replace("_", "-")
156
+
157
+ parameter = {
158
+ "name": name,
159
+ "in": param_type.value,
160
+ "required": param.required,
161
+ "schema": param_schema,
162
+ }
163
+ if field_info.description:
164
+ parameter["description"] = field_info.description
165
+ openapi_examples = getattr(field_info, "openapi_examples", None)
166
+ example = getattr(field_info, "example", None)
167
+ if openapi_examples:
168
+ parameter["examples"] = jsonable_encoder(openapi_examples)
169
+ elif example != Undefined:
170
+ parameter["example"] = jsonable_encoder(example)
171
+ if getattr(field_info, "deprecated", None):
172
+ parameter["deprecated"] = True
173
+ parameters.append(parameter)
174
+ return parameters
175
+
176
+
177
+ def get_openapi_operation_request_body(
178
+ *,
179
+ body_field: Optional[ModelField],
180
+ model_name_map: ModelNameMap,
181
+ field_mapping: dict[
182
+ tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
183
+ ],
184
+ separate_input_output_schemas: bool = True,
185
+ ) -> Optional[dict[str, Any]]:
186
+ if not body_field:
187
+ return None
188
+ assert isinstance(body_field, ModelField)
189
+ body_schema = get_schema_from_model_field(
190
+ field=body_field,
191
+ model_name_map=model_name_map,
192
+ field_mapping=field_mapping,
193
+ separate_input_output_schemas=separate_input_output_schemas,
194
+ )
195
+ field_info = cast(Body, body_field.field_info)
196
+ request_media_type = field_info.media_type
197
+ required = body_field.required
198
+ request_body_oai: dict[str, Any] = {}
199
+ if required:
200
+ request_body_oai["required"] = required
201
+ request_media_content: dict[str, Any] = {"schema": body_schema}
202
+ if field_info.openapi_examples:
203
+ request_media_content["examples"] = jsonable_encoder(
204
+ field_info.openapi_examples
205
+ )
206
+ elif field_info.example != Undefined:
207
+ request_media_content["example"] = jsonable_encoder(field_info.example)
208
+ request_body_oai["content"] = {request_media_type: request_media_content}
209
+ return request_body_oai
210
+
211
+
212
+ def generate_operation_id(
213
+ *, route: routing.APIRoute, method: str
214
+ ) -> str: # pragma: nocover
215
+ warnings.warn(
216
+ message="fastapi.openapi.utils.generate_operation_id() was deprecated, "
217
+ "it is not used internally, and will be removed soon",
218
+ category=FastAPIDeprecationWarning,
219
+ stacklevel=2,
220
+ )
221
+ if route.operation_id:
222
+ return route.operation_id
223
+ path: str = route.path_format
224
+ return generate_operation_id_for_path(name=route.name, path=path, method=method)
225
+
226
+
227
+ def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:
228
+ if route.summary:
229
+ return route.summary
230
+ return route.name.replace("_", " ").title()
231
+
232
+
233
+ def get_openapi_operation_metadata(
234
+ *, route: routing.APIRoute, method: str, operation_ids: set[str]
235
+ ) -> dict[str, Any]:
236
+ operation: dict[str, Any] = {}
237
+ if route.tags:
238
+ operation["tags"] = route.tags
239
+ operation["summary"] = generate_operation_summary(route=route, method=method)
240
+ if route.description:
241
+ operation["description"] = route.description
242
+ operation_id = route.operation_id or route.unique_id
243
+ if operation_id in operation_ids:
244
+ message = (
245
+ f"Duplicate Operation ID {operation_id} for function "
246
+ + f"{route.endpoint.__name__}"
247
+ )
248
+ file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
249
+ if file_name:
250
+ message += f" at {file_name}"
251
+ warnings.warn(message, stacklevel=1)
252
+ operation_ids.add(operation_id)
253
+ operation["operationId"] = operation_id
254
+ if route.deprecated:
255
+ operation["deprecated"] = route.deprecated
256
+ return operation
257
+
258
+
259
+ def get_openapi_path(
260
+ *,
261
+ route: routing.APIRoute,
262
+ operation_ids: set[str],
263
+ model_name_map: ModelNameMap,
264
+ field_mapping: dict[
265
+ tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
266
+ ],
267
+ separate_input_output_schemas: bool = True,
268
+ ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
269
+ path = {}
270
+ security_schemes: dict[str, Any] = {}
271
+ definitions: dict[str, Any] = {}
272
+ assert route.methods is not None, "Methods must be a list"
273
+ if isinstance(route.response_class, DefaultPlaceholder):
274
+ current_response_class: type[Response] = route.response_class.value
275
+ else:
276
+ current_response_class = route.response_class
277
+ assert current_response_class, "A response class is needed to generate OpenAPI"
278
+ route_response_media_type: Optional[str] = current_response_class.media_type
279
+ if route.include_in_schema:
280
+ for method in route.methods:
281
+ operation = get_openapi_operation_metadata(
282
+ route=route, method=method, operation_ids=operation_ids
283
+ )
284
+ parameters: list[dict[str, Any]] = []
285
+ flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
286
+ security_definitions, operation_security = get_openapi_security_definitions(
287
+ flat_dependant=flat_dependant
288
+ )
289
+ if operation_security:
290
+ operation.setdefault("security", []).extend(operation_security)
291
+ if security_definitions:
292
+ security_schemes.update(security_definitions)
293
+ operation_parameters = _get_openapi_operation_parameters(
294
+ dependant=route.dependant,
295
+ model_name_map=model_name_map,
296
+ field_mapping=field_mapping,
297
+ separate_input_output_schemas=separate_input_output_schemas,
298
+ )
299
+ parameters.extend(operation_parameters)
300
+ if parameters:
301
+ all_parameters = {
302
+ (param["in"], param["name"]): param for param in parameters
303
+ }
304
+ required_parameters = {
305
+ (param["in"], param["name"]): param
306
+ for param in parameters
307
+ if param.get("required")
308
+ }
309
+ # Make sure required definitions of the same parameter take precedence
310
+ # over non-required definitions
311
+ all_parameters.update(required_parameters)
312
+ operation["parameters"] = list(all_parameters.values())
313
+ if method in METHODS_WITH_BODY:
314
+ request_body_oai = get_openapi_operation_request_body(
315
+ body_field=route.body_field,
316
+ model_name_map=model_name_map,
317
+ field_mapping=field_mapping,
318
+ separate_input_output_schemas=separate_input_output_schemas,
319
+ )
320
+ if request_body_oai:
321
+ operation["requestBody"] = request_body_oai
322
+ if route.callbacks:
323
+ callbacks = {}
324
+ for callback in route.callbacks:
325
+ if isinstance(callback, routing.APIRoute):
326
+ (
327
+ cb_path,
328
+ cb_security_schemes,
329
+ cb_definitions,
330
+ ) = get_openapi_path(
331
+ route=callback,
332
+ operation_ids=operation_ids,
333
+ model_name_map=model_name_map,
334
+ field_mapping=field_mapping,
335
+ separate_input_output_schemas=separate_input_output_schemas,
336
+ )
337
+ callbacks[callback.name] = {callback.path: cb_path}
338
+ operation["callbacks"] = callbacks
339
+ if route.status_code is not None:
340
+ status_code = str(route.status_code)
341
+ else:
342
+ # It would probably make more sense for all response classes to have an
343
+ # explicit default status_code, and to extract it from them, instead of
344
+ # doing this inspection tricks, that would probably be in the future
345
+ # TODO: probably make status_code a default class attribute for all
346
+ # responses in Starlette
347
+ response_signature = inspect.signature(current_response_class.__init__)
348
+ status_code_param = response_signature.parameters.get("status_code")
349
+ if status_code_param is not None:
350
+ if isinstance(status_code_param.default, int):
351
+ status_code = str(status_code_param.default)
352
+ operation.setdefault("responses", {}).setdefault(status_code, {})[
353
+ "description"
354
+ ] = route.response_description
355
+ if route_response_media_type and is_body_allowed_for_status_code(
356
+ route.status_code
357
+ ):
358
+ response_schema = {"type": "string"}
359
+ if lenient_issubclass(current_response_class, JSONResponse):
360
+ if route.response_field:
361
+ response_schema = get_schema_from_model_field(
362
+ field=route.response_field,
363
+ model_name_map=model_name_map,
364
+ field_mapping=field_mapping,
365
+ separate_input_output_schemas=separate_input_output_schemas,
366
+ )
367
+ else:
368
+ response_schema = {}
369
+ operation.setdefault("responses", {}).setdefault(
370
+ status_code, {}
371
+ ).setdefault("content", {}).setdefault(route_response_media_type, {})[
372
+ "schema"
373
+ ] = response_schema
374
+ if route.responses:
375
+ operation_responses = operation.setdefault("responses", {})
376
+ for (
377
+ additional_status_code,
378
+ additional_response,
379
+ ) in route.responses.items():
380
+ process_response = additional_response.copy()
381
+ process_response.pop("model", None)
382
+ status_code_key = str(additional_status_code).upper()
383
+ if status_code_key == "DEFAULT":
384
+ status_code_key = "default"
385
+ openapi_response = operation_responses.setdefault(
386
+ status_code_key, {}
387
+ )
388
+ assert isinstance(process_response, dict), (
389
+ "An additional response must be a dict"
390
+ )
391
+ field = route.response_fields.get(additional_status_code)
392
+ additional_field_schema: Optional[dict[str, Any]] = None
393
+ if field:
394
+ additional_field_schema = get_schema_from_model_field(
395
+ field=field,
396
+ model_name_map=model_name_map,
397
+ field_mapping=field_mapping,
398
+ separate_input_output_schemas=separate_input_output_schemas,
399
+ )
400
+ media_type = route_response_media_type or "application/json"
401
+ additional_schema = (
402
+ process_response.setdefault("content", {})
403
+ .setdefault(media_type, {})
404
+ .setdefault("schema", {})
405
+ )
406
+ deep_dict_update(additional_schema, additional_field_schema)
407
+ status_text: Optional[str] = status_code_ranges.get(
408
+ str(additional_status_code).upper()
409
+ ) or http.client.responses.get(int(additional_status_code))
410
+ description = (
411
+ process_response.get("description")
412
+ or openapi_response.get("description")
413
+ or status_text
414
+ or "Additional Response"
415
+ )
416
+ deep_dict_update(openapi_response, process_response)
417
+ openapi_response["description"] = description
418
+ http422 = "422"
419
+ all_route_params = get_flat_params(route.dependant)
420
+ if (all_route_params or route.body_field) and not any(
421
+ status in operation["responses"]
422
+ for status in [http422, "4XX", "default"]
423
+ ):
424
+ operation["responses"][http422] = {
425
+ "description": "Validation Error",
426
+ "content": {
427
+ "application/json": {
428
+ "schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
429
+ }
430
+ },
431
+ }
432
+ if "ValidationError" not in definitions:
433
+ definitions.update(
434
+ {
435
+ "ValidationError": validation_error_definition,
436
+ "HTTPValidationError": validation_error_response_definition,
437
+ }
438
+ )
439
+ if route.openapi_extra:
440
+ deep_dict_update(operation, route.openapi_extra)
441
+ path[method.lower()] = operation
442
+ return path, security_schemes, definitions
443
+
444
+
445
+ def get_fields_from_routes(
446
+ routes: Sequence[BaseRoute],
447
+ ) -> list[ModelField]:
448
+ body_fields_from_routes: list[ModelField] = []
449
+ responses_from_routes: list[ModelField] = []
450
+ request_fields_from_routes: list[ModelField] = []
451
+ callback_flat_models: list[ModelField] = []
452
+ for route in routes:
453
+ if getattr(route, "include_in_schema", None) and isinstance(
454
+ route, routing.APIRoute
455
+ ):
456
+ if route.body_field:
457
+ assert isinstance(route.body_field, ModelField), (
458
+ "A request body must be a Pydantic Field"
459
+ )
460
+ body_fields_from_routes.append(route.body_field)
461
+ if route.response_field:
462
+ responses_from_routes.append(route.response_field)
463
+ if route.response_fields:
464
+ responses_from_routes.extend(route.response_fields.values())
465
+ if route.callbacks:
466
+ callback_flat_models.extend(get_fields_from_routes(route.callbacks))
467
+ params = get_flat_params(route.dependant)
468
+ request_fields_from_routes.extend(params)
469
+
470
+ flat_models = callback_flat_models + list(
471
+ body_fields_from_routes + responses_from_routes + request_fields_from_routes
472
+ )
473
+ return flat_models
474
+
475
+
476
+ def get_openapi(
477
+ *,
478
+ title: str,
479
+ version: str,
480
+ openapi_version: str = "3.1.0",
481
+ summary: Optional[str] = None,
482
+ description: Optional[str] = None,
483
+ routes: Sequence[BaseRoute],
484
+ webhooks: Optional[Sequence[BaseRoute]] = None,
485
+ tags: Optional[list[dict[str, Any]]] = None,
486
+ servers: Optional[list[dict[str, Union[str, Any]]]] = None,
487
+ terms_of_service: Optional[str] = None,
488
+ contact: Optional[dict[str, Union[str, Any]]] = None,
489
+ license_info: Optional[dict[str, Union[str, Any]]] = None,
490
+ separate_input_output_schemas: bool = True,
491
+ external_docs: Optional[dict[str, Any]] = None,
492
+ ) -> dict[str, Any]:
493
+ info: dict[str, Any] = {"title": title, "version": version}
494
+ if summary:
495
+ info["summary"] = summary
496
+ if description:
497
+ info["description"] = description
498
+ if terms_of_service:
499
+ info["termsOfService"] = terms_of_service
500
+ if contact:
501
+ info["contact"] = contact
502
+ if license_info:
503
+ info["license"] = license_info
504
+ output: dict[str, Any] = {"openapi": openapi_version, "info": info}
505
+ if servers:
506
+ output["servers"] = servers
507
+ components: dict[str, dict[str, Any]] = {}
508
+ paths: dict[str, dict[str, Any]] = {}
509
+ webhook_paths: dict[str, dict[str, Any]] = {}
510
+ operation_ids: set[str] = set()
511
+ all_fields = get_fields_from_routes(list(routes or []) + list(webhooks or []))
512
+ model_name_map = get_compat_model_name_map(all_fields)
513
+ field_mapping, definitions = get_definitions(
514
+ fields=all_fields,
515
+ model_name_map=model_name_map,
516
+ separate_input_output_schemas=separate_input_output_schemas,
517
+ )
518
+ for route in routes or []:
519
+ if isinstance(route, routing.APIRoute):
520
+ result = get_openapi_path(
521
+ route=route,
522
+ operation_ids=operation_ids,
523
+ model_name_map=model_name_map,
524
+ field_mapping=field_mapping,
525
+ separate_input_output_schemas=separate_input_output_schemas,
526
+ )
527
+ if result:
528
+ path, security_schemes, path_definitions = result
529
+ if path:
530
+ paths.setdefault(route.path_format, {}).update(path)
531
+ if security_schemes:
532
+ components.setdefault("securitySchemes", {}).update(
533
+ security_schemes
534
+ )
535
+ if path_definitions:
536
+ definitions.update(path_definitions)
537
+ for webhook in webhooks or []:
538
+ if isinstance(webhook, routing.APIRoute):
539
+ result = get_openapi_path(
540
+ route=webhook,
541
+ operation_ids=operation_ids,
542
+ model_name_map=model_name_map,
543
+ field_mapping=field_mapping,
544
+ separate_input_output_schemas=separate_input_output_schemas,
545
+ )
546
+ if result:
547
+ path, security_schemes, path_definitions = result
548
+ if path:
549
+ webhook_paths.setdefault(webhook.path_format, {}).update(path)
550
+ if security_schemes:
551
+ components.setdefault("securitySchemes", {}).update(
552
+ security_schemes
553
+ )
554
+ if path_definitions:
555
+ definitions.update(path_definitions)
556
+ if definitions:
557
+ components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
558
+ if components:
559
+ output["components"] = components
560
+ output["paths"] = paths
561
+ if webhook_paths:
562
+ output["webhooks"] = webhook_paths
563
+ if tags:
564
+ output["tags"] = tags
565
+ if external_docs:
566
+ output["externalDocs"] = external_docs
567
+ return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore