prefect-client 3.1.14__py3-none-any.whl → 3.2.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/__main__.py +4 -0
- prefect/_experimental/lineage.py +40 -22
- prefect/_experimental/sla/objects.py +29 -1
- prefect/_internal/compatibility/deprecated.py +4 -4
- prefect/_internal/compatibility/migration.py +1 -1
- prefect/_internal/concurrency/calls.py +1 -2
- prefect/_internal/concurrency/cancellation.py +2 -4
- prefect/_internal/concurrency/services.py +1 -1
- prefect/_internal/concurrency/threads.py +3 -3
- prefect/_internal/schemas/bases.py +3 -11
- prefect/_internal/schemas/validators.py +36 -60
- prefect/_result_records.py +235 -0
- prefect/_version.py +3 -3
- prefect/agent.py +1 -0
- prefect/artifacts.py +408 -105
- prefect/automations.py +4 -8
- prefect/blocks/core.py +1 -1
- prefect/blocks/notifications.py +13 -8
- prefect/cache_policies.py +2 -0
- prefect/client/base.py +7 -8
- prefect/client/collections.py +3 -6
- prefect/client/orchestration/__init__.py +15 -263
- prefect/client/orchestration/_deployments/client.py +14 -6
- prefect/client/orchestration/_flow_runs/client.py +10 -6
- prefect/client/orchestration/_work_pools/__init__.py +0 -0
- prefect/client/orchestration/_work_pools/client.py +598 -0
- prefect/client/orchestration/base.py +9 -2
- prefect/client/schemas/actions.py +77 -3
- prefect/client/schemas/objects.py +22 -50
- prefect/client/schemas/schedules.py +11 -22
- prefect/client/types/flexible_schedule_list.py +2 -1
- prefect/context.py +2 -3
- prefect/deployments/base.py +13 -16
- prefect/deployments/flow_runs.py +1 -1
- prefect/deployments/runner.py +236 -47
- prefect/deployments/schedules.py +7 -1
- prefect/engine.py +4 -9
- prefect/events/clients.py +39 -0
- prefect/events/schemas/automations.py +4 -2
- prefect/events/utilities.py +15 -13
- prefect/exceptions.py +1 -1
- prefect/flow_engine.py +119 -0
- prefect/flow_runs.py +4 -8
- prefect/flows.py +282 -31
- prefect/infrastructure/__init__.py +1 -0
- prefect/infrastructure/base.py +1 -0
- prefect/infrastructure/provisioners/__init__.py +3 -6
- prefect/infrastructure/provisioners/coiled.py +3 -3
- prefect/infrastructure/provisioners/container_instance.py +1 -0
- prefect/infrastructure/provisioners/ecs.py +6 -6
- prefect/infrastructure/provisioners/modal.py +3 -3
- prefect/input/run_input.py +5 -7
- prefect/locking/filesystem.py +4 -3
- prefect/main.py +1 -1
- prefect/results.py +42 -249
- prefect/runner/runner.py +9 -4
- prefect/runner/server.py +5 -5
- prefect/runner/storage.py +12 -10
- prefect/runner/submit.py +2 -4
- prefect/runtime/task_run.py +37 -9
- prefect/schedules.py +231 -0
- prefect/serializers.py +5 -5
- prefect/settings/__init__.py +2 -1
- prefect/settings/base.py +3 -3
- prefect/settings/models/root.py +4 -0
- prefect/settings/models/server/services.py +50 -9
- prefect/settings/sources.py +4 -4
- prefect/states.py +42 -11
- prefect/task_engine.py +10 -10
- prefect/task_runners.py +11 -22
- prefect/task_worker.py +9 -9
- prefect/tasks.py +28 -45
- prefect/telemetry/bootstrap.py +4 -6
- prefect/telemetry/services.py +2 -4
- prefect/types/__init__.py +2 -1
- prefect/types/_datetime.py +28 -1
- prefect/utilities/_engine.py +0 -1
- prefect/utilities/asyncutils.py +4 -8
- prefect/utilities/collections.py +13 -22
- prefect/utilities/dispatch.py +2 -4
- prefect/utilities/dockerutils.py +6 -6
- prefect/utilities/importtools.py +1 -68
- prefect/utilities/names.py +1 -1
- prefect/utilities/processutils.py +3 -6
- prefect/utilities/pydantic.py +4 -6
- prefect/utilities/render_swagger.py +1 -1
- prefect/utilities/schema_tools/hydration.py +6 -5
- prefect/utilities/templating.py +21 -8
- prefect/utilities/visualization.py +2 -4
- prefect/workers/base.py +3 -3
- prefect/workers/block.py +1 -0
- prefect/workers/cloud.py +1 -0
- prefect/workers/process.py +1 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/METADATA +1 -1
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/RECORD +98 -93
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/top_level.txt +0 -0
prefect/artifacts.py
CHANGED
@@ -2,22 +2,27 @@
|
|
2
2
|
Interface for creating and reading artifacts.
|
3
3
|
"""
|
4
4
|
|
5
|
-
import
|
6
|
-
|
5
|
+
from __future__ import annotations
|
6
|
+
|
7
|
+
import json
|
7
8
|
import math
|
8
9
|
import warnings
|
9
|
-
from
|
10
|
+
from contextlib import nullcontext
|
11
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
10
12
|
from uuid import UUID
|
11
13
|
|
12
14
|
from typing_extensions import Self
|
13
15
|
|
16
|
+
from prefect._internal.compatibility.async_dispatch import async_dispatch
|
17
|
+
from prefect.client.orchestration import PrefectClient, get_client
|
14
18
|
from prefect.client.schemas.actions import ArtifactCreate as ArtifactRequest
|
15
19
|
from prefect.client.schemas.actions import ArtifactUpdate
|
16
20
|
from prefect.client.schemas.filters import ArtifactFilter, ArtifactFilterKey
|
21
|
+
from prefect.client.schemas.objects import Artifact as ArtifactResponse
|
17
22
|
from prefect.client.schemas.sorting import ArtifactSort
|
18
|
-
from prefect.
|
23
|
+
from prefect.context import MissingContextError, get_run_context
|
19
24
|
from prefect.logging.loggers import get_logger
|
20
|
-
from prefect.utilities.asyncutils import
|
25
|
+
from prefect.utilities.asyncutils import asyncnullcontext
|
21
26
|
from prefect.utilities.context import get_task_and_flow_run_ids
|
22
27
|
|
23
28
|
if TYPE_CHECKING:
|
@@ -25,10 +30,6 @@ if TYPE_CHECKING:
|
|
25
30
|
|
26
31
|
logger: "logging.Logger" = get_logger("artifacts")
|
27
32
|
|
28
|
-
if TYPE_CHECKING:
|
29
|
-
from prefect.client.orchestration import PrefectClient
|
30
|
-
from prefect.client.schemas.objects import Artifact as ArtifactResponse
|
31
|
-
|
32
33
|
|
33
34
|
class Artifact(ArtifactRequest):
|
34
35
|
"""
|
@@ -43,10 +44,47 @@ class Artifact(ArtifactRequest):
|
|
43
44
|
data: A JSON payload that allows for a result to be retrieved.
|
44
45
|
"""
|
45
46
|
|
46
|
-
|
47
|
-
|
47
|
+
async def acreate(
|
48
|
+
self,
|
49
|
+
client: "PrefectClient | None" = None,
|
50
|
+
) -> "ArtifactResponse":
|
51
|
+
"""
|
52
|
+
An async method to create an artifact.
|
53
|
+
|
54
|
+
Arguments:
|
55
|
+
client: The PrefectClient
|
56
|
+
|
57
|
+
Returns:
|
58
|
+
- The created artifact.
|
59
|
+
"""
|
60
|
+
|
61
|
+
local_client_context = asyncnullcontext(client) if client else get_client()
|
62
|
+
async with local_client_context as client:
|
63
|
+
task_run_id, flow_run_id = get_task_and_flow_run_ids()
|
64
|
+
|
65
|
+
try:
|
66
|
+
get_run_context()
|
67
|
+
except MissingContextError:
|
68
|
+
warnings.warn(
|
69
|
+
"Artifact creation outside of a flow or task run is deprecated and will be removed in a later version.",
|
70
|
+
FutureWarning,
|
71
|
+
)
|
72
|
+
|
73
|
+
return await client.create_artifact(
|
74
|
+
artifact=ArtifactRequest(
|
75
|
+
type=self.type,
|
76
|
+
key=self.key,
|
77
|
+
description=self.description,
|
78
|
+
task_run_id=self.task_run_id or task_run_id,
|
79
|
+
flow_run_id=self.flow_run_id or flow_run_id,
|
80
|
+
data=await self.aformat(),
|
81
|
+
)
|
82
|
+
)
|
83
|
+
|
84
|
+
@async_dispatch(acreate)
|
85
|
+
def create(
|
48
86
|
self: Self,
|
49
|
-
client:
|
87
|
+
client: "PrefectClient | None" = None,
|
50
88
|
) -> "ArtifactResponse":
|
51
89
|
"""
|
52
90
|
A method to create an artifact.
|
@@ -57,9 +95,9 @@ class Artifact(ArtifactRequest):
|
|
57
95
|
Returns:
|
58
96
|
- The created artifact.
|
59
97
|
"""
|
60
|
-
from prefect.context import MissingContextError, get_run_context
|
61
98
|
|
62
|
-
client
|
99
|
+
# Create sync client since this is a sync method.
|
100
|
+
sync_client = get_client(sync_client=True)
|
63
101
|
task_run_id, flow_run_id = get_task_and_flow_run_ids()
|
64
102
|
|
65
103
|
try:
|
@@ -70,35 +108,67 @@ class Artifact(ArtifactRequest):
|
|
70
108
|
FutureWarning,
|
71
109
|
)
|
72
110
|
|
73
|
-
return
|
111
|
+
return sync_client.create_artifact(
|
74
112
|
artifact=ArtifactRequest(
|
75
113
|
type=self.type,
|
76
114
|
key=self.key,
|
77
115
|
description=self.description,
|
78
116
|
task_run_id=self.task_run_id or task_run_id,
|
79
117
|
flow_run_id=self.flow_run_id or flow_run_id,
|
80
|
-
data=
|
118
|
+
data=cast(str, self.format(_sync=True)), # pyright: ignore[reportCallIssue] _sync is valid because .format is wrapped in async_dispatch
|
81
119
|
)
|
82
120
|
)
|
83
121
|
|
84
122
|
@classmethod
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
123
|
+
async def aget(
|
124
|
+
cls,
|
125
|
+
key: str | None = None,
|
126
|
+
client: "PrefectClient | None" = None,
|
127
|
+
) -> "ArtifactResponse | None":
|
128
|
+
"""
|
129
|
+
A async method to get an artifact.
|
130
|
+
|
131
|
+
Arguments:
|
132
|
+
key: The key of the artifact to get.
|
133
|
+
client: A client to use when calling the Prefect API.
|
134
|
+
|
135
|
+
Returns:
|
136
|
+
The artifact (if found).
|
137
|
+
"""
|
138
|
+
|
139
|
+
local_client_context = asyncnullcontext(client) if client else get_client()
|
140
|
+
async with local_client_context as client:
|
141
|
+
filter_key_value = None if key is None else [key]
|
142
|
+
artifacts = await client.read_artifacts(
|
143
|
+
limit=1,
|
144
|
+
sort=ArtifactSort.UPDATED_DESC,
|
145
|
+
artifact_filter=ArtifactFilter(
|
146
|
+
key=ArtifactFilterKey(any_=filter_key_value)
|
147
|
+
),
|
148
|
+
)
|
149
|
+
return None if not artifacts else artifacts[0]
|
150
|
+
|
151
|
+
@classmethod
|
152
|
+
@async_dispatch(aget)
|
153
|
+
def get(
|
154
|
+
cls, key: str | None = None, client: "PrefectClient | None" = None
|
155
|
+
) -> "ArtifactResponse | None":
|
89
156
|
"""
|
90
157
|
A method to get an artifact.
|
91
158
|
|
92
159
|
Arguments:
|
93
|
-
key
|
94
|
-
client
|
160
|
+
key: The key of the artifact to get.
|
161
|
+
client: A client to use when calling the Prefect API.
|
95
162
|
|
96
163
|
Returns:
|
97
|
-
|
164
|
+
The artifact (if found).
|
98
165
|
"""
|
99
|
-
|
166
|
+
|
167
|
+
# Create sync client since this is a sync method.
|
168
|
+
sync_client = get_client(sync_client=True)
|
169
|
+
|
100
170
|
filter_key_value = None if key is None else [key]
|
101
|
-
artifacts =
|
171
|
+
artifacts = sync_client.read_artifacts(
|
102
172
|
limit=1,
|
103
173
|
sort=ArtifactSort.UPDATED_DESC,
|
104
174
|
artifact_filter=ArtifactFilter(
|
@@ -108,41 +178,75 @@ class Artifact(ArtifactRequest):
|
|
108
178
|
return None if not artifacts else artifacts[0]
|
109
179
|
|
110
180
|
@classmethod
|
111
|
-
|
112
|
-
async def get_or_create(
|
181
|
+
async def aget_or_create(
|
113
182
|
cls,
|
114
|
-
key:
|
115
|
-
description:
|
116
|
-
data:
|
117
|
-
client:
|
183
|
+
key: str | None = None,
|
184
|
+
description: str | None = None,
|
185
|
+
data: dict[str, Any] | Any | None = None,
|
186
|
+
client: "PrefectClient | None" = None,
|
187
|
+
**kwargs: Any,
|
188
|
+
) -> tuple["ArtifactResponse", bool]:
|
189
|
+
"""
|
190
|
+
A async method to get or create an artifact.
|
191
|
+
|
192
|
+
Arguments:
|
193
|
+
key: The key of the artifact to get or create.
|
194
|
+
description: The description of the artifact to create.
|
195
|
+
data: The data of the artifact to create.
|
196
|
+
client: The PrefectClient
|
197
|
+
**kwargs: Additional keyword arguments to use when creating the artifact.
|
198
|
+
|
199
|
+
Returns:
|
200
|
+
The artifact, either retrieved or created.
|
201
|
+
"""
|
202
|
+
artifact = await cls.aget(key, client)
|
203
|
+
if artifact:
|
204
|
+
return artifact, False
|
205
|
+
|
206
|
+
new_artifact = cls(key=key, description=description, data=data, **kwargs)
|
207
|
+
created_artifact = await new_artifact.acreate(client)
|
208
|
+
return created_artifact, True
|
209
|
+
|
210
|
+
@classmethod
|
211
|
+
@async_dispatch(aget_or_create)
|
212
|
+
def get_or_create(
|
213
|
+
cls,
|
214
|
+
key: str | None = None,
|
215
|
+
description: str | None = None,
|
216
|
+
data: dict[str, Any] | Any | None = None,
|
217
|
+
client: "PrefectClient | None" = None,
|
118
218
|
**kwargs: Any,
|
119
219
|
) -> tuple["ArtifactResponse", bool]:
|
120
220
|
"""
|
121
221
|
A method to get or create an artifact.
|
122
222
|
|
123
223
|
Arguments:
|
124
|
-
key
|
125
|
-
description
|
126
|
-
data
|
127
|
-
client
|
224
|
+
key: The key of the artifact to get or create.
|
225
|
+
description: The description of the artifact to create.
|
226
|
+
data: The data of the artifact to create.
|
227
|
+
client: The PrefectClient
|
228
|
+
**kwargs: Additional keyword arguments to use when creating the artifact.
|
128
229
|
|
129
230
|
Returns:
|
130
|
-
|
231
|
+
The artifact, either retrieved or created.
|
131
232
|
"""
|
132
|
-
|
133
|
-
if TYPE_CHECKING:
|
134
|
-
assert asyncio.iscoroutine(artifact_coro)
|
135
|
-
artifact = await artifact_coro
|
233
|
+
artifact = cast(ArtifactResponse, cls.get(key, _sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .get is wrapped in async_dispatch
|
136
234
|
if artifact:
|
137
235
|
return artifact, False
|
138
236
|
|
139
237
|
new_artifact = cls(key=key, description=description, data=data, **kwargs)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
238
|
+
created_artifact = cast(
|
239
|
+
ArtifactResponse,
|
240
|
+
new_artifact.create(_sync=True), # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
241
|
+
)
|
242
|
+
return created_artifact, True
|
243
|
+
|
244
|
+
# TODO: Remove this when we remove async_dispatch because it doesn't need to be async
|
245
|
+
async def aformat(self) -> str | float | int | dict[str, Any]:
|
246
|
+
return json.dumps(self.data)
|
144
247
|
|
145
|
-
|
248
|
+
@async_dispatch(aformat)
|
249
|
+
def format(self) -> str | float | int | dict[str, Any]:
|
146
250
|
return json.dumps(self.data)
|
147
251
|
|
148
252
|
|
@@ -151,19 +255,30 @@ class LinkArtifact(Artifact):
|
|
151
255
|
link_text: Optional[str] = None
|
152
256
|
type: Optional[str] = "markdown"
|
153
257
|
|
154
|
-
|
258
|
+
def _format(self) -> str:
|
155
259
|
return (
|
156
260
|
f"[{self.link_text}]({self.link})"
|
157
261
|
if self.link_text
|
158
262
|
else f"[{self.link}]({self.link})"
|
159
263
|
)
|
160
264
|
|
265
|
+
async def aformat(self) -> str:
|
266
|
+
return self._format()
|
267
|
+
|
268
|
+
@async_dispatch(aformat)
|
269
|
+
def format(self) -> str:
|
270
|
+
return self._format()
|
271
|
+
|
161
272
|
|
162
273
|
class MarkdownArtifact(Artifact):
|
163
274
|
markdown: str
|
164
275
|
type: Optional[str] = "markdown"
|
165
276
|
|
166
|
-
async def
|
277
|
+
async def aformat(self) -> str:
|
278
|
+
return self.markdown
|
279
|
+
|
280
|
+
@async_dispatch(aformat)
|
281
|
+
def format(self) -> str:
|
167
282
|
return self.markdown
|
168
283
|
|
169
284
|
|
@@ -173,8 +288,8 @@ class TableArtifact(Artifact):
|
|
173
288
|
|
174
289
|
@classmethod
|
175
290
|
def _sanitize(
|
176
|
-
cls, item:
|
177
|
-
) ->
|
291
|
+
cls, item: dict[str, Any] | list[Any] | float
|
292
|
+
) -> dict[str, Any] | list[Any] | int | float | None:
|
178
293
|
"""
|
179
294
|
Sanitize NaN values in a given item.
|
180
295
|
The item can be a dict, list or float.
|
@@ -188,7 +303,11 @@ class TableArtifact(Artifact):
|
|
188
303
|
else:
|
189
304
|
return item
|
190
305
|
|
191
|
-
async def
|
306
|
+
async def aformat(self) -> str:
|
307
|
+
return json.dumps(self._sanitize(self.table))
|
308
|
+
|
309
|
+
@async_dispatch(aformat)
|
310
|
+
def format(self) -> str:
|
192
311
|
return json.dumps(self._sanitize(self.table))
|
193
312
|
|
194
313
|
|
@@ -196,7 +315,7 @@ class ProgressArtifact(Artifact):
|
|
196
315
|
progress: float
|
197
316
|
type: Optional[str] = "progress"
|
198
317
|
|
199
|
-
|
318
|
+
def _format(self) -> float:
|
200
319
|
# Ensure progress is between 0 and 100
|
201
320
|
min_progress = 0.0
|
202
321
|
max_progress = 100.0
|
@@ -209,6 +328,13 @@ class ProgressArtifact(Artifact):
|
|
209
328
|
|
210
329
|
return self.progress
|
211
330
|
|
331
|
+
async def aformat(self) -> float:
|
332
|
+
return self._format()
|
333
|
+
|
334
|
+
@async_dispatch(aformat)
|
335
|
+
def format(self) -> float:
|
336
|
+
return self._format()
|
337
|
+
|
212
338
|
|
213
339
|
class ImageArtifact(Artifact):
|
214
340
|
"""
|
@@ -221,11 +347,14 @@ class ImageArtifact(Artifact):
|
|
221
347
|
image_url: str
|
222
348
|
type: Optional[str] = "image"
|
223
349
|
|
224
|
-
async def
|
350
|
+
async def aformat(self) -> str:
|
351
|
+
return self.image_url
|
352
|
+
|
353
|
+
@async_dispatch(aformat)
|
354
|
+
def format(self) -> str:
|
225
355
|
"""
|
226
356
|
This method is used to format the artifact data so it can be properly sent
|
227
|
-
to the API when the .create() method is called.
|
228
|
-
method is awaited in the parent class.
|
357
|
+
to the API when the .create() method is called.
|
229
358
|
|
230
359
|
Returns:
|
231
360
|
str: The image URL.
|
@@ -233,13 +362,46 @@ class ImageArtifact(Artifact):
|
|
233
362
|
return self.image_url
|
234
363
|
|
235
364
|
|
236
|
-
|
237
|
-
|
365
|
+
async def acreate_link_artifact(
|
366
|
+
link: str,
|
367
|
+
link_text: str | None = None,
|
368
|
+
key: str | None = None,
|
369
|
+
description: str | None = None,
|
370
|
+
client: "PrefectClient | None" = None,
|
371
|
+
) -> UUID:
|
372
|
+
"""
|
373
|
+
Create a link artifact.
|
374
|
+
|
375
|
+
Arguments:
|
376
|
+
link: The link to create.
|
377
|
+
link_text: The link text.
|
378
|
+
key: A user-provided string identifier.
|
379
|
+
Required for the artifact to show in the Artifacts page in the UI.
|
380
|
+
The key must only contain lowercase letters, numbers, and dashes.
|
381
|
+
description: A user-specified description of the artifact.
|
382
|
+
|
383
|
+
|
384
|
+
Returns:
|
385
|
+
The table artifact ID.
|
386
|
+
"""
|
387
|
+
new_artifact = LinkArtifact(
|
388
|
+
key=key,
|
389
|
+
description=description,
|
390
|
+
link=link,
|
391
|
+
link_text=link_text,
|
392
|
+
)
|
393
|
+
artifact = await new_artifact.acreate(client)
|
394
|
+
|
395
|
+
return artifact.id
|
396
|
+
|
397
|
+
|
398
|
+
@async_dispatch(acreate_link_artifact)
|
399
|
+
def create_link_artifact(
|
238
400
|
link: str,
|
239
|
-
link_text:
|
240
|
-
key:
|
241
|
-
description:
|
242
|
-
client:
|
401
|
+
link_text: str | None = None,
|
402
|
+
key: str | None = None,
|
403
|
+
description: str | None = None,
|
404
|
+
client: "PrefectClient | None" = None,
|
243
405
|
) -> UUID:
|
244
406
|
"""
|
245
407
|
Create a link artifact.
|
@@ -262,19 +424,44 @@ async def create_link_artifact(
|
|
262
424
|
link=link,
|
263
425
|
link_text=link_text,
|
264
426
|
)
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
427
|
+
artifact = cast(ArtifactResponse, new_artifact.create(_sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
428
|
+
|
429
|
+
return artifact.id
|
430
|
+
|
431
|
+
|
432
|
+
async def acreate_markdown_artifact(
|
433
|
+
markdown: str,
|
434
|
+
key: str | None = None,
|
435
|
+
description: str | None = None,
|
436
|
+
) -> UUID:
|
437
|
+
"""
|
438
|
+
Create a markdown artifact.
|
439
|
+
|
440
|
+
Arguments:
|
441
|
+
markdown: The markdown to create.
|
442
|
+
key: A user-provided string identifier.
|
443
|
+
Required for the artifact to show in the Artifacts page in the UI.
|
444
|
+
The key must only contain lowercase letters, numbers, and dashes.
|
445
|
+
description: A user-specified description of the artifact.
|
446
|
+
|
447
|
+
Returns:
|
448
|
+
The table artifact ID.
|
449
|
+
"""
|
450
|
+
new_artifact = MarkdownArtifact(
|
451
|
+
key=key,
|
452
|
+
description=description,
|
453
|
+
markdown=markdown,
|
454
|
+
)
|
455
|
+
artifact = await new_artifact.acreate()
|
269
456
|
|
270
457
|
return artifact.id
|
271
458
|
|
272
459
|
|
273
|
-
@
|
274
|
-
|
460
|
+
@async_dispatch(acreate_markdown_artifact)
|
461
|
+
def create_markdown_artifact(
|
275
462
|
markdown: str,
|
276
|
-
key:
|
277
|
-
description:
|
463
|
+
key: str | None = None,
|
464
|
+
description: str | None = None,
|
278
465
|
) -> UUID:
|
279
466
|
"""
|
280
467
|
Create a markdown artifact.
|
@@ -294,19 +481,45 @@ async def create_markdown_artifact(
|
|
294
481
|
description=description,
|
295
482
|
markdown=markdown,
|
296
483
|
)
|
297
|
-
|
298
|
-
if TYPE_CHECKING:
|
299
|
-
assert asyncio.iscoroutine(create_coro)
|
300
|
-
artifact = await create_coro
|
484
|
+
artifact = cast(ArtifactResponse, new_artifact.create(_sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
301
485
|
|
302
486
|
return artifact.id
|
303
487
|
|
304
488
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
489
|
+
async def acreate_table_artifact(
|
490
|
+
table: dict[str, list[Any]] | list[dict[str, Any]] | list[list[Any]],
|
491
|
+
key: str | None = None,
|
492
|
+
description: str | None = None,
|
493
|
+
) -> UUID:
|
494
|
+
"""
|
495
|
+
Create a table artifact asynchronously.
|
496
|
+
|
497
|
+
Arguments:
|
498
|
+
table: The table to create.
|
499
|
+
key: A user-provided string identifier.
|
500
|
+
Required for the artifact to show in the Artifacts page in the UI.
|
501
|
+
The key must only contain lowercase letters, numbers, and dashes.
|
502
|
+
description: A user-specified description of the artifact.
|
503
|
+
|
504
|
+
Returns:
|
505
|
+
The table artifact ID.
|
506
|
+
"""
|
507
|
+
|
508
|
+
new_artifact = TableArtifact(
|
509
|
+
key=key,
|
510
|
+
description=description,
|
511
|
+
table=table,
|
512
|
+
)
|
513
|
+
artifact = await new_artifact.acreate()
|
514
|
+
|
515
|
+
return artifact.id
|
516
|
+
|
517
|
+
|
518
|
+
@async_dispatch(acreate_table_artifact)
|
519
|
+
def create_table_artifact(
|
520
|
+
table: dict[str, list[Any]] | list[dict[str, Any]] | list[list[Any]],
|
521
|
+
key: str | None = None,
|
522
|
+
description: str | None = None,
|
310
523
|
) -> UUID:
|
311
524
|
"""
|
312
525
|
Create a table artifact.
|
@@ -327,19 +540,45 @@ async def create_table_artifact(
|
|
327
540
|
description=description,
|
328
541
|
table=table,
|
329
542
|
)
|
330
|
-
|
331
|
-
if TYPE_CHECKING:
|
332
|
-
assert asyncio.iscoroutine(create_coro)
|
333
|
-
artifact = await create_coro
|
543
|
+
artifact = cast(ArtifactResponse, new_artifact.create(_sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
334
544
|
|
335
545
|
return artifact.id
|
336
546
|
|
337
547
|
|
338
|
-
|
339
|
-
async def create_progress_artifact(
|
548
|
+
async def acreate_progress_artifact(
|
340
549
|
progress: float,
|
341
|
-
key:
|
342
|
-
description:
|
550
|
+
key: str | None = None,
|
551
|
+
description: str | None = None,
|
552
|
+
) -> UUID:
|
553
|
+
"""
|
554
|
+
Create a progress artifact asynchronously.
|
555
|
+
|
556
|
+
Arguments:
|
557
|
+
progress: The percentage of progress represented by a float between 0 and 100.
|
558
|
+
key: A user-provided string identifier.
|
559
|
+
Required for the artifact to show in the Artifacts page in the UI.
|
560
|
+
The key must only contain lowercase letters, numbers, and dashes.
|
561
|
+
description: A user-specified description of the artifact.
|
562
|
+
|
563
|
+
Returns:
|
564
|
+
The progress artifact ID.
|
565
|
+
"""
|
566
|
+
|
567
|
+
new_artifact = ProgressArtifact(
|
568
|
+
key=key,
|
569
|
+
description=description,
|
570
|
+
progress=progress,
|
571
|
+
)
|
572
|
+
artifact = await new_artifact.acreate()
|
573
|
+
|
574
|
+
return artifact.id
|
575
|
+
|
576
|
+
|
577
|
+
@async_dispatch(acreate_progress_artifact)
|
578
|
+
def create_progress_artifact(
|
579
|
+
progress: float,
|
580
|
+
key: str | None = None,
|
581
|
+
description: str | None = None,
|
343
582
|
) -> UUID:
|
344
583
|
"""
|
345
584
|
Create a progress artifact.
|
@@ -360,20 +599,58 @@ async def create_progress_artifact(
|
|
360
599
|
description=description,
|
361
600
|
progress=progress,
|
362
601
|
)
|
363
|
-
|
364
|
-
if TYPE_CHECKING:
|
365
|
-
assert asyncio.iscoroutine(create_coro)
|
366
|
-
artifact = await create_coro
|
602
|
+
artifact = cast(ArtifactResponse, new_artifact.create(_sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
367
603
|
|
368
604
|
return artifact.id
|
369
605
|
|
370
606
|
|
371
|
-
|
372
|
-
|
607
|
+
async def aupdate_progress_artifact(
|
608
|
+
artifact_id: UUID,
|
609
|
+
progress: float,
|
610
|
+
description: str | None = None,
|
611
|
+
client: "PrefectClient | None" = None,
|
612
|
+
) -> UUID:
|
613
|
+
"""
|
614
|
+
Update a progress artifact asynchronously.
|
615
|
+
|
616
|
+
Arguments:
|
617
|
+
artifact_id: The ID of the artifact to update.
|
618
|
+
progress: The percentage of progress represented by a float between 0 and 100.
|
619
|
+
description: A user-specified description of the artifact.
|
620
|
+
|
621
|
+
Returns:
|
622
|
+
The progress artifact ID.
|
623
|
+
"""
|
624
|
+
|
625
|
+
local_client_context = nullcontext(client) if client else get_client()
|
626
|
+
async with local_client_context as client:
|
627
|
+
artifact = ProgressArtifact(
|
628
|
+
description=description,
|
629
|
+
progress=progress,
|
630
|
+
)
|
631
|
+
update = (
|
632
|
+
ArtifactUpdate(
|
633
|
+
description=artifact.description,
|
634
|
+
data=await artifact.aformat(),
|
635
|
+
)
|
636
|
+
if description
|
637
|
+
else ArtifactUpdate(data=await artifact.aformat())
|
638
|
+
)
|
639
|
+
|
640
|
+
await client.update_artifact(
|
641
|
+
artifact_id=artifact_id,
|
642
|
+
artifact=update,
|
643
|
+
)
|
644
|
+
|
645
|
+
return artifact_id
|
646
|
+
|
647
|
+
|
648
|
+
@async_dispatch(aupdate_progress_artifact)
|
649
|
+
def update_progress_artifact(
|
373
650
|
artifact_id: UUID,
|
374
651
|
progress: float,
|
375
|
-
description:
|
376
|
-
client:
|
652
|
+
description: str | None = None,
|
653
|
+
client: "PrefectClient | None" = None,
|
377
654
|
) -> UUID:
|
378
655
|
"""
|
379
656
|
Update a progress artifact.
|
@@ -387,7 +664,7 @@ async def update_progress_artifact(
|
|
387
664
|
The progress artifact ID.
|
388
665
|
"""
|
389
666
|
|
390
|
-
|
667
|
+
sync_client = get_client(sync_client=True)
|
391
668
|
|
392
669
|
artifact = ProgressArtifact(
|
393
670
|
description=description,
|
@@ -396,13 +673,13 @@ async def update_progress_artifact(
|
|
396
673
|
update = (
|
397
674
|
ArtifactUpdate(
|
398
675
|
description=artifact.description,
|
399
|
-
data=
|
676
|
+
data=cast(float, artifact.format(_sync=True)), # pyright: ignore[reportCallIssue] _sync is valid because .format is wrapped in async_dispatch
|
400
677
|
)
|
401
678
|
if description
|
402
|
-
else ArtifactUpdate(data=
|
679
|
+
else ArtifactUpdate(data=cast(float, artifact.format(_sync=True))) # pyright: ignore[reportCallIssue] _sync is valid because .format is wrapped in async_dispatch
|
403
680
|
)
|
404
681
|
|
405
|
-
|
682
|
+
sync_client.update_artifact(
|
406
683
|
artifact_id=artifact_id,
|
407
684
|
artifact=update,
|
408
685
|
)
|
@@ -410,11 +687,40 @@ async def update_progress_artifact(
|
|
410
687
|
return artifact_id
|
411
688
|
|
412
689
|
|
413
|
-
|
414
|
-
|
690
|
+
async def acreate_image_artifact(
|
691
|
+
image_url: str,
|
692
|
+
key: str | None = None,
|
693
|
+
description: str | None = None,
|
694
|
+
) -> UUID:
|
695
|
+
"""
|
696
|
+
Create an image artifact asynchronously.
|
697
|
+
|
698
|
+
Arguments:
|
699
|
+
image_url: The URL of the image to display.
|
700
|
+
key: A user-provided string identifier.
|
701
|
+
Required for the artifact to show in the Artifacts page in the UI.
|
702
|
+
The key must only contain lowercase letters, numbers, and dashes.
|
703
|
+
description: A user-specified description of the artifact.
|
704
|
+
|
705
|
+
Returns:
|
706
|
+
The image artifact ID.
|
707
|
+
"""
|
708
|
+
|
709
|
+
new_artifact = ImageArtifact(
|
710
|
+
key=key,
|
711
|
+
description=description,
|
712
|
+
image_url=image_url,
|
713
|
+
)
|
714
|
+
artifact = await new_artifact.acreate()
|
715
|
+
|
716
|
+
return artifact.id
|
717
|
+
|
718
|
+
|
719
|
+
@async_dispatch(acreate_image_artifact)
|
720
|
+
def create_image_artifact(
|
415
721
|
image_url: str,
|
416
|
-
key:
|
417
|
-
description:
|
722
|
+
key: str | None = None,
|
723
|
+
description: str | None = None,
|
418
724
|
) -> UUID:
|
419
725
|
"""
|
420
726
|
Create an image artifact.
|
@@ -435,9 +741,6 @@ async def create_image_artifact(
|
|
435
741
|
description=description,
|
436
742
|
image_url=image_url,
|
437
743
|
)
|
438
|
-
|
439
|
-
if TYPE_CHECKING:
|
440
|
-
assert asyncio.iscoroutine(create_coro)
|
441
|
-
artifact = await create_coro
|
744
|
+
artifact = cast(ArtifactResponse, new_artifact.create(_sync=True)) # pyright: ignore[reportCallIssue] _sync is valid because .create is wrapped in async_dispatch
|
442
745
|
|
443
746
|
return artifact.id
|