prefect-client 2.19.3__py3-none-any.whl → 3.0.0rc1__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 +8 -56
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/concurrency/api.py +0 -34
- 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/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/pydantic/__init__.py +0 -45
- 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/schemas/bases.py +44 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +60 -158
- prefect/artifacts.py +161 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +268 -148
- prefect/blocks/fields.py +2 -57
- prefect/blocks/kubernetes.py +8 -12
- prefect/blocks/notifications.py +40 -20
- prefect/blocks/system.py +22 -11
- prefect/blocks/webhook.py +2 -9
- prefect/client/base.py +4 -4
- prefect/client/cloud.py +8 -13
- prefect/client/orchestration.py +347 -341
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +147 -145
- prefect/client/schemas/responses.py +16 -24
- prefect/client/schemas/schedules.py +47 -35
- prefect/client/subscriptions.py +2 -2
- prefect/client/utilities.py +5 -2
- prefect/concurrency/asyncio.py +3 -1
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +6 -3
- prefect/context.py +195 -27
- prefect/deployments/__init__.py +5 -6
- prefect/deployments/base.py +7 -5
- prefect/deployments/flow_runs.py +185 -0
- prefect/deployments/runner.py +50 -45
- prefect/deployments/schedules.py +28 -23
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +1 -0
- prefect/deployments/steps/pull.py +7 -21
- prefect/engine.py +12 -2422
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +19 -6
- prefect/events/clients.py +14 -37
- prefect/events/filters.py +14 -18
- prefect/events/related.py +2 -2
- 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 +34 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +2 -3
- prefect/events/worker.py +2 -3
- prefect/filesystems.py +6 -517
- prefect/{new_flow_engine.py → flow_engine.py} +313 -72
- prefect/flow_runs.py +377 -5
- prefect/flows.py +248 -165
- prefect/futures.py +186 -345
- prefect/infrastructure/__init__.py +0 -27
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +11 -6
- prefect/infrastructure/provisioners/container_instance.py +11 -7
- prefect/infrastructure/provisioners/ecs.py +6 -4
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +5 -7
- prefect/logging/formatters.py +0 -2
- prefect/logging/handlers.py +3 -11
- prefect/logging/loggers.py +2 -2
- prefect/manifests.py +2 -1
- prefect/records/__init__.py +1 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +9 -9
- prefect/runner/server.py +6 -10
- prefect/runner/storage.py +3 -8
- prefect/runner/submit.py +2 -2
- prefect/runner/utils.py +2 -2
- prefect/serializers.py +24 -35
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +70 -133
- prefect/states.py +17 -47
- prefect/task_engine.py +697 -58
- prefect/task_runners.py +269 -301
- prefect/task_server.py +53 -34
- prefect/tasks.py +327 -337
- prefect/transactions.py +220 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +121 -41
- prefect/utilities/collections.py +23 -38
- prefect/utilities/dispatch.py +11 -3
- prefect/utilities/dockerutils.py +4 -0
- prefect/utilities/engine.py +140 -20
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +78 -61
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +15 -17
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
- prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
- 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/__init__.py +0 -0
- 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/__init__.py +0 -0
- 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/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/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/agent.py +0 -698
- prefect/deployments/deployments.py +0 -1042
- 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/base.py +0 -323
- prefect/infrastructure/container.py +0 -818
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- 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/workers/block.py +0 -218
- prefect_client-2.19.3.dist-info/RECORD +0 -292
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,485 +0,0 @@
|
|
1
|
-
import http.client
|
2
|
-
import inspect
|
3
|
-
import warnings
|
4
|
-
from enum import Enum
|
5
|
-
from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Type, Union, cast
|
6
|
-
|
7
|
-
from prefect._vendor.fastapi import routing
|
8
|
-
from prefect._vendor.fastapi.datastructures import DefaultPlaceholder
|
9
|
-
from prefect._vendor.fastapi.dependencies.models import Dependant
|
10
|
-
from prefect._vendor.fastapi.dependencies.utils import (
|
11
|
-
get_flat_dependant,
|
12
|
-
get_flat_params,
|
13
|
-
)
|
14
|
-
from prefect._vendor.fastapi.encoders import jsonable_encoder
|
15
|
-
from prefect._vendor.fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
|
16
|
-
from prefect._vendor.fastapi.openapi.models import OpenAPI
|
17
|
-
from prefect._vendor.fastapi.params import Body, Param
|
18
|
-
from prefect._vendor.fastapi.responses import Response
|
19
|
-
from prefect._vendor.fastapi.utils import (
|
20
|
-
deep_dict_update,
|
21
|
-
generate_operation_id_for_path,
|
22
|
-
get_model_definitions,
|
23
|
-
is_body_allowed_for_status_code,
|
24
|
-
)
|
25
|
-
|
26
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
27
|
-
|
28
|
-
if HAS_PYDANTIC_V2:
|
29
|
-
from pydantic.v1 import BaseModel
|
30
|
-
from pydantic.v1.fields import ModelField, Undefined
|
31
|
-
from pydantic.v1.schema import (
|
32
|
-
field_schema,
|
33
|
-
get_flat_models_from_fields,
|
34
|
-
get_model_name_map,
|
35
|
-
)
|
36
|
-
from pydantic.v1.utils import lenient_issubclass
|
37
|
-
else:
|
38
|
-
from pydantic import BaseModel
|
39
|
-
from pydantic.fields import ModelField, Undefined
|
40
|
-
from pydantic.schema import (
|
41
|
-
field_schema,
|
42
|
-
get_flat_models_from_fields,
|
43
|
-
get_model_name_map,
|
44
|
-
)
|
45
|
-
from pydantic.utils import lenient_issubclass
|
46
|
-
|
47
|
-
from prefect._vendor.starlette.responses import JSONResponse
|
48
|
-
from prefect._vendor.starlette.routing import BaseRoute
|
49
|
-
from prefect._vendor.starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
50
|
-
|
51
|
-
validation_error_definition = {
|
52
|
-
"title": "ValidationError",
|
53
|
-
"type": "object",
|
54
|
-
"properties": {
|
55
|
-
"loc": {
|
56
|
-
"title": "Location",
|
57
|
-
"type": "array",
|
58
|
-
"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]},
|
59
|
-
},
|
60
|
-
"msg": {"title": "Message", "type": "string"},
|
61
|
-
"type": {"title": "Error Type", "type": "string"},
|
62
|
-
},
|
63
|
-
"required": ["loc", "msg", "type"],
|
64
|
-
}
|
65
|
-
|
66
|
-
validation_error_response_definition = {
|
67
|
-
"title": "HTTPValidationError",
|
68
|
-
"type": "object",
|
69
|
-
"properties": {
|
70
|
-
"detail": {
|
71
|
-
"title": "Detail",
|
72
|
-
"type": "array",
|
73
|
-
"items": {"$ref": REF_PREFIX + "ValidationError"},
|
74
|
-
}
|
75
|
-
},
|
76
|
-
}
|
77
|
-
|
78
|
-
status_code_ranges: Dict[str, str] = {
|
79
|
-
"1XX": "Information",
|
80
|
-
"2XX": "Success",
|
81
|
-
"3XX": "Redirection",
|
82
|
-
"4XX": "Client Error",
|
83
|
-
"5XX": "Server Error",
|
84
|
-
"DEFAULT": "Default Response",
|
85
|
-
}
|
86
|
-
|
87
|
-
|
88
|
-
def get_openapi_security_definitions(
|
89
|
-
flat_dependant: Dependant,
|
90
|
-
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]:
|
91
|
-
security_definitions = {}
|
92
|
-
operation_security = []
|
93
|
-
for security_requirement in flat_dependant.security_requirements:
|
94
|
-
security_definition = jsonable_encoder(
|
95
|
-
security_requirement.security_scheme.model,
|
96
|
-
by_alias=True,
|
97
|
-
exclude_none=True,
|
98
|
-
)
|
99
|
-
security_name = security_requirement.security_scheme.scheme_name
|
100
|
-
security_definitions[security_name] = security_definition
|
101
|
-
operation_security.append({security_name: security_requirement.scopes})
|
102
|
-
return security_definitions, operation_security
|
103
|
-
|
104
|
-
|
105
|
-
def get_openapi_operation_parameters(
|
106
|
-
*,
|
107
|
-
all_route_params: Sequence[ModelField],
|
108
|
-
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
109
|
-
) -> List[Dict[str, Any]]:
|
110
|
-
parameters = []
|
111
|
-
for param in all_route_params:
|
112
|
-
field_info = param.field_info
|
113
|
-
field_info = cast(Param, field_info)
|
114
|
-
if not field_info.include_in_schema:
|
115
|
-
continue
|
116
|
-
parameter = {
|
117
|
-
"name": param.alias,
|
118
|
-
"in": field_info.in_.value,
|
119
|
-
"required": param.required,
|
120
|
-
"schema": field_schema(
|
121
|
-
param, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
122
|
-
)[0],
|
123
|
-
}
|
124
|
-
if field_info.description:
|
125
|
-
parameter["description"] = field_info.description
|
126
|
-
if field_info.example != Undefined:
|
127
|
-
parameter["example"] = jsonable_encoder(field_info.example)
|
128
|
-
if field_info.deprecated:
|
129
|
-
parameter["deprecated"] = field_info.deprecated
|
130
|
-
parameters.append(parameter)
|
131
|
-
return parameters
|
132
|
-
|
133
|
-
|
134
|
-
def get_openapi_operation_request_body(
|
135
|
-
*,
|
136
|
-
body_field: Optional[ModelField],
|
137
|
-
model_name_map: Dict[Union[Type[BaseModel], Type[Enum]], str],
|
138
|
-
) -> Optional[Dict[str, Any]]:
|
139
|
-
if not body_field:
|
140
|
-
return None
|
141
|
-
assert isinstance(body_field, ModelField)
|
142
|
-
body_schema, _, _ = field_schema(
|
143
|
-
body_field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
144
|
-
)
|
145
|
-
field_info = cast(Body, body_field.field_info)
|
146
|
-
request_media_type = field_info.media_type
|
147
|
-
required = body_field.required
|
148
|
-
request_body_oai: Dict[str, Any] = {}
|
149
|
-
if required:
|
150
|
-
request_body_oai["required"] = required
|
151
|
-
request_media_content: Dict[str, Any] = {"schema": body_schema}
|
152
|
-
if field_info.example != Undefined:
|
153
|
-
request_media_content["example"] = jsonable_encoder(field_info.example)
|
154
|
-
request_body_oai["content"] = {request_media_type: request_media_content}
|
155
|
-
return request_body_oai
|
156
|
-
|
157
|
-
|
158
|
-
def generate_operation_id(
|
159
|
-
*, route: routing.APIRoute, method: str
|
160
|
-
) -> str: # pragma: nocover
|
161
|
-
warnings.warn(
|
162
|
-
(
|
163
|
-
"fastapi.openapi.utils.generate_operation_id() was deprecated, "
|
164
|
-
"it is not used internally, and will be removed soon"
|
165
|
-
),
|
166
|
-
DeprecationWarning,
|
167
|
-
stacklevel=2,
|
168
|
-
)
|
169
|
-
if route.operation_id:
|
170
|
-
return route.operation_id
|
171
|
-
path: str = route.path_format
|
172
|
-
return generate_operation_id_for_path(name=route.name, path=path, method=method)
|
173
|
-
|
174
|
-
|
175
|
-
def generate_operation_summary(*, route: routing.APIRoute, method: str) -> str:
|
176
|
-
if route.summary:
|
177
|
-
return route.summary
|
178
|
-
return route.name.replace("_", " ").title()
|
179
|
-
|
180
|
-
|
181
|
-
def get_openapi_operation_metadata(
|
182
|
-
*, route: routing.APIRoute, method: str, operation_ids: Set[str]
|
183
|
-
) -> Dict[str, Any]:
|
184
|
-
operation: Dict[str, Any] = {}
|
185
|
-
if route.tags:
|
186
|
-
operation["tags"] = route.tags
|
187
|
-
operation["summary"] = generate_operation_summary(route=route, method=method)
|
188
|
-
if route.description:
|
189
|
-
operation["description"] = route.description
|
190
|
-
operation_id = route.operation_id or route.unique_id
|
191
|
-
if operation_id in operation_ids:
|
192
|
-
message = (
|
193
|
-
f"Duplicate Operation ID {operation_id} for function "
|
194
|
-
+ f"{route.endpoint.__name__}"
|
195
|
-
)
|
196
|
-
file_name = getattr(route.endpoint, "__globals__", {}).get("__file__")
|
197
|
-
if file_name:
|
198
|
-
message += f" at {file_name}"
|
199
|
-
warnings.warn(message, stacklevel=1)
|
200
|
-
operation_ids.add(operation_id)
|
201
|
-
operation["operationId"] = operation_id
|
202
|
-
if route.deprecated:
|
203
|
-
operation["deprecated"] = route.deprecated
|
204
|
-
return operation
|
205
|
-
|
206
|
-
|
207
|
-
def get_openapi_path(
|
208
|
-
*, route: routing.APIRoute, model_name_map: Dict[type, str], operation_ids: Set[str]
|
209
|
-
) -> Tuple[Dict[str, Any], Dict[str, Any], Dict[str, Any]]:
|
210
|
-
path = {}
|
211
|
-
security_schemes: Dict[str, Any] = {}
|
212
|
-
definitions: Dict[str, Any] = {}
|
213
|
-
assert route.methods is not None, "Methods must be a list"
|
214
|
-
if isinstance(route.response_class, DefaultPlaceholder):
|
215
|
-
current_response_class: Type[Response] = route.response_class.value
|
216
|
-
else:
|
217
|
-
current_response_class = route.response_class
|
218
|
-
assert current_response_class, "A response class is needed to generate OpenAPI"
|
219
|
-
route_response_media_type: Optional[str] = current_response_class.media_type
|
220
|
-
if route.include_in_schema:
|
221
|
-
for method in route.methods:
|
222
|
-
operation = get_openapi_operation_metadata(
|
223
|
-
route=route, method=method, operation_ids=operation_ids
|
224
|
-
)
|
225
|
-
parameters: List[Dict[str, Any]] = []
|
226
|
-
flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
|
227
|
-
security_definitions, operation_security = get_openapi_security_definitions(
|
228
|
-
flat_dependant=flat_dependant
|
229
|
-
)
|
230
|
-
if operation_security:
|
231
|
-
operation.setdefault("security", []).extend(operation_security)
|
232
|
-
if security_definitions:
|
233
|
-
security_schemes.update(security_definitions)
|
234
|
-
all_route_params = get_flat_params(route.dependant)
|
235
|
-
operation_parameters = get_openapi_operation_parameters(
|
236
|
-
all_route_params=all_route_params, model_name_map=model_name_map
|
237
|
-
)
|
238
|
-
parameters.extend(operation_parameters)
|
239
|
-
if parameters:
|
240
|
-
all_parameters = {
|
241
|
-
(param["in"], param["name"]): param for param in parameters
|
242
|
-
}
|
243
|
-
required_parameters = {
|
244
|
-
(param["in"], param["name"]): param
|
245
|
-
for param in parameters
|
246
|
-
if param.get("required")
|
247
|
-
}
|
248
|
-
# Make sure required definitions of the same parameter take precedence
|
249
|
-
# over non-required definitions
|
250
|
-
all_parameters.update(required_parameters)
|
251
|
-
operation["parameters"] = list(all_parameters.values())
|
252
|
-
if method in METHODS_WITH_BODY:
|
253
|
-
request_body_oai = get_openapi_operation_request_body(
|
254
|
-
body_field=route.body_field, model_name_map=model_name_map
|
255
|
-
)
|
256
|
-
if request_body_oai:
|
257
|
-
operation["requestBody"] = request_body_oai
|
258
|
-
if route.callbacks:
|
259
|
-
callbacks = {}
|
260
|
-
for callback in route.callbacks:
|
261
|
-
if isinstance(callback, routing.APIRoute):
|
262
|
-
(
|
263
|
-
cb_path,
|
264
|
-
cb_security_schemes,
|
265
|
-
cb_definitions,
|
266
|
-
) = get_openapi_path(
|
267
|
-
route=callback,
|
268
|
-
model_name_map=model_name_map,
|
269
|
-
operation_ids=operation_ids,
|
270
|
-
)
|
271
|
-
callbacks[callback.name] = {callback.path: cb_path}
|
272
|
-
operation["callbacks"] = callbacks
|
273
|
-
if route.status_code is not None:
|
274
|
-
status_code = str(route.status_code)
|
275
|
-
else:
|
276
|
-
# It would probably make more sense for all response classes to have an
|
277
|
-
# explicit default status_code, and to extract it from them, instead of
|
278
|
-
# doing this inspection tricks, that would probably be in the future
|
279
|
-
# TODO: probably make status_code a default class attribute for all
|
280
|
-
# responses in Starlette
|
281
|
-
response_signature = inspect.signature(current_response_class.__init__)
|
282
|
-
status_code_param = response_signature.parameters.get("status_code")
|
283
|
-
if status_code_param is not None:
|
284
|
-
if isinstance(status_code_param.default, int):
|
285
|
-
status_code = str(status_code_param.default)
|
286
|
-
operation.setdefault("responses", {}).setdefault(status_code, {})[
|
287
|
-
"description"
|
288
|
-
] = route.response_description
|
289
|
-
if route_response_media_type and is_body_allowed_for_status_code(
|
290
|
-
route.status_code
|
291
|
-
):
|
292
|
-
response_schema = {"type": "string"}
|
293
|
-
if lenient_issubclass(current_response_class, JSONResponse):
|
294
|
-
if route.response_field:
|
295
|
-
response_schema, _, _ = field_schema(
|
296
|
-
route.response_field,
|
297
|
-
model_name_map=model_name_map,
|
298
|
-
ref_prefix=REF_PREFIX,
|
299
|
-
)
|
300
|
-
else:
|
301
|
-
response_schema = {}
|
302
|
-
operation.setdefault("responses", {}).setdefault(
|
303
|
-
status_code, {}
|
304
|
-
).setdefault("content", {}).setdefault(route_response_media_type, {})[
|
305
|
-
"schema"
|
306
|
-
] = response_schema
|
307
|
-
if route.responses:
|
308
|
-
operation_responses = operation.setdefault("responses", {})
|
309
|
-
for (
|
310
|
-
additional_status_code,
|
311
|
-
additional_response,
|
312
|
-
) in route.responses.items():
|
313
|
-
process_response = additional_response.copy()
|
314
|
-
process_response.pop("model", None)
|
315
|
-
status_code_key = str(additional_status_code).upper()
|
316
|
-
if status_code_key == "DEFAULT":
|
317
|
-
status_code_key = "default"
|
318
|
-
openapi_response = operation_responses.setdefault(
|
319
|
-
status_code_key, {}
|
320
|
-
)
|
321
|
-
assert isinstance(
|
322
|
-
process_response, dict
|
323
|
-
), "An additional response must be a dict"
|
324
|
-
field = route.response_fields.get(additional_status_code)
|
325
|
-
additional_field_schema: Optional[Dict[str, Any]] = None
|
326
|
-
if field:
|
327
|
-
additional_field_schema, _, _ = field_schema(
|
328
|
-
field, model_name_map=model_name_map, ref_prefix=REF_PREFIX
|
329
|
-
)
|
330
|
-
media_type = route_response_media_type or "application/json"
|
331
|
-
additional_schema = (
|
332
|
-
process_response.setdefault("content", {})
|
333
|
-
.setdefault(media_type, {})
|
334
|
-
.setdefault("schema", {})
|
335
|
-
)
|
336
|
-
deep_dict_update(additional_schema, additional_field_schema)
|
337
|
-
status_text: Optional[str] = status_code_ranges.get(
|
338
|
-
str(additional_status_code).upper()
|
339
|
-
) or http.client.responses.get(int(additional_status_code))
|
340
|
-
description = (
|
341
|
-
process_response.get("description")
|
342
|
-
or openapi_response.get("description")
|
343
|
-
or status_text
|
344
|
-
or "Additional Response"
|
345
|
-
)
|
346
|
-
deep_dict_update(openapi_response, process_response)
|
347
|
-
openapi_response["description"] = description
|
348
|
-
http422 = str(HTTP_422_UNPROCESSABLE_ENTITY)
|
349
|
-
if (all_route_params or route.body_field) and not any(
|
350
|
-
status in operation["responses"]
|
351
|
-
for status in [http422, "4XX", "default"]
|
352
|
-
):
|
353
|
-
operation["responses"][http422] = {
|
354
|
-
"description": "Validation Error",
|
355
|
-
"content": {
|
356
|
-
"application/json": {
|
357
|
-
"schema": {"$ref": REF_PREFIX + "HTTPValidationError"}
|
358
|
-
}
|
359
|
-
},
|
360
|
-
}
|
361
|
-
if "ValidationError" not in definitions:
|
362
|
-
definitions.update(
|
363
|
-
{
|
364
|
-
"ValidationError": validation_error_definition,
|
365
|
-
"HTTPValidationError": validation_error_response_definition,
|
366
|
-
}
|
367
|
-
)
|
368
|
-
if route.openapi_extra:
|
369
|
-
deep_dict_update(operation, route.openapi_extra)
|
370
|
-
path[method.lower()] = operation
|
371
|
-
return path, security_schemes, definitions
|
372
|
-
|
373
|
-
|
374
|
-
def get_flat_models_from_routes(
|
375
|
-
routes: Sequence[BaseRoute],
|
376
|
-
) -> Set[Union[Type[BaseModel], Type[Enum]]]:
|
377
|
-
body_fields_from_routes: List[ModelField] = []
|
378
|
-
responses_from_routes: List[ModelField] = []
|
379
|
-
request_fields_from_routes: List[ModelField] = []
|
380
|
-
callback_flat_models: Set[Union[Type[BaseModel], Type[Enum]]] = set()
|
381
|
-
for route in routes:
|
382
|
-
if getattr(route, "include_in_schema", None) and isinstance(
|
383
|
-
route, routing.APIRoute
|
384
|
-
):
|
385
|
-
if route.body_field:
|
386
|
-
assert isinstance(
|
387
|
-
route.body_field, ModelField
|
388
|
-
), "A request body must be a Pydantic Field"
|
389
|
-
body_fields_from_routes.append(route.body_field)
|
390
|
-
if route.response_field:
|
391
|
-
responses_from_routes.append(route.response_field)
|
392
|
-
if route.response_fields:
|
393
|
-
responses_from_routes.extend(route.response_fields.values())
|
394
|
-
if route.callbacks:
|
395
|
-
callback_flat_models |= get_flat_models_from_routes(route.callbacks)
|
396
|
-
params = get_flat_params(route.dependant)
|
397
|
-
request_fields_from_routes.extend(params)
|
398
|
-
|
399
|
-
flat_models = callback_flat_models | get_flat_models_from_fields(
|
400
|
-
body_fields_from_routes + responses_from_routes + request_fields_from_routes,
|
401
|
-
known_models=set(),
|
402
|
-
)
|
403
|
-
return flat_models
|
404
|
-
|
405
|
-
|
406
|
-
def get_openapi(
|
407
|
-
*,
|
408
|
-
title: str,
|
409
|
-
version: str,
|
410
|
-
openapi_version: str = "3.1.0",
|
411
|
-
summary: Optional[str] = None,
|
412
|
-
description: Optional[str] = None,
|
413
|
-
routes: Sequence[BaseRoute],
|
414
|
-
webhooks: Optional[Sequence[BaseRoute]] = None,
|
415
|
-
tags: Optional[List[Dict[str, Any]]] = None,
|
416
|
-
servers: Optional[List[Dict[str, Union[str, Any]]]] = None,
|
417
|
-
terms_of_service: Optional[str] = None,
|
418
|
-
contact: Optional[Dict[str, Union[str, Any]]] = None,
|
419
|
-
license_info: Optional[Dict[str, Union[str, Any]]] = None,
|
420
|
-
) -> Dict[str, Any]:
|
421
|
-
info: Dict[str, Any] = {"title": title, "version": version}
|
422
|
-
if summary:
|
423
|
-
info["summary"] = summary
|
424
|
-
if description:
|
425
|
-
info["description"] = description
|
426
|
-
if terms_of_service:
|
427
|
-
info["termsOfService"] = terms_of_service
|
428
|
-
if contact:
|
429
|
-
info["contact"] = contact
|
430
|
-
if license_info:
|
431
|
-
info["license"] = license_info
|
432
|
-
output: Dict[str, Any] = {"openapi": openapi_version, "info": info}
|
433
|
-
if servers:
|
434
|
-
output["servers"] = servers
|
435
|
-
components: Dict[str, Dict[str, Any]] = {}
|
436
|
-
paths: Dict[str, Dict[str, Any]] = {}
|
437
|
-
webhook_paths: Dict[str, Dict[str, Any]] = {}
|
438
|
-
operation_ids: Set[str] = set()
|
439
|
-
flat_models = get_flat_models_from_routes(list(routes or []) + list(webhooks or []))
|
440
|
-
model_name_map = get_model_name_map(flat_models)
|
441
|
-
definitions = get_model_definitions(
|
442
|
-
flat_models=flat_models, model_name_map=model_name_map
|
443
|
-
)
|
444
|
-
for route in routes or []:
|
445
|
-
if isinstance(route, routing.APIRoute):
|
446
|
-
result = get_openapi_path(
|
447
|
-
route=route, model_name_map=model_name_map, operation_ids=operation_ids
|
448
|
-
)
|
449
|
-
if result:
|
450
|
-
path, security_schemes, path_definitions = result
|
451
|
-
if path:
|
452
|
-
paths.setdefault(route.path_format, {}).update(path)
|
453
|
-
if security_schemes:
|
454
|
-
components.setdefault("securitySchemes", {}).update(
|
455
|
-
security_schemes
|
456
|
-
)
|
457
|
-
if path_definitions:
|
458
|
-
definitions.update(path_definitions)
|
459
|
-
for webhook in webhooks or []:
|
460
|
-
if isinstance(webhook, routing.APIRoute):
|
461
|
-
result = get_openapi_path(
|
462
|
-
route=webhook,
|
463
|
-
model_name_map=model_name_map,
|
464
|
-
operation_ids=operation_ids,
|
465
|
-
)
|
466
|
-
if result:
|
467
|
-
path, security_schemes, path_definitions = result
|
468
|
-
if path:
|
469
|
-
webhook_paths.setdefault(webhook.path_format, {}).update(path)
|
470
|
-
if security_schemes:
|
471
|
-
components.setdefault("securitySchemes", {}).update(
|
472
|
-
security_schemes
|
473
|
-
)
|
474
|
-
if path_definitions:
|
475
|
-
definitions.update(path_definitions)
|
476
|
-
if definitions:
|
477
|
-
components["schemas"] = {k: definitions[k] for k in sorted(definitions)}
|
478
|
-
if components:
|
479
|
-
output["components"] = components
|
480
|
-
output["paths"] = paths
|
481
|
-
if webhook_paths:
|
482
|
-
output["webhooks"] = webhook_paths
|
483
|
-
if tags:
|
484
|
-
output["tags"] = tags
|
485
|
-
return jsonable_encoder(OpenAPI(**output), by_alias=True, exclude_none=True) # type: ignore
|