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.
- 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 +423 -164
- 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 +667 -440
- 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 -2466
- 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 +124 -51
- 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 +138 -48
- 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.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.2.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.2.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.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/client/orchestration.py
CHANGED
@@ -8,12 +8,13 @@ from typing import (
|
|
8
8
|
Dict,
|
9
9
|
Iterable,
|
10
10
|
List,
|
11
|
-
|
11
|
+
Literal,
|
12
12
|
Optional,
|
13
13
|
Set,
|
14
14
|
Tuple,
|
15
15
|
TypeVar,
|
16
16
|
Union,
|
17
|
+
overload,
|
17
18
|
)
|
18
19
|
from uuid import UUID, uuid4
|
19
20
|
|
@@ -21,35 +22,20 @@ import certifi
|
|
21
22
|
import httpcore
|
22
23
|
import httpx
|
23
24
|
import pendulum
|
24
|
-
|
25
|
-
|
26
|
-
from prefect._internal.compatibility.deprecated import (
|
27
|
-
handle_deprecated_infra_overrides_parameter,
|
28
|
-
)
|
29
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
30
|
-
from prefect.client.schemas import sorting
|
31
|
-
from prefect.events import filters
|
32
|
-
from prefect.settings import (
|
33
|
-
PREFECT_API_SERVICES_TRIGGERS_ENABLED,
|
34
|
-
PREFECT_EXPERIMENTAL_EVENTS,
|
35
|
-
)
|
36
|
-
|
37
|
-
if HAS_PYDANTIC_V2:
|
38
|
-
import pydantic.v1 as pydantic
|
39
|
-
else:
|
40
|
-
import pydantic
|
41
|
-
|
25
|
+
import pydantic
|
42
26
|
from asgi_lifespan import LifespanManager
|
43
|
-
from
|
27
|
+
from starlette import status
|
28
|
+
from typing_extensions import ParamSpec
|
44
29
|
|
45
30
|
import prefect
|
46
31
|
import prefect.exceptions
|
47
32
|
import prefect.settings
|
48
33
|
import prefect.states
|
49
34
|
from prefect.client.constants import SERVER_API_VERSION
|
50
|
-
from prefect.client.schemas import FlowRun, OrchestrationResult, TaskRun
|
35
|
+
from prefect.client.schemas import FlowRun, OrchestrationResult, TaskRun, sorting
|
51
36
|
from prefect.client.schemas.actions import (
|
52
37
|
ArtifactCreate,
|
38
|
+
ArtifactUpdate,
|
53
39
|
BlockDocumentCreate,
|
54
40
|
BlockDocumentUpdate,
|
55
41
|
BlockSchemaCreate,
|
@@ -100,7 +86,6 @@ from prefect.client.schemas.objects import (
|
|
100
86
|
BlockType,
|
101
87
|
ConcurrencyLimit,
|
102
88
|
Constant,
|
103
|
-
Deployment,
|
104
89
|
DeploymentSchedule,
|
105
90
|
Flow,
|
106
91
|
FlowRunInput,
|
@@ -133,7 +118,7 @@ from prefect.client.schemas.sorting import (
|
|
133
118
|
LogSort,
|
134
119
|
TaskRunSort,
|
135
120
|
)
|
136
|
-
from prefect.
|
121
|
+
from prefect.events import filters
|
137
122
|
from prefect.events.schemas.automations import Automation, AutomationCore
|
138
123
|
from prefect.logging import get_logger
|
139
124
|
from prefect.settings import (
|
@@ -146,9 +131,9 @@ from prefect.settings import (
|
|
146
131
|
PREFECT_API_URL,
|
147
132
|
PREFECT_CLIENT_CSRF_SUPPORT_ENABLED,
|
148
133
|
PREFECT_CLOUD_API_URL,
|
134
|
+
PREFECT_SERVER_ALLOW_EPHEMERAL_MODE,
|
149
135
|
PREFECT_UNIT_TEST_MODE,
|
150
136
|
)
|
151
|
-
from prefect.utilities.collections import AutoEnum
|
152
137
|
|
153
138
|
if TYPE_CHECKING:
|
154
139
|
from prefect.flows import Flow as FlowObject
|
@@ -158,7 +143,7 @@ from prefect.client.base import (
|
|
158
143
|
ASGIApp,
|
159
144
|
PrefectHttpxAsyncClient,
|
160
145
|
PrefectHttpxSyncClient,
|
161
|
-
|
146
|
+
ServerType,
|
162
147
|
app_lifespan_context,
|
163
148
|
)
|
164
149
|
|
@@ -166,21 +151,23 @@ P = ParamSpec("P")
|
|
166
151
|
R = TypeVar("R")
|
167
152
|
|
168
153
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
154
|
+
@overload
|
155
|
+
def get_client(
|
156
|
+
httpx_settings: Optional[Dict[str, Any]] = None, sync_client: Literal[False] = False
|
157
|
+
) -> "PrefectClient":
|
158
|
+
...
|
173
159
|
|
174
|
-
def supports_automations(self) -> bool:
|
175
|
-
if self == ServerType.CLOUD:
|
176
|
-
return True
|
177
160
|
|
178
|
-
|
161
|
+
@overload
|
162
|
+
def get_client(
|
163
|
+
httpx_settings: Optional[Dict[str, Any]] = None, sync_client: Literal[True] = True
|
164
|
+
) -> "SyncPrefectClient":
|
165
|
+
...
|
179
166
|
|
180
167
|
|
181
168
|
def get_client(
|
182
169
|
httpx_settings: Optional[Dict[str, Any]] = None, sync_client: bool = False
|
183
|
-
)
|
170
|
+
):
|
184
171
|
"""
|
185
172
|
Retrieve a HTTP client for communicating with the Prefect REST API.
|
186
173
|
|
@@ -198,26 +185,59 @@ def get_client(
|
|
198
185
|
client.hello()
|
199
186
|
```
|
200
187
|
"""
|
201
|
-
|
188
|
+
import prefect.context
|
189
|
+
|
190
|
+
# try to load clients from a client context, if possible
|
191
|
+
# only load clients that match the provided config / loop
|
192
|
+
try:
|
193
|
+
loop = asyncio.get_running_loop()
|
194
|
+
except RuntimeError:
|
195
|
+
loop = None
|
196
|
+
|
197
|
+
if sync_client:
|
198
|
+
if client_ctx := prefect.context.SyncClientContext.get():
|
199
|
+
if client_ctx.client and client_ctx._httpx_settings == httpx_settings:
|
200
|
+
return client_ctx.client
|
201
|
+
else:
|
202
|
+
if client_ctx := prefect.context.AsyncClientContext.get():
|
203
|
+
if (
|
204
|
+
client_ctx.client
|
205
|
+
and client_ctx._httpx_settings == httpx_settings
|
206
|
+
and loop in (client_ctx.client._loop, None)
|
207
|
+
):
|
208
|
+
return client_ctx.client
|
209
|
+
|
202
210
|
api = PREFECT_API_URL.value()
|
211
|
+
server_type = None
|
203
212
|
|
204
|
-
if not api:
|
213
|
+
if not api and PREFECT_SERVER_ALLOW_EPHEMERAL_MODE:
|
205
214
|
# create an ephemeral API if none was provided
|
206
|
-
from prefect.server.api.server import
|
215
|
+
from prefect.server.api.server import SubprocessASGIServer
|
207
216
|
|
208
|
-
|
217
|
+
server = SubprocessASGIServer()
|
218
|
+
server.start()
|
219
|
+
assert server.server_process is not None, "Server process did not start"
|
220
|
+
|
221
|
+
api = server.api_url
|
222
|
+
server_type = ServerType.EPHEMERAL
|
223
|
+
elif not api and not PREFECT_SERVER_ALLOW_EPHEMERAL_MODE:
|
224
|
+
raise ValueError(
|
225
|
+
"No Prefect API URL provided. Please set PREFECT_API_URL to the address of a running Prefect server."
|
226
|
+
)
|
209
227
|
|
210
228
|
if sync_client:
|
211
229
|
return SyncPrefectClient(
|
212
230
|
api,
|
213
231
|
api_key=PREFECT_API_KEY.value(),
|
214
232
|
httpx_settings=httpx_settings,
|
233
|
+
server_type=server_type,
|
215
234
|
)
|
216
235
|
else:
|
217
236
|
return PrefectClient(
|
218
237
|
api,
|
219
238
|
api_key=PREFECT_API_KEY.value(),
|
220
239
|
httpx_settings=httpx_settings,
|
240
|
+
server_type=server_type,
|
221
241
|
)
|
222
242
|
|
223
243
|
|
@@ -251,9 +271,10 @@ class PrefectClient:
|
|
251
271
|
self,
|
252
272
|
api: Union[str, ASGIApp],
|
253
273
|
*,
|
254
|
-
api_key: str = None,
|
255
|
-
api_version: str = None,
|
274
|
+
api_key: Optional[str] = None,
|
275
|
+
api_version: Optional[str] = None,
|
256
276
|
httpx_settings: Optional[Dict[str, Any]] = None,
|
277
|
+
server_type: Optional[ServerType] = None,
|
257
278
|
) -> None:
|
258
279
|
httpx_settings = httpx_settings.copy() if httpx_settings else {}
|
259
280
|
httpx_settings.setdefault("headers", {})
|
@@ -273,6 +294,7 @@ class PrefectClient:
|
|
273
294
|
httpx_settings["headers"].setdefault("Authorization", f"Bearer {api_key}")
|
274
295
|
|
275
296
|
# Context management
|
297
|
+
self._context_stack: int = 0
|
276
298
|
self._exit_stack = AsyncExitStack()
|
277
299
|
self._ephemeral_app: Optional[ASGIApp] = None
|
278
300
|
self.manage_lifespan = True
|
@@ -315,11 +337,14 @@ class PrefectClient:
|
|
315
337
|
# client will use a standard HTTP/1.1 connection instead.
|
316
338
|
httpx_settings.setdefault("http2", PREFECT_API_ENABLE_HTTP2.value())
|
317
339
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
340
|
+
if server_type:
|
341
|
+
self.server_type = server_type
|
342
|
+
else:
|
343
|
+
self.server_type = (
|
344
|
+
ServerType.CLOUD
|
345
|
+
if api.startswith(PREFECT_CLOUD_API_URL.value())
|
346
|
+
else ServerType.SERVER
|
347
|
+
)
|
323
348
|
|
324
349
|
# Connect to an in-process application
|
325
350
|
elif isinstance(api, ASGIApp):
|
@@ -453,7 +478,7 @@ class PrefectClient:
|
|
453
478
|
"""
|
454
479
|
flow_data = FlowCreate(name=flow_name)
|
455
480
|
response = await self._client.post(
|
456
|
-
"/flows/", json=flow_data.
|
481
|
+
"/flows/", json=flow_data.model_dump(mode="json")
|
457
482
|
)
|
458
483
|
|
459
484
|
flow_id = response.json().get("id")
|
@@ -474,7 +499,7 @@ class PrefectClient:
|
|
474
499
|
a [Flow model][prefect.client.schemas.objects.Flow] representation of the flow
|
475
500
|
"""
|
476
501
|
response = await self._client.get(f"/flows/{flow_id}")
|
477
|
-
return Flow.
|
502
|
+
return Flow.model_validate(response.json())
|
478
503
|
|
479
504
|
async def read_flows(
|
480
505
|
self,
|
@@ -486,7 +511,7 @@ class PrefectClient:
|
|
486
511
|
work_pool_filter: WorkPoolFilter = None,
|
487
512
|
work_queue_filter: WorkQueueFilter = None,
|
488
513
|
sort: FlowSort = None,
|
489
|
-
limit: int = None,
|
514
|
+
limit: Optional[int] = None,
|
490
515
|
offset: int = 0,
|
491
516
|
) -> List[Flow]:
|
492
517
|
"""
|
@@ -508,29 +533,23 @@ class PrefectClient:
|
|
508
533
|
a list of Flow model representations of the flows
|
509
534
|
"""
|
510
535
|
body = {
|
511
|
-
"flows": flow_filter.
|
536
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
512
537
|
"flow_runs": (
|
513
|
-
flow_run_filter.
|
538
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
514
539
|
if flow_run_filter
|
515
540
|
else None
|
516
541
|
),
|
517
542
|
"task_runs": (
|
518
|
-
task_run_filter.
|
543
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
519
544
|
),
|
520
545
|
"deployments": (
|
521
|
-
deployment_filter.
|
522
|
-
if deployment_filter
|
523
|
-
else None
|
546
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
524
547
|
),
|
525
548
|
"work_pools": (
|
526
|
-
work_pool_filter.
|
527
|
-
if work_pool_filter
|
528
|
-
else None
|
549
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
529
550
|
),
|
530
551
|
"work_queues": (
|
531
|
-
work_queue_filter.
|
532
|
-
if work_queue_filter
|
533
|
-
else None
|
552
|
+
work_queue_filter.model_dump(mode="json") if work_queue_filter else None
|
534
553
|
),
|
535
554
|
"sort": sort,
|
536
555
|
"limit": limit,
|
@@ -538,7 +557,7 @@ class PrefectClient:
|
|
538
557
|
}
|
539
558
|
|
540
559
|
response = await self._client.post("/flows/filter", json=body)
|
541
|
-
return pydantic.
|
560
|
+
return pydantic.TypeAdapter(List[Flow]).validate_python(response.json())
|
542
561
|
|
543
562
|
async def read_flow_by_name(
|
544
563
|
self,
|
@@ -554,7 +573,7 @@ class PrefectClient:
|
|
554
573
|
a fully hydrated Flow model
|
555
574
|
"""
|
556
575
|
response = await self._client.get(f"/flows/name/{flow_name}")
|
557
|
-
return Flow.
|
576
|
+
return Flow.model_validate(response.json())
|
558
577
|
|
559
578
|
async def create_flow_run_from_deployment(
|
560
579
|
self,
|
@@ -562,12 +581,12 @@ class PrefectClient:
|
|
562
581
|
*,
|
563
582
|
parameters: Optional[Dict[str, Any]] = None,
|
564
583
|
context: Optional[Dict[str, Any]] = None,
|
565
|
-
state: prefect.states.State = None,
|
566
|
-
name: str = None,
|
567
|
-
tags: Iterable[str] = None,
|
568
|
-
idempotency_key: str = None,
|
569
|
-
parent_task_run_id: UUID = None,
|
570
|
-
work_queue_name: str = None,
|
584
|
+
state: Optional[prefect.states.State] = None,
|
585
|
+
name: Optional[str] = None,
|
586
|
+
tags: Optional[Iterable[str]] = None,
|
587
|
+
idempotency_key: Optional[str] = None,
|
588
|
+
parent_task_run_id: Optional[UUID] = None,
|
589
|
+
work_queue_name: Optional[str] = None,
|
571
590
|
job_variables: Optional[Dict[str, Any]] = None,
|
572
591
|
) -> FlowRun:
|
573
592
|
"""
|
@@ -622,9 +641,9 @@ class PrefectClient:
|
|
622
641
|
|
623
642
|
response = await self._client.post(
|
624
643
|
f"/deployments/{deployment_id}/create_flow_run",
|
625
|
-
json=flow_run_create.
|
644
|
+
json=flow_run_create.model_dump(mode="json", exclude_unset=True),
|
626
645
|
)
|
627
|
-
return FlowRun.
|
646
|
+
return FlowRun.model_validate(response.json())
|
628
647
|
|
629
648
|
async def create_flow_run(
|
630
649
|
self,
|
@@ -680,9 +699,9 @@ class PrefectClient:
|
|
680
699
|
),
|
681
700
|
)
|
682
701
|
|
683
|
-
flow_run_create_json = flow_run_create.
|
702
|
+
flow_run_create_json = flow_run_create.model_dump(mode="json")
|
684
703
|
response = await self._client.post("/flow_runs/", json=flow_run_create_json)
|
685
|
-
flow_run = FlowRun.
|
704
|
+
flow_run = FlowRun.model_validate(response.json())
|
686
705
|
|
687
706
|
# Restore the parameters to the local objects to retain expectations about
|
688
707
|
# Python objects
|
@@ -740,7 +759,7 @@ class PrefectClient:
|
|
740
759
|
|
741
760
|
return await self._client.patch(
|
742
761
|
f"/flow_runs/{flow_run_id}",
|
743
|
-
json=flow_run_data.
|
762
|
+
json=flow_run_data.model_dump(mode="json", exclude_unset=True),
|
744
763
|
)
|
745
764
|
|
746
765
|
async def delete_flow_run(
|
@@ -790,7 +809,7 @@ class PrefectClient:
|
|
790
809
|
)
|
791
810
|
response = await self._client.post(
|
792
811
|
"/concurrency_limits/",
|
793
|
-
json=concurrency_limit_create.
|
812
|
+
json=concurrency_limit_create.model_dump(mode="json"),
|
794
813
|
)
|
795
814
|
|
796
815
|
concurrency_limit_id = response.json().get("id")
|
@@ -832,7 +851,7 @@ class PrefectClient:
|
|
832
851
|
if not concurrency_limit_id:
|
833
852
|
raise httpx.RequestError(f"Malformed response: {response}")
|
834
853
|
|
835
|
-
concurrency_limit = ConcurrencyLimit.
|
854
|
+
concurrency_limit = ConcurrencyLimit.model_validate(response.json())
|
836
855
|
return concurrency_limit
|
837
856
|
|
838
857
|
async def read_concurrency_limits(
|
@@ -857,7 +876,9 @@ class PrefectClient:
|
|
857
876
|
}
|
858
877
|
|
859
878
|
response = await self._client.post("/concurrency_limits/filter", json=body)
|
860
|
-
return pydantic.
|
879
|
+
return pydantic.TypeAdapter(List[ConcurrencyLimit]).validate_python(
|
880
|
+
response.json()
|
881
|
+
)
|
861
882
|
|
862
883
|
async def reset_concurrency_limit_by_tag(
|
863
884
|
self,
|
@@ -918,6 +939,57 @@ class PrefectClient:
|
|
918
939
|
else:
|
919
940
|
raise
|
920
941
|
|
942
|
+
async def increment_v1_concurrency_slots(
|
943
|
+
self,
|
944
|
+
names: List[str],
|
945
|
+
task_run_id: UUID,
|
946
|
+
) -> httpx.Response:
|
947
|
+
"""
|
948
|
+
Increment concurrency limit slots for the specified limits.
|
949
|
+
|
950
|
+
Args:
|
951
|
+
names (List[str]): A list of limit names for which to increment limits.
|
952
|
+
task_run_id (UUID): The task run ID incrementing the limits.
|
953
|
+
"""
|
954
|
+
data = {
|
955
|
+
"names": names,
|
956
|
+
"task_run_id": str(task_run_id),
|
957
|
+
}
|
958
|
+
|
959
|
+
return await self._client.post(
|
960
|
+
"/concurrency_limits/increment",
|
961
|
+
json=data,
|
962
|
+
)
|
963
|
+
|
964
|
+
async def decrement_v1_concurrency_slots(
|
965
|
+
self,
|
966
|
+
names: List[str],
|
967
|
+
task_run_id: UUID,
|
968
|
+
occupancy_seconds: float,
|
969
|
+
) -> httpx.Response:
|
970
|
+
"""
|
971
|
+
Decrement concurrency limit slots for the specified limits.
|
972
|
+
|
973
|
+
Args:
|
974
|
+
names (List[str]): A list of limit names to decrement.
|
975
|
+
task_run_id (UUID): The task run ID that incremented the limits.
|
976
|
+
occupancy_seconds (float): The duration in seconds that the limits
|
977
|
+
were held.
|
978
|
+
|
979
|
+
Returns:
|
980
|
+
httpx.Response: The HTTP response from the server.
|
981
|
+
"""
|
982
|
+
data = {
|
983
|
+
"names": names,
|
984
|
+
"task_run_id": str(task_run_id),
|
985
|
+
"occupancy_seconds": occupancy_seconds,
|
986
|
+
}
|
987
|
+
|
988
|
+
return await self._client.post(
|
989
|
+
"/concurrency_limits/decrement",
|
990
|
+
json=data,
|
991
|
+
)
|
992
|
+
|
921
993
|
async def create_work_queue(
|
922
994
|
self,
|
923
995
|
name: str,
|
@@ -969,7 +1041,7 @@ class PrefectClient:
|
|
969
1041
|
if priority is not None:
|
970
1042
|
create_model.priority = priority
|
971
1043
|
|
972
|
-
data = create_model.
|
1044
|
+
data = create_model.model_dump(mode="json")
|
973
1045
|
try:
|
974
1046
|
if work_pool_name is not None:
|
975
1047
|
response = await self._client.post(
|
@@ -984,7 +1056,7 @@ class PrefectClient:
|
|
984
1056
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
985
1057
|
else:
|
986
1058
|
raise
|
987
|
-
return WorkQueue.
|
1059
|
+
return WorkQueue.model_validate(response.json())
|
988
1060
|
|
989
1061
|
async def read_work_queue_by_name(
|
990
1062
|
self,
|
@@ -1019,7 +1091,7 @@ class PrefectClient:
|
|
1019
1091
|
else:
|
1020
1092
|
raise
|
1021
1093
|
|
1022
|
-
return WorkQueue.
|
1094
|
+
return WorkQueue.model_validate(response.json())
|
1023
1095
|
|
1024
1096
|
async def update_work_queue(self, id: UUID, **kwargs):
|
1025
1097
|
"""
|
@@ -1038,7 +1110,7 @@ class PrefectClient:
|
|
1038
1110
|
if not kwargs:
|
1039
1111
|
raise ValueError("No fields provided to update.")
|
1040
1112
|
|
1041
|
-
data = WorkQueueUpdate(**kwargs).
|
1113
|
+
data = WorkQueueUpdate(**kwargs).model_dump(mode="json", exclude_unset=True)
|
1042
1114
|
try:
|
1043
1115
|
await self._client.patch(f"/work_queues/{id}", json=data)
|
1044
1116
|
except httpx.HTTPStatusError as e:
|
@@ -1085,7 +1157,7 @@ class PrefectClient:
|
|
1085
1157
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1086
1158
|
else:
|
1087
1159
|
raise
|
1088
|
-
return pydantic.
|
1160
|
+
return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
|
1089
1161
|
|
1090
1162
|
async def read_work_queue(
|
1091
1163
|
self,
|
@@ -1111,7 +1183,7 @@ class PrefectClient:
|
|
1111
1183
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1112
1184
|
else:
|
1113
1185
|
raise
|
1114
|
-
return WorkQueue.
|
1186
|
+
return WorkQueue.model_validate(response.json())
|
1115
1187
|
|
1116
1188
|
async def read_work_queue_status(
|
1117
1189
|
self,
|
@@ -1137,7 +1209,7 @@ class PrefectClient:
|
|
1137
1209
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1138
1210
|
else:
|
1139
1211
|
raise
|
1140
|
-
return WorkQueueStatusDetail.
|
1212
|
+
return WorkQueueStatusDetail.model_validate(response.json())
|
1141
1213
|
|
1142
1214
|
async def match_work_queues(
|
1143
1215
|
self,
|
@@ -1206,8 +1278,8 @@ class PrefectClient:
|
|
1206
1278
|
try:
|
1207
1279
|
response = await self._client.post(
|
1208
1280
|
"/block_types/",
|
1209
|
-
json=block_type.
|
1210
|
-
|
1281
|
+
json=block_type.model_dump(
|
1282
|
+
mode="json", exclude_unset=True, exclude={"id"}
|
1211
1283
|
),
|
1212
1284
|
)
|
1213
1285
|
except httpx.HTTPStatusError as e:
|
@@ -1215,7 +1287,7 @@ class PrefectClient:
|
|
1215
1287
|
raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
|
1216
1288
|
else:
|
1217
1289
|
raise
|
1218
|
-
return BlockType.
|
1290
|
+
return BlockType.model_validate(response.json())
|
1219
1291
|
|
1220
1292
|
async def create_block_schema(self, block_schema: BlockSchemaCreate) -> BlockSchema:
|
1221
1293
|
"""
|
@@ -1224,8 +1296,8 @@ class PrefectClient:
|
|
1224
1296
|
try:
|
1225
1297
|
response = await self._client.post(
|
1226
1298
|
"/block_schemas/",
|
1227
|
-
json=block_schema.
|
1228
|
-
|
1299
|
+
json=block_schema.model_dump(
|
1300
|
+
mode="json",
|
1229
1301
|
exclude_unset=True,
|
1230
1302
|
exclude={"id", "block_type", "checksum"},
|
1231
1303
|
),
|
@@ -1235,7 +1307,7 @@ class PrefectClient:
|
|
1235
1307
|
raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
|
1236
1308
|
else:
|
1237
1309
|
raise
|
1238
|
-
return BlockSchema.
|
1310
|
+
return BlockSchema.model_validate(response.json())
|
1239
1311
|
|
1240
1312
|
async def create_block_document(
|
1241
1313
|
self,
|
@@ -1252,32 +1324,24 @@ class PrefectClient:
|
|
1252
1324
|
`SecretBytes` fields. Note Blocks may not work as expected if
|
1253
1325
|
this is set to `False`.
|
1254
1326
|
"""
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
),
|
1263
|
-
)
|
1264
|
-
|
1327
|
+
block_document_data = block_document.model_dump(
|
1328
|
+
mode="json",
|
1329
|
+
exclude_unset=True,
|
1330
|
+
exclude={"id", "block_schema", "block_type"},
|
1331
|
+
context={"include_secrets": include_secrets},
|
1332
|
+
serialize_as_any=True,
|
1333
|
+
)
|
1265
1334
|
try:
|
1266
1335
|
response = await self._client.post(
|
1267
1336
|
"/block_documents/",
|
1268
|
-
json=
|
1269
|
-
json_compatible=True,
|
1270
|
-
include_secrets=include_secrets,
|
1271
|
-
exclude_unset=True,
|
1272
|
-
exclude={"id", "block_schema", "block_type"},
|
1273
|
-
),
|
1337
|
+
json=block_document_data,
|
1274
1338
|
)
|
1275
1339
|
except httpx.HTTPStatusError as e:
|
1276
1340
|
if e.response.status_code == status.HTTP_409_CONFLICT:
|
1277
1341
|
raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
|
1278
1342
|
else:
|
1279
1343
|
raise
|
1280
|
-
return BlockDocument.
|
1344
|
+
return BlockDocument.model_validate(response.json())
|
1281
1345
|
|
1282
1346
|
async def update_block_document(
|
1283
1347
|
self,
|
@@ -1290,11 +1354,10 @@ class PrefectClient:
|
|
1290
1354
|
try:
|
1291
1355
|
await self._client.patch(
|
1292
1356
|
f"/block_documents/{block_document_id}",
|
1293
|
-
json=block_document.
|
1294
|
-
|
1357
|
+
json=block_document.model_dump(
|
1358
|
+
mode="json",
|
1295
1359
|
exclude_unset=True,
|
1296
1360
|
include={"data", "merge_existing_data", "block_schema_id"},
|
1297
|
-
include_secrets=True,
|
1298
1361
|
),
|
1299
1362
|
)
|
1300
1363
|
except httpx.HTTPStatusError as e:
|
@@ -1326,7 +1389,7 @@ class PrefectClient:
|
|
1326
1389
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1327
1390
|
else:
|
1328
1391
|
raise
|
1329
|
-
return BlockType.
|
1392
|
+
return BlockType.model_validate(response.json())
|
1330
1393
|
|
1331
1394
|
async def read_block_schema_by_checksum(
|
1332
1395
|
self, checksum: str, version: Optional[str] = None
|
@@ -1344,7 +1407,7 @@ class PrefectClient:
|
|
1344
1407
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1345
1408
|
else:
|
1346
1409
|
raise
|
1347
|
-
return BlockSchema.
|
1410
|
+
return BlockSchema.model_validate(response.json())
|
1348
1411
|
|
1349
1412
|
async def update_block_type(self, block_type_id: UUID, block_type: BlockTypeUpdate):
|
1350
1413
|
"""
|
@@ -1353,11 +1416,10 @@ class PrefectClient:
|
|
1353
1416
|
try:
|
1354
1417
|
await self._client.patch(
|
1355
1418
|
f"/block_types/{block_type_id}",
|
1356
|
-
json=block_type.
|
1357
|
-
|
1419
|
+
json=block_type.model_dump(
|
1420
|
+
mode="json",
|
1358
1421
|
exclude_unset=True,
|
1359
1422
|
include=BlockTypeUpdate.updatable_fields(),
|
1360
|
-
include_secrets=True,
|
1361
1423
|
),
|
1362
1424
|
)
|
1363
1425
|
except httpx.HTTPStatusError as e:
|
@@ -1396,7 +1458,7 @@ class PrefectClient:
|
|
1396
1458
|
List of BlockTypes.
|
1397
1459
|
"""
|
1398
1460
|
response = await self._client.post("/block_types/filter", json={})
|
1399
|
-
return pydantic.
|
1461
|
+
return pydantic.TypeAdapter(List[BlockType]).validate_python(response.json())
|
1400
1462
|
|
1401
1463
|
async def read_block_schemas(self) -> List[BlockSchema]:
|
1402
1464
|
"""
|
@@ -1408,7 +1470,7 @@ class PrefectClient:
|
|
1408
1470
|
A BlockSchema.
|
1409
1471
|
"""
|
1410
1472
|
response = await self._client.post("/block_schemas/filter", json={})
|
1411
|
-
return pydantic.
|
1473
|
+
return pydantic.TypeAdapter(List[BlockSchema]).validate_python(response.json())
|
1412
1474
|
|
1413
1475
|
async def get_most_recent_block_schema_for_block_type(
|
1414
1476
|
self,
|
@@ -1436,7 +1498,9 @@ class PrefectClient:
|
|
1436
1498
|
)
|
1437
1499
|
except httpx.HTTPStatusError:
|
1438
1500
|
raise
|
1439
|
-
return
|
1501
|
+
return (
|
1502
|
+
BlockSchema.model_validate(response.json()[0]) if response.json() else None
|
1503
|
+
)
|
1440
1504
|
|
1441
1505
|
async def read_block_document(
|
1442
1506
|
self,
|
@@ -1474,7 +1538,7 @@ class PrefectClient:
|
|
1474
1538
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1475
1539
|
else:
|
1476
1540
|
raise
|
1477
|
-
return BlockDocument.
|
1541
|
+
return BlockDocument.model_validate(response.json())
|
1478
1542
|
|
1479
1543
|
async def read_block_document_by_name(
|
1480
1544
|
self,
|
@@ -1512,7 +1576,7 @@ class PrefectClient:
|
|
1512
1576
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1513
1577
|
else:
|
1514
1578
|
raise
|
1515
|
-
return BlockDocument.
|
1579
|
+
return BlockDocument.model_validate(response.json())
|
1516
1580
|
|
1517
1581
|
async def read_block_documents(
|
1518
1582
|
self,
|
@@ -1547,7 +1611,9 @@ class PrefectClient:
|
|
1547
1611
|
include_secrets=include_secrets,
|
1548
1612
|
),
|
1549
1613
|
)
|
1550
|
-
return pydantic.
|
1614
|
+
return pydantic.TypeAdapter(List[BlockDocument]).validate_python(
|
1615
|
+
response.json()
|
1616
|
+
)
|
1551
1617
|
|
1552
1618
|
async def read_block_documents_by_type(
|
1553
1619
|
self,
|
@@ -1576,28 +1642,27 @@ class PrefectClient:
|
|
1576
1642
|
),
|
1577
1643
|
)
|
1578
1644
|
|
1579
|
-
return pydantic.
|
1645
|
+
return pydantic.TypeAdapter(List[BlockDocument]).validate_python(
|
1646
|
+
response.json()
|
1647
|
+
)
|
1580
1648
|
|
1581
1649
|
async def create_deployment(
|
1582
1650
|
self,
|
1583
1651
|
flow_id: UUID,
|
1584
1652
|
name: str,
|
1585
|
-
version: str = None,
|
1586
|
-
|
1587
|
-
|
1653
|
+
version: Optional[str] = None,
|
1654
|
+
schedules: Optional[List[DeploymentScheduleCreate]] = None,
|
1655
|
+
concurrency_limit: Optional[int] = None,
|
1588
1656
|
parameters: Optional[Dict[str, Any]] = None,
|
1589
|
-
description: str = None,
|
1590
|
-
work_queue_name: str = None,
|
1591
|
-
work_pool_name: str = None,
|
1592
|
-
tags: List[str] = None,
|
1593
|
-
storage_document_id: UUID = None,
|
1594
|
-
|
1595
|
-
|
1596
|
-
|
1597
|
-
infrastructure_document_id: UUID = None,
|
1598
|
-
infra_overrides: Optional[Dict[str, Any]] = None, # for backwards compat
|
1657
|
+
description: Optional[str] = None,
|
1658
|
+
work_queue_name: Optional[str] = None,
|
1659
|
+
work_pool_name: Optional[str] = None,
|
1660
|
+
tags: Optional[List[str]] = None,
|
1661
|
+
storage_document_id: Optional[UUID] = None,
|
1662
|
+
path: Optional[str] = None,
|
1663
|
+
entrypoint: Optional[str] = None,
|
1664
|
+
infrastructure_document_id: Optional[UUID] = None,
|
1599
1665
|
parameter_openapi_schema: Optional[Dict[str, Any]] = None,
|
1600
|
-
is_schedule_active: Optional[bool] = None,
|
1601
1666
|
paused: Optional[bool] = None,
|
1602
1667
|
pull_steps: Optional[List[dict]] = None,
|
1603
1668
|
enforce_parameter_schema: Optional[bool] = None,
|
@@ -1610,7 +1675,6 @@ class PrefectClient:
|
|
1610
1675
|
flow_id: the flow ID to create a deployment for
|
1611
1676
|
name: the name of the deployment
|
1612
1677
|
version: an optional version string for the deployment
|
1613
|
-
schedule: an optional schedule to apply to the deployment
|
1614
1678
|
tags: an optional list of tags to apply to the deployment
|
1615
1679
|
storage_document_id: an reference to the storage block document
|
1616
1680
|
used for the deployed flow
|
@@ -1627,8 +1691,9 @@ class PrefectClient:
|
|
1627
1691
|
Returns:
|
1628
1692
|
the ID of the deployment in the backend
|
1629
1693
|
"""
|
1630
|
-
jv = handle_deprecated_infra_overrides_parameter(job_variables, infra_overrides)
|
1631
1694
|
|
1695
|
+
if parameter_openapi_schema is None:
|
1696
|
+
parameter_openapi_schema = {}
|
1632
1697
|
deployment_create = DeploymentCreate(
|
1633
1698
|
flow_id=flow_id,
|
1634
1699
|
name=name,
|
@@ -1640,14 +1705,12 @@ class PrefectClient:
|
|
1640
1705
|
storage_document_id=storage_document_id,
|
1641
1706
|
path=path,
|
1642
1707
|
entrypoint=entrypoint,
|
1643
|
-
manifest_path=manifest_path, # for backwards compat
|
1644
1708
|
infrastructure_document_id=infrastructure_document_id,
|
1645
|
-
job_variables=
|
1709
|
+
job_variables=dict(job_variables or {}),
|
1646
1710
|
parameter_openapi_schema=parameter_openapi_schema,
|
1647
|
-
is_schedule_active=is_schedule_active,
|
1648
1711
|
paused=paused,
|
1649
|
-
schedule=schedule,
|
1650
1712
|
schedules=schedules or [],
|
1713
|
+
concurrency_limit=concurrency_limit,
|
1651
1714
|
pull_steps=pull_steps,
|
1652
1715
|
enforce_parameter_schema=enforce_parameter_schema,
|
1653
1716
|
)
|
@@ -1659,12 +1722,9 @@ class PrefectClient:
|
|
1659
1722
|
exclude = {
|
1660
1723
|
field
|
1661
1724
|
for field in ["work_pool_name", "work_queue_name"]
|
1662
|
-
if field not in deployment_create.
|
1725
|
+
if field not in deployment_create.model_fields_set
|
1663
1726
|
}
|
1664
1727
|
|
1665
|
-
if deployment_create.is_schedule_active is None:
|
1666
|
-
exclude.add("is_schedule_active")
|
1667
|
-
|
1668
1728
|
if deployment_create.paused is None:
|
1669
1729
|
exclude.add("paused")
|
1670
1730
|
|
@@ -1674,7 +1734,7 @@ class PrefectClient:
|
|
1674
1734
|
if deployment_create.enforce_parameter_schema is None:
|
1675
1735
|
exclude.add("enforce_parameter_schema")
|
1676
1736
|
|
1677
|
-
json = deployment_create.
|
1737
|
+
json = deployment_create.model_dump(mode="json", exclude=exclude)
|
1678
1738
|
response = await self._client.post(
|
1679
1739
|
"/deployments/",
|
1680
1740
|
json=json,
|
@@ -1685,12 +1745,6 @@ class PrefectClient:
|
|
1685
1745
|
|
1686
1746
|
return UUID(deployment_id)
|
1687
1747
|
|
1688
|
-
async def update_schedule(self, deployment_id: UUID, active: bool = True):
|
1689
|
-
path = "set_schedule_active" if active else "set_schedule_inactive"
|
1690
|
-
await self._client.post(
|
1691
|
-
f"/deployments/{deployment_id}/{path}",
|
1692
|
-
)
|
1693
|
-
|
1694
1748
|
async def set_deployment_paused_state(self, deployment_id: UUID, paused: bool):
|
1695
1749
|
await self._client.patch(
|
1696
1750
|
f"/deployments/{deployment_id}", json={"paused": paused}
|
@@ -1698,41 +1752,12 @@ class PrefectClient:
|
|
1698
1752
|
|
1699
1753
|
async def update_deployment(
|
1700
1754
|
self,
|
1701
|
-
|
1702
|
-
|
1703
|
-
is_schedule_active: bool = None,
|
1755
|
+
deployment_id: UUID,
|
1756
|
+
deployment: DeploymentUpdate,
|
1704
1757
|
):
|
1705
|
-
deployment_update = DeploymentUpdate(
|
1706
|
-
version=deployment.version,
|
1707
|
-
schedule=schedule if schedule is not None else deployment.schedule,
|
1708
|
-
is_schedule_active=(
|
1709
|
-
is_schedule_active
|
1710
|
-
if is_schedule_active is not None
|
1711
|
-
else deployment.is_schedule_active
|
1712
|
-
),
|
1713
|
-
description=deployment.description,
|
1714
|
-
work_queue_name=deployment.work_queue_name,
|
1715
|
-
tags=deployment.tags,
|
1716
|
-
manifest_path=deployment.manifest_path,
|
1717
|
-
path=deployment.path,
|
1718
|
-
entrypoint=deployment.entrypoint,
|
1719
|
-
parameters=deployment.parameters,
|
1720
|
-
storage_document_id=deployment.storage_document_id,
|
1721
|
-
infrastructure_document_id=deployment.infrastructure_document_id,
|
1722
|
-
job_variables=deployment.job_variables,
|
1723
|
-
enforce_parameter_schema=deployment.enforce_parameter_schema,
|
1724
|
-
)
|
1725
|
-
|
1726
|
-
if getattr(deployment, "work_pool_name", None) is not None:
|
1727
|
-
deployment_update.work_pool_name = deployment.work_pool_name
|
1728
|
-
|
1729
|
-
exclude = set()
|
1730
|
-
if deployment.enforce_parameter_schema is None:
|
1731
|
-
exclude.add("enforce_parameter_schema")
|
1732
|
-
|
1733
1758
|
await self._client.patch(
|
1734
|
-
f"/deployments/{
|
1735
|
-
json=
|
1759
|
+
f"/deployments/{deployment_id}",
|
1760
|
+
json=deployment.model_dump(mode="json", exclude_unset=True),
|
1736
1761
|
)
|
1737
1762
|
|
1738
1763
|
async def _create_deployment_from_schema(self, schema: DeploymentCreate) -> UUID:
|
@@ -1742,7 +1767,7 @@ class PrefectClient:
|
|
1742
1767
|
# TODO: We are likely to remove this method once we have considered the
|
1743
1768
|
# packaging interface for deployments further.
|
1744
1769
|
response = await self._client.post(
|
1745
|
-
"/deployments/", json=schema.
|
1770
|
+
"/deployments/", json=schema.model_dump(mode="json")
|
1746
1771
|
)
|
1747
1772
|
deployment_id = response.json().get("id")
|
1748
1773
|
if not deployment_id:
|
@@ -1763,6 +1788,12 @@ class PrefectClient:
|
|
1763
1788
|
Returns:
|
1764
1789
|
a [Deployment model][prefect.client.schemas.objects.Deployment] representation of the deployment
|
1765
1790
|
"""
|
1791
|
+
if not isinstance(deployment_id, UUID):
|
1792
|
+
try:
|
1793
|
+
deployment_id = UUID(deployment_id)
|
1794
|
+
except ValueError:
|
1795
|
+
raise ValueError(f"Invalid deployment ID: {deployment_id}")
|
1796
|
+
|
1766
1797
|
try:
|
1767
1798
|
response = await self._client.get(f"/deployments/{deployment_id}")
|
1768
1799
|
except httpx.HTTPStatusError as e:
|
@@ -1770,7 +1801,7 @@ class PrefectClient:
|
|
1770
1801
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1771
1802
|
else:
|
1772
1803
|
raise
|
1773
|
-
return DeploymentResponse.
|
1804
|
+
return DeploymentResponse.model_validate(response.json())
|
1774
1805
|
|
1775
1806
|
async def read_deployment_by_name(
|
1776
1807
|
self,
|
@@ -1797,19 +1828,19 @@ class PrefectClient:
|
|
1797
1828
|
else:
|
1798
1829
|
raise
|
1799
1830
|
|
1800
|
-
return DeploymentResponse.
|
1831
|
+
return DeploymentResponse.model_validate(response.json())
|
1801
1832
|
|
1802
1833
|
async def read_deployments(
|
1803
1834
|
self,
|
1804
1835
|
*,
|
1805
|
-
flow_filter: FlowFilter = None,
|
1806
|
-
flow_run_filter: FlowRunFilter = None,
|
1807
|
-
task_run_filter: TaskRunFilter = None,
|
1808
|
-
deployment_filter: DeploymentFilter = None,
|
1809
|
-
work_pool_filter: WorkPoolFilter = None,
|
1810
|
-
work_queue_filter: WorkQueueFilter = None,
|
1811
|
-
limit: int = None,
|
1812
|
-
sort: DeploymentSort = None,
|
1836
|
+
flow_filter: Optional[FlowFilter] = None,
|
1837
|
+
flow_run_filter: Optional[FlowRunFilter] = None,
|
1838
|
+
task_run_filter: Optional[TaskRunFilter] = None,
|
1839
|
+
deployment_filter: Optional[DeploymentFilter] = None,
|
1840
|
+
work_pool_filter: Optional[WorkPoolFilter] = None,
|
1841
|
+
work_queue_filter: Optional[WorkQueueFilter] = None,
|
1842
|
+
limit: Optional[int] = None,
|
1843
|
+
sort: Optional[DeploymentSort] = None,
|
1813
1844
|
offset: int = 0,
|
1814
1845
|
) -> List[DeploymentResponse]:
|
1815
1846
|
"""
|
@@ -1831,29 +1862,23 @@ class PrefectClient:
|
|
1831
1862
|
of the deployments
|
1832
1863
|
"""
|
1833
1864
|
body = {
|
1834
|
-
"flows": flow_filter.
|
1865
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
1835
1866
|
"flow_runs": (
|
1836
|
-
flow_run_filter.
|
1867
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
1837
1868
|
if flow_run_filter
|
1838
1869
|
else None
|
1839
1870
|
),
|
1840
1871
|
"task_runs": (
|
1841
|
-
task_run_filter.
|
1872
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
1842
1873
|
),
|
1843
1874
|
"deployments": (
|
1844
|
-
deployment_filter.
|
1845
|
-
if deployment_filter
|
1846
|
-
else None
|
1875
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
1847
1876
|
),
|
1848
1877
|
"work_pools": (
|
1849
|
-
work_pool_filter.
|
1850
|
-
if work_pool_filter
|
1851
|
-
else None
|
1878
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
1852
1879
|
),
|
1853
1880
|
"work_pool_queues": (
|
1854
|
-
work_queue_filter.
|
1855
|
-
if work_queue_filter
|
1856
|
-
else None
|
1881
|
+
work_queue_filter.model_dump(mode="json") if work_queue_filter else None
|
1857
1882
|
),
|
1858
1883
|
"limit": limit,
|
1859
1884
|
"offset": offset,
|
@@ -1861,7 +1886,9 @@ class PrefectClient:
|
|
1861
1886
|
}
|
1862
1887
|
|
1863
1888
|
response = await self._client.post("/deployments/filter", json=body)
|
1864
|
-
return pydantic.
|
1889
|
+
return pydantic.TypeAdapter(List[DeploymentResponse]).validate_python(
|
1890
|
+
response.json()
|
1891
|
+
)
|
1865
1892
|
|
1866
1893
|
async def delete_deployment(
|
1867
1894
|
self,
|
@@ -1909,13 +1936,15 @@ class PrefectClient:
|
|
1909
1936
|
]
|
1910
1937
|
|
1911
1938
|
json = [
|
1912
|
-
deployment_schedule_create.
|
1939
|
+
deployment_schedule_create.model_dump(mode="json")
|
1913
1940
|
for deployment_schedule_create in deployment_schedule_create
|
1914
1941
|
]
|
1915
1942
|
response = await self._client.post(
|
1916
1943
|
f"/deployments/{deployment_id}/schedules", json=json
|
1917
1944
|
)
|
1918
|
-
return pydantic.
|
1945
|
+
return pydantic.TypeAdapter(List[DeploymentSchedule]).validate_python(
|
1946
|
+
response.json()
|
1947
|
+
)
|
1919
1948
|
|
1920
1949
|
async def read_deployment_schedules(
|
1921
1950
|
self,
|
@@ -1937,7 +1966,9 @@ class PrefectClient:
|
|
1937
1966
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
1938
1967
|
else:
|
1939
1968
|
raise
|
1940
|
-
return pydantic.
|
1969
|
+
return pydantic.TypeAdapter(List[DeploymentSchedule]).validate_python(
|
1970
|
+
response.json()
|
1971
|
+
)
|
1941
1972
|
|
1942
1973
|
async def update_deployment_schedule(
|
1943
1974
|
self,
|
@@ -1962,7 +1993,7 @@ class PrefectClient:
|
|
1962
1993
|
kwargs["schedule"] = schedule
|
1963
1994
|
|
1964
1995
|
deployment_schedule_update = DeploymentScheduleUpdate(**kwargs)
|
1965
|
-
json = deployment_schedule_update.
|
1996
|
+
json = deployment_schedule_update.model_dump(mode="json", exclude_unset=True)
|
1966
1997
|
|
1967
1998
|
try:
|
1968
1999
|
await self._client.patch(
|
@@ -2016,7 +2047,7 @@ class PrefectClient:
|
|
2016
2047
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
2017
2048
|
else:
|
2018
2049
|
raise
|
2019
|
-
return FlowRun.
|
2050
|
+
return FlowRun.model_validate(response.json())
|
2020
2051
|
|
2021
2052
|
async def resume_flow_run(
|
2022
2053
|
self, flow_run_id: UUID, run_input: Optional[Dict] = None
|
@@ -2038,7 +2069,7 @@ class PrefectClient:
|
|
2038
2069
|
except httpx.HTTPStatusError:
|
2039
2070
|
raise
|
2040
2071
|
|
2041
|
-
return OrchestrationResult.
|
2072
|
+
return OrchestrationResult.model_validate(response.json())
|
2042
2073
|
|
2043
2074
|
async def read_flow_runs(
|
2044
2075
|
self,
|
@@ -2050,7 +2081,7 @@ class PrefectClient:
|
|
2050
2081
|
work_pool_filter: WorkPoolFilter = None,
|
2051
2082
|
work_queue_filter: WorkQueueFilter = None,
|
2052
2083
|
sort: FlowRunSort = None,
|
2053
|
-
limit: int = None,
|
2084
|
+
limit: Optional[int] = None,
|
2054
2085
|
offset: int = 0,
|
2055
2086
|
) -> List[FlowRun]:
|
2056
2087
|
"""
|
@@ -2073,29 +2104,23 @@ class PrefectClient:
|
|
2073
2104
|
of the flow runs
|
2074
2105
|
"""
|
2075
2106
|
body = {
|
2076
|
-
"flows": flow_filter.
|
2107
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
2077
2108
|
"flow_runs": (
|
2078
|
-
flow_run_filter.
|
2109
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
2079
2110
|
if flow_run_filter
|
2080
2111
|
else None
|
2081
2112
|
),
|
2082
2113
|
"task_runs": (
|
2083
|
-
task_run_filter.
|
2114
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2084
2115
|
),
|
2085
2116
|
"deployments": (
|
2086
|
-
deployment_filter.
|
2087
|
-
if deployment_filter
|
2088
|
-
else None
|
2117
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
2089
2118
|
),
|
2090
2119
|
"work_pools": (
|
2091
|
-
work_pool_filter.
|
2092
|
-
if work_pool_filter
|
2093
|
-
else None
|
2120
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
2094
2121
|
),
|
2095
2122
|
"work_pool_queues": (
|
2096
|
-
work_queue_filter.
|
2097
|
-
if work_queue_filter
|
2098
|
-
else None
|
2123
|
+
work_queue_filter.model_dump(mode="json") if work_queue_filter else None
|
2099
2124
|
),
|
2100
2125
|
"sort": sort,
|
2101
2126
|
"limit": limit,
|
@@ -2103,7 +2128,7 @@ class PrefectClient:
|
|
2103
2128
|
}
|
2104
2129
|
|
2105
2130
|
response = await self._client.post("/flow_runs/filter", json=body)
|
2106
|
-
return pydantic.
|
2131
|
+
return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
|
2107
2132
|
|
2108
2133
|
async def set_flow_run_state(
|
2109
2134
|
self,
|
@@ -2123,13 +2148,16 @@ class PrefectClient:
|
|
2123
2148
|
Returns:
|
2124
2149
|
an OrchestrationResult model representation of state orchestration output
|
2125
2150
|
"""
|
2151
|
+
flow_run_id = (
|
2152
|
+
flow_run_id if isinstance(flow_run_id, UUID) else UUID(flow_run_id)
|
2153
|
+
)
|
2126
2154
|
state_create = state.to_state_create()
|
2127
2155
|
state_create.state_details.flow_run_id = flow_run_id
|
2128
2156
|
state_create.state_details.transition_id = uuid4()
|
2129
2157
|
try:
|
2130
2158
|
response = await self._client.post(
|
2131
2159
|
f"/flow_runs/{flow_run_id}/set_state",
|
2132
|
-
json=dict(state=state_create.
|
2160
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
2133
2161
|
)
|
2134
2162
|
except httpx.HTTPStatusError as e:
|
2135
2163
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -2137,7 +2165,7 @@ class PrefectClient:
|
|
2137
2165
|
else:
|
2138
2166
|
raise
|
2139
2167
|
|
2140
|
-
return OrchestrationResult.
|
2168
|
+
return OrchestrationResult.model_validate(response.json())
|
2141
2169
|
|
2142
2170
|
async def read_flow_run_states(
|
2143
2171
|
self, flow_run_id: UUID
|
@@ -2155,13 +2183,15 @@ class PrefectClient:
|
|
2155
2183
|
response = await self._client.get(
|
2156
2184
|
"/flow_run_states/", params=dict(flow_run_id=str(flow_run_id))
|
2157
2185
|
)
|
2158
|
-
return pydantic.
|
2186
|
+
return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
|
2187
|
+
response.json()
|
2188
|
+
)
|
2159
2189
|
|
2160
2190
|
async def set_task_run_name(self, task_run_id: UUID, name: str):
|
2161
2191
|
task_run_data = TaskRunUpdate(name=name)
|
2162
2192
|
return await self._client.patch(
|
2163
2193
|
f"/task_runs/{task_run_id}",
|
2164
|
-
json=task_run_data.
|
2194
|
+
json=task_run_data.model_dump(mode="json", exclude_unset=True),
|
2165
2195
|
)
|
2166
2196
|
|
2167
2197
|
async def create_task_run(
|
@@ -2169,6 +2199,7 @@ class PrefectClient:
|
|
2169
2199
|
task: "TaskObject[P, R]",
|
2170
2200
|
flow_run_id: Optional[UUID],
|
2171
2201
|
dynamic_key: str,
|
2202
|
+
id: Optional[UUID] = None,
|
2172
2203
|
name: Optional[str] = None,
|
2173
2204
|
extra_tags: Optional[Iterable[str]] = None,
|
2174
2205
|
state: Optional[prefect.states.State[R]] = None,
|
@@ -2192,6 +2223,8 @@ class PrefectClient:
|
|
2192
2223
|
task: The Task to run
|
2193
2224
|
flow_run_id: The flow run id with which to associate the task run
|
2194
2225
|
dynamic_key: A key unique to this particular run of a Task within the flow
|
2226
|
+
id: An optional ID for the task run. If not provided, one will be generated
|
2227
|
+
server-side.
|
2195
2228
|
name: An optional name for the task run
|
2196
2229
|
extra_tags: an optional list of extra tags to apply to the task run in
|
2197
2230
|
addition to `task.tags`
|
@@ -2208,10 +2241,11 @@ class PrefectClient:
|
|
2208
2241
|
state = prefect.states.Pending()
|
2209
2242
|
|
2210
2243
|
task_run_data = TaskRunCreate(
|
2244
|
+
id=id,
|
2211
2245
|
name=name,
|
2212
2246
|
flow_run_id=flow_run_id,
|
2213
2247
|
task_key=task.task_key,
|
2214
|
-
dynamic_key=dynamic_key,
|
2248
|
+
dynamic_key=str(dynamic_key),
|
2215
2249
|
tags=list(tags),
|
2216
2250
|
task_version=task.version,
|
2217
2251
|
empirical_policy=TaskRunPolicy(
|
@@ -2222,11 +2256,10 @@ class PrefectClient:
|
|
2222
2256
|
state=state.to_state_create(),
|
2223
2257
|
task_inputs=task_inputs or {},
|
2224
2258
|
)
|
2259
|
+
content = task_run_data.model_dump_json(exclude={"id"} if id is None else None)
|
2225
2260
|
|
2226
|
-
response = await self._client.post(
|
2227
|
-
|
2228
|
-
)
|
2229
|
-
return TaskRun.parse_obj(response.json())
|
2261
|
+
response = await self._client.post("/task_runs/", content=content)
|
2262
|
+
return TaskRun.model_validate(response.json())
|
2230
2263
|
|
2231
2264
|
async def read_task_run(self, task_run_id: UUID) -> TaskRun:
|
2232
2265
|
"""
|
@@ -2238,8 +2271,14 @@ class PrefectClient:
|
|
2238
2271
|
Returns:
|
2239
2272
|
a Task Run model representation of the task run
|
2240
2273
|
"""
|
2241
|
-
|
2242
|
-
|
2274
|
+
try:
|
2275
|
+
response = await self._client.get(f"/task_runs/{task_run_id}")
|
2276
|
+
return TaskRun.model_validate(response.json())
|
2277
|
+
except httpx.HTTPStatusError as e:
|
2278
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
2279
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
2280
|
+
else:
|
2281
|
+
raise
|
2243
2282
|
|
2244
2283
|
async def read_task_runs(
|
2245
2284
|
self,
|
@@ -2249,7 +2288,7 @@ class PrefectClient:
|
|
2249
2288
|
task_run_filter: TaskRunFilter = None,
|
2250
2289
|
deployment_filter: DeploymentFilter = None,
|
2251
2290
|
sort: TaskRunSort = None,
|
2252
|
-
limit: int = None,
|
2291
|
+
limit: Optional[int] = None,
|
2253
2292
|
offset: int = 0,
|
2254
2293
|
) -> List[TaskRun]:
|
2255
2294
|
"""
|
@@ -2270,26 +2309,24 @@ class PrefectClient:
|
|
2270
2309
|
of the task runs
|
2271
2310
|
"""
|
2272
2311
|
body = {
|
2273
|
-
"flows": flow_filter.
|
2312
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
2274
2313
|
"flow_runs": (
|
2275
|
-
flow_run_filter.
|
2314
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
2276
2315
|
if flow_run_filter
|
2277
2316
|
else None
|
2278
2317
|
),
|
2279
2318
|
"task_runs": (
|
2280
|
-
task_run_filter.
|
2319
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2281
2320
|
),
|
2282
2321
|
"deployments": (
|
2283
|
-
deployment_filter.
|
2284
|
-
if deployment_filter
|
2285
|
-
else None
|
2322
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
2286
2323
|
),
|
2287
2324
|
"sort": sort,
|
2288
2325
|
"limit": limit,
|
2289
2326
|
"offset": offset,
|
2290
2327
|
}
|
2291
2328
|
response = await self._client.post("/task_runs/filter", json=body)
|
2292
|
-
return pydantic.
|
2329
|
+
return pydantic.TypeAdapter(List[TaskRun]).validate_python(response.json())
|
2293
2330
|
|
2294
2331
|
async def delete_task_run(self, task_run_id: UUID) -> None:
|
2295
2332
|
"""
|
@@ -2331,9 +2368,9 @@ class PrefectClient:
|
|
2331
2368
|
state_create.state_details.task_run_id = task_run_id
|
2332
2369
|
response = await self._client.post(
|
2333
2370
|
f"/task_runs/{task_run_id}/set_state",
|
2334
|
-
json=dict(state=state_create.
|
2371
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
2335
2372
|
)
|
2336
|
-
return OrchestrationResult.
|
2373
|
+
return OrchestrationResult.model_validate(response.json())
|
2337
2374
|
|
2338
2375
|
async def read_task_run_states(
|
2339
2376
|
self, task_run_id: UUID
|
@@ -2350,7 +2387,9 @@ class PrefectClient:
|
|
2350
2387
|
response = await self._client.get(
|
2351
2388
|
"/task_run_states/", params=dict(task_run_id=str(task_run_id))
|
2352
2389
|
)
|
2353
|
-
return pydantic.
|
2390
|
+
return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
|
2391
|
+
response.json()
|
2392
|
+
)
|
2354
2393
|
|
2355
2394
|
async def create_logs(self, logs: Iterable[Union[LogCreate, dict]]) -> None:
|
2356
2395
|
"""
|
@@ -2360,7 +2399,7 @@ class PrefectClient:
|
|
2360
2399
|
logs: An iterable of `LogCreate` objects or already json-compatible dicts
|
2361
2400
|
"""
|
2362
2401
|
serialized_logs = [
|
2363
|
-
log.
|
2402
|
+
log.model_dump(mode="json") if isinstance(log, LogCreate) else log
|
2364
2403
|
for log in logs
|
2365
2404
|
]
|
2366
2405
|
await self._client.post("/logs/", json=serialized_logs)
|
@@ -2397,7 +2436,7 @@ class PrefectClient:
|
|
2397
2436
|
)
|
2398
2437
|
response = await self._client.post(
|
2399
2438
|
"/flow_run_notification_policies/",
|
2400
|
-
json=policy.
|
2439
|
+
json=policy.model_dump(mode="json"),
|
2401
2440
|
)
|
2402
2441
|
|
2403
2442
|
policy_id = response.json().get("id")
|
@@ -2467,7 +2506,7 @@ class PrefectClient:
|
|
2467
2506
|
try:
|
2468
2507
|
await self._client.patch(
|
2469
2508
|
f"/flow_run_notification_policies/{id}",
|
2470
|
-
json=policy.
|
2509
|
+
json=policy.model_dump(mode="json", exclude_unset=True),
|
2471
2510
|
)
|
2472
2511
|
except httpx.HTTPStatusError as e:
|
2473
2512
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -2496,7 +2535,7 @@ class PrefectClient:
|
|
2496
2535
|
"""
|
2497
2536
|
body = {
|
2498
2537
|
"flow_run_notification_policy_filter": (
|
2499
|
-
flow_run_notification_policy_filter.
|
2538
|
+
flow_run_notification_policy_filter.model_dump(mode="json")
|
2500
2539
|
if flow_run_notification_policy_filter
|
2501
2540
|
else None
|
2502
2541
|
),
|
@@ -2506,58 +2545,29 @@ class PrefectClient:
|
|
2506
2545
|
response = await self._client.post(
|
2507
2546
|
"/flow_run_notification_policies/filter", json=body
|
2508
2547
|
)
|
2509
|
-
return pydantic.
|
2548
|
+
return pydantic.TypeAdapter(List[FlowRunNotificationPolicy]).validate_python(
|
2549
|
+
response.json()
|
2550
|
+
)
|
2510
2551
|
|
2511
2552
|
async def read_logs(
|
2512
2553
|
self,
|
2513
2554
|
log_filter: LogFilter = None,
|
2514
|
-
limit: int = None,
|
2515
|
-
offset: int = None,
|
2555
|
+
limit: Optional[int] = None,
|
2556
|
+
offset: Optional[int] = None,
|
2516
2557
|
sort: LogSort = LogSort.TIMESTAMP_ASC,
|
2517
2558
|
) -> List[Log]:
|
2518
2559
|
"""
|
2519
2560
|
Read flow and task run logs.
|
2520
2561
|
"""
|
2521
2562
|
body = {
|
2522
|
-
"logs": log_filter.
|
2563
|
+
"logs": log_filter.model_dump(mode="json") if log_filter else None,
|
2523
2564
|
"limit": limit,
|
2524
2565
|
"offset": offset,
|
2525
2566
|
"sort": sort,
|
2526
2567
|
}
|
2527
2568
|
|
2528
2569
|
response = await self._client.post("/logs/filter", json=body)
|
2529
|
-
return pydantic.
|
2530
|
-
|
2531
|
-
async def resolve_datadoc(self, datadoc: DataDocument) -> Any:
|
2532
|
-
"""
|
2533
|
-
Recursively decode possibly nested data documents.
|
2534
|
-
|
2535
|
-
"server" encoded documents will be retrieved from the server.
|
2536
|
-
|
2537
|
-
Args:
|
2538
|
-
datadoc: The data document to resolve
|
2539
|
-
|
2540
|
-
Returns:
|
2541
|
-
a decoded object, the innermost data
|
2542
|
-
"""
|
2543
|
-
if not isinstance(datadoc, DataDocument):
|
2544
|
-
raise TypeError(
|
2545
|
-
f"`resolve_datadoc` received invalid type {type(datadoc).__name__}"
|
2546
|
-
)
|
2547
|
-
|
2548
|
-
async def resolve_inner(data):
|
2549
|
-
if isinstance(data, bytes):
|
2550
|
-
try:
|
2551
|
-
data = DataDocument.parse_raw(data)
|
2552
|
-
except pydantic.ValidationError:
|
2553
|
-
return data
|
2554
|
-
|
2555
|
-
if isinstance(data, DataDocument):
|
2556
|
-
return await resolve_inner(data.decode())
|
2557
|
-
|
2558
|
-
return data
|
2559
|
-
|
2560
|
-
return await resolve_inner(datadoc)
|
2570
|
+
return pydantic.TypeAdapter(List[Log]).validate_python(response.json())
|
2561
2571
|
|
2562
2572
|
async def send_worker_heartbeat(
|
2563
2573
|
self,
|
@@ -2601,7 +2611,7 @@ class PrefectClient:
|
|
2601
2611
|
f"/work_pools/{work_pool_name}/workers/filter",
|
2602
2612
|
json={
|
2603
2613
|
"worker_filter": (
|
2604
|
-
worker_filter.
|
2614
|
+
worker_filter.model_dump(mode="json", exclude_unset=True)
|
2605
2615
|
if worker_filter
|
2606
2616
|
else None
|
2607
2617
|
),
|
@@ -2610,7 +2620,7 @@ class PrefectClient:
|
|
2610
2620
|
},
|
2611
2621
|
)
|
2612
2622
|
|
2613
|
-
return pydantic.
|
2623
|
+
return pydantic.TypeAdapter(List[Worker]).validate_python(response.json())
|
2614
2624
|
|
2615
2625
|
async def read_work_pool(self, work_pool_name: str) -> WorkPool:
|
2616
2626
|
"""
|
@@ -2625,7 +2635,7 @@ class PrefectClient:
|
|
2625
2635
|
"""
|
2626
2636
|
try:
|
2627
2637
|
response = await self._client.get(f"/work_pools/{work_pool_name}")
|
2628
|
-
return
|
2638
|
+
return WorkPool.model_validate(response.json())
|
2629
2639
|
except httpx.HTTPStatusError as e:
|
2630
2640
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
2631
2641
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
@@ -2654,17 +2664,16 @@ class PrefectClient:
|
|
2654
2664
|
"limit": limit,
|
2655
2665
|
"offset": offset,
|
2656
2666
|
"work_pools": (
|
2657
|
-
work_pool_filter.
|
2658
|
-
if work_pool_filter
|
2659
|
-
else None
|
2667
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
2660
2668
|
),
|
2661
2669
|
}
|
2662
2670
|
response = await self._client.post("/work_pools/filter", json=body)
|
2663
|
-
return pydantic.
|
2671
|
+
return pydantic.TypeAdapter(List[WorkPool]).validate_python(response.json())
|
2664
2672
|
|
2665
2673
|
async def create_work_pool(
|
2666
2674
|
self,
|
2667
2675
|
work_pool: WorkPoolCreate,
|
2676
|
+
overwrite: bool = False,
|
2668
2677
|
) -> WorkPool:
|
2669
2678
|
"""
|
2670
2679
|
Creates a work pool with the provided configuration.
|
@@ -2678,15 +2687,32 @@ class PrefectClient:
|
|
2678
2687
|
try:
|
2679
2688
|
response = await self._client.post(
|
2680
2689
|
"/work_pools/",
|
2681
|
-
json=work_pool.
|
2690
|
+
json=work_pool.model_dump(mode="json", exclude_unset=True),
|
2682
2691
|
)
|
2683
2692
|
except httpx.HTTPStatusError as e:
|
2684
2693
|
if e.response.status_code == status.HTTP_409_CONFLICT:
|
2685
|
-
|
2694
|
+
if overwrite:
|
2695
|
+
existing_work_pool = await self.read_work_pool(
|
2696
|
+
work_pool_name=work_pool.name
|
2697
|
+
)
|
2698
|
+
if existing_work_pool.type != work_pool.type:
|
2699
|
+
warnings.warn(
|
2700
|
+
"Overwriting work pool type is not supported. Ignoring provided type.",
|
2701
|
+
category=UserWarning,
|
2702
|
+
)
|
2703
|
+
await self.update_work_pool(
|
2704
|
+
work_pool_name=work_pool.name,
|
2705
|
+
work_pool=WorkPoolUpdate.model_validate(
|
2706
|
+
work_pool.model_dump(exclude={"name", "type"})
|
2707
|
+
),
|
2708
|
+
)
|
2709
|
+
response = await self._client.get(f"/work_pools/{work_pool.name}")
|
2710
|
+
else:
|
2711
|
+
raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
|
2686
2712
|
else:
|
2687
2713
|
raise
|
2688
2714
|
|
2689
|
-
return
|
2715
|
+
return WorkPool.model_validate(response.json())
|
2690
2716
|
|
2691
2717
|
async def update_work_pool(
|
2692
2718
|
self,
|
@@ -2703,7 +2729,7 @@ class PrefectClient:
|
|
2703
2729
|
try:
|
2704
2730
|
await self._client.patch(
|
2705
2731
|
f"/work_pools/{work_pool_name}",
|
2706
|
-
json=work_pool.
|
2732
|
+
json=work_pool.model_dump(mode="json", exclude_unset=True),
|
2707
2733
|
)
|
2708
2734
|
except httpx.HTTPStatusError as e:
|
2709
2735
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -2750,7 +2776,7 @@ class PrefectClient:
|
|
2750
2776
|
"""
|
2751
2777
|
json = {
|
2752
2778
|
"work_queues": (
|
2753
|
-
work_queue_filter.
|
2779
|
+
work_queue_filter.model_dump(mode="json", exclude_unset=True)
|
2754
2780
|
if work_queue_filter
|
2755
2781
|
else None
|
2756
2782
|
),
|
@@ -2772,14 +2798,14 @@ class PrefectClient:
|
|
2772
2798
|
else:
|
2773
2799
|
response = await self._client.post("/work_queues/filter", json=json)
|
2774
2800
|
|
2775
|
-
return pydantic.
|
2801
|
+
return pydantic.TypeAdapter(List[WorkQueue]).validate_python(response.json())
|
2776
2802
|
|
2777
2803
|
async def get_scheduled_flow_runs_for_deployments(
|
2778
2804
|
self,
|
2779
2805
|
deployment_ids: List[UUID],
|
2780
2806
|
scheduled_before: Optional[datetime.datetime] = None,
|
2781
2807
|
limit: Optional[int] = None,
|
2782
|
-
):
|
2808
|
+
) -> List[FlowRunResponse]:
|
2783
2809
|
body: Dict[str, Any] = dict(deployment_ids=[str(id) for id in deployment_ids])
|
2784
2810
|
if scheduled_before:
|
2785
2811
|
body["scheduled_before"] = str(scheduled_before)
|
@@ -2791,7 +2817,9 @@ class PrefectClient:
|
|
2791
2817
|
json=body,
|
2792
2818
|
)
|
2793
2819
|
|
2794
|
-
return pydantic.
|
2820
|
+
return pydantic.TypeAdapter(List[FlowRunResponse]).validate_python(
|
2821
|
+
response.json()
|
2822
|
+
)
|
2795
2823
|
|
2796
2824
|
async def get_scheduled_flow_runs_for_work_pool(
|
2797
2825
|
self,
|
@@ -2824,7 +2852,9 @@ class PrefectClient:
|
|
2824
2852
|
f"/work_pools/{work_pool_name}/get_scheduled_flow_runs",
|
2825
2853
|
json=body,
|
2826
2854
|
)
|
2827
|
-
return pydantic.
|
2855
|
+
return pydantic.TypeAdapter(List[WorkerFlowRunResponse]).validate_python(
|
2856
|
+
response.json()
|
2857
|
+
)
|
2828
2858
|
|
2829
2859
|
async def create_artifact(
|
2830
2860
|
self,
|
@@ -2841,10 +2871,29 @@ class PrefectClient:
|
|
2841
2871
|
|
2842
2872
|
response = await self._client.post(
|
2843
2873
|
"/artifacts/",
|
2844
|
-
json=artifact.
|
2874
|
+
json=artifact.model_dump(mode="json", exclude_unset=True),
|
2845
2875
|
)
|
2846
2876
|
|
2847
|
-
return
|
2877
|
+
return Artifact.model_validate(response.json())
|
2878
|
+
|
2879
|
+
async def update_artifact(
|
2880
|
+
self,
|
2881
|
+
artifact_id: UUID,
|
2882
|
+
artifact: ArtifactUpdate,
|
2883
|
+
) -> None:
|
2884
|
+
"""
|
2885
|
+
Updates an artifact
|
2886
|
+
|
2887
|
+
Args:
|
2888
|
+
artifact: Desired values for the updated artifact.
|
2889
|
+
Returns:
|
2890
|
+
Information about the updated artifact.
|
2891
|
+
"""
|
2892
|
+
|
2893
|
+
await self._client.patch(
|
2894
|
+
f"/artifacts/{artifact_id}",
|
2895
|
+
json=artifact.model_dump(mode="json", exclude_unset=True),
|
2896
|
+
)
|
2848
2897
|
|
2849
2898
|
async def read_artifacts(
|
2850
2899
|
self,
|
@@ -2853,7 +2902,7 @@ class PrefectClient:
|
|
2853
2902
|
flow_run_filter: FlowRunFilter = None,
|
2854
2903
|
task_run_filter: TaskRunFilter = None,
|
2855
2904
|
sort: ArtifactSort = None,
|
2856
|
-
limit: int = None,
|
2905
|
+
limit: Optional[int] = None,
|
2857
2906
|
offset: int = 0,
|
2858
2907
|
) -> List[Artifact]:
|
2859
2908
|
"""
|
@@ -2871,20 +2920,20 @@ class PrefectClient:
|
|
2871
2920
|
"""
|
2872
2921
|
body = {
|
2873
2922
|
"artifacts": (
|
2874
|
-
artifact_filter.
|
2923
|
+
artifact_filter.model_dump(mode="json") if artifact_filter else None
|
2875
2924
|
),
|
2876
2925
|
"flow_runs": (
|
2877
|
-
flow_run_filter.
|
2926
|
+
flow_run_filter.model_dump(mode="json") if flow_run_filter else None
|
2878
2927
|
),
|
2879
2928
|
"task_runs": (
|
2880
|
-
task_run_filter.
|
2929
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2881
2930
|
),
|
2882
2931
|
"sort": sort,
|
2883
2932
|
"limit": limit,
|
2884
2933
|
"offset": offset,
|
2885
2934
|
}
|
2886
2935
|
response = await self._client.post("/artifacts/filter", json=body)
|
2887
|
-
return pydantic.
|
2936
|
+
return pydantic.TypeAdapter(List[Artifact]).validate_python(response.json())
|
2888
2937
|
|
2889
2938
|
async def read_latest_artifacts(
|
2890
2939
|
self,
|
@@ -2893,7 +2942,7 @@ class PrefectClient:
|
|
2893
2942
|
flow_run_filter: FlowRunFilter = None,
|
2894
2943
|
task_run_filter: TaskRunFilter = None,
|
2895
2944
|
sort: ArtifactCollectionSort = None,
|
2896
|
-
limit: int = None,
|
2945
|
+
limit: Optional[int] = None,
|
2897
2946
|
offset: int = 0,
|
2898
2947
|
) -> List[ArtifactCollection]:
|
2899
2948
|
"""
|
@@ -2911,20 +2960,22 @@ class PrefectClient:
|
|
2911
2960
|
"""
|
2912
2961
|
body = {
|
2913
2962
|
"artifacts": (
|
2914
|
-
artifact_filter.
|
2963
|
+
artifact_filter.model_dump(mode="json") if artifact_filter else None
|
2915
2964
|
),
|
2916
2965
|
"flow_runs": (
|
2917
|
-
flow_run_filter.
|
2966
|
+
flow_run_filter.model_dump(mode="json") if flow_run_filter else None
|
2918
2967
|
),
|
2919
2968
|
"task_runs": (
|
2920
|
-
task_run_filter.
|
2969
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2921
2970
|
),
|
2922
2971
|
"sort": sort,
|
2923
2972
|
"limit": limit,
|
2924
2973
|
"offset": offset,
|
2925
2974
|
}
|
2926
2975
|
response = await self._client.post("/artifacts/latest/filter", json=body)
|
2927
|
-
return pydantic.
|
2976
|
+
return pydantic.TypeAdapter(List[ArtifactCollection]).validate_python(
|
2977
|
+
response.json()
|
2978
|
+
)
|
2928
2979
|
|
2929
2980
|
async def delete_artifact(self, artifact_id: UUID) -> None:
|
2930
2981
|
"""
|
@@ -2952,7 +3003,7 @@ class PrefectClient:
|
|
2952
3003
|
"""
|
2953
3004
|
response = await self._client.post(
|
2954
3005
|
"/variables/",
|
2955
|
-
json=variable.
|
3006
|
+
json=variable.model_dump(mode="json", exclude_unset=True),
|
2956
3007
|
)
|
2957
3008
|
return Variable(**response.json())
|
2958
3009
|
|
@@ -2967,7 +3018,7 @@ class PrefectClient:
|
|
2967
3018
|
"""
|
2968
3019
|
await self._client.patch(
|
2969
3020
|
f"/variables/name/{variable.name}",
|
2970
|
-
json=variable.
|
3021
|
+
json=variable.model_dump(mode="json", exclude_unset=True),
|
2971
3022
|
)
|
2972
3023
|
|
2973
3024
|
async def read_variable_by_name(self, name: str) -> Optional[Variable]:
|
@@ -2991,10 +3042,10 @@ class PrefectClient:
|
|
2991
3042
|
else:
|
2992
3043
|
raise
|
2993
3044
|
|
2994
|
-
async def read_variables(self, limit: int = None) -> List[Variable]:
|
3045
|
+
async def read_variables(self, limit: Optional[int] = None) -> List[Variable]:
|
2995
3046
|
"""Reads all variables."""
|
2996
3047
|
response = await self._client.post("/variables/filter", json={"limit": limit})
|
2997
|
-
return pydantic.
|
3048
|
+
return pydantic.TypeAdapter(List[Variable]).validate_python(response.json())
|
2998
3049
|
|
2999
3050
|
async def read_worker_metadata(self) -> Dict[str, Any]:
|
3000
3051
|
"""Reads worker metadata stored in Prefect collection registry."""
|
@@ -3003,16 +3054,34 @@ class PrefectClient:
|
|
3003
3054
|
return response.json()
|
3004
3055
|
|
3005
3056
|
async def increment_concurrency_slots(
|
3006
|
-
self, names: List[str], slots: int, mode: str
|
3057
|
+
self, names: List[str], slots: int, mode: str, create_if_missing: Optional[bool]
|
3007
3058
|
) -> httpx.Response:
|
3008
3059
|
return await self._client.post(
|
3009
3060
|
"/v2/concurrency_limits/increment",
|
3010
|
-
json={
|
3061
|
+
json={
|
3062
|
+
"names": names,
|
3063
|
+
"slots": slots,
|
3064
|
+
"mode": mode,
|
3065
|
+
"create_if_missing": create_if_missing,
|
3066
|
+
},
|
3011
3067
|
)
|
3012
3068
|
|
3013
3069
|
async def release_concurrency_slots(
|
3014
3070
|
self, names: List[str], slots: int, occupancy_seconds: float
|
3015
3071
|
) -> httpx.Response:
|
3072
|
+
"""
|
3073
|
+
Release concurrency slots for the specified limits.
|
3074
|
+
|
3075
|
+
Args:
|
3076
|
+
names (List[str]): A list of limit names for which to release slots.
|
3077
|
+
slots (int): The number of concurrency slots to release.
|
3078
|
+
occupancy_seconds (float): The duration in seconds that the slots
|
3079
|
+
were occupied.
|
3080
|
+
|
3081
|
+
Returns:
|
3082
|
+
httpx.Response: The HTTP response from the server.
|
3083
|
+
"""
|
3084
|
+
|
3016
3085
|
return await self._client.post(
|
3017
3086
|
"/v2/concurrency_limits/decrement",
|
3018
3087
|
json={
|
@@ -3027,7 +3096,7 @@ class PrefectClient:
|
|
3027
3096
|
) -> UUID:
|
3028
3097
|
response = await self._client.post(
|
3029
3098
|
"/v2/concurrency_limits/",
|
3030
|
-
json=concurrency_limit.
|
3099
|
+
json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
|
3031
3100
|
)
|
3032
3101
|
return UUID(response.json()["id"])
|
3033
3102
|
|
@@ -3037,7 +3106,7 @@ class PrefectClient:
|
|
3037
3106
|
try:
|
3038
3107
|
response = await self._client.patch(
|
3039
3108
|
f"/v2/concurrency_limits/{name}",
|
3040
|
-
json=concurrency_limit.
|
3109
|
+
json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
|
3041
3110
|
)
|
3042
3111
|
return response
|
3043
3112
|
except httpx.HTTPStatusError as e:
|
@@ -3063,7 +3132,7 @@ class PrefectClient:
|
|
3063
3132
|
) -> GlobalConcurrencyLimitResponse:
|
3064
3133
|
try:
|
3065
3134
|
response = await self._client.get(f"/v2/concurrency_limits/{name}")
|
3066
|
-
return GlobalConcurrencyLimitResponse.
|
3135
|
+
return GlobalConcurrencyLimitResponse.model_validate(response.json())
|
3067
3136
|
except httpx.HTTPStatusError as e:
|
3068
3137
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
3069
3138
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
@@ -3080,9 +3149,9 @@ class PrefectClient:
|
|
3080
3149
|
"offset": offset,
|
3081
3150
|
},
|
3082
3151
|
)
|
3083
|
-
return pydantic.
|
3084
|
-
List[GlobalConcurrencyLimitResponse]
|
3085
|
-
)
|
3152
|
+
return pydantic.TypeAdapter(
|
3153
|
+
List[GlobalConcurrencyLimitResponse]
|
3154
|
+
).validate_python(response.json())
|
3086
3155
|
|
3087
3156
|
async def create_flow_run_input(
|
3088
3157
|
self, flow_run_id: UUID, key: str, value: str, sender: Optional[str] = None
|
@@ -3118,7 +3187,7 @@ class PrefectClient:
|
|
3118
3187
|
},
|
3119
3188
|
)
|
3120
3189
|
response.raise_for_status()
|
3121
|
-
return pydantic.
|
3190
|
+
return pydantic.TypeAdapter(List[FlowRunInput]).validate_python(response.json())
|
3122
3191
|
|
3123
3192
|
async def read_flow_run_input(self, flow_run_id: UUID, key: str) -> str:
|
3124
3193
|
"""
|
@@ -3143,52 +3212,30 @@ class PrefectClient:
|
|
3143
3212
|
response = await self._client.delete(f"/flow_runs/{flow_run_id}/input/{key}")
|
3144
3213
|
response.raise_for_status()
|
3145
3214
|
|
3146
|
-
def _raise_for_unsupported_automations(self) -> NoReturn:
|
3147
|
-
if not PREFECT_EXPERIMENTAL_EVENTS:
|
3148
|
-
raise RuntimeError(
|
3149
|
-
"The current server and client configuration does not support "
|
3150
|
-
"events. Enable experimental events support with the "
|
3151
|
-
"PREFECT_EXPERIMENTAL_EVENTS setting."
|
3152
|
-
)
|
3153
|
-
else:
|
3154
|
-
raise RuntimeError(
|
3155
|
-
"The current server and client configuration does not support "
|
3156
|
-
"automations. Enable experimental automations with the "
|
3157
|
-
"PREFECT_API_SERVICES_TRIGGERS_ENABLED setting."
|
3158
|
-
)
|
3159
|
-
|
3160
3215
|
async def create_automation(self, automation: AutomationCore) -> UUID:
|
3161
3216
|
"""Creates an automation in Prefect Cloud."""
|
3162
|
-
if not self.server_type.supports_automations():
|
3163
|
-
self._raise_for_unsupported_automations()
|
3164
|
-
|
3165
3217
|
response = await self._client.post(
|
3166
3218
|
"/automations/",
|
3167
|
-
json=automation.
|
3219
|
+
json=automation.model_dump(mode="json"),
|
3168
3220
|
)
|
3169
3221
|
|
3170
3222
|
return UUID(response.json()["id"])
|
3171
3223
|
|
3172
3224
|
async def update_automation(self, automation_id: UUID, automation: AutomationCore):
|
3173
3225
|
"""Updates an automation in Prefect Cloud."""
|
3174
|
-
if not self.server_type.supports_automations():
|
3175
|
-
self._raise_for_unsupported_automations()
|
3176
3226
|
response = await self._client.put(
|
3177
3227
|
f"/automations/{automation_id}",
|
3178
|
-
json=automation.
|
3228
|
+
json=automation.model_dump(mode="json", exclude_unset=True),
|
3179
3229
|
)
|
3180
3230
|
response.raise_for_status
|
3181
3231
|
|
3182
3232
|
async def read_automations(self) -> List[Automation]:
|
3183
|
-
if not self.server_type.supports_automations():
|
3184
|
-
self._raise_for_unsupported_automations()
|
3185
|
-
|
3186
3233
|
response = await self._client.post("/automations/filter")
|
3187
3234
|
response.raise_for_status()
|
3188
|
-
return pydantic.
|
3235
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3189
3236
|
|
3190
3237
|
async def find_automation(
|
3191
|
-
self, id_or_name: Union[str, UUID]
|
3238
|
+
self, id_or_name: Union[str, UUID]
|
3192
3239
|
) -> Optional[Automation]:
|
3193
3240
|
if isinstance(id_or_name, str):
|
3194
3241
|
try:
|
@@ -3221,14 +3268,11 @@ class PrefectClient:
|
|
3221
3268
|
return None
|
3222
3269
|
|
3223
3270
|
async def read_automation(self, automation_id: UUID) -> Optional[Automation]:
|
3224
|
-
if not self.server_type.supports_automations():
|
3225
|
-
self._raise_for_unsupported_automations()
|
3226
|
-
|
3227
3271
|
response = await self._client.get(f"/automations/{automation_id}")
|
3228
3272
|
if response.status_code == 404:
|
3229
3273
|
return None
|
3230
3274
|
response.raise_for_status()
|
3231
|
-
return Automation.
|
3275
|
+
return Automation.model_validate(response.json())
|
3232
3276
|
|
3233
3277
|
async def read_automations_by_name(self, name: str) -> List[Automation]:
|
3234
3278
|
"""
|
@@ -3240,15 +3284,13 @@ class PrefectClient:
|
|
3240
3284
|
Returns:
|
3241
3285
|
a list of Automation model representations of the automations
|
3242
3286
|
"""
|
3243
|
-
if not self.server_type.supports_automations():
|
3244
|
-
self._raise_for_unsupported_automations()
|
3245
3287
|
automation_filter = filters.AutomationFilter(name=dict(any_=[name]))
|
3246
3288
|
|
3247
3289
|
response = await self._client.post(
|
3248
3290
|
"/automations/filter",
|
3249
3291
|
json={
|
3250
3292
|
"sort": sorting.AutomationSort.UPDATED_DESC,
|
3251
|
-
"automations": automation_filter.
|
3293
|
+
"automations": automation_filter.model_dump(mode="json")
|
3252
3294
|
if automation_filter
|
3253
3295
|
else None,
|
3254
3296
|
},
|
@@ -3256,30 +3298,21 @@ class PrefectClient:
|
|
3256
3298
|
|
3257
3299
|
response.raise_for_status()
|
3258
3300
|
|
3259
|
-
return pydantic.
|
3301
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3260
3302
|
|
3261
3303
|
async def pause_automation(self, automation_id: UUID):
|
3262
|
-
if not self.server_type.supports_automations():
|
3263
|
-
self._raise_for_unsupported_automations()
|
3264
|
-
|
3265
3304
|
response = await self._client.patch(
|
3266
3305
|
f"/automations/{automation_id}", json={"enabled": False}
|
3267
3306
|
)
|
3268
3307
|
response.raise_for_status()
|
3269
3308
|
|
3270
3309
|
async def resume_automation(self, automation_id: UUID):
|
3271
|
-
if not self.server_type.supports_automations():
|
3272
|
-
self._raise_for_unsupported_automations()
|
3273
|
-
|
3274
3310
|
response = await self._client.patch(
|
3275
3311
|
f"/automations/{automation_id}", json={"enabled": True}
|
3276
3312
|
)
|
3277
3313
|
response.raise_for_status()
|
3278
3314
|
|
3279
3315
|
async def delete_automation(self, automation_id: UUID):
|
3280
|
-
if not self.server_type.supports_automations():
|
3281
|
-
self._raise_for_unsupported_automations()
|
3282
|
-
|
3283
3316
|
response = await self._client.delete(f"/automations/{automation_id}")
|
3284
3317
|
if response.status_code == 404:
|
3285
3318
|
return
|
@@ -3289,17 +3322,11 @@ class PrefectClient:
|
|
3289
3322
|
async def read_resource_related_automations(
|
3290
3323
|
self, resource_id: str
|
3291
3324
|
) -> List[Automation]:
|
3292
|
-
if not self.server_type.supports_automations():
|
3293
|
-
self._raise_for_unsupported_automations()
|
3294
|
-
|
3295
3325
|
response = await self._client.get(f"/automations/related-to/{resource_id}")
|
3296
3326
|
response.raise_for_status()
|
3297
|
-
return pydantic.
|
3327
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3298
3328
|
|
3299
3329
|
async def delete_resource_owned_automations(self, resource_id: str):
|
3300
|
-
if not self.server_type.supports_automations():
|
3301
|
-
self._raise_for_unsupported_automations()
|
3302
|
-
|
3303
3330
|
await self._client.delete(f"/automations/owned-by/{resource_id}")
|
3304
3331
|
|
3305
3332
|
async def __aenter__(self):
|
@@ -3318,9 +3345,11 @@ class PrefectClient:
|
|
3318
3345
|
"Retrieve a new client with `get_client()` instead."
|
3319
3346
|
)
|
3320
3347
|
|
3348
|
+
self._context_stack += 1
|
3349
|
+
|
3321
3350
|
if self._started:
|
3322
|
-
#
|
3323
|
-
|
3351
|
+
# allow reentrancy
|
3352
|
+
return self
|
3324
3353
|
|
3325
3354
|
self._loop = asyncio.get_running_loop()
|
3326
3355
|
await self._exit_stack.__aenter__()
|
@@ -3351,6 +3380,10 @@ class PrefectClient:
|
|
3351
3380
|
"""
|
3352
3381
|
Shutdown the client.
|
3353
3382
|
"""
|
3383
|
+
|
3384
|
+
self._context_stack -= 1
|
3385
|
+
if self._context_stack > 0:
|
3386
|
+
return
|
3354
3387
|
self._closed = True
|
3355
3388
|
return await self._exit_stack.__aexit__(*exc_info)
|
3356
3389
|
|
@@ -3394,9 +3427,10 @@ class SyncPrefectClient:
|
|
3394
3427
|
self,
|
3395
3428
|
api: Union[str, ASGIApp],
|
3396
3429
|
*,
|
3397
|
-
api_key: str = None,
|
3398
|
-
api_version: str = None,
|
3430
|
+
api_key: Optional[str] = None,
|
3431
|
+
api_version: Optional[str] = None,
|
3399
3432
|
httpx_settings: Optional[Dict[str, Any]] = None,
|
3433
|
+
server_type: Optional[ServerType] = None,
|
3400
3434
|
) -> None:
|
3401
3435
|
httpx_settings = httpx_settings.copy() if httpx_settings else {}
|
3402
3436
|
httpx_settings.setdefault("headers", {})
|
@@ -3416,6 +3450,7 @@ class SyncPrefectClient:
|
|
3416
3450
|
httpx_settings["headers"].setdefault("Authorization", f"Bearer {api_key}")
|
3417
3451
|
|
3418
3452
|
# Context management
|
3453
|
+
self._context_stack: int = 0
|
3419
3454
|
self._ephemeral_app: Optional[ASGIApp] = None
|
3420
3455
|
self.manage_lifespan = True
|
3421
3456
|
self.server_type: ServerType
|
@@ -3454,11 +3489,14 @@ class SyncPrefectClient:
|
|
3454
3489
|
# client will use a standard HTTP/1.1 connection instead.
|
3455
3490
|
httpx_settings.setdefault("http2", PREFECT_API_ENABLE_HTTP2.value())
|
3456
3491
|
|
3457
|
-
|
3458
|
-
|
3459
|
-
|
3460
|
-
|
3461
|
-
|
3492
|
+
if server_type:
|
3493
|
+
self.server_type = server_type
|
3494
|
+
else:
|
3495
|
+
self.server_type = (
|
3496
|
+
ServerType.CLOUD
|
3497
|
+
if api.startswith(PREFECT_CLOUD_API_URL.value())
|
3498
|
+
else ServerType.SERVER
|
3499
|
+
)
|
3462
3500
|
|
3463
3501
|
# Connect to an in-process application
|
3464
3502
|
elif isinstance(api, ASGIApp):
|
@@ -3490,14 +3528,9 @@ class SyncPrefectClient:
|
|
3490
3528
|
and PREFECT_CLIENT_CSRF_SUPPORT_ENABLED.value()
|
3491
3529
|
)
|
3492
3530
|
|
3493
|
-
|
3494
|
-
|
3495
|
-
|
3496
|
-
)
|
3497
|
-
else:
|
3498
|
-
self._client = PrefectHttpxSyncClient(
|
3499
|
-
**httpx_settings, enable_csrf_support=enable_csrf_support
|
3500
|
-
)
|
3531
|
+
self._client = PrefectHttpxSyncClient(
|
3532
|
+
**httpx_settings, enable_csrf_support=enable_csrf_support
|
3533
|
+
)
|
3501
3534
|
|
3502
3535
|
# See https://www.python-httpx.org/advanced/#custom-transports
|
3503
3536
|
#
|
@@ -3547,9 +3580,12 @@ class SyncPrefectClient:
|
|
3547
3580
|
"Retrieve a new client with `get_client()` instead."
|
3548
3581
|
)
|
3549
3582
|
|
3583
|
+
self._context_stack += 1
|
3584
|
+
|
3550
3585
|
if self._started:
|
3551
|
-
#
|
3552
|
-
|
3586
|
+
# allow reentrancy
|
3587
|
+
return self
|
3588
|
+
|
3553
3589
|
self._client.__enter__()
|
3554
3590
|
self._started = True
|
3555
3591
|
|
@@ -3559,6 +3595,9 @@ class SyncPrefectClient:
|
|
3559
3595
|
"""
|
3560
3596
|
Shutdown the client.
|
3561
3597
|
"""
|
3598
|
+
self._context_stack -= 1
|
3599
|
+
if self._context_stack > 0:
|
3600
|
+
return
|
3562
3601
|
self._closed = True
|
3563
3602
|
self._client.__exit__(*exc_info)
|
3564
3603
|
|
@@ -3612,9 +3651,7 @@ class SyncPrefectClient:
|
|
3612
3651
|
the ID of the flow in the backend
|
3613
3652
|
"""
|
3614
3653
|
flow_data = FlowCreate(name=flow_name)
|
3615
|
-
response = self._client.post(
|
3616
|
-
"/flows/", json=flow_data.dict(json_compatible=True)
|
3617
|
-
)
|
3654
|
+
response = self._client.post("/flows/", json=flow_data.model_dump(mode="json"))
|
3618
3655
|
|
3619
3656
|
flow_id = response.json().get("id")
|
3620
3657
|
if not flow_id:
|
@@ -3677,9 +3714,9 @@ class SyncPrefectClient:
|
|
3677
3714
|
),
|
3678
3715
|
)
|
3679
3716
|
|
3680
|
-
flow_run_create_json = flow_run_create.
|
3717
|
+
flow_run_create_json = flow_run_create.model_dump(mode="json")
|
3681
3718
|
response = self._client.post("/flow_runs/", json=flow_run_create_json)
|
3682
|
-
flow_run = FlowRun.
|
3719
|
+
flow_run = FlowRun.model_validate(response.json())
|
3683
3720
|
|
3684
3721
|
# Restore the parameters to the local objects to retain expectations about
|
3685
3722
|
# Python objects
|
@@ -3687,6 +3724,61 @@ class SyncPrefectClient:
|
|
3687
3724
|
|
3688
3725
|
return flow_run
|
3689
3726
|
|
3727
|
+
def update_flow_run(
|
3728
|
+
self,
|
3729
|
+
flow_run_id: UUID,
|
3730
|
+
flow_version: Optional[str] = None,
|
3731
|
+
parameters: Optional[dict] = None,
|
3732
|
+
name: Optional[str] = None,
|
3733
|
+
tags: Optional[Iterable[str]] = None,
|
3734
|
+
empirical_policy: Optional[FlowRunPolicy] = None,
|
3735
|
+
infrastructure_pid: Optional[str] = None,
|
3736
|
+
job_variables: Optional[dict] = None,
|
3737
|
+
) -> httpx.Response:
|
3738
|
+
"""
|
3739
|
+
Update a flow run's details.
|
3740
|
+
|
3741
|
+
Args:
|
3742
|
+
flow_run_id: The identifier for the flow run to update.
|
3743
|
+
flow_version: A new version string for the flow run.
|
3744
|
+
parameters: A dictionary of parameter values for the flow run. This will not
|
3745
|
+
be merged with any existing parameters.
|
3746
|
+
name: A new name for the flow run.
|
3747
|
+
empirical_policy: A new flow run orchestration policy. This will not be
|
3748
|
+
merged with any existing policy.
|
3749
|
+
tags: An iterable of new tags for the flow run. These will not be merged with
|
3750
|
+
any existing tags.
|
3751
|
+
infrastructure_pid: The id of flow run as returned by an
|
3752
|
+
infrastructure block.
|
3753
|
+
|
3754
|
+
Returns:
|
3755
|
+
an `httpx.Response` object from the PATCH request
|
3756
|
+
"""
|
3757
|
+
params = {}
|
3758
|
+
if flow_version is not None:
|
3759
|
+
params["flow_version"] = flow_version
|
3760
|
+
if parameters is not None:
|
3761
|
+
params["parameters"] = parameters
|
3762
|
+
if name is not None:
|
3763
|
+
params["name"] = name
|
3764
|
+
if tags is not None:
|
3765
|
+
params["tags"] = tags
|
3766
|
+
if empirical_policy is not None:
|
3767
|
+
params["empirical_policy"] = empirical_policy.model_dump(
|
3768
|
+
mode="json", exclude_unset=True
|
3769
|
+
)
|
3770
|
+
if infrastructure_pid:
|
3771
|
+
params["infrastructure_pid"] = infrastructure_pid
|
3772
|
+
if job_variables is not None:
|
3773
|
+
params["job_variables"] = job_variables
|
3774
|
+
|
3775
|
+
flow_run_data = FlowRunUpdate(**params)
|
3776
|
+
|
3777
|
+
return self._client.patch(
|
3778
|
+
f"/flow_runs/{flow_run_id}",
|
3779
|
+
json=flow_run_data.model_dump(mode="json", exclude_unset=True),
|
3780
|
+
)
|
3781
|
+
|
3690
3782
|
def read_flow_run(self, flow_run_id: UUID) -> FlowRun:
|
3691
3783
|
"""
|
3692
3784
|
Query the Prefect API for a flow run by id.
|
@@ -3704,7 +3796,7 @@ class SyncPrefectClient:
|
|
3704
3796
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
3705
3797
|
else:
|
3706
3798
|
raise
|
3707
|
-
return FlowRun.
|
3799
|
+
return FlowRun.model_validate(response.json())
|
3708
3800
|
|
3709
3801
|
def read_flow_runs(
|
3710
3802
|
self,
|
@@ -3716,7 +3808,7 @@ class SyncPrefectClient:
|
|
3716
3808
|
work_pool_filter: WorkPoolFilter = None,
|
3717
3809
|
work_queue_filter: WorkQueueFilter = None,
|
3718
3810
|
sort: FlowRunSort = None,
|
3719
|
-
limit: int = None,
|
3811
|
+
limit: Optional[int] = None,
|
3720
3812
|
offset: int = 0,
|
3721
3813
|
) -> List[FlowRun]:
|
3722
3814
|
"""
|
@@ -3739,29 +3831,23 @@ class SyncPrefectClient:
|
|
3739
3831
|
of the flow runs
|
3740
3832
|
"""
|
3741
3833
|
body = {
|
3742
|
-
"flows": flow_filter.
|
3834
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
3743
3835
|
"flow_runs": (
|
3744
|
-
flow_run_filter.
|
3836
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
3745
3837
|
if flow_run_filter
|
3746
3838
|
else None
|
3747
3839
|
),
|
3748
3840
|
"task_runs": (
|
3749
|
-
task_run_filter.
|
3841
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
3750
3842
|
),
|
3751
3843
|
"deployments": (
|
3752
|
-
deployment_filter.
|
3753
|
-
if deployment_filter
|
3754
|
-
else None
|
3844
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
3755
3845
|
),
|
3756
3846
|
"work_pools": (
|
3757
|
-
work_pool_filter.
|
3758
|
-
if work_pool_filter
|
3759
|
-
else None
|
3847
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
3760
3848
|
),
|
3761
3849
|
"work_pool_queues": (
|
3762
|
-
work_queue_filter.
|
3763
|
-
if work_queue_filter
|
3764
|
-
else None
|
3850
|
+
work_queue_filter.model_dump(mode="json") if work_queue_filter else None
|
3765
3851
|
),
|
3766
3852
|
"sort": sort,
|
3767
3853
|
"limit": limit,
|
@@ -3769,7 +3855,7 @@ class SyncPrefectClient:
|
|
3769
3855
|
}
|
3770
3856
|
|
3771
3857
|
response = self._client.post("/flow_runs/filter", json=body)
|
3772
|
-
return pydantic.
|
3858
|
+
return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
|
3773
3859
|
|
3774
3860
|
def set_flow_run_state(
|
3775
3861
|
self,
|
@@ -3795,7 +3881,7 @@ class SyncPrefectClient:
|
|
3795
3881
|
try:
|
3796
3882
|
response = self._client.post(
|
3797
3883
|
f"/flow_runs/{flow_run_id}/set_state",
|
3798
|
-
json=dict(state=state_create.
|
3884
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
3799
3885
|
)
|
3800
3886
|
except httpx.HTTPStatusError as e:
|
3801
3887
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -3803,13 +3889,28 @@ class SyncPrefectClient:
|
|
3803
3889
|
else:
|
3804
3890
|
raise
|
3805
3891
|
|
3806
|
-
return OrchestrationResult.
|
3892
|
+
return OrchestrationResult.model_validate(response.json())
|
3893
|
+
|
3894
|
+
def set_flow_run_name(self, flow_run_id: UUID, name: str):
|
3895
|
+
flow_run_data = TaskRunUpdate(name=name)
|
3896
|
+
return self._client.patch(
|
3897
|
+
f"/flow_runs/{flow_run_id}",
|
3898
|
+
json=flow_run_data.model_dump(mode="json", exclude_unset=True),
|
3899
|
+
)
|
3900
|
+
|
3901
|
+
def set_task_run_name(self, task_run_id: UUID, name: str):
|
3902
|
+
task_run_data = TaskRunUpdate(name=name)
|
3903
|
+
return self._client.patch(
|
3904
|
+
f"/task_runs/{task_run_id}",
|
3905
|
+
json=task_run_data.model_dump(mode="json", exclude_unset=True),
|
3906
|
+
)
|
3807
3907
|
|
3808
3908
|
def create_task_run(
|
3809
3909
|
self,
|
3810
3910
|
task: "TaskObject[P, R]",
|
3811
3911
|
flow_run_id: Optional[UUID],
|
3812
3912
|
dynamic_key: str,
|
3913
|
+
id: Optional[UUID] = None,
|
3813
3914
|
name: Optional[str] = None,
|
3814
3915
|
extra_tags: Optional[Iterable[str]] = None,
|
3815
3916
|
state: Optional[prefect.states.State[R]] = None,
|
@@ -3833,6 +3934,8 @@ class SyncPrefectClient:
|
|
3833
3934
|
task: The Task to run
|
3834
3935
|
flow_run_id: The flow run id with which to associate the task run
|
3835
3936
|
dynamic_key: A key unique to this particular run of a Task within the flow
|
3937
|
+
id: An optional ID for the task run. If not provided, one will be generated
|
3938
|
+
server-side.
|
3836
3939
|
name: An optional name for the task run
|
3837
3940
|
extra_tags: an optional list of extra tags to apply to the task run in
|
3838
3941
|
addition to `task.tags`
|
@@ -3849,6 +3952,7 @@ class SyncPrefectClient:
|
|
3849
3952
|
state = prefect.states.Pending()
|
3850
3953
|
|
3851
3954
|
task_run_data = TaskRunCreate(
|
3955
|
+
id=id,
|
3852
3956
|
name=name,
|
3853
3957
|
flow_run_id=flow_run_id,
|
3854
3958
|
task_key=task.task_key,
|
@@ -3864,10 +3968,10 @@ class SyncPrefectClient:
|
|
3864
3968
|
task_inputs=task_inputs or {},
|
3865
3969
|
)
|
3866
3970
|
|
3867
|
-
|
3868
|
-
|
3869
|
-
)
|
3870
|
-
return TaskRun.
|
3971
|
+
content = task_run_data.model_dump_json(exclude={"id"} if id is None else None)
|
3972
|
+
|
3973
|
+
response = self._client.post("/task_runs/", content=content)
|
3974
|
+
return TaskRun.model_validate(response.json())
|
3871
3975
|
|
3872
3976
|
def read_task_run(self, task_run_id: UUID) -> TaskRun:
|
3873
3977
|
"""
|
@@ -3879,8 +3983,14 @@ class SyncPrefectClient:
|
|
3879
3983
|
Returns:
|
3880
3984
|
a Task Run model representation of the task run
|
3881
3985
|
"""
|
3882
|
-
|
3883
|
-
|
3986
|
+
try:
|
3987
|
+
response = self._client.get(f"/task_runs/{task_run_id}")
|
3988
|
+
return TaskRun.model_validate(response.json())
|
3989
|
+
except httpx.HTTPStatusError as e:
|
3990
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
3991
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
3992
|
+
else:
|
3993
|
+
raise
|
3884
3994
|
|
3885
3995
|
def read_task_runs(
|
3886
3996
|
self,
|
@@ -3890,7 +4000,7 @@ class SyncPrefectClient:
|
|
3890
4000
|
task_run_filter: TaskRunFilter = None,
|
3891
4001
|
deployment_filter: DeploymentFilter = None,
|
3892
4002
|
sort: TaskRunSort = None,
|
3893
|
-
limit: int = None,
|
4003
|
+
limit: Optional[int] = None,
|
3894
4004
|
offset: int = 0,
|
3895
4005
|
) -> List[TaskRun]:
|
3896
4006
|
"""
|
@@ -3911,26 +4021,24 @@ class SyncPrefectClient:
|
|
3911
4021
|
of the task runs
|
3912
4022
|
"""
|
3913
4023
|
body = {
|
3914
|
-
"flows": flow_filter.
|
4024
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
3915
4025
|
"flow_runs": (
|
3916
|
-
flow_run_filter.
|
4026
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
3917
4027
|
if flow_run_filter
|
3918
4028
|
else None
|
3919
4029
|
),
|
3920
4030
|
"task_runs": (
|
3921
|
-
task_run_filter.
|
4031
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
3922
4032
|
),
|
3923
4033
|
"deployments": (
|
3924
|
-
deployment_filter.
|
3925
|
-
if deployment_filter
|
3926
|
-
else None
|
4034
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
3927
4035
|
),
|
3928
4036
|
"sort": sort,
|
3929
4037
|
"limit": limit,
|
3930
4038
|
"offset": offset,
|
3931
4039
|
}
|
3932
4040
|
response = self._client.post("/task_runs/filter", json=body)
|
3933
|
-
return pydantic.
|
4041
|
+
return pydantic.TypeAdapter(List[TaskRun]).validate_python(response.json())
|
3934
4042
|
|
3935
4043
|
def set_task_run_state(
|
3936
4044
|
self,
|
@@ -3954,9 +4062,9 @@ class SyncPrefectClient:
|
|
3954
4062
|
state_create.state_details.task_run_id = task_run_id
|
3955
4063
|
response = self._client.post(
|
3956
4064
|
f"/task_runs/{task_run_id}/set_state",
|
3957
|
-
json=dict(state=state_create.
|
4065
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
3958
4066
|
)
|
3959
|
-
return OrchestrationResult.
|
4067
|
+
return OrchestrationResult.model_validate(response.json())
|
3960
4068
|
|
3961
4069
|
def read_task_run_states(self, task_run_id: UUID) -> List[prefect.states.State]:
|
3962
4070
|
"""
|
@@ -3971,4 +4079,123 @@ class SyncPrefectClient:
|
|
3971
4079
|
response = self._client.get(
|
3972
4080
|
"/task_run_states/", params=dict(task_run_id=str(task_run_id))
|
3973
4081
|
)
|
3974
|
-
return pydantic.
|
4082
|
+
return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
|
4083
|
+
response.json()
|
4084
|
+
)
|
4085
|
+
|
4086
|
+
def read_deployment(
|
4087
|
+
self,
|
4088
|
+
deployment_id: UUID,
|
4089
|
+
) -> DeploymentResponse:
|
4090
|
+
"""
|
4091
|
+
Query the Prefect API for a deployment by id.
|
4092
|
+
|
4093
|
+
Args:
|
4094
|
+
deployment_id: the deployment ID of interest
|
4095
|
+
|
4096
|
+
Returns:
|
4097
|
+
a [Deployment model][prefect.client.schemas.objects.Deployment] representation of the deployment
|
4098
|
+
"""
|
4099
|
+
try:
|
4100
|
+
response = self._client.get(f"/deployments/{deployment_id}")
|
4101
|
+
except httpx.HTTPStatusError as e:
|
4102
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
4103
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
4104
|
+
else:
|
4105
|
+
raise
|
4106
|
+
return DeploymentResponse.model_validate(response.json())
|
4107
|
+
|
4108
|
+
def read_deployment_by_name(
|
4109
|
+
self,
|
4110
|
+
name: str,
|
4111
|
+
) -> DeploymentResponse:
|
4112
|
+
"""
|
4113
|
+
Query the Prefect API for a deployment by name.
|
4114
|
+
|
4115
|
+
Args:
|
4116
|
+
name: A deployed flow's name: <FLOW_NAME>/<DEPLOYMENT_NAME>
|
4117
|
+
|
4118
|
+
Raises:
|
4119
|
+
prefect.exceptions.ObjectNotFound: If request returns 404
|
4120
|
+
httpx.RequestError: If request fails
|
4121
|
+
|
4122
|
+
Returns:
|
4123
|
+
a Deployment model representation of the deployment
|
4124
|
+
"""
|
4125
|
+
try:
|
4126
|
+
response = self._client.get(f"/deployments/name/{name}")
|
4127
|
+
except httpx.HTTPStatusError as e:
|
4128
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
4129
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
4130
|
+
else:
|
4131
|
+
raise
|
4132
|
+
|
4133
|
+
return DeploymentResponse.model_validate(response.json())
|
4134
|
+
|
4135
|
+
def create_artifact(
|
4136
|
+
self,
|
4137
|
+
artifact: ArtifactCreate,
|
4138
|
+
) -> Artifact:
|
4139
|
+
"""
|
4140
|
+
Creates an artifact with the provided configuration.
|
4141
|
+
|
4142
|
+
Args:
|
4143
|
+
artifact: Desired configuration for the new artifact.
|
4144
|
+
Returns:
|
4145
|
+
Information about the newly created artifact.
|
4146
|
+
"""
|
4147
|
+
|
4148
|
+
response = self._client.post(
|
4149
|
+
"/artifacts/",
|
4150
|
+
json=artifact.model_dump(mode="json", exclude_unset=True),
|
4151
|
+
)
|
4152
|
+
|
4153
|
+
return Artifact.model_validate(response.json())
|
4154
|
+
|
4155
|
+
def release_concurrency_slots(
|
4156
|
+
self, names: List[str], slots: int, occupancy_seconds: float
|
4157
|
+
) -> httpx.Response:
|
4158
|
+
"""
|
4159
|
+
Release concurrency slots for the specified limits.
|
4160
|
+
|
4161
|
+
Args:
|
4162
|
+
names (List[str]): A list of limit names for which to release slots.
|
4163
|
+
slots (int): The number of concurrency slots to release.
|
4164
|
+
occupancy_seconds (float): The duration in seconds that the slots
|
4165
|
+
were occupied.
|
4166
|
+
|
4167
|
+
Returns:
|
4168
|
+
httpx.Response: The HTTP response from the server.
|
4169
|
+
"""
|
4170
|
+
return self._client.post(
|
4171
|
+
"/v2/concurrency_limits/decrement",
|
4172
|
+
json={
|
4173
|
+
"names": names,
|
4174
|
+
"slots": slots,
|
4175
|
+
"occupancy_seconds": occupancy_seconds,
|
4176
|
+
},
|
4177
|
+
)
|
4178
|
+
|
4179
|
+
def decrement_v1_concurrency_slots(
|
4180
|
+
self, names: List[str], occupancy_seconds: float, task_run_id: UUID
|
4181
|
+
) -> httpx.Response:
|
4182
|
+
"""
|
4183
|
+
Release the specified concurrency limits.
|
4184
|
+
|
4185
|
+
Args:
|
4186
|
+
names (List[str]): A list of limit names to decrement.
|
4187
|
+
occupancy_seconds (float): The duration in seconds that the slots
|
4188
|
+
were held.
|
4189
|
+
task_run_id (UUID): The task run ID that incremented the limits.
|
4190
|
+
|
4191
|
+
Returns:
|
4192
|
+
httpx.Response: The HTTP response from the server.
|
4193
|
+
"""
|
4194
|
+
return self._client.post(
|
4195
|
+
"/concurrency_limits/decrement",
|
4196
|
+
json={
|
4197
|
+
"names": names,
|
4198
|
+
"occupancy_seconds": occupancy_seconds,
|
4199
|
+
"task_run_id": str(task_run_id),
|
4200
|
+
},
|
4201
|
+
)
|