prefect-client 2.20.4__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.
- prefect/__init__.py +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.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
|