prefect-client 2.20.4__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/__init__.py +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
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,13 +2664,11 @@ 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,
|
@@ -2672,7 +2680,6 @@ class PrefectClient:
|
|
2672
2680
|
|
2673
2681
|
Args:
|
2674
2682
|
work_pool: Desired configuration for the new work pool.
|
2675
|
-
overwrite: Whether to overwrite an existing work pool with the same name.
|
2676
2683
|
|
2677
2684
|
Returns:
|
2678
2685
|
Information about the newly created work pool.
|
@@ -2680,7 +2687,7 @@ class PrefectClient:
|
|
2680
2687
|
try:
|
2681
2688
|
response = await self._client.post(
|
2682
2689
|
"/work_pools/",
|
2683
|
-
json=work_pool.
|
2690
|
+
json=work_pool.model_dump(mode="json", exclude_unset=True),
|
2684
2691
|
)
|
2685
2692
|
except httpx.HTTPStatusError as e:
|
2686
2693
|
if e.response.status_code == status.HTTP_409_CONFLICT:
|
@@ -2695,8 +2702,8 @@ class PrefectClient:
|
|
2695
2702
|
)
|
2696
2703
|
await self.update_work_pool(
|
2697
2704
|
work_pool_name=work_pool.name,
|
2698
|
-
work_pool=WorkPoolUpdate.
|
2699
|
-
work_pool.
|
2705
|
+
work_pool=WorkPoolUpdate.model_validate(
|
2706
|
+
work_pool.model_dump(exclude={"name", "type"})
|
2700
2707
|
),
|
2701
2708
|
)
|
2702
2709
|
response = await self._client.get(f"/work_pools/{work_pool.name}")
|
@@ -2705,7 +2712,7 @@ class PrefectClient:
|
|
2705
2712
|
else:
|
2706
2713
|
raise
|
2707
2714
|
|
2708
|
-
return
|
2715
|
+
return WorkPool.model_validate(response.json())
|
2709
2716
|
|
2710
2717
|
async def update_work_pool(
|
2711
2718
|
self,
|
@@ -2722,7 +2729,7 @@ class PrefectClient:
|
|
2722
2729
|
try:
|
2723
2730
|
await self._client.patch(
|
2724
2731
|
f"/work_pools/{work_pool_name}",
|
2725
|
-
json=work_pool.
|
2732
|
+
json=work_pool.model_dump(mode="json", exclude_unset=True),
|
2726
2733
|
)
|
2727
2734
|
except httpx.HTTPStatusError as e:
|
2728
2735
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -2769,7 +2776,7 @@ class PrefectClient:
|
|
2769
2776
|
"""
|
2770
2777
|
json = {
|
2771
2778
|
"work_queues": (
|
2772
|
-
work_queue_filter.
|
2779
|
+
work_queue_filter.model_dump(mode="json", exclude_unset=True)
|
2773
2780
|
if work_queue_filter
|
2774
2781
|
else None
|
2775
2782
|
),
|
@@ -2791,14 +2798,14 @@ class PrefectClient:
|
|
2791
2798
|
else:
|
2792
2799
|
response = await self._client.post("/work_queues/filter", json=json)
|
2793
2800
|
|
2794
|
-
return pydantic.
|
2801
|
+
return pydantic.TypeAdapter(List[WorkQueue]).validate_python(response.json())
|
2795
2802
|
|
2796
2803
|
async def get_scheduled_flow_runs_for_deployments(
|
2797
2804
|
self,
|
2798
2805
|
deployment_ids: List[UUID],
|
2799
2806
|
scheduled_before: Optional[datetime.datetime] = None,
|
2800
2807
|
limit: Optional[int] = None,
|
2801
|
-
):
|
2808
|
+
) -> List[FlowRunResponse]:
|
2802
2809
|
body: Dict[str, Any] = dict(deployment_ids=[str(id) for id in deployment_ids])
|
2803
2810
|
if scheduled_before:
|
2804
2811
|
body["scheduled_before"] = str(scheduled_before)
|
@@ -2810,7 +2817,9 @@ class PrefectClient:
|
|
2810
2817
|
json=body,
|
2811
2818
|
)
|
2812
2819
|
|
2813
|
-
return pydantic.
|
2820
|
+
return pydantic.TypeAdapter(List[FlowRunResponse]).validate_python(
|
2821
|
+
response.json()
|
2822
|
+
)
|
2814
2823
|
|
2815
2824
|
async def get_scheduled_flow_runs_for_work_pool(
|
2816
2825
|
self,
|
@@ -2843,7 +2852,9 @@ class PrefectClient:
|
|
2843
2852
|
f"/work_pools/{work_pool_name}/get_scheduled_flow_runs",
|
2844
2853
|
json=body,
|
2845
2854
|
)
|
2846
|
-
return pydantic.
|
2855
|
+
return pydantic.TypeAdapter(List[WorkerFlowRunResponse]).validate_python(
|
2856
|
+
response.json()
|
2857
|
+
)
|
2847
2858
|
|
2848
2859
|
async def create_artifact(
|
2849
2860
|
self,
|
@@ -2860,10 +2871,29 @@ class PrefectClient:
|
|
2860
2871
|
|
2861
2872
|
response = await self._client.post(
|
2862
2873
|
"/artifacts/",
|
2863
|
-
json=artifact.
|
2874
|
+
json=artifact.model_dump(mode="json", exclude_unset=True),
|
2864
2875
|
)
|
2865
2876
|
|
2866
|
-
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
|
+
)
|
2867
2897
|
|
2868
2898
|
async def read_artifacts(
|
2869
2899
|
self,
|
@@ -2872,7 +2902,7 @@ class PrefectClient:
|
|
2872
2902
|
flow_run_filter: FlowRunFilter = None,
|
2873
2903
|
task_run_filter: TaskRunFilter = None,
|
2874
2904
|
sort: ArtifactSort = None,
|
2875
|
-
limit: int = None,
|
2905
|
+
limit: Optional[int] = None,
|
2876
2906
|
offset: int = 0,
|
2877
2907
|
) -> List[Artifact]:
|
2878
2908
|
"""
|
@@ -2890,20 +2920,20 @@ class PrefectClient:
|
|
2890
2920
|
"""
|
2891
2921
|
body = {
|
2892
2922
|
"artifacts": (
|
2893
|
-
artifact_filter.
|
2923
|
+
artifact_filter.model_dump(mode="json") if artifact_filter else None
|
2894
2924
|
),
|
2895
2925
|
"flow_runs": (
|
2896
|
-
flow_run_filter.
|
2926
|
+
flow_run_filter.model_dump(mode="json") if flow_run_filter else None
|
2897
2927
|
),
|
2898
2928
|
"task_runs": (
|
2899
|
-
task_run_filter.
|
2929
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2900
2930
|
),
|
2901
2931
|
"sort": sort,
|
2902
2932
|
"limit": limit,
|
2903
2933
|
"offset": offset,
|
2904
2934
|
}
|
2905
2935
|
response = await self._client.post("/artifacts/filter", json=body)
|
2906
|
-
return pydantic.
|
2936
|
+
return pydantic.TypeAdapter(List[Artifact]).validate_python(response.json())
|
2907
2937
|
|
2908
2938
|
async def read_latest_artifacts(
|
2909
2939
|
self,
|
@@ -2912,7 +2942,7 @@ class PrefectClient:
|
|
2912
2942
|
flow_run_filter: FlowRunFilter = None,
|
2913
2943
|
task_run_filter: TaskRunFilter = None,
|
2914
2944
|
sort: ArtifactCollectionSort = None,
|
2915
|
-
limit: int = None,
|
2945
|
+
limit: Optional[int] = None,
|
2916
2946
|
offset: int = 0,
|
2917
2947
|
) -> List[ArtifactCollection]:
|
2918
2948
|
"""
|
@@ -2930,20 +2960,22 @@ class PrefectClient:
|
|
2930
2960
|
"""
|
2931
2961
|
body = {
|
2932
2962
|
"artifacts": (
|
2933
|
-
artifact_filter.
|
2963
|
+
artifact_filter.model_dump(mode="json") if artifact_filter else None
|
2934
2964
|
),
|
2935
2965
|
"flow_runs": (
|
2936
|
-
flow_run_filter.
|
2966
|
+
flow_run_filter.model_dump(mode="json") if flow_run_filter else None
|
2937
2967
|
),
|
2938
2968
|
"task_runs": (
|
2939
|
-
task_run_filter.
|
2969
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
2940
2970
|
),
|
2941
2971
|
"sort": sort,
|
2942
2972
|
"limit": limit,
|
2943
2973
|
"offset": offset,
|
2944
2974
|
}
|
2945
2975
|
response = await self._client.post("/artifacts/latest/filter", json=body)
|
2946
|
-
return pydantic.
|
2976
|
+
return pydantic.TypeAdapter(List[ArtifactCollection]).validate_python(
|
2977
|
+
response.json()
|
2978
|
+
)
|
2947
2979
|
|
2948
2980
|
async def delete_artifact(self, artifact_id: UUID) -> None:
|
2949
2981
|
"""
|
@@ -2971,7 +3003,7 @@ class PrefectClient:
|
|
2971
3003
|
"""
|
2972
3004
|
response = await self._client.post(
|
2973
3005
|
"/variables/",
|
2974
|
-
json=variable.
|
3006
|
+
json=variable.model_dump(mode="json", exclude_unset=True),
|
2975
3007
|
)
|
2976
3008
|
return Variable(**response.json())
|
2977
3009
|
|
@@ -2986,7 +3018,7 @@ class PrefectClient:
|
|
2986
3018
|
"""
|
2987
3019
|
await self._client.patch(
|
2988
3020
|
f"/variables/name/{variable.name}",
|
2989
|
-
json=variable.
|
3021
|
+
json=variable.model_dump(mode="json", exclude_unset=True),
|
2990
3022
|
)
|
2991
3023
|
|
2992
3024
|
async def read_variable_by_name(self, name: str) -> Optional[Variable]:
|
@@ -3010,10 +3042,10 @@ class PrefectClient:
|
|
3010
3042
|
else:
|
3011
3043
|
raise
|
3012
3044
|
|
3013
|
-
async def read_variables(self, limit: int = None) -> List[Variable]:
|
3045
|
+
async def read_variables(self, limit: Optional[int] = None) -> List[Variable]:
|
3014
3046
|
"""Reads all variables."""
|
3015
3047
|
response = await self._client.post("/variables/filter", json={"limit": limit})
|
3016
|
-
return pydantic.
|
3048
|
+
return pydantic.TypeAdapter(List[Variable]).validate_python(response.json())
|
3017
3049
|
|
3018
3050
|
async def read_worker_metadata(self) -> Dict[str, Any]:
|
3019
3051
|
"""Reads worker metadata stored in Prefect collection registry."""
|
@@ -3022,16 +3054,34 @@ class PrefectClient:
|
|
3022
3054
|
return response.json()
|
3023
3055
|
|
3024
3056
|
async def increment_concurrency_slots(
|
3025
|
-
self, names: List[str], slots: int, mode: str
|
3057
|
+
self, names: List[str], slots: int, mode: str, create_if_missing: Optional[bool]
|
3026
3058
|
) -> httpx.Response:
|
3027
3059
|
return await self._client.post(
|
3028
3060
|
"/v2/concurrency_limits/increment",
|
3029
|
-
json={
|
3061
|
+
json={
|
3062
|
+
"names": names,
|
3063
|
+
"slots": slots,
|
3064
|
+
"mode": mode,
|
3065
|
+
"create_if_missing": create_if_missing,
|
3066
|
+
},
|
3030
3067
|
)
|
3031
3068
|
|
3032
3069
|
async def release_concurrency_slots(
|
3033
3070
|
self, names: List[str], slots: int, occupancy_seconds: float
|
3034
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
|
+
|
3035
3085
|
return await self._client.post(
|
3036
3086
|
"/v2/concurrency_limits/decrement",
|
3037
3087
|
json={
|
@@ -3046,7 +3096,7 @@ class PrefectClient:
|
|
3046
3096
|
) -> UUID:
|
3047
3097
|
response = await self._client.post(
|
3048
3098
|
"/v2/concurrency_limits/",
|
3049
|
-
json=concurrency_limit.
|
3099
|
+
json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
|
3050
3100
|
)
|
3051
3101
|
return UUID(response.json()["id"])
|
3052
3102
|
|
@@ -3056,7 +3106,7 @@ class PrefectClient:
|
|
3056
3106
|
try:
|
3057
3107
|
response = await self._client.patch(
|
3058
3108
|
f"/v2/concurrency_limits/{name}",
|
3059
|
-
json=concurrency_limit.
|
3109
|
+
json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
|
3060
3110
|
)
|
3061
3111
|
return response
|
3062
3112
|
except httpx.HTTPStatusError as e:
|
@@ -3082,7 +3132,7 @@ class PrefectClient:
|
|
3082
3132
|
) -> GlobalConcurrencyLimitResponse:
|
3083
3133
|
try:
|
3084
3134
|
response = await self._client.get(f"/v2/concurrency_limits/{name}")
|
3085
|
-
return GlobalConcurrencyLimitResponse.
|
3135
|
+
return GlobalConcurrencyLimitResponse.model_validate(response.json())
|
3086
3136
|
except httpx.HTTPStatusError as e:
|
3087
3137
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
3088
3138
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
@@ -3099,9 +3149,9 @@ class PrefectClient:
|
|
3099
3149
|
"offset": offset,
|
3100
3150
|
},
|
3101
3151
|
)
|
3102
|
-
return pydantic.
|
3103
|
-
List[GlobalConcurrencyLimitResponse]
|
3104
|
-
)
|
3152
|
+
return pydantic.TypeAdapter(
|
3153
|
+
List[GlobalConcurrencyLimitResponse]
|
3154
|
+
).validate_python(response.json())
|
3105
3155
|
|
3106
3156
|
async def create_flow_run_input(
|
3107
3157
|
self, flow_run_id: UUID, key: str, value: str, sender: Optional[str] = None
|
@@ -3137,7 +3187,7 @@ class PrefectClient:
|
|
3137
3187
|
},
|
3138
3188
|
)
|
3139
3189
|
response.raise_for_status()
|
3140
|
-
return pydantic.
|
3190
|
+
return pydantic.TypeAdapter(List[FlowRunInput]).validate_python(response.json())
|
3141
3191
|
|
3142
3192
|
async def read_flow_run_input(self, flow_run_id: UUID, key: str) -> str:
|
3143
3193
|
"""
|
@@ -3162,52 +3212,30 @@ class PrefectClient:
|
|
3162
3212
|
response = await self._client.delete(f"/flow_runs/{flow_run_id}/input/{key}")
|
3163
3213
|
response.raise_for_status()
|
3164
3214
|
|
3165
|
-
def _raise_for_unsupported_automations(self) -> NoReturn:
|
3166
|
-
if not PREFECT_EXPERIMENTAL_EVENTS:
|
3167
|
-
raise RuntimeError(
|
3168
|
-
"The current server and client configuration does not support "
|
3169
|
-
"events. Enable experimental events support with the "
|
3170
|
-
"PREFECT_EXPERIMENTAL_EVENTS setting."
|
3171
|
-
)
|
3172
|
-
else:
|
3173
|
-
raise RuntimeError(
|
3174
|
-
"The current server and client configuration does not support "
|
3175
|
-
"automations. Enable experimental automations with the "
|
3176
|
-
"PREFECT_API_SERVICES_TRIGGERS_ENABLED setting."
|
3177
|
-
)
|
3178
|
-
|
3179
3215
|
async def create_automation(self, automation: AutomationCore) -> UUID:
|
3180
3216
|
"""Creates an automation in Prefect Cloud."""
|
3181
|
-
if not self.server_type.supports_automations():
|
3182
|
-
self._raise_for_unsupported_automations()
|
3183
|
-
|
3184
3217
|
response = await self._client.post(
|
3185
3218
|
"/automations/",
|
3186
|
-
json=automation.
|
3219
|
+
json=automation.model_dump(mode="json"),
|
3187
3220
|
)
|
3188
3221
|
|
3189
3222
|
return UUID(response.json()["id"])
|
3190
3223
|
|
3191
3224
|
async def update_automation(self, automation_id: UUID, automation: AutomationCore):
|
3192
3225
|
"""Updates an automation in Prefect Cloud."""
|
3193
|
-
if not self.server_type.supports_automations():
|
3194
|
-
self._raise_for_unsupported_automations()
|
3195
3226
|
response = await self._client.put(
|
3196
3227
|
f"/automations/{automation_id}",
|
3197
|
-
json=automation.
|
3228
|
+
json=automation.model_dump(mode="json", exclude_unset=True),
|
3198
3229
|
)
|
3199
3230
|
response.raise_for_status
|
3200
3231
|
|
3201
3232
|
async def read_automations(self) -> List[Automation]:
|
3202
|
-
if not self.server_type.supports_automations():
|
3203
|
-
self._raise_for_unsupported_automations()
|
3204
|
-
|
3205
3233
|
response = await self._client.post("/automations/filter")
|
3206
3234
|
response.raise_for_status()
|
3207
|
-
return pydantic.
|
3235
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3208
3236
|
|
3209
3237
|
async def find_automation(
|
3210
|
-
self, id_or_name: Union[str, UUID]
|
3238
|
+
self, id_or_name: Union[str, UUID]
|
3211
3239
|
) -> Optional[Automation]:
|
3212
3240
|
if isinstance(id_or_name, str):
|
3213
3241
|
try:
|
@@ -3240,14 +3268,11 @@ class PrefectClient:
|
|
3240
3268
|
return None
|
3241
3269
|
|
3242
3270
|
async def read_automation(self, automation_id: UUID) -> Optional[Automation]:
|
3243
|
-
if not self.server_type.supports_automations():
|
3244
|
-
self._raise_for_unsupported_automations()
|
3245
|
-
|
3246
3271
|
response = await self._client.get(f"/automations/{automation_id}")
|
3247
3272
|
if response.status_code == 404:
|
3248
3273
|
return None
|
3249
3274
|
response.raise_for_status()
|
3250
|
-
return Automation.
|
3275
|
+
return Automation.model_validate(response.json())
|
3251
3276
|
|
3252
3277
|
async def read_automations_by_name(self, name: str) -> List[Automation]:
|
3253
3278
|
"""
|
@@ -3259,15 +3284,13 @@ class PrefectClient:
|
|
3259
3284
|
Returns:
|
3260
3285
|
a list of Automation model representations of the automations
|
3261
3286
|
"""
|
3262
|
-
if not self.server_type.supports_automations():
|
3263
|
-
self._raise_for_unsupported_automations()
|
3264
3287
|
automation_filter = filters.AutomationFilter(name=dict(any_=[name]))
|
3265
3288
|
|
3266
3289
|
response = await self._client.post(
|
3267
3290
|
"/automations/filter",
|
3268
3291
|
json={
|
3269
3292
|
"sort": sorting.AutomationSort.UPDATED_DESC,
|
3270
|
-
"automations": automation_filter.
|
3293
|
+
"automations": automation_filter.model_dump(mode="json")
|
3271
3294
|
if automation_filter
|
3272
3295
|
else None,
|
3273
3296
|
},
|
@@ -3275,30 +3298,21 @@ class PrefectClient:
|
|
3275
3298
|
|
3276
3299
|
response.raise_for_status()
|
3277
3300
|
|
3278
|
-
return pydantic.
|
3301
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3279
3302
|
|
3280
3303
|
async def pause_automation(self, automation_id: UUID):
|
3281
|
-
if not self.server_type.supports_automations():
|
3282
|
-
self._raise_for_unsupported_automations()
|
3283
|
-
|
3284
3304
|
response = await self._client.patch(
|
3285
3305
|
f"/automations/{automation_id}", json={"enabled": False}
|
3286
3306
|
)
|
3287
3307
|
response.raise_for_status()
|
3288
3308
|
|
3289
3309
|
async def resume_automation(self, automation_id: UUID):
|
3290
|
-
if not self.server_type.supports_automations():
|
3291
|
-
self._raise_for_unsupported_automations()
|
3292
|
-
|
3293
3310
|
response = await self._client.patch(
|
3294
3311
|
f"/automations/{automation_id}", json={"enabled": True}
|
3295
3312
|
)
|
3296
3313
|
response.raise_for_status()
|
3297
3314
|
|
3298
3315
|
async def delete_automation(self, automation_id: UUID):
|
3299
|
-
if not self.server_type.supports_automations():
|
3300
|
-
self._raise_for_unsupported_automations()
|
3301
|
-
|
3302
3316
|
response = await self._client.delete(f"/automations/{automation_id}")
|
3303
3317
|
if response.status_code == 404:
|
3304
3318
|
return
|
@@ -3308,17 +3322,11 @@ class PrefectClient:
|
|
3308
3322
|
async def read_resource_related_automations(
|
3309
3323
|
self, resource_id: str
|
3310
3324
|
) -> List[Automation]:
|
3311
|
-
if not self.server_type.supports_automations():
|
3312
|
-
self._raise_for_unsupported_automations()
|
3313
|
-
|
3314
3325
|
response = await self._client.get(f"/automations/related-to/{resource_id}")
|
3315
3326
|
response.raise_for_status()
|
3316
|
-
return pydantic.
|
3327
|
+
return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
|
3317
3328
|
|
3318
3329
|
async def delete_resource_owned_automations(self, resource_id: str):
|
3319
|
-
if not self.server_type.supports_automations():
|
3320
|
-
self._raise_for_unsupported_automations()
|
3321
|
-
|
3322
3330
|
await self._client.delete(f"/automations/owned-by/{resource_id}")
|
3323
3331
|
|
3324
3332
|
async def __aenter__(self):
|
@@ -3337,9 +3345,11 @@ class PrefectClient:
|
|
3337
3345
|
"Retrieve a new client with `get_client()` instead."
|
3338
3346
|
)
|
3339
3347
|
|
3348
|
+
self._context_stack += 1
|
3349
|
+
|
3340
3350
|
if self._started:
|
3341
|
-
#
|
3342
|
-
|
3351
|
+
# allow reentrancy
|
3352
|
+
return self
|
3343
3353
|
|
3344
3354
|
self._loop = asyncio.get_running_loop()
|
3345
3355
|
await self._exit_stack.__aenter__()
|
@@ -3370,6 +3380,10 @@ class PrefectClient:
|
|
3370
3380
|
"""
|
3371
3381
|
Shutdown the client.
|
3372
3382
|
"""
|
3383
|
+
|
3384
|
+
self._context_stack -= 1
|
3385
|
+
if self._context_stack > 0:
|
3386
|
+
return
|
3373
3387
|
self._closed = True
|
3374
3388
|
return await self._exit_stack.__aexit__(*exc_info)
|
3375
3389
|
|
@@ -3413,9 +3427,10 @@ class SyncPrefectClient:
|
|
3413
3427
|
self,
|
3414
3428
|
api: Union[str, ASGIApp],
|
3415
3429
|
*,
|
3416
|
-
api_key: str = None,
|
3417
|
-
api_version: str = None,
|
3430
|
+
api_key: Optional[str] = None,
|
3431
|
+
api_version: Optional[str] = None,
|
3418
3432
|
httpx_settings: Optional[Dict[str, Any]] = None,
|
3433
|
+
server_type: Optional[ServerType] = None,
|
3419
3434
|
) -> None:
|
3420
3435
|
httpx_settings = httpx_settings.copy() if httpx_settings else {}
|
3421
3436
|
httpx_settings.setdefault("headers", {})
|
@@ -3435,6 +3450,7 @@ class SyncPrefectClient:
|
|
3435
3450
|
httpx_settings["headers"].setdefault("Authorization", f"Bearer {api_key}")
|
3436
3451
|
|
3437
3452
|
# Context management
|
3453
|
+
self._context_stack: int = 0
|
3438
3454
|
self._ephemeral_app: Optional[ASGIApp] = None
|
3439
3455
|
self.manage_lifespan = True
|
3440
3456
|
self.server_type: ServerType
|
@@ -3473,11 +3489,14 @@ class SyncPrefectClient:
|
|
3473
3489
|
# client will use a standard HTTP/1.1 connection instead.
|
3474
3490
|
httpx_settings.setdefault("http2", PREFECT_API_ENABLE_HTTP2.value())
|
3475
3491
|
|
3476
|
-
|
3477
|
-
|
3478
|
-
|
3479
|
-
|
3480
|
-
|
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
|
+
)
|
3481
3500
|
|
3482
3501
|
# Connect to an in-process application
|
3483
3502
|
elif isinstance(api, ASGIApp):
|
@@ -3509,14 +3528,9 @@ class SyncPrefectClient:
|
|
3509
3528
|
and PREFECT_CLIENT_CSRF_SUPPORT_ENABLED.value()
|
3510
3529
|
)
|
3511
3530
|
|
3512
|
-
|
3513
|
-
|
3514
|
-
|
3515
|
-
)
|
3516
|
-
else:
|
3517
|
-
self._client = PrefectHttpxSyncClient(
|
3518
|
-
**httpx_settings, enable_csrf_support=enable_csrf_support
|
3519
|
-
)
|
3531
|
+
self._client = PrefectHttpxSyncClient(
|
3532
|
+
**httpx_settings, enable_csrf_support=enable_csrf_support
|
3533
|
+
)
|
3520
3534
|
|
3521
3535
|
# See https://www.python-httpx.org/advanced/#custom-transports
|
3522
3536
|
#
|
@@ -3566,9 +3580,12 @@ class SyncPrefectClient:
|
|
3566
3580
|
"Retrieve a new client with `get_client()` instead."
|
3567
3581
|
)
|
3568
3582
|
|
3583
|
+
self._context_stack += 1
|
3584
|
+
|
3569
3585
|
if self._started:
|
3570
|
-
#
|
3571
|
-
|
3586
|
+
# allow reentrancy
|
3587
|
+
return self
|
3588
|
+
|
3572
3589
|
self._client.__enter__()
|
3573
3590
|
self._started = True
|
3574
3591
|
|
@@ -3578,6 +3595,9 @@ class SyncPrefectClient:
|
|
3578
3595
|
"""
|
3579
3596
|
Shutdown the client.
|
3580
3597
|
"""
|
3598
|
+
self._context_stack -= 1
|
3599
|
+
if self._context_stack > 0:
|
3600
|
+
return
|
3581
3601
|
self._closed = True
|
3582
3602
|
self._client.__exit__(*exc_info)
|
3583
3603
|
|
@@ -3631,9 +3651,7 @@ class SyncPrefectClient:
|
|
3631
3651
|
the ID of the flow in the backend
|
3632
3652
|
"""
|
3633
3653
|
flow_data = FlowCreate(name=flow_name)
|
3634
|
-
response = self._client.post(
|
3635
|
-
"/flows/", json=flow_data.dict(json_compatible=True)
|
3636
|
-
)
|
3654
|
+
response = self._client.post("/flows/", json=flow_data.model_dump(mode="json"))
|
3637
3655
|
|
3638
3656
|
flow_id = response.json().get("id")
|
3639
3657
|
if not flow_id:
|
@@ -3696,9 +3714,9 @@ class SyncPrefectClient:
|
|
3696
3714
|
),
|
3697
3715
|
)
|
3698
3716
|
|
3699
|
-
flow_run_create_json = flow_run_create.
|
3717
|
+
flow_run_create_json = flow_run_create.model_dump(mode="json")
|
3700
3718
|
response = self._client.post("/flow_runs/", json=flow_run_create_json)
|
3701
|
-
flow_run = FlowRun.
|
3719
|
+
flow_run = FlowRun.model_validate(response.json())
|
3702
3720
|
|
3703
3721
|
# Restore the parameters to the local objects to retain expectations about
|
3704
3722
|
# Python objects
|
@@ -3706,6 +3724,61 @@ class SyncPrefectClient:
|
|
3706
3724
|
|
3707
3725
|
return flow_run
|
3708
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
|
+
|
3709
3782
|
def read_flow_run(self, flow_run_id: UUID) -> FlowRun:
|
3710
3783
|
"""
|
3711
3784
|
Query the Prefect API for a flow run by id.
|
@@ -3723,7 +3796,7 @@ class SyncPrefectClient:
|
|
3723
3796
|
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
3724
3797
|
else:
|
3725
3798
|
raise
|
3726
|
-
return FlowRun.
|
3799
|
+
return FlowRun.model_validate(response.json())
|
3727
3800
|
|
3728
3801
|
def read_flow_runs(
|
3729
3802
|
self,
|
@@ -3735,7 +3808,7 @@ class SyncPrefectClient:
|
|
3735
3808
|
work_pool_filter: WorkPoolFilter = None,
|
3736
3809
|
work_queue_filter: WorkQueueFilter = None,
|
3737
3810
|
sort: FlowRunSort = None,
|
3738
|
-
limit: int = None,
|
3811
|
+
limit: Optional[int] = None,
|
3739
3812
|
offset: int = 0,
|
3740
3813
|
) -> List[FlowRun]:
|
3741
3814
|
"""
|
@@ -3758,29 +3831,23 @@ class SyncPrefectClient:
|
|
3758
3831
|
of the flow runs
|
3759
3832
|
"""
|
3760
3833
|
body = {
|
3761
|
-
"flows": flow_filter.
|
3834
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
3762
3835
|
"flow_runs": (
|
3763
|
-
flow_run_filter.
|
3836
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
3764
3837
|
if flow_run_filter
|
3765
3838
|
else None
|
3766
3839
|
),
|
3767
3840
|
"task_runs": (
|
3768
|
-
task_run_filter.
|
3841
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
3769
3842
|
),
|
3770
3843
|
"deployments": (
|
3771
|
-
deployment_filter.
|
3772
|
-
if deployment_filter
|
3773
|
-
else None
|
3844
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
3774
3845
|
),
|
3775
3846
|
"work_pools": (
|
3776
|
-
work_pool_filter.
|
3777
|
-
if work_pool_filter
|
3778
|
-
else None
|
3847
|
+
work_pool_filter.model_dump(mode="json") if work_pool_filter else None
|
3779
3848
|
),
|
3780
3849
|
"work_pool_queues": (
|
3781
|
-
work_queue_filter.
|
3782
|
-
if work_queue_filter
|
3783
|
-
else None
|
3850
|
+
work_queue_filter.model_dump(mode="json") if work_queue_filter else None
|
3784
3851
|
),
|
3785
3852
|
"sort": sort,
|
3786
3853
|
"limit": limit,
|
@@ -3788,7 +3855,7 @@ class SyncPrefectClient:
|
|
3788
3855
|
}
|
3789
3856
|
|
3790
3857
|
response = self._client.post("/flow_runs/filter", json=body)
|
3791
|
-
return pydantic.
|
3858
|
+
return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
|
3792
3859
|
|
3793
3860
|
def set_flow_run_state(
|
3794
3861
|
self,
|
@@ -3814,7 +3881,7 @@ class SyncPrefectClient:
|
|
3814
3881
|
try:
|
3815
3882
|
response = self._client.post(
|
3816
3883
|
f"/flow_runs/{flow_run_id}/set_state",
|
3817
|
-
json=dict(state=state_create.
|
3884
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
3818
3885
|
)
|
3819
3886
|
except httpx.HTTPStatusError as e:
|
3820
3887
|
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
@@ -3822,13 +3889,28 @@ class SyncPrefectClient:
|
|
3822
3889
|
else:
|
3823
3890
|
raise
|
3824
3891
|
|
3825
|
-
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
|
+
)
|
3826
3907
|
|
3827
3908
|
def create_task_run(
|
3828
3909
|
self,
|
3829
3910
|
task: "TaskObject[P, R]",
|
3830
3911
|
flow_run_id: Optional[UUID],
|
3831
3912
|
dynamic_key: str,
|
3913
|
+
id: Optional[UUID] = None,
|
3832
3914
|
name: Optional[str] = None,
|
3833
3915
|
extra_tags: Optional[Iterable[str]] = None,
|
3834
3916
|
state: Optional[prefect.states.State[R]] = None,
|
@@ -3852,6 +3934,8 @@ class SyncPrefectClient:
|
|
3852
3934
|
task: The Task to run
|
3853
3935
|
flow_run_id: The flow run id with which to associate the task run
|
3854
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.
|
3855
3939
|
name: An optional name for the task run
|
3856
3940
|
extra_tags: an optional list of extra tags to apply to the task run in
|
3857
3941
|
addition to `task.tags`
|
@@ -3868,6 +3952,7 @@ class SyncPrefectClient:
|
|
3868
3952
|
state = prefect.states.Pending()
|
3869
3953
|
|
3870
3954
|
task_run_data = TaskRunCreate(
|
3955
|
+
id=id,
|
3871
3956
|
name=name,
|
3872
3957
|
flow_run_id=flow_run_id,
|
3873
3958
|
task_key=task.task_key,
|
@@ -3883,10 +3968,10 @@ class SyncPrefectClient:
|
|
3883
3968
|
task_inputs=task_inputs or {},
|
3884
3969
|
)
|
3885
3970
|
|
3886
|
-
|
3887
|
-
|
3888
|
-
)
|
3889
|
-
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())
|
3890
3975
|
|
3891
3976
|
def read_task_run(self, task_run_id: UUID) -> TaskRun:
|
3892
3977
|
"""
|
@@ -3898,8 +3983,14 @@ class SyncPrefectClient:
|
|
3898
3983
|
Returns:
|
3899
3984
|
a Task Run model representation of the task run
|
3900
3985
|
"""
|
3901
|
-
|
3902
|
-
|
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
|
3903
3994
|
|
3904
3995
|
def read_task_runs(
|
3905
3996
|
self,
|
@@ -3909,7 +4000,7 @@ class SyncPrefectClient:
|
|
3909
4000
|
task_run_filter: TaskRunFilter = None,
|
3910
4001
|
deployment_filter: DeploymentFilter = None,
|
3911
4002
|
sort: TaskRunSort = None,
|
3912
|
-
limit: int = None,
|
4003
|
+
limit: Optional[int] = None,
|
3913
4004
|
offset: int = 0,
|
3914
4005
|
) -> List[TaskRun]:
|
3915
4006
|
"""
|
@@ -3930,26 +4021,24 @@ class SyncPrefectClient:
|
|
3930
4021
|
of the task runs
|
3931
4022
|
"""
|
3932
4023
|
body = {
|
3933
|
-
"flows": flow_filter.
|
4024
|
+
"flows": flow_filter.model_dump(mode="json") if flow_filter else None,
|
3934
4025
|
"flow_runs": (
|
3935
|
-
flow_run_filter.
|
4026
|
+
flow_run_filter.model_dump(mode="json", exclude_unset=True)
|
3936
4027
|
if flow_run_filter
|
3937
4028
|
else None
|
3938
4029
|
),
|
3939
4030
|
"task_runs": (
|
3940
|
-
task_run_filter.
|
4031
|
+
task_run_filter.model_dump(mode="json") if task_run_filter else None
|
3941
4032
|
),
|
3942
4033
|
"deployments": (
|
3943
|
-
deployment_filter.
|
3944
|
-
if deployment_filter
|
3945
|
-
else None
|
4034
|
+
deployment_filter.model_dump(mode="json") if deployment_filter else None
|
3946
4035
|
),
|
3947
4036
|
"sort": sort,
|
3948
4037
|
"limit": limit,
|
3949
4038
|
"offset": offset,
|
3950
4039
|
}
|
3951
4040
|
response = self._client.post("/task_runs/filter", json=body)
|
3952
|
-
return pydantic.
|
4041
|
+
return pydantic.TypeAdapter(List[TaskRun]).validate_python(response.json())
|
3953
4042
|
|
3954
4043
|
def set_task_run_state(
|
3955
4044
|
self,
|
@@ -3973,9 +4062,9 @@ class SyncPrefectClient:
|
|
3973
4062
|
state_create.state_details.task_run_id = task_run_id
|
3974
4063
|
response = self._client.post(
|
3975
4064
|
f"/task_runs/{task_run_id}/set_state",
|
3976
|
-
json=dict(state=state_create.
|
4065
|
+
json=dict(state=state_create.model_dump(mode="json"), force=force),
|
3977
4066
|
)
|
3978
|
-
return OrchestrationResult.
|
4067
|
+
return OrchestrationResult.model_validate(response.json())
|
3979
4068
|
|
3980
4069
|
def read_task_run_states(self, task_run_id: UUID) -> List[prefect.states.State]:
|
3981
4070
|
"""
|
@@ -3990,4 +4079,123 @@ class SyncPrefectClient:
|
|
3990
4079
|
response = self._client.get(
|
3991
4080
|
"/task_run_states/", params=dict(task_run_id=str(task_run_id))
|
3992
4081
|
)
|
3993
|
-
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
|
+
)
|