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.
Files changed (44) hide show
  1. prefect/__init__.py +53 -59
  2. prefect/_internal/concurrency/services.py +6 -4
  3. prefect/_version.py +3 -3
  4. prefect/agent.py +3 -1
  5. prefect/artifacts.py +61 -74
  6. prefect/automations.py +27 -7
  7. prefect/client/cloud.py +0 -21
  8. prefect/client/schemas/objects.py +11 -0
  9. prefect/client/utilities.py +1 -15
  10. prefect/context.py +16 -27
  11. prefect/deployments/deployments.py +4 -2
  12. prefect/deployments/runner.py +3 -1
  13. prefect/engine.py +2 -1
  14. prefect/events/filters.py +2 -8
  15. prefect/exceptions.py +31 -41
  16. prefect/filesystems.py +2 -2
  17. prefect/flow_engine.py +2 -2
  18. prefect/flows.py +230 -186
  19. prefect/futures.py +42 -27
  20. prefect/infrastructure/__init__.py +3 -1
  21. prefect/infrastructure/base.py +3 -1
  22. prefect/locking/filesystem.py +8 -7
  23. prefect/locking/memory.py +5 -3
  24. prefect/locking/protocol.py +1 -1
  25. prefect/plugins.py +12 -10
  26. prefect/results.py +76 -19
  27. prefect/runner/runner.py +2 -3
  28. prefect/states.py +22 -10
  29. prefect/task_engine.py +1 -1
  30. prefect/telemetry/instrumentation.py +9 -10
  31. prefect/telemetry/processors.py +6 -6
  32. prefect/telemetry/services.py +68 -0
  33. prefect/utilities/engine.py +15 -1
  34. prefect/utilities/importtools.py +28 -21
  35. prefect/variables.py +2 -2
  36. prefect/workers/__init__.py +2 -0
  37. prefect/workers/base.py +6 -12
  38. prefect/workers/block.py +3 -1
  39. prefect/workers/cloud.py +3 -1
  40. {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/METADATA +1 -1
  41. {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/RECORD +44 -43
  42. {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/LICENSE +0 -0
  43. {prefect_client-3.1.8.dist-info → prefect_client-3.1.10.dist-info}/WHEEL +0 -0
  44. {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
- _slots: dict[str, Any] = {
49
- "__version_info__": __version_info__,
50
- "__version__": __version__,
51
- "__module_path__": __module_path__,
52
- "__development_base_path__": __development_base_path__,
53
- "__ui_static_subpath__": __ui_static_subpath__,
54
- "__ui_static_path__": __ui_static_path__,
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
- _public_api: dict[str, tuple[str, str]] = {
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) -> object:
106
- if attr_name in _slots:
107
- return _slots[attr_name]
104
+ def __getattr__(attr_name: str) -> Any:
108
105
  try:
109
- dynamic_attr = _public_api.get(attr_name)
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, module_name = dynamic_attr
114
-
115
- from importlib import import_module
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(module_name, package=package)
113
+ module = importlib.import_module(mname, package=package)
121
114
  return getattr(module, attr_name)
122
115
  except ModuleNotFoundError as ex:
123
- module, _, attribute = ex.name.rpartition(".")
124
- raise AttributeError(f"module {module} has no attribute {attribute}") from ex
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(cls, timeout: Optional[float] = None) -> Union[Awaitable, None]:
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-17T10:20:43-0800",
11
+ "date": "2024-12-24T22:58:40-0800",
12
12
  "dirty": true,
13
13
  "error": null,
14
- "full-revisionid": "53a83ebc4a9a26a1d7cfd91d6be7cf6bd30e7f21",
15
- "version": "3.1.8"
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
- from __future__ import annotations
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, Dict, List, Optional, Tuple, Union
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, inject_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: "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
- return next(
99
- iter(
100
- await client.read_artifacts(
101
- limit=1,
102
- sort=ArtifactSort.UPDATED_DESC,
103
- artifact_filter=ArtifactFilter(key=ArtifactFilterKey(any_=[key])),
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[Dict[str, Any], Any]] = None,
113
+ data: Optional[Union[dict[str, Any], Any]] = None,
116
114
  client: Optional["PrefectClient"] = None,
117
115
  **kwargs: Any,
118
- ) -> Tuple["ArtifactResponse", bool]:
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
- artifact = await cls.get(key, client)
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
- async def format(self) -> Optional[Union[Dict[str, Any], Any]]:
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[Dict[str, List[Any]], List[Dict[str, Any]], List[List[Any]]]
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[Dict[str, Any], List[Any], float]
174
- ) -> Union[Dict[str, Any], List[Any], int, float, None]:
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
- artifact = await LinkArtifact(
256
+ new_artifact = LinkArtifact(
290
257
  key=key,
291
258
  description=description,
292
259
  link=link,
293
260
  link_text=link_text,
294
- ).create(client)
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
- artifact = await MarkdownArtifact(
289
+ new_artifact = MarkdownArtifact(
319
290
  key=key,
320
291
  description=description,
321
292
  markdown=markdown,
322
- ).create()
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[Dict[str, List[Any]], List[Dict[str, Any]], List[List[Any]]],
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
- artifact = await TableArtifact(
322
+ new_artifact = TableArtifact(
348
323
  key=key,
349
324
  description=description,
350
325
  table=table,
351
- ).create()
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
- artifact = await ProgressArtifact(
355
+ new_artifact = ProgressArtifact(
377
356
  key=key,
378
357
  description=description,
379
358
  progress=progress,
380
- ).create()
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
- artifact = await ImageArtifact(
430
+ new_artifact = ImageArtifact(
448
431
  key=key,
449
432
  description=description,
450
433
  image_url=image_url,
451
- ).create()
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, Type
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: Type[Self], id: Optional[UUID] = None, name: Optional[str] = None
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 Automation(**automation.model_dump())
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,
@@ -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, overload
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