prefect-client 2.20.2__py3-none-any.whl → 3.0.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 (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +423 -164
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +667 -440
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2466
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +124 -51
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +138 -48
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.2.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
@@ -1,877 +0,0 @@
1
- import dataclasses
2
- import inspect
3
- from contextlib import contextmanager
4
- from copy import copy, deepcopy
5
- from typing import (
6
- Any,
7
- Callable,
8
- Coroutine,
9
- Dict,
10
- ForwardRef,
11
- List,
12
- Mapping,
13
- Optional,
14
- Sequence,
15
- Tuple,
16
- Type,
17
- Union,
18
- cast,
19
- )
20
-
21
- import anyio
22
- from prefect._vendor.fastapi import params
23
- from prefect._vendor.fastapi.concurrency import (
24
- AsyncExitStack,
25
- asynccontextmanager,
26
- contextmanager_in_threadpool,
27
- )
28
- from prefect._vendor.fastapi.dependencies.models import Dependant, SecurityRequirement
29
- from prefect._vendor.fastapi.logger import logger
30
- from prefect._vendor.fastapi.security.base import SecurityBase
31
- from prefect._vendor.fastapi.security.oauth2 import OAuth2, SecurityScopes
32
- from prefect._vendor.fastapi.security.open_id_connect_url import OpenIdConnect
33
- from prefect._vendor.fastapi.utils import create_response_field, get_path_param_names
34
-
35
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
36
-
37
- if HAS_PYDANTIC_V2:
38
- from pydantic.v1 import BaseModel, create_model
39
- from pydantic.v1.error_wrappers import ErrorWrapper
40
- from pydantic.v1.errors import MissingError
41
- from pydantic.v1.fields import (
42
- SHAPE_FROZENSET,
43
- SHAPE_LIST,
44
- SHAPE_SEQUENCE,
45
- SHAPE_SET,
46
- SHAPE_SINGLETON,
47
- SHAPE_TUPLE,
48
- SHAPE_TUPLE_ELLIPSIS,
49
- FieldInfo,
50
- ModelField,
51
- Required,
52
- Undefined,
53
- )
54
- from pydantic.v1.schema import get_annotation_from_field_info
55
- from pydantic.v1.typing import evaluate_forwardref, get_args, get_origin
56
- from pydantic.v1.utils import lenient_issubclass
57
- else:
58
- from pydantic import BaseModel, create_model
59
- from pydantic.error_wrappers import ErrorWrapper
60
- from pydantic.errors import MissingError
61
- from pydantic.fields import (
62
- SHAPE_FROZENSET,
63
- SHAPE_LIST,
64
- SHAPE_SEQUENCE,
65
- SHAPE_SET,
66
- SHAPE_SINGLETON,
67
- SHAPE_TUPLE,
68
- SHAPE_TUPLE_ELLIPSIS,
69
- FieldInfo,
70
- ModelField,
71
- Required,
72
- Undefined,
73
- )
74
- from pydantic.schema import get_annotation_from_field_info
75
- from pydantic.typing import evaluate_forwardref, get_args, get_origin
76
- from pydantic.utils import lenient_issubclass
77
-
78
- from prefect._vendor.starlette.background import BackgroundTasks
79
- from prefect._vendor.starlette.concurrency import run_in_threadpool
80
- from prefect._vendor.starlette.datastructures import (
81
- FormData,
82
- Headers,
83
- QueryParams,
84
- UploadFile,
85
- )
86
- from prefect._vendor.starlette.requests import HTTPConnection, Request
87
- from prefect._vendor.starlette.responses import Response
88
- from prefect._vendor.starlette.websockets import WebSocket
89
- from typing_extensions import Annotated
90
-
91
- sequence_shapes = {
92
- SHAPE_LIST,
93
- SHAPE_SET,
94
- SHAPE_FROZENSET,
95
- SHAPE_TUPLE,
96
- SHAPE_SEQUENCE,
97
- SHAPE_TUPLE_ELLIPSIS,
98
- }
99
- sequence_types = (list, set, tuple)
100
- sequence_shape_to_type = {
101
- SHAPE_LIST: list,
102
- SHAPE_SET: set,
103
- SHAPE_TUPLE: tuple,
104
- SHAPE_SEQUENCE: list,
105
- SHAPE_TUPLE_ELLIPSIS: list,
106
- }
107
-
108
-
109
- multipart_not_installed_error = (
110
- 'Form data requires "python-multipart" to be installed. \n'
111
- 'You can install "python-multipart" with: \n\n'
112
- "pip install python-multipart\n"
113
- )
114
- multipart_incorrect_install_error = (
115
- 'Form data requires "python-multipart" to be installed. '
116
- 'It seems you installed "multipart" instead. \n'
117
- 'You can remove "multipart" with: \n\n'
118
- "pip uninstall multipart\n\n"
119
- 'And then install "python-multipart" with: \n\n'
120
- "pip install python-multipart\n"
121
- )
122
-
123
-
124
- def check_file_field(field: ModelField) -> None:
125
- field_info = field.field_info
126
- if isinstance(field_info, params.Form):
127
- try:
128
- # __version__ is available in both multiparts, and can be mocked
129
- from multipart import __version__ # type: ignore
130
-
131
- assert __version__
132
- try:
133
- # parse_options_header is only available in the right multipart
134
- from multipart.multipart import parse_options_header # type: ignore
135
-
136
- assert parse_options_header
137
- except ImportError:
138
- logger.error(multipart_incorrect_install_error)
139
- raise RuntimeError(multipart_incorrect_install_error) from None
140
- except ImportError:
141
- logger.error(multipart_not_installed_error)
142
- raise RuntimeError(multipart_not_installed_error) from None
143
-
144
-
145
- def get_param_sub_dependant(
146
- *,
147
- param_name: str,
148
- depends: params.Depends,
149
- path: str,
150
- security_scopes: Optional[List[str]] = None,
151
- ) -> Dependant:
152
- assert depends.dependency
153
- return get_sub_dependant(
154
- depends=depends,
155
- dependency=depends.dependency,
156
- path=path,
157
- name=param_name,
158
- security_scopes=security_scopes,
159
- )
160
-
161
-
162
- def get_parameterless_sub_dependant(*, depends: params.Depends, path: str) -> Dependant:
163
- assert callable(
164
- depends.dependency
165
- ), "A parameter-less dependency must have a callable dependency"
166
- return get_sub_dependant(depends=depends, dependency=depends.dependency, path=path)
167
-
168
-
169
- def get_sub_dependant(
170
- *,
171
- depends: params.Depends,
172
- dependency: Callable[..., Any],
173
- path: str,
174
- name: Optional[str] = None,
175
- security_scopes: Optional[List[str]] = None,
176
- ) -> Dependant:
177
- security_requirement = None
178
- security_scopes = security_scopes or []
179
- if isinstance(depends, params.Security):
180
- dependency_scopes = depends.scopes
181
- security_scopes.extend(dependency_scopes)
182
- if isinstance(dependency, SecurityBase):
183
- use_scopes: List[str] = []
184
- if isinstance(dependency, (OAuth2, OpenIdConnect)):
185
- use_scopes = security_scopes
186
- security_requirement = SecurityRequirement(
187
- security_scheme=dependency, scopes=use_scopes
188
- )
189
- sub_dependant = get_dependant(
190
- path=path,
191
- call=dependency,
192
- name=name,
193
- security_scopes=security_scopes,
194
- use_cache=depends.use_cache,
195
- )
196
- if security_requirement:
197
- sub_dependant.security_requirements.append(security_requirement)
198
- return sub_dependant
199
-
200
-
201
- CacheKey = Tuple[Optional[Callable[..., Any]], Tuple[str, ...]]
202
-
203
-
204
- def get_flat_dependant(
205
- dependant: Dependant,
206
- *,
207
- skip_repeats: bool = False,
208
- visited: Optional[List[CacheKey]] = None,
209
- ) -> Dependant:
210
- if visited is None:
211
- visited = []
212
- visited.append(dependant.cache_key)
213
-
214
- flat_dependant = Dependant(
215
- path_params=dependant.path_params.copy(),
216
- query_params=dependant.query_params.copy(),
217
- header_params=dependant.header_params.copy(),
218
- cookie_params=dependant.cookie_params.copy(),
219
- body_params=dependant.body_params.copy(),
220
- security_schemes=dependant.security_requirements.copy(),
221
- use_cache=dependant.use_cache,
222
- path=dependant.path,
223
- )
224
- for sub_dependant in dependant.dependencies:
225
- if skip_repeats and sub_dependant.cache_key in visited:
226
- continue
227
- flat_sub = get_flat_dependant(
228
- sub_dependant, skip_repeats=skip_repeats, visited=visited
229
- )
230
- flat_dependant.path_params.extend(flat_sub.path_params)
231
- flat_dependant.query_params.extend(flat_sub.query_params)
232
- flat_dependant.header_params.extend(flat_sub.header_params)
233
- flat_dependant.cookie_params.extend(flat_sub.cookie_params)
234
- flat_dependant.body_params.extend(flat_sub.body_params)
235
- flat_dependant.security_requirements.extend(flat_sub.security_requirements)
236
- return flat_dependant
237
-
238
-
239
- def get_flat_params(dependant: Dependant) -> List[ModelField]:
240
- flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
241
- return (
242
- flat_dependant.path_params
243
- + flat_dependant.query_params
244
- + flat_dependant.header_params
245
- + flat_dependant.cookie_params
246
- )
247
-
248
-
249
- def is_scalar_field(field: ModelField) -> bool:
250
- field_info = field.field_info
251
- if not (
252
- field.shape == SHAPE_SINGLETON
253
- and not lenient_issubclass(field.type_, BaseModel)
254
- and not lenient_issubclass(field.type_, sequence_types + (dict,))
255
- and not dataclasses.is_dataclass(field.type_)
256
- and not isinstance(field_info, params.Body)
257
- ):
258
- return False
259
- if field.sub_fields:
260
- if not all(is_scalar_field(f) for f in field.sub_fields):
261
- return False
262
- return True
263
-
264
-
265
- def is_scalar_sequence_field(field: ModelField) -> bool:
266
- if (field.shape in sequence_shapes) and not lenient_issubclass(
267
- field.type_, BaseModel
268
- ):
269
- if field.sub_fields is not None:
270
- for sub_field in field.sub_fields:
271
- if not is_scalar_field(sub_field):
272
- return False
273
- return True
274
- if lenient_issubclass(field.type_, sequence_types):
275
- return True
276
- return False
277
-
278
-
279
- def get_typed_signature(call: Callable[..., Any]) -> inspect.Signature:
280
- signature = inspect.signature(call)
281
- globalns = getattr(call, "__globals__", {})
282
- typed_params = [
283
- inspect.Parameter(
284
- name=param.name,
285
- kind=param.kind,
286
- default=param.default,
287
- annotation=get_typed_annotation(param.annotation, globalns),
288
- )
289
- for param in signature.parameters.values()
290
- ]
291
- typed_signature = inspect.Signature(typed_params)
292
- return typed_signature
293
-
294
-
295
- def get_typed_annotation(annotation: Any, globalns: Dict[str, Any]) -> Any:
296
- if isinstance(annotation, str):
297
- annotation = ForwardRef(annotation)
298
- annotation = evaluate_forwardref(annotation, globalns, globalns)
299
- return annotation
300
-
301
-
302
- def get_typed_return_annotation(call: Callable[..., Any]) -> Any:
303
- signature = inspect.signature(call)
304
- annotation = signature.return_annotation
305
-
306
- if annotation is inspect.Signature.empty:
307
- return None
308
-
309
- globalns = getattr(call, "__globals__", {})
310
- return get_typed_annotation(annotation, globalns)
311
-
312
-
313
- def get_dependant(
314
- *,
315
- path: str,
316
- call: Callable[..., Any],
317
- name: Optional[str] = None,
318
- security_scopes: Optional[List[str]] = None,
319
- use_cache: bool = True,
320
- ) -> Dependant:
321
- path_param_names = get_path_param_names(path)
322
- endpoint_signature = get_typed_signature(call)
323
- signature_params = endpoint_signature.parameters
324
- dependant = Dependant(
325
- call=call,
326
- name=name,
327
- path=path,
328
- security_scopes=security_scopes,
329
- use_cache=use_cache,
330
- )
331
- for param_name, param in signature_params.items():
332
- is_path_param = param_name in path_param_names
333
- type_annotation, depends, param_field = analyze_param(
334
- param_name=param_name,
335
- annotation=param.annotation,
336
- value=param.default,
337
- is_path_param=is_path_param,
338
- )
339
- if depends is not None:
340
- sub_dependant = get_param_sub_dependant(
341
- param_name=param_name,
342
- depends=depends,
343
- path=path,
344
- security_scopes=security_scopes,
345
- )
346
- dependant.dependencies.append(sub_dependant)
347
- continue
348
- if add_non_field_param_to_dependency(
349
- param_name=param_name,
350
- type_annotation=type_annotation,
351
- dependant=dependant,
352
- ):
353
- assert (
354
- param_field is None
355
- ), f"Cannot specify multiple FastAPI annotations for {param_name!r}"
356
- continue
357
- assert param_field is not None
358
- if is_body_param(param_field=param_field, is_path_param=is_path_param):
359
- dependant.body_params.append(param_field)
360
- else:
361
- add_param_to_fields(field=param_field, dependant=dependant)
362
- return dependant
363
-
364
-
365
- def add_non_field_param_to_dependency(
366
- *, param_name: str, type_annotation: Any, dependant: Dependant
367
- ) -> Optional[bool]:
368
- if lenient_issubclass(type_annotation, Request):
369
- dependant.request_param_name = param_name
370
- return True
371
- elif lenient_issubclass(type_annotation, WebSocket):
372
- dependant.websocket_param_name = param_name
373
- return True
374
- elif lenient_issubclass(type_annotation, HTTPConnection):
375
- dependant.http_connection_param_name = param_name
376
- return True
377
- elif lenient_issubclass(type_annotation, Response):
378
- dependant.response_param_name = param_name
379
- return True
380
- elif lenient_issubclass(type_annotation, BackgroundTasks):
381
- dependant.background_tasks_param_name = param_name
382
- return True
383
- elif lenient_issubclass(type_annotation, SecurityScopes):
384
- dependant.security_scopes_param_name = param_name
385
- return True
386
- return None
387
-
388
-
389
- def analyze_param(
390
- *,
391
- param_name: str,
392
- annotation: Any,
393
- value: Any,
394
- is_path_param: bool,
395
- ) -> Tuple[Any, Optional[params.Depends], Optional[ModelField]]:
396
- field_info = None
397
- used_default_field_info = False
398
- depends = None
399
- type_annotation: Any = Any
400
- if (
401
- annotation is not inspect.Signature.empty
402
- and get_origin(annotation) is Annotated # type: ignore[comparison-overlap]
403
- ):
404
- annotated_args = get_args(annotation)
405
- type_annotation = annotated_args[0]
406
- fastapi_annotations = [
407
- arg
408
- for arg in annotated_args[1:]
409
- if isinstance(arg, (FieldInfo, params.Depends))
410
- ]
411
- assert (
412
- len(fastapi_annotations) <= 1
413
- ), f"Cannot specify multiple `Annotated` FastAPI arguments for {param_name!r}"
414
- fastapi_annotation = next(iter(fastapi_annotations), None)
415
- if isinstance(fastapi_annotation, FieldInfo):
416
- # Copy `field_info` because we mutate `field_info.default` below.
417
- field_info = copy(fastapi_annotation)
418
- assert field_info.default is Undefined or field_info.default is Required, (
419
- f"`{field_info.__class__.__name__}` default value cannot be set in"
420
- f" `Annotated` for {param_name!r}. Set the default value with `=`"
421
- " instead."
422
- )
423
- if value is not inspect.Signature.empty:
424
- assert not is_path_param, "Path parameters cannot have default values"
425
- field_info.default = value
426
- else:
427
- field_info.default = Required
428
- elif isinstance(fastapi_annotation, params.Depends):
429
- depends = fastapi_annotation
430
- elif annotation is not inspect.Signature.empty:
431
- type_annotation = annotation
432
-
433
- if isinstance(value, params.Depends):
434
- assert depends is None, (
435
- "Cannot specify `Depends` in `Annotated` and default value"
436
- f" together for {param_name!r}"
437
- )
438
- assert field_info is None, (
439
- "Cannot specify a FastAPI annotation in `Annotated` and `Depends` as a"
440
- f" default value together for {param_name!r}"
441
- )
442
- depends = value
443
- elif isinstance(value, FieldInfo):
444
- assert field_info is None, (
445
- "Cannot specify FastAPI annotations in `Annotated` and default value"
446
- f" together for {param_name!r}"
447
- )
448
- field_info = value
449
-
450
- if depends is not None and depends.dependency is None:
451
- depends.dependency = type_annotation
452
-
453
- if lenient_issubclass(
454
- type_annotation,
455
- (Request, WebSocket, HTTPConnection, Response, BackgroundTasks, SecurityScopes),
456
- ):
457
- assert depends is None, f"Cannot specify `Depends` for type {type_annotation!r}"
458
- assert (
459
- field_info is None
460
- ), f"Cannot specify FastAPI annotation for type {type_annotation!r}"
461
- elif field_info is None and depends is None:
462
- default_value = value if value is not inspect.Signature.empty else Required
463
- if is_path_param:
464
- # We might check here that `default_value is Required`, but the fact is that the same
465
- # parameter might sometimes be a path parameter and sometimes not. See
466
- # `tests/test_infer_param_optionality.py` for an example.
467
- field_info = params.Path()
468
- else:
469
- field_info = params.Query(default=default_value)
470
- used_default_field_info = True
471
-
472
- field = None
473
- if field_info is not None:
474
- if is_path_param:
475
- assert isinstance(field_info, params.Path), (
476
- f"Cannot use `{field_info.__class__.__name__}` for path param"
477
- f" {param_name!r}"
478
- )
479
- elif (
480
- isinstance(field_info, params.Param)
481
- and getattr(field_info, "in_", None) is None
482
- ):
483
- field_info.in_ = params.ParamTypes.query
484
- annotation = get_annotation_from_field_info(
485
- annotation if annotation is not inspect.Signature.empty else Any,
486
- field_info,
487
- param_name,
488
- )
489
- if not field_info.alias and getattr(field_info, "convert_underscores", None):
490
- alias = param_name.replace("_", "-")
491
- else:
492
- alias = field_info.alias or param_name
493
- field = create_response_field(
494
- name=param_name,
495
- type_=annotation,
496
- default=field_info.default,
497
- alias=alias,
498
- required=field_info.default in (Required, Undefined),
499
- field_info=field_info,
500
- )
501
- if used_default_field_info:
502
- if lenient_issubclass(field.type_, UploadFile):
503
- field.field_info = params.File(field_info.default)
504
- elif not is_scalar_field(field=field):
505
- field.field_info = params.Body(field_info.default)
506
-
507
- return type_annotation, depends, field
508
-
509
-
510
- def is_body_param(*, param_field: ModelField, is_path_param: bool) -> bool:
511
- if is_path_param:
512
- assert is_scalar_field(
513
- field=param_field
514
- ), "Path params must be of one of the supported types"
515
- return False
516
- elif is_scalar_field(field=param_field):
517
- return False
518
- elif isinstance(
519
- param_field.field_info, (params.Query, params.Header)
520
- ) and is_scalar_sequence_field(param_field):
521
- return False
522
- else:
523
- assert isinstance(
524
- param_field.field_info, params.Body
525
- ), f"Param: {param_field.name} can only be a request body, using Body()"
526
- return True
527
-
528
-
529
- def add_param_to_fields(*, field: ModelField, dependant: Dependant) -> None:
530
- field_info = cast(params.Param, field.field_info)
531
- if field_info.in_ == params.ParamTypes.path:
532
- dependant.path_params.append(field)
533
- elif field_info.in_ == params.ParamTypes.query:
534
- dependant.query_params.append(field)
535
- elif field_info.in_ == params.ParamTypes.header:
536
- dependant.header_params.append(field)
537
- else:
538
- assert (
539
- field_info.in_ == params.ParamTypes.cookie
540
- ), f"non-body parameters must be in path, query, header or cookie: {field.name}"
541
- dependant.cookie_params.append(field)
542
-
543
-
544
- def is_coroutine_callable(call: Callable[..., Any]) -> bool:
545
- if inspect.isroutine(call):
546
- return inspect.iscoroutinefunction(call)
547
- if inspect.isclass(call):
548
- return False
549
- dunder_call = getattr(call, "__call__", None) # noqa: B004
550
- return inspect.iscoroutinefunction(dunder_call)
551
-
552
-
553
- def is_async_gen_callable(call: Callable[..., Any]) -> bool:
554
- if inspect.isasyncgenfunction(call):
555
- return True
556
- dunder_call = getattr(call, "__call__", None) # noqa: B004
557
- return inspect.isasyncgenfunction(dunder_call)
558
-
559
-
560
- def is_gen_callable(call: Callable[..., Any]) -> bool:
561
- if inspect.isgeneratorfunction(call):
562
- return True
563
- dunder_call = getattr(call, "__call__", None) # noqa: B004
564
- return inspect.isgeneratorfunction(dunder_call)
565
-
566
-
567
- async def solve_generator(
568
- *, call: Callable[..., Any], stack: AsyncExitStack, sub_values: Dict[str, Any]
569
- ) -> Any:
570
- if is_gen_callable(call):
571
- cm = contextmanager_in_threadpool(contextmanager(call)(**sub_values))
572
- elif is_async_gen_callable(call):
573
- cm = asynccontextmanager(call)(**sub_values)
574
- return await stack.enter_async_context(cm)
575
-
576
-
577
- async def solve_dependencies(
578
- *,
579
- request: Union[Request, WebSocket],
580
- dependant: Dependant,
581
- body: Optional[Union[Dict[str, Any], FormData]] = None,
582
- background_tasks: Optional[BackgroundTasks] = None,
583
- response: Optional[Response] = None,
584
- dependency_overrides_provider: Optional[Any] = None,
585
- dependency_cache: Optional[Dict[Tuple[Callable[..., Any], Tuple[str]], Any]] = None,
586
- ) -> Tuple[
587
- Dict[str, Any],
588
- List[ErrorWrapper],
589
- Optional[BackgroundTasks],
590
- Response,
591
- Dict[Tuple[Callable[..., Any], Tuple[str]], Any],
592
- ]:
593
- values: Dict[str, Any] = {}
594
- errors: List[ErrorWrapper] = []
595
- if response is None:
596
- response = Response()
597
- del response.headers["content-length"]
598
- response.status_code = None # type: ignore
599
- dependency_cache = dependency_cache or {}
600
- sub_dependant: Dependant
601
- for sub_dependant in dependant.dependencies:
602
- sub_dependant.call = cast(Callable[..., Any], sub_dependant.call)
603
- sub_dependant.cache_key = cast(
604
- Tuple[Callable[..., Any], Tuple[str]], sub_dependant.cache_key
605
- )
606
- call = sub_dependant.call
607
- use_sub_dependant = sub_dependant
608
- if (
609
- dependency_overrides_provider
610
- and dependency_overrides_provider.dependency_overrides
611
- ):
612
- original_call = sub_dependant.call
613
- call = getattr(
614
- dependency_overrides_provider, "dependency_overrides", {}
615
- ).get(original_call, original_call)
616
- use_path: str = sub_dependant.path # type: ignore
617
- use_sub_dependant = get_dependant(
618
- path=use_path,
619
- call=call,
620
- name=sub_dependant.name,
621
- security_scopes=sub_dependant.security_scopes,
622
- )
623
-
624
- solved_result = await solve_dependencies(
625
- request=request,
626
- dependant=use_sub_dependant,
627
- body=body,
628
- background_tasks=background_tasks,
629
- response=response,
630
- dependency_overrides_provider=dependency_overrides_provider,
631
- dependency_cache=dependency_cache,
632
- )
633
- (
634
- sub_values,
635
- sub_errors,
636
- background_tasks,
637
- _, # the subdependency returns the same response we have
638
- sub_dependency_cache,
639
- ) = solved_result
640
- dependency_cache.update(sub_dependency_cache)
641
- if sub_errors:
642
- errors.extend(sub_errors)
643
- continue
644
- if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
645
- solved = dependency_cache[sub_dependant.cache_key]
646
- elif is_gen_callable(call) or is_async_gen_callable(call):
647
- stack = request.scope.get("fastapi_astack")
648
- assert isinstance(stack, AsyncExitStack)
649
- solved = await solve_generator(
650
- call=call, stack=stack, sub_values=sub_values
651
- )
652
- elif is_coroutine_callable(call):
653
- solved = await call(**sub_values)
654
- else:
655
- solved = await run_in_threadpool(call, **sub_values)
656
- if sub_dependant.name is not None:
657
- values[sub_dependant.name] = solved
658
- if sub_dependant.cache_key not in dependency_cache:
659
- dependency_cache[sub_dependant.cache_key] = solved
660
- path_values, path_errors = request_params_to_args(
661
- dependant.path_params, request.path_params
662
- )
663
- query_values, query_errors = request_params_to_args(
664
- dependant.query_params, request.query_params
665
- )
666
- header_values, header_errors = request_params_to_args(
667
- dependant.header_params, request.headers
668
- )
669
- cookie_values, cookie_errors = request_params_to_args(
670
- dependant.cookie_params, request.cookies
671
- )
672
- values.update(path_values)
673
- values.update(query_values)
674
- values.update(header_values)
675
- values.update(cookie_values)
676
- errors += path_errors + query_errors + header_errors + cookie_errors
677
- if dependant.body_params:
678
- (
679
- body_values,
680
- body_errors,
681
- ) = await request_body_to_args( # body_params checked above
682
- required_params=dependant.body_params, received_body=body
683
- )
684
- values.update(body_values)
685
- errors.extend(body_errors)
686
- if dependant.http_connection_param_name:
687
- values[dependant.http_connection_param_name] = request
688
- if dependant.request_param_name and isinstance(request, Request):
689
- values[dependant.request_param_name] = request
690
- elif dependant.websocket_param_name and isinstance(request, WebSocket):
691
- values[dependant.websocket_param_name] = request
692
- if dependant.background_tasks_param_name:
693
- if background_tasks is None:
694
- background_tasks = BackgroundTasks()
695
- values[dependant.background_tasks_param_name] = background_tasks
696
- if dependant.response_param_name:
697
- values[dependant.response_param_name] = response
698
- if dependant.security_scopes_param_name:
699
- values[dependant.security_scopes_param_name] = SecurityScopes(
700
- scopes=dependant.security_scopes
701
- )
702
- return values, errors, background_tasks, response, dependency_cache
703
-
704
-
705
- def request_params_to_args(
706
- required_params: Sequence[ModelField],
707
- received_params: Union[Mapping[str, Any], QueryParams, Headers],
708
- ) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
709
- values = {}
710
- errors = []
711
- for field in required_params:
712
- if is_scalar_sequence_field(field) and isinstance(
713
- received_params, (QueryParams, Headers)
714
- ):
715
- value = received_params.getlist(field.alias) or field.default
716
- else:
717
- value = received_params.get(field.alias)
718
- field_info = field.field_info
719
- assert isinstance(
720
- field_info, params.Param
721
- ), "Params must be subclasses of Param"
722
- if value is None:
723
- if field.required:
724
- errors.append(
725
- ErrorWrapper(
726
- MissingError(), loc=(field_info.in_.value, field.alias)
727
- )
728
- )
729
- else:
730
- values[field.name] = deepcopy(field.default)
731
- continue
732
- v_, errors_ = field.validate(
733
- value, values, loc=(field_info.in_.value, field.alias)
734
- )
735
- if isinstance(errors_, ErrorWrapper):
736
- errors.append(errors_)
737
- elif isinstance(errors_, list):
738
- errors.extend(errors_)
739
- else:
740
- values[field.name] = v_
741
- return values, errors
742
-
743
-
744
- async def request_body_to_args(
745
- required_params: List[ModelField],
746
- received_body: Optional[Union[Dict[str, Any], FormData]],
747
- ) -> Tuple[Dict[str, Any], List[ErrorWrapper]]:
748
- values = {}
749
- errors = []
750
- if required_params:
751
- field = required_params[0]
752
- field_info = field.field_info
753
- embed = getattr(field_info, "embed", None)
754
- field_alias_omitted = len(required_params) == 1 and not embed
755
- if field_alias_omitted:
756
- received_body = {field.alias: received_body}
757
-
758
- for field in required_params:
759
- loc: Tuple[str, ...]
760
- if field_alias_omitted:
761
- loc = ("body",)
762
- else:
763
- loc = ("body", field.alias)
764
-
765
- value: Optional[Any] = None
766
- if received_body is not None:
767
- if (
768
- field.shape in sequence_shapes or field.type_ in sequence_types
769
- ) and isinstance(received_body, FormData):
770
- value = received_body.getlist(field.alias)
771
- else:
772
- try:
773
- value = received_body.get(field.alias)
774
- except AttributeError:
775
- errors.append(get_missing_field_error(loc))
776
- continue
777
- if (
778
- value is None
779
- or (isinstance(field_info, params.Form) and value == "")
780
- or (
781
- isinstance(field_info, params.Form)
782
- and field.shape in sequence_shapes
783
- and len(value) == 0
784
- )
785
- ):
786
- if field.required:
787
- errors.append(get_missing_field_error(loc))
788
- else:
789
- values[field.name] = deepcopy(field.default)
790
- continue
791
- if (
792
- isinstance(field_info, params.File)
793
- and lenient_issubclass(field.type_, bytes)
794
- and isinstance(value, UploadFile)
795
- ):
796
- value = await value.read()
797
- elif (
798
- field.shape in sequence_shapes
799
- and isinstance(field_info, params.File)
800
- and lenient_issubclass(field.type_, bytes)
801
- and isinstance(value, sequence_types)
802
- ):
803
- results: List[Union[bytes, str]] = []
804
-
805
- async def process_fn(
806
- fn: Callable[[], Coroutine[Any, Any, Any]],
807
- ) -> None:
808
- result = await fn()
809
- results.append(result) # noqa: B023
810
-
811
- async with anyio.create_task_group() as tg:
812
- for sub_value in value:
813
- tg.start_soon(process_fn, sub_value.read)
814
- value = sequence_shape_to_type[field.shape](results)
815
-
816
- v_, errors_ = field.validate(value, values, loc=loc)
817
-
818
- if isinstance(errors_, ErrorWrapper):
819
- errors.append(errors_)
820
- elif isinstance(errors_, list):
821
- errors.extend(errors_)
822
- else:
823
- values[field.name] = v_
824
- return values, errors
825
-
826
-
827
- def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
828
- missing_field_error = ErrorWrapper(MissingError(), loc=loc)
829
- return missing_field_error
830
-
831
-
832
- def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
833
- flat_dependant = get_flat_dependant(dependant)
834
- if not flat_dependant.body_params:
835
- return None
836
- first_param = flat_dependant.body_params[0]
837
- field_info = first_param.field_info
838
- embed = getattr(field_info, "embed", None)
839
- body_param_names_set = {param.name for param in flat_dependant.body_params}
840
- if len(body_param_names_set) == 1 and not embed:
841
- check_file_field(first_param)
842
- return first_param
843
- # If one field requires to embed, all have to be embedded
844
- # in case a sub-dependency is evaluated with a single unique body field
845
- # That is combined (embedded) with other body fields
846
- for param in flat_dependant.body_params:
847
- setattr(param.field_info, "embed", True) # noqa: B010
848
- model_name = "Body_" + name
849
- BodyModel: Type[BaseModel] = create_model(model_name)
850
- for f in flat_dependant.body_params:
851
- BodyModel.__fields__[f.name] = f
852
- required = any(True for f in flat_dependant.body_params if f.required)
853
-
854
- BodyFieldInfo_kwargs: Dict[str, Any] = {"default": None}
855
- if any(isinstance(f.field_info, params.File) for f in flat_dependant.body_params):
856
- BodyFieldInfo: Type[params.Body] = params.File
857
- elif any(isinstance(f.field_info, params.Form) for f in flat_dependant.body_params):
858
- BodyFieldInfo = params.Form
859
- else:
860
- BodyFieldInfo = params.Body
861
-
862
- body_param_media_types = [
863
- f.field_info.media_type
864
- for f in flat_dependant.body_params
865
- if isinstance(f.field_info, params.Body)
866
- ]
867
- if len(set(body_param_media_types)) == 1:
868
- BodyFieldInfo_kwargs["media_type"] = body_param_media_types[0]
869
- final_field = create_response_field(
870
- name="body",
871
- type_=BodyModel,
872
- required=required,
873
- alias="body",
874
- field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
875
- )
876
- check_file_field(final_field)
877
- return final_field