prefect-client 3.1.8__py3-none-any.whl → 3.1.10__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 +53 -59
- prefect/_internal/concurrency/services.py +6 -4
- prefect/_version.py +3 -3
- prefect/agent.py +3 -1
- prefect/artifacts.py +61 -74
- prefect/automations.py +27 -7
- prefect/client/cloud.py +0 -21
- prefect/client/schemas/objects.py +11 -0
- prefect/client/utilities.py +1 -15
- prefect/context.py +16 -27
- prefect/deployments/deployments.py +4 -2
- prefect/deployments/runner.py +3 -1
- prefect/engine.py +2 -1
- prefect/events/filters.py +2 -8
- prefect/exceptions.py +31 -41
- prefect/filesystems.py +2 -2
- prefect/flow_engine.py +2 -2
- prefect/flows.py +230 -186
- prefect/futures.py +42 -27
- prefect/infrastructure/__init__.py +3 -1
- prefect/infrastructure/base.py +3 -1
- prefect/locking/filesystem.py +8 -7
- prefect/locking/memory.py +5 -3
- prefect/locking/protocol.py +1 -1
- prefect/plugins.py +12 -10
- prefect/results.py +76 -19
- prefect/runner/runner.py +2 -3
- prefect/states.py +22 -10
- prefect/task_engine.py +1 -1
- prefect/telemetry/instrumentation.py +9 -10
- prefect/telemetry/processors.py +6 -6
- prefect/telemetry/services.py +68 -0
- prefect/utilities/engine.py +15 -1
- prefect/utilities/importtools.py +28 -21
- prefect/variables.py +2 -2
- prefect/workers/__init__.py +2 -0
- prefect/workers/base.py +6 -12
- prefect/workers/block.py +3 -1
- prefect/workers/cloud.py +3 -1
- {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/METADATA +1 -1
- {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/RECORD +44 -43
- {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/top_level.txt +0 -0
prefect/__init__.py
CHANGED
@@ -2,29 +2,14 @@
|
|
2
2
|
|
3
3
|
# Setup version and path constants
|
4
4
|
|
5
|
+
import sys
|
5
6
|
from . import _version
|
6
7
|
import importlib
|
7
8
|
import pathlib
|
8
|
-
from typing import TYPE_CHECKING, Any
|
9
|
-
|
10
|
-
__version_info__ = _version.get_versions()
|
11
|
-
__version__ = __version_info__["version"]
|
12
|
-
|
13
|
-
# The absolute path to this module
|
14
|
-
__module_path__ = pathlib.Path(__file__).parent
|
15
|
-
# The absolute path to the root of the repository, only valid for use during development
|
16
|
-
__development_base_path__ = __module_path__.parents[1]
|
17
|
-
|
18
|
-
# The absolute path to the built UI within the Python module, used by
|
19
|
-
# `prefect server start` to serve a dynamic build of the UI
|
20
|
-
__ui_static_subpath__ = __module_path__ / "server" / "ui_build"
|
21
|
-
|
22
|
-
# The absolute path to the built UI within the Python module
|
23
|
-
__ui_static_path__ = __module_path__ / "server" / "ui"
|
24
|
-
|
25
|
-
del _version, pathlib
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional, TypedDict, cast
|
26
10
|
|
27
11
|
if TYPE_CHECKING:
|
12
|
+
from importlib.machinery import ModuleSpec
|
28
13
|
from .main import (
|
29
14
|
allow_failure,
|
30
15
|
flow,
|
@@ -45,80 +30,89 @@ if TYPE_CHECKING:
|
|
45
30
|
suspend_flow_run,
|
46
31
|
)
|
47
32
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
"
|
53
|
-
|
54
|
-
|
55
|
-
|
33
|
+
__spec__: ModuleSpec
|
34
|
+
|
35
|
+
# Versioneer provides version information as dictionaries
|
36
|
+
# with these keys
|
37
|
+
class VersionInfo(TypedDict("_FullRevisionId", {"full-revisionid": str})):
|
38
|
+
version: str
|
39
|
+
dirty: Optional[bool]
|
40
|
+
error: Optional[str]
|
41
|
+
date: Optional[str]
|
56
42
|
|
57
|
-
|
43
|
+
|
44
|
+
__version_info__: "VersionInfo" = cast("VersionInfo", _version.get_versions())
|
45
|
+
__version__ = __version_info__["version"]
|
46
|
+
|
47
|
+
# The absolute path to this module
|
48
|
+
__module_path__: pathlib.Path = pathlib.Path(__file__).parent
|
49
|
+
# The absolute path to the root of the repository, only valid for use during development
|
50
|
+
__development_base_path__: pathlib.Path = __module_path__.parents[1]
|
51
|
+
|
52
|
+
# The absolute path to the built UI within the Python module, used by
|
53
|
+
# `prefect server start` to serve a dynamic build of the UI
|
54
|
+
__ui_static_subpath__: pathlib.Path = __module_path__ / "server" / "ui_build"
|
55
|
+
|
56
|
+
# The absolute path to the built UI within the Python module
|
57
|
+
__ui_static_path__: pathlib.Path = __module_path__ / "server" / "ui"
|
58
|
+
|
59
|
+
del _version, pathlib
|
60
|
+
|
61
|
+
_public_api: dict[str, tuple[Optional[str], str]] = {
|
58
62
|
"allow_failure": (__spec__.parent, ".main"),
|
63
|
+
"aserve": (__spec__.parent, ".main"),
|
64
|
+
"deploy": (__spec__.parent, ".main"),
|
59
65
|
"flow": (__spec__.parent, ".main"),
|
60
66
|
"Flow": (__spec__.parent, ".main"),
|
61
67
|
"get_client": (__spec__.parent, ".main"),
|
62
68
|
"get_run_logger": (__spec__.parent, ".main"),
|
69
|
+
"pause_flow_run": (__spec__.parent, ".main"),
|
70
|
+
"resume_flow_run": (__spec__.parent, ".main"),
|
71
|
+
"serve": (__spec__.parent, ".main"),
|
63
72
|
"State": (__spec__.parent, ".main"),
|
73
|
+
"suspend_flow_run": (__spec__.parent, ".main"),
|
64
74
|
"tags": (__spec__.parent, ".main"),
|
65
75
|
"task": (__spec__.parent, ".main"),
|
66
76
|
"Task": (__spec__.parent, ".main"),
|
67
77
|
"Transaction": (__spec__.parent, ".main"),
|
68
78
|
"unmapped": (__spec__.parent, ".main"),
|
69
|
-
"serve": (__spec__.parent, ".main"),
|
70
|
-
"aserve": (__spec__.parent, ".main"),
|
71
|
-
"deploy": (__spec__.parent, ".main"),
|
72
|
-
"pause_flow_run": (__spec__.parent, ".main"),
|
73
|
-
"resume_flow_run": (__spec__.parent, ".main"),
|
74
|
-
"suspend_flow_run": (__spec__.parent, ".main"),
|
75
79
|
}
|
76
80
|
|
77
81
|
# Declare API for type-checkers
|
78
82
|
__all__ = [
|
83
|
+
"__version__",
|
79
84
|
"allow_failure",
|
85
|
+
"aserve",
|
86
|
+
"deploy",
|
80
87
|
"flow",
|
81
88
|
"Flow",
|
82
89
|
"get_client",
|
83
90
|
"get_run_logger",
|
91
|
+
"pause_flow_run",
|
92
|
+
"resume_flow_run",
|
93
|
+
"serve",
|
84
94
|
"State",
|
95
|
+
"suspend_flow_run",
|
85
96
|
"tags",
|
86
97
|
"task",
|
87
98
|
"Task",
|
88
99
|
"Transaction",
|
89
100
|
"unmapped",
|
90
|
-
"serve",
|
91
|
-
"aserve",
|
92
|
-
"deploy",
|
93
|
-
"pause_flow_run",
|
94
|
-
"resume_flow_run",
|
95
|
-
"suspend_flow_run",
|
96
|
-
"__version_info__",
|
97
|
-
"__version__",
|
98
|
-
"__module_path__",
|
99
|
-
"__development_base_path__",
|
100
|
-
"__ui_static_subpath__",
|
101
|
-
"__ui_static_path__",
|
102
101
|
]
|
103
102
|
|
104
103
|
|
105
|
-
def __getattr__(attr_name: str) ->
|
106
|
-
if attr_name in _slots:
|
107
|
-
return _slots[attr_name]
|
104
|
+
def __getattr__(attr_name: str) -> Any:
|
108
105
|
try:
|
109
|
-
dynamic_attr
|
110
|
-
if dynamic_attr is None:
|
106
|
+
if (dynamic_attr := _public_api.get(attr_name)) is None:
|
111
107
|
return importlib.import_module(f".{attr_name}", package=__name__)
|
112
108
|
|
113
|
-
package,
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
if module_name == "__module__":
|
118
|
-
return import_module(f".{attr_name}", package=package)
|
109
|
+
package, mname = dynamic_attr
|
110
|
+
if mname == "__module__":
|
111
|
+
return importlib.import_module(f".{attr_name}", package=package)
|
119
112
|
else:
|
120
|
-
module = import_module(
|
113
|
+
module = importlib.import_module(mname, package=package)
|
121
114
|
return getattr(module, attr_name)
|
122
115
|
except ModuleNotFoundError as ex:
|
123
|
-
|
124
|
-
|
116
|
+
mname, _, attr = (ex.name or "").rpartition(".")
|
117
|
+
ctx = {"name": mname, "obj": attr} if sys.version_info >= (3, 10) else {}
|
118
|
+
raise AttributeError(f"module {mname} has no attribute {attr}", **ctx) from ex
|
@@ -225,7 +225,9 @@ class QueueService(abc.ABC, Generic[T]):
|
|
225
225
|
return future.result()
|
226
226
|
|
227
227
|
@classmethod
|
228
|
-
def drain_all(
|
228
|
+
def drain_all(
|
229
|
+
cls, timeout: Optional[float] = None, at_exit=True
|
230
|
+
) -> Union[Awaitable, None]:
|
229
231
|
"""
|
230
232
|
Stop all instances of the service and wait for all remaining work to be
|
231
233
|
completed.
|
@@ -237,7 +239,7 @@ class QueueService(abc.ABC, Generic[T]):
|
|
237
239
|
instances = tuple(cls._instances.values())
|
238
240
|
|
239
241
|
for instance in instances:
|
240
|
-
futures.append(instance._drain())
|
242
|
+
futures.append(instance._drain(at_exit=at_exit))
|
241
243
|
|
242
244
|
if get_running_loop() is not None:
|
243
245
|
return (
|
@@ -376,10 +378,10 @@ class BatchedQueueService(QueueService[T]):
|
|
376
378
|
@contextlib.contextmanager
|
377
379
|
def drain_on_exit(service: QueueService):
|
378
380
|
yield
|
379
|
-
service.drain_all()
|
381
|
+
service.drain_all(at_exit=True)
|
380
382
|
|
381
383
|
|
382
384
|
@contextlib.asynccontextmanager
|
383
385
|
async def drain_on_exit_async(service: QueueService):
|
384
386
|
yield
|
385
|
-
await service.drain_all()
|
387
|
+
await service.drain_all(at_exit=True)
|
prefect/_version.py
CHANGED
@@ -8,11 +8,11 @@ import json
|
|
8
8
|
|
9
9
|
version_json = '''
|
10
10
|
{
|
11
|
-
"date": "2024-12-
|
11
|
+
"date": "2024-12-24T22:58:40-0800",
|
12
12
|
"dirty": true,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "3.1.
|
14
|
+
"full-revisionid": "b11b56fc6143df2bd4c7018f7a974dc656fe9aa3",
|
15
|
+
"version": "3.1.10"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
prefect/agent.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
"""
|
2
2
|
2024-06-27: This surfaces an actionable error message for moved or removed objects in Prefect 3.0 upgrade.
|
3
3
|
"""
|
4
|
+
from typing import Any, Callable
|
5
|
+
|
4
6
|
from prefect._internal.compatibility.migration import getattr_migration
|
5
7
|
|
6
|
-
__getattr__ = getattr_migration(__name__)
|
8
|
+
__getattr__: Callable[[str], Any] = getattr_migration(__name__)
|
prefect/artifacts.py
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
Interface for creating and reading artifacts.
|
3
3
|
"""
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
import asyncio
|
7
6
|
import json # noqa: I001
|
8
7
|
import math
|
9
8
|
import warnings
|
10
|
-
from typing import TYPE_CHECKING, Any,
|
9
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
11
10
|
from uuid import UUID
|
12
11
|
|
12
|
+
from typing_extensions import Self
|
13
|
+
|
13
14
|
from prefect.client.schemas.actions import ArtifactCreate as ArtifactRequest
|
14
15
|
from prefect.client.schemas.actions import ArtifactUpdate
|
15
16
|
from prefect.client.schemas.filters import ArtifactFilter, ArtifactFilterKey
|
16
17
|
from prefect.client.schemas.sorting import ArtifactSort
|
17
|
-
from prefect.client.utilities import get_or_create_client
|
18
|
+
from prefect.client.utilities import get_or_create_client
|
18
19
|
from prefect.logging.loggers import get_logger
|
19
20
|
from prefect.utilities.asyncutils import sync_compatible
|
20
21
|
from prefect.utilities.context import get_task_and_flow_run_ids
|
@@ -22,8 +23,6 @@ from prefect.utilities.context import get_task_and_flow_run_ids
|
|
22
23
|
logger = get_logger("artifacts")
|
23
24
|
|
24
25
|
if TYPE_CHECKING:
|
25
|
-
from typing_extensions import Self
|
26
|
-
|
27
26
|
from prefect.client.orchestration import PrefectClient
|
28
27
|
from prefect.client.schemas.objects import Artifact as ArtifactResponse
|
29
28
|
|
@@ -43,7 +42,7 @@ class Artifact(ArtifactRequest):
|
|
43
42
|
|
44
43
|
@sync_compatible
|
45
44
|
async def create(
|
46
|
-
self:
|
45
|
+
self: Self,
|
47
46
|
client: Optional["PrefectClient"] = None,
|
48
47
|
) -> "ArtifactResponse":
|
49
48
|
"""
|
@@ -95,16 +94,15 @@ class Artifact(ArtifactRequest):
|
|
95
94
|
(ArtifactResponse, optional): The artifact (if found).
|
96
95
|
"""
|
97
96
|
client, _ = get_or_create_client(client)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
)
|
97
|
+
filter_key_value = None if key is None else [key]
|
98
|
+
artifacts = await client.read_artifacts(
|
99
|
+
limit=1,
|
100
|
+
sort=ArtifactSort.UPDATED_DESC,
|
101
|
+
artifact_filter=ArtifactFilter(
|
102
|
+
key=ArtifactFilterKey(any_=filter_key_value)
|
105
103
|
),
|
106
|
-
None,
|
107
104
|
)
|
105
|
+
return None if not artifacts else artifacts[0]
|
108
106
|
|
109
107
|
@classmethod
|
110
108
|
@sync_compatible
|
@@ -112,10 +110,10 @@ class Artifact(ArtifactRequest):
|
|
112
110
|
cls,
|
113
111
|
key: Optional[str] = None,
|
114
112
|
description: Optional[str] = None,
|
115
|
-
data: Optional[Union[
|
113
|
+
data: Optional[Union[dict[str, Any], Any]] = None,
|
116
114
|
client: Optional["PrefectClient"] = None,
|
117
115
|
**kwargs: Any,
|
118
|
-
) ->
|
116
|
+
) -> tuple["ArtifactResponse", bool]:
|
119
117
|
"""
|
120
118
|
A method to get or create an artifact.
|
121
119
|
|
@@ -128,18 +126,20 @@ class Artifact(ArtifactRequest):
|
|
128
126
|
Returns:
|
129
127
|
(ArtifactResponse): The artifact, either retrieved or created.
|
130
128
|
"""
|
131
|
-
|
129
|
+
artifact_coro = cls.get(key, client)
|
130
|
+
if TYPE_CHECKING:
|
131
|
+
assert asyncio.iscoroutine(artifact_coro)
|
132
|
+
artifact = await artifact_coro
|
132
133
|
if artifact:
|
133
134
|
return artifact, False
|
134
|
-
else:
|
135
|
-
return (
|
136
|
-
await cls(key=key, description=description, data=data, **kwargs).create(
|
137
|
-
client
|
138
|
-
),
|
139
|
-
True,
|
140
|
-
)
|
141
135
|
|
142
|
-
|
136
|
+
new_artifact = cls(key=key, description=description, data=data, **kwargs)
|
137
|
+
create_coro = new_artifact.create(client)
|
138
|
+
if TYPE_CHECKING:
|
139
|
+
assert asyncio.iscoroutine(create_coro)
|
140
|
+
return await create_coro, True
|
141
|
+
|
142
|
+
async def format(self) -> Optional[Union[dict[str, Any], Any]]:
|
143
143
|
return json.dumps(self.data)
|
144
144
|
|
145
145
|
|
@@ -165,13 +165,13 @@ class MarkdownArtifact(Artifact):
|
|
165
165
|
|
166
166
|
|
167
167
|
class TableArtifact(Artifact):
|
168
|
-
table: Union[
|
168
|
+
table: Union[dict[str, list[Any]], list[dict[str, Any]], list[list[Any]]]
|
169
169
|
type: Optional[str] = "table"
|
170
170
|
|
171
171
|
@classmethod
|
172
172
|
def _sanitize(
|
173
|
-
cls, item: Union[
|
174
|
-
) -> Union[
|
173
|
+
cls, item: Union[dict[str, Any], list[Any], float]
|
174
|
+
) -> Union[dict[str, Any], list[Any], int, float, None]:
|
175
175
|
"""
|
176
176
|
Sanitize NaN values in a given item.
|
177
177
|
The item can be a dict, list or float.
|
@@ -230,39 +230,6 @@ class ImageArtifact(Artifact):
|
|
230
230
|
return self.image_url
|
231
231
|
|
232
232
|
|
233
|
-
@inject_client
|
234
|
-
async def _create_artifact(
|
235
|
-
type: str,
|
236
|
-
key: Optional[str] = None,
|
237
|
-
description: Optional[str] = None,
|
238
|
-
data: Optional[Union[Dict[str, Any], Any]] = None,
|
239
|
-
client: Optional["PrefectClient"] = None,
|
240
|
-
) -> UUID:
|
241
|
-
"""
|
242
|
-
Helper function to create an artifact.
|
243
|
-
|
244
|
-
Arguments:
|
245
|
-
type: A string identifying the type of artifact.
|
246
|
-
key: A user-provided string identifier.
|
247
|
-
The key must only contain lowercase letters, numbers, and dashes.
|
248
|
-
description: A user-specified description of the artifact.
|
249
|
-
data: A JSON payload that allows for a result to be retrieved.
|
250
|
-
client: The PrefectClient
|
251
|
-
|
252
|
-
Returns:
|
253
|
-
- The table artifact ID.
|
254
|
-
"""
|
255
|
-
|
256
|
-
artifact = await Artifact(
|
257
|
-
type=type,
|
258
|
-
key=key,
|
259
|
-
description=description,
|
260
|
-
data=data,
|
261
|
-
).create(client)
|
262
|
-
|
263
|
-
return artifact.id
|
264
|
-
|
265
|
-
|
266
233
|
@sync_compatible
|
267
234
|
async def create_link_artifact(
|
268
235
|
link: str,
|
@@ -286,12 +253,16 @@ async def create_link_artifact(
|
|
286
253
|
Returns:
|
287
254
|
The table artifact ID.
|
288
255
|
"""
|
289
|
-
|
256
|
+
new_artifact = LinkArtifact(
|
290
257
|
key=key,
|
291
258
|
description=description,
|
292
259
|
link=link,
|
293
260
|
link_text=link_text,
|
294
|
-
)
|
261
|
+
)
|
262
|
+
create_coro = new_artifact.create(client)
|
263
|
+
if TYPE_CHECKING:
|
264
|
+
assert asyncio.iscoroutine(create_coro)
|
265
|
+
artifact = await create_coro
|
295
266
|
|
296
267
|
return artifact.id
|
297
268
|
|
@@ -315,18 +286,22 @@ async def create_markdown_artifact(
|
|
315
286
|
Returns:
|
316
287
|
The table artifact ID.
|
317
288
|
"""
|
318
|
-
|
289
|
+
new_artifact = MarkdownArtifact(
|
319
290
|
key=key,
|
320
291
|
description=description,
|
321
292
|
markdown=markdown,
|
322
|
-
)
|
293
|
+
)
|
294
|
+
create_coro = new_artifact.create()
|
295
|
+
if TYPE_CHECKING:
|
296
|
+
assert asyncio.iscoroutine(create_coro)
|
297
|
+
artifact = await create_coro
|
323
298
|
|
324
299
|
return artifact.id
|
325
300
|
|
326
301
|
|
327
302
|
@sync_compatible
|
328
303
|
async def create_table_artifact(
|
329
|
-
table: Union[
|
304
|
+
table: Union[dict[str, list[Any]], list[dict[str, Any]], list[list[Any]]],
|
330
305
|
key: Optional[str] = None,
|
331
306
|
description: Optional[str] = None,
|
332
307
|
) -> UUID:
|
@@ -344,11 +319,15 @@ async def create_table_artifact(
|
|
344
319
|
The table artifact ID.
|
345
320
|
"""
|
346
321
|
|
347
|
-
|
322
|
+
new_artifact = TableArtifact(
|
348
323
|
key=key,
|
349
324
|
description=description,
|
350
325
|
table=table,
|
351
|
-
)
|
326
|
+
)
|
327
|
+
create_coro = new_artifact.create()
|
328
|
+
if TYPE_CHECKING:
|
329
|
+
assert asyncio.iscoroutine(create_coro)
|
330
|
+
artifact = await create_coro
|
352
331
|
|
353
332
|
return artifact.id
|
354
333
|
|
@@ -373,11 +352,15 @@ async def create_progress_artifact(
|
|
373
352
|
The progress artifact ID.
|
374
353
|
"""
|
375
354
|
|
376
|
-
|
355
|
+
new_artifact = ProgressArtifact(
|
377
356
|
key=key,
|
378
357
|
description=description,
|
379
358
|
progress=progress,
|
380
|
-
)
|
359
|
+
)
|
360
|
+
create_coro = new_artifact.create()
|
361
|
+
if TYPE_CHECKING:
|
362
|
+
assert asyncio.iscoroutine(create_coro)
|
363
|
+
artifact = await create_coro
|
381
364
|
|
382
365
|
return artifact.id
|
383
366
|
|
@@ -387,7 +370,7 @@ async def update_progress_artifact(
|
|
387
370
|
artifact_id: UUID,
|
388
371
|
progress: float,
|
389
372
|
description: Optional[str] = None,
|
390
|
-
client: Optional[PrefectClient] = None,
|
373
|
+
client: Optional["PrefectClient"] = None,
|
391
374
|
) -> UUID:
|
392
375
|
"""
|
393
376
|
Update a progress artifact.
|
@@ -444,10 +427,14 @@ async def create_image_artifact(
|
|
444
427
|
The image artifact ID.
|
445
428
|
"""
|
446
429
|
|
447
|
-
|
430
|
+
new_artifact = ImageArtifact(
|
448
431
|
key=key,
|
449
432
|
description=description,
|
450
433
|
image_url=image_url,
|
451
|
-
)
|
434
|
+
)
|
435
|
+
create_coro = new_artifact.create()
|
436
|
+
if TYPE_CHECKING:
|
437
|
+
assert asyncio.iscoroutine(create_coro)
|
438
|
+
artifact = await create_coro
|
452
439
|
|
453
440
|
return artifact.id
|
prefect/automations.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import Optional,
|
1
|
+
from typing import TYPE_CHECKING, Optional, overload
|
2
2
|
from uuid import UUID
|
3
3
|
|
4
4
|
from pydantic import Field
|
@@ -112,17 +112,28 @@ class Automation(AutomationCore):
|
|
112
112
|
auto.name = "new name"
|
113
113
|
auto.update()
|
114
114
|
"""
|
115
|
+
assert self.id is not None
|
115
116
|
async with get_client() as client:
|
116
117
|
automation = AutomationCore(
|
117
118
|
**self.model_dump(exclude={"id", "owner_resource"})
|
118
119
|
)
|
119
120
|
await client.update_automation(automation_id=self.id, automation=automation)
|
120
121
|
|
122
|
+
@overload
|
123
|
+
@classmethod
|
124
|
+
async def read(cls, id: UUID, name: Optional[str] = ...) -> Self:
|
125
|
+
...
|
126
|
+
|
127
|
+
@overload
|
128
|
+
@classmethod
|
129
|
+
async def read(cls, id: None = None, name: str = ...) -> Optional[Self]:
|
130
|
+
...
|
131
|
+
|
121
132
|
@classmethod
|
122
133
|
@sync_compatible
|
123
134
|
async def read(
|
124
|
-
cls
|
125
|
-
) -> Self:
|
135
|
+
cls, id: Optional[UUID] = None, name: Optional[str] = None
|
136
|
+
) -> Optional[Self]:
|
126
137
|
"""
|
127
138
|
Read an automation by ID or name.
|
128
139
|
automation = Automation.read(name="woodchonk")
|
@@ -145,13 +156,13 @@ class Automation(AutomationCore):
|
|
145
156
|
raise
|
146
157
|
if automation is None:
|
147
158
|
raise ValueError(f"Automation with ID {id!r} not found")
|
148
|
-
return
|
159
|
+
return cls(**automation.model_dump())
|
149
160
|
else:
|
161
|
+
if TYPE_CHECKING:
|
162
|
+
assert name is not None
|
150
163
|
automation = await client.read_automations_by_name(name=name)
|
151
164
|
if len(automation) > 0:
|
152
|
-
return (
|
153
|
-
Automation(**automation[0].model_dump()) if automation else None
|
154
|
-
)
|
165
|
+
return cls(**automation[0].model_dump()) if automation else None
|
155
166
|
else:
|
156
167
|
raise ValueError(f"Automation with name {name!r} not found")
|
157
168
|
|
@@ -161,6 +172,9 @@ class Automation(AutomationCore):
|
|
161
172
|
auto = Automation.read(id = 123)
|
162
173
|
auto.delete()
|
163
174
|
"""
|
175
|
+
if self.id is None:
|
176
|
+
raise ValueError("Can't delete an automation without an id")
|
177
|
+
|
164
178
|
async with get_client() as client:
|
165
179
|
try:
|
166
180
|
await client.delete_automation(self.id)
|
@@ -177,6 +191,9 @@ class Automation(AutomationCore):
|
|
177
191
|
auto = Automation.read(id = 123)
|
178
192
|
auto.disable()
|
179
193
|
"""
|
194
|
+
if self.id is None:
|
195
|
+
raise ValueError("Can't disable an automation without an id")
|
196
|
+
|
180
197
|
async with get_client() as client:
|
181
198
|
try:
|
182
199
|
await client.pause_automation(self.id)
|
@@ -193,6 +210,9 @@ class Automation(AutomationCore):
|
|
193
210
|
auto = Automation.read(id = 123)
|
194
211
|
auto.enable()
|
195
212
|
"""
|
213
|
+
if self.id is None:
|
214
|
+
raise ValueError("Can't enable an automation without an id")
|
215
|
+
|
196
216
|
async with get_client() as client:
|
197
217
|
try:
|
198
218
|
await client.resume_automation(self.id)
|
prefect/client/cloud.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import re
|
2
2
|
from typing import Any, NoReturn, Optional, cast
|
3
|
-
from uuid import UUID
|
4
3
|
|
5
4
|
import anyio
|
6
5
|
import httpx
|
@@ -23,7 +22,6 @@ from prefect.settings import (
|
|
23
22
|
PREFECT_CLOUD_API_URL,
|
24
23
|
PREFECT_TESTING_UNIT_TEST_MODE,
|
25
24
|
)
|
26
|
-
from prefect.types import KeyValueLabels
|
27
25
|
|
28
26
|
PARSE_API_URL_REGEX = re.compile(r"accounts/(.{36})/workspaces/(.{36})")
|
29
27
|
|
@@ -160,25 +158,6 @@ class CloudClient:
|
|
160
158
|
response = await self.get(f"{self.account_base_url}/ip_allowlist/my_access")
|
161
159
|
return IPAllowlistMyAccessResponse.model_validate(response)
|
162
160
|
|
163
|
-
async def update_flow_run_labels(
|
164
|
-
self, flow_run_id: UUID, labels: KeyValueLabels
|
165
|
-
) -> httpx.Response:
|
166
|
-
"""
|
167
|
-
Update the labels for a flow run.
|
168
|
-
|
169
|
-
Args:
|
170
|
-
flow_run_id: The identifier for the flow run to update.
|
171
|
-
labels: A dictionary of labels to update for the flow run.
|
172
|
-
|
173
|
-
Returns:
|
174
|
-
an `httpx.Response` object from the PATCH request
|
175
|
-
"""
|
176
|
-
|
177
|
-
return await self._client.patch(
|
178
|
-
f"{self.workspace_base_url}/flow_runs/{flow_run_id}/labels",
|
179
|
-
json=labels,
|
180
|
-
)
|
181
|
-
|
182
161
|
async def __aenter__(self) -> Self:
|
183
162
|
await self._client.__aenter__()
|
184
163
|
return self
|
@@ -190,6 +190,8 @@ class StateDetails(PrefectBaseModel):
|
|
190
190
|
retriable: Optional[bool] = None
|
191
191
|
transition_id: Optional[UUID] = None
|
192
192
|
task_parameters_id: Optional[UUID] = None
|
193
|
+
# Captures the trace_id and span_id of the span where this state was created
|
194
|
+
traceparent: Optional[str] = None
|
193
195
|
|
194
196
|
|
195
197
|
def data_discriminator(x: Any) -> str:
|
@@ -237,6 +239,15 @@ class State(ObjectBaseModel, Generic[R]):
|
|
237
239
|
) -> Union[R, Exception]:
|
238
240
|
...
|
239
241
|
|
242
|
+
@overload
|
243
|
+
def result(
|
244
|
+
self: "State[R]",
|
245
|
+
raise_on_failure: bool = ...,
|
246
|
+
fetch: bool = ...,
|
247
|
+
retry_result_failure: bool = ...,
|
248
|
+
) -> Union[R, Exception]:
|
249
|
+
...
|
250
|
+
|
240
251
|
@deprecated.deprecated_parameter(
|
241
252
|
"fetch",
|
242
253
|
when=lambda fetch: fetch is not True,
|
prefect/client/utilities.py
CHANGED
@@ -7,7 +7,7 @@ Utilities for working with clients.
|
|
7
7
|
|
8
8
|
from collections.abc import Awaitable, Coroutine
|
9
9
|
from functools import wraps
|
10
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
10
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
11
11
|
|
12
12
|
from typing_extensions import Concatenate, ParamSpec, TypeGuard, TypeVar
|
13
13
|
|
@@ -71,23 +71,9 @@ def client_injector(
|
|
71
71
|
return wrapper
|
72
72
|
|
73
73
|
|
74
|
-
@overload
|
75
74
|
def inject_client(
|
76
75
|
fn: Callable[P, Coroutine[Any, Any, R]],
|
77
76
|
) -> Callable[P, Coroutine[Any, Any, R]]:
|
78
|
-
...
|
79
|
-
|
80
|
-
|
81
|
-
@overload
|
82
|
-
def inject_client(
|
83
|
-
fn: Callable[P, R],
|
84
|
-
) -> Callable[P, R]:
|
85
|
-
...
|
86
|
-
|
87
|
-
|
88
|
-
def inject_client(
|
89
|
-
fn: Callable[P, Union[Coroutine[Any, Any, R], R]],
|
90
|
-
) -> Callable[P, Union[Coroutine[Any, Any, R], R]]:
|
91
77
|
"""
|
92
78
|
Simple helper to provide a context managed client to an asynchronous function.
|
93
79
|
|