prefect-client 3.1.11__py3-none-any.whl → 3.1.13__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/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +92 -0
- prefect/_experimental/sla/objects.py +61 -0
- prefect/_internal/concurrency/services.py +2 -2
- prefect/_internal/concurrency/threads.py +6 -0
- prefect/_internal/retries.py +6 -3
- prefect/_internal/schemas/validators.py +6 -4
- prefect/_version.py +3 -3
- prefect/artifacts.py +4 -1
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +57 -31
- prefect/blocks/core.py +181 -82
- prefect/blocks/notifications.py +134 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +23 -22
- prefect/client/orchestration/__init__.py +103 -2006
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/client.py +16 -14
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/objects.py +5 -5
- prefect/client/utilities.py +3 -3
- prefect/context.py +15 -1
- prefect/deployments/base.py +13 -4
- prefect/deployments/flow_runs.py +5 -1
- prefect/deployments/runner.py +37 -1
- prefect/deployments/steps/core.py +1 -1
- prefect/deployments/steps/pull.py +8 -3
- prefect/deployments/steps/utility.py +2 -2
- prefect/docker/docker_image.py +13 -9
- prefect/engine.py +33 -11
- prefect/events/cli/automations.py +4 -4
- prefect/events/clients.py +17 -14
- prefect/events/schemas/automations.py +12 -8
- prefect/events/schemas/events.py +5 -1
- prefect/events/worker.py +1 -1
- prefect/filesystems.py +7 -3
- prefect/flow_engine.py +64 -47
- prefect/flows.py +128 -74
- prefect/futures.py +14 -7
- prefect/infrastructure/provisioners/__init__.py +2 -0
- prefect/infrastructure/provisioners/cloud_run.py +4 -4
- prefect/infrastructure/provisioners/coiled.py +249 -0
- prefect/infrastructure/provisioners/container_instance.py +4 -3
- prefect/infrastructure/provisioners/ecs.py +55 -43
- prefect/infrastructure/provisioners/modal.py +5 -4
- prefect/input/actions.py +5 -1
- prefect/input/run_input.py +157 -43
- prefect/logging/configuration.py +3 -3
- prefect/logging/filters.py +2 -2
- prefect/logging/formatters.py +15 -11
- prefect/logging/handlers.py +24 -14
- prefect/logging/highlighters.py +5 -5
- prefect/logging/loggers.py +28 -18
- prefect/logging/logging.yml +1 -1
- prefect/main.py +3 -1
- prefect/results.py +166 -86
- prefect/runner/runner.py +38 -29
- prefect/runner/server.py +3 -1
- prefect/runner/storage.py +18 -18
- prefect/runner/submit.py +19 -12
- prefect/runtime/deployment.py +15 -8
- prefect/runtime/flow_run.py +19 -6
- prefect/runtime/task_run.py +7 -3
- prefect/settings/base.py +17 -7
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +4 -3
- prefect/settings/models/cli.py +4 -3
- prefect/settings/models/client.py +7 -4
- prefect/settings/models/cloud.py +9 -3
- prefect/settings/models/deployments.py +4 -3
- prefect/settings/models/experiments.py +4 -8
- prefect/settings/models/flows.py +4 -3
- prefect/settings/models/internal.py +4 -3
- prefect/settings/models/logging.py +8 -6
- prefect/settings/models/results.py +4 -3
- prefect/settings/models/root.py +11 -16
- prefect/settings/models/runner.py +8 -5
- prefect/settings/models/server/api.py +6 -3
- prefect/settings/models/server/database.py +120 -25
- prefect/settings/models/server/deployments.py +4 -3
- prefect/settings/models/server/ephemeral.py +7 -4
- prefect/settings/models/server/events.py +6 -3
- prefect/settings/models/server/flow_run_graph.py +4 -3
- prefect/settings/models/server/root.py +4 -3
- prefect/settings/models/server/services.py +15 -12
- prefect/settings/models/server/tasks.py +7 -4
- prefect/settings/models/server/ui.py +4 -3
- prefect/settings/models/tasks.py +10 -5
- prefect/settings/models/testing.py +4 -3
- prefect/settings/models/worker.py +7 -4
- prefect/settings/profiles.py +13 -12
- prefect/settings/sources.py +20 -19
- prefect/states.py +74 -51
- prefect/task_engine.py +43 -33
- prefect/task_runners.py +85 -72
- prefect/task_runs.py +20 -11
- prefect/task_worker.py +14 -9
- prefect/tasks.py +36 -28
- prefect/telemetry/bootstrap.py +13 -9
- prefect/telemetry/run_telemetry.py +15 -13
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +3 -3
- prefect/types/__init__.py +3 -1
- prefect/utilities/_deprecated.py +38 -0
- prefect/utilities/engine.py +11 -4
- prefect/utilities/filesystem.py +2 -2
- prefect/utilities/generics.py +1 -1
- prefect/utilities/pydantic.py +21 -36
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +58 -33
- prefect/workers/process.py +20 -15
- prefect/workers/server.py +4 -5
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
File without changes
|
@@ -0,0 +1,92 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import TYPE_CHECKING
|
4
|
+
|
5
|
+
from prefect.client.orchestration.base import BaseAsyncClient, BaseClient
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from uuid import UUID
|
9
|
+
|
10
|
+
from prefect._experimental.sla.objects import SlaMergeResponse, SlaTypes
|
11
|
+
|
12
|
+
|
13
|
+
class SlaClient(BaseClient):
|
14
|
+
def apply_slas_for_deployment(
|
15
|
+
self, deployment_id: "UUID", slas: "list[SlaTypes]"
|
16
|
+
) -> "SlaMergeResponse":
|
17
|
+
"""
|
18
|
+
Applies service level agreements for a deployment. Performs matching by SLA name. If a SLA with the same name already exists, it will be updated. If a SLA with the same name does not exist, it will be created. Existing SLAs that are not in the list will be deleted.
|
19
|
+
Args:
|
20
|
+
deployment_id: The ID of the deployment to update SLAs for
|
21
|
+
slas: List of SLAs to associate with the deployment
|
22
|
+
Raises:
|
23
|
+
httpx.RequestError: if the SLAs were not updated for any reason
|
24
|
+
Returns:
|
25
|
+
SlaMergeResponse: The response from the backend, containing the names of the created, updated, and deleted SLAs
|
26
|
+
"""
|
27
|
+
resource_id = f"prefect.deployment.{deployment_id}"
|
28
|
+
|
29
|
+
for sla in slas:
|
30
|
+
sla.set_deployment_id(deployment_id)
|
31
|
+
|
32
|
+
slas_spec_list = [
|
33
|
+
sla.model_dump(mode="json", exclude_unset=True) for sla in slas
|
34
|
+
]
|
35
|
+
|
36
|
+
response = self.request(
|
37
|
+
"POST",
|
38
|
+
f"/slas/apply-resource-slas/{resource_id}",
|
39
|
+
json=slas_spec_list,
|
40
|
+
)
|
41
|
+
response.raise_for_status()
|
42
|
+
|
43
|
+
response_json = response.json()
|
44
|
+
|
45
|
+
from prefect._experimental.sla.objects import SlaMergeResponse
|
46
|
+
|
47
|
+
return SlaMergeResponse(
|
48
|
+
created=[sla.get("name") for sla in response_json.get("created")],
|
49
|
+
updated=[sla.get("name") for sla in response_json.get("updated")],
|
50
|
+
deleted=[sla.get("name") for sla in response_json.get("deleted")],
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
class SlaAsyncClient(BaseAsyncClient):
|
55
|
+
async def apply_slas_for_deployment(
|
56
|
+
self, deployment_id: "UUID", slas: "list[SlaTypes]"
|
57
|
+
) -> "UUID":
|
58
|
+
"""
|
59
|
+
Applies service level agreements for a deployment. Performs matching by SLA name. If a SLA with the same name already exists, it will be updated. If a SLA with the same name does not exist, it will be created. Existing SLAs that are not in the list will be deleted.
|
60
|
+
Args:
|
61
|
+
deployment_id: The ID of the deployment to update SLAs for
|
62
|
+
slas: List of SLAs to associate with the deployment
|
63
|
+
Raises:
|
64
|
+
httpx.RequestError: if the SLAs were not updated for any reason
|
65
|
+
Returns:
|
66
|
+
SlaMergeResponse: The response from the backend, containing the names of the created, updated, and deleted SLAs
|
67
|
+
"""
|
68
|
+
resource_id = f"prefect.deployment.{deployment_id}"
|
69
|
+
|
70
|
+
for sla in slas:
|
71
|
+
sla.set_deployment_id(deployment_id)
|
72
|
+
|
73
|
+
slas_spec_list = [
|
74
|
+
sla.model_dump(mode="json", exclude_unset=True) for sla in slas
|
75
|
+
]
|
76
|
+
|
77
|
+
response = await self.request(
|
78
|
+
"POST",
|
79
|
+
f"/slas/apply-resource-slas/{resource_id}",
|
80
|
+
json=slas_spec_list,
|
81
|
+
)
|
82
|
+
response.raise_for_status()
|
83
|
+
|
84
|
+
response_json = response.json()
|
85
|
+
|
86
|
+
from prefect._experimental.sla.objects import SlaMergeResponse
|
87
|
+
|
88
|
+
return SlaMergeResponse(
|
89
|
+
created=[sla.get("name") for sla in response_json.get("created")],
|
90
|
+
updated=[sla.get("name") for sla in response_json.get("updated")],
|
91
|
+
deleted=[sla.get("name") for sla in response_json.get("deleted")],
|
92
|
+
)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import abc
|
4
|
+
from typing import Literal, Optional, Union
|
5
|
+
from uuid import UUID
|
6
|
+
|
7
|
+
from pydantic import Field, PrivateAttr, computed_field
|
8
|
+
from typing_extensions import Self, TypeAlias
|
9
|
+
|
10
|
+
from prefect._internal.schemas.bases import PrefectBaseModel
|
11
|
+
|
12
|
+
|
13
|
+
class ServiceLevelAgreement(PrefectBaseModel, abc.ABC):
|
14
|
+
"""An ORM representation of a Service Level Agreement."""
|
15
|
+
|
16
|
+
_deployment_id: Optional[UUID] = PrivateAttr(default=None)
|
17
|
+
|
18
|
+
name: str = Field(
|
19
|
+
default=...,
|
20
|
+
description="The name of the SLA. Names must be unique on a per-deployment basis.",
|
21
|
+
)
|
22
|
+
severity: Literal["minor", "low", "moderate", "high", "critical"] = Field(
|
23
|
+
default="moderate",
|
24
|
+
description="The severity of the SLA.",
|
25
|
+
)
|
26
|
+
enabled: Optional[bool] = Field(
|
27
|
+
default=True,
|
28
|
+
description="Whether the SLA is enabled.",
|
29
|
+
)
|
30
|
+
|
31
|
+
def set_deployment_id(self, deployment_id: UUID) -> Self:
|
32
|
+
self._deployment_id = deployment_id
|
33
|
+
return self
|
34
|
+
|
35
|
+
@computed_field
|
36
|
+
@property
|
37
|
+
def owner_resource(self) -> Union[str, None]:
|
38
|
+
if self._deployment_id:
|
39
|
+
return f"prefect.deployment.{self._deployment_id}"
|
40
|
+
return None
|
41
|
+
|
42
|
+
|
43
|
+
class TimeToCompletionSla(ServiceLevelAgreement):
|
44
|
+
"""An SLA that triggers when a flow run takes longer than the specified duration."""
|
45
|
+
|
46
|
+
duration: int = Field(
|
47
|
+
default=...,
|
48
|
+
description="The maximum flow run duration allowed before the SLA is violated, expressed in seconds.",
|
49
|
+
)
|
50
|
+
|
51
|
+
|
52
|
+
class SlaMergeResponse(PrefectBaseModel):
|
53
|
+
"""A response object for the apply_slas_for_deployment method. Contains the names of the created, updated, and deleted SLAs."""
|
54
|
+
|
55
|
+
created: list[str]
|
56
|
+
updated: list[str]
|
57
|
+
deleted: list[str]
|
58
|
+
|
59
|
+
|
60
|
+
# Concrete SLA types
|
61
|
+
SlaTypes: TypeAlias = Union[TimeToCompletionSla]
|
@@ -32,7 +32,7 @@ class _QueueServiceBase(abc.ABC, Generic[T]):
|
|
32
32
|
self._task: Optional[asyncio.Task[None]] = None
|
33
33
|
self._stopped: bool = False
|
34
34
|
self._started: bool = False
|
35
|
-
self._key = hash(args)
|
35
|
+
self._key = hash((self.__class__, *args))
|
36
36
|
self._lock = threading.Lock()
|
37
37
|
self._queue_get_thread = WorkerThread(
|
38
38
|
# TODO: This thread should not need to be a daemon but when it is not, it
|
@@ -256,7 +256,7 @@ class _QueueServiceBase(abc.ABC, Generic[T]):
|
|
256
256
|
If an instance already exists with the given arguments, it will be returned.
|
257
257
|
"""
|
258
258
|
with cls._instance_lock:
|
259
|
-
key = hash(args)
|
259
|
+
key = hash((cls, *args))
|
260
260
|
if key not in cls._instances:
|
261
261
|
cls._instances[key] = cls._new_instance(*args)
|
262
262
|
|
@@ -2,6 +2,8 @@
|
|
2
2
|
Utilities for managing worker threads.
|
3
3
|
"""
|
4
4
|
|
5
|
+
from __future__ import annotations
|
6
|
+
|
5
7
|
import asyncio
|
6
8
|
import atexit
|
7
9
|
import concurrent.futures
|
@@ -197,6 +199,10 @@ class EventLoopThread(Portal):
|
|
197
199
|
def running(self) -> bool:
|
198
200
|
return not self._shutdown_event.is_set()
|
199
201
|
|
202
|
+
@property
|
203
|
+
def loop(self) -> asyncio.AbstractEventLoop | None:
|
204
|
+
return self._loop
|
205
|
+
|
200
206
|
def _entrypoint(self):
|
201
207
|
"""
|
202
208
|
Entrypoint for the thread.
|
prefect/_internal/retries.py
CHANGED
@@ -29,7 +29,7 @@ def retry_async_fn(
|
|
29
29
|
retry_on_exceptions: tuple[type[Exception], ...] = (Exception,),
|
30
30
|
operation_name: Optional[str] = None,
|
31
31
|
) -> Callable[
|
32
|
-
[Callable[P, Coroutine[Any, Any, R]]], Callable[P, Coroutine[Any, Any,
|
32
|
+
[Callable[P, Coroutine[Any, Any, R]]], Callable[P, Coroutine[Any, Any, R]]
|
33
33
|
]:
|
34
34
|
"""A decorator for retrying an async function.
|
35
35
|
|
@@ -48,9 +48,9 @@ def retry_async_fn(
|
|
48
48
|
|
49
49
|
def decorator(
|
50
50
|
func: Callable[P, Coroutine[Any, Any, R]],
|
51
|
-
) -> Callable[P, Coroutine[Any, Any,
|
51
|
+
) -> Callable[P, Coroutine[Any, Any, R]]:
|
52
52
|
@wraps(func)
|
53
|
-
async def wrapper(*args: P.args, **kwargs: P.kwargs) ->
|
53
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
54
54
|
name = operation_name or func.__name__
|
55
55
|
for attempt in range(max_attempts):
|
56
56
|
try:
|
@@ -67,6 +67,9 @@ def retry_async_fn(
|
|
67
67
|
f"Retrying in {delay:.2f} seconds..."
|
68
68
|
)
|
69
69
|
await asyncio.sleep(delay)
|
70
|
+
# Technically unreachable, but this raise helps pyright know that this function
|
71
|
+
# won't return None.
|
72
|
+
raise Exception(f"Function {name!r} failed after {max_attempts} attempts")
|
70
73
|
|
71
74
|
return wrapper
|
72
75
|
|
@@ -6,6 +6,8 @@ format.
|
|
6
6
|
This will be subject to consolidation and refactoring over the next few months.
|
7
7
|
"""
|
8
8
|
|
9
|
+
from __future__ import annotations
|
10
|
+
|
9
11
|
import os
|
10
12
|
import re
|
11
13
|
import urllib.parse
|
@@ -627,18 +629,18 @@ def validate_name_present_on_nonanonymous_blocks(values: M) -> M:
|
|
627
629
|
|
628
630
|
|
629
631
|
@overload
|
630
|
-
def
|
632
|
+
def validate_working_dir(v: str) -> Path:
|
631
633
|
...
|
632
634
|
|
633
635
|
|
634
636
|
@overload
|
635
|
-
def
|
637
|
+
def validate_working_dir(v: None) -> None:
|
636
638
|
...
|
637
639
|
|
638
640
|
|
639
|
-
def
|
641
|
+
def validate_working_dir(v: Optional[Path | str]) -> Optional[Path]:
|
640
642
|
"""Make sure that the working directory is formatted for the current platform."""
|
641
|
-
if v
|
643
|
+
if isinstance(v, str):
|
642
644
|
return relative_path_to_current_platform(v)
|
643
645
|
return v
|
644
646
|
|
prefect/_version.py
CHANGED
@@ -8,11 +8,11 @@ import json
|
|
8
8
|
|
9
9
|
version_json = '''
|
10
10
|
{
|
11
|
-
"date": "2025-01-
|
11
|
+
"date": "2025-01-17T08:46:53-0800",
|
12
12
|
"dirty": true,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "3.1.
|
14
|
+
"full-revisionid": "16e85ce3c281778f5ab6487a73377eed63bcac8b",
|
15
|
+
"version": "3.1.13"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
prefect/artifacts.py
CHANGED
@@ -20,7 +20,10 @@ from prefect.logging.loggers import get_logger
|
|
20
20
|
from prefect.utilities.asyncutils import sync_compatible
|
21
21
|
from prefect.utilities.context import get_task_and_flow_run_ids
|
22
22
|
|
23
|
-
|
23
|
+
if TYPE_CHECKING:
|
24
|
+
import logging
|
25
|
+
|
26
|
+
logger: "logging.Logger" = get_logger("artifacts")
|
24
27
|
|
25
28
|
if TYPE_CHECKING:
|
26
29
|
from prefect.client.orchestration import PrefectClient
|
prefect/automations.py
CHANGED
@@ -4,6 +4,7 @@ from uuid import UUID
|
|
4
4
|
from pydantic import Field
|
5
5
|
from typing_extensions import Self
|
6
6
|
|
7
|
+
from prefect._internal.compatibility.async_dispatch import async_dispatch
|
7
8
|
from prefect.client.orchestration import get_client
|
8
9
|
from prefect.events.actions import (
|
9
10
|
CallWebhook,
|
@@ -39,7 +40,6 @@ from prefect.events.schemas.automations import (
|
|
39
40
|
Trigger,
|
40
41
|
)
|
41
42
|
from prefect.exceptions import PrefectHTTPStatusError
|
42
|
-
from prefect.utilities.asyncutils import sync_compatible
|
43
43
|
|
44
44
|
__all__ = [
|
45
45
|
"AutomationCore",
|
@@ -78,11 +78,13 @@ __all__ = [
|
|
78
78
|
class Automation(AutomationCore):
|
79
79
|
id: Optional[UUID] = Field(default=None, description="The ID of this automation")
|
80
80
|
|
81
|
-
|
82
|
-
async def create(self: Self) -> Self:
|
81
|
+
async def acreate(self: Self) -> Self:
|
83
82
|
"""
|
84
|
-
|
83
|
+
Asynchronously create a new automation.
|
84
|
+
|
85
|
+
Examples:
|
85
86
|
|
87
|
+
```python
|
86
88
|
auto_to_create = Automation(
|
87
89
|
name="woodchonk",
|
88
90
|
trigger=EventTrigger(
|
@@ -97,20 +99,55 @@ class Automation(AutomationCore):
|
|
97
99
|
),
|
98
100
|
actions=[CancelFlowRun()]
|
99
101
|
)
|
100
|
-
created_automation = auto_to_create.
|
102
|
+
created_automation = await auto_to_create.acreate()
|
103
|
+
```
|
101
104
|
"""
|
102
105
|
async with get_client() as client:
|
103
106
|
automation = AutomationCore(**self.model_dump(exclude={"id"}))
|
104
107
|
self.id = await client.create_automation(automation=automation)
|
105
108
|
return self
|
106
109
|
|
107
|
-
@
|
108
|
-
|
110
|
+
@async_dispatch(acreate)
|
111
|
+
def create(self: Self) -> Self:
|
112
|
+
"""
|
113
|
+
Create a new automation.
|
114
|
+
|
115
|
+
Examples:
|
116
|
+
|
117
|
+
```python
|
118
|
+
auto_to_create = Automation(
|
119
|
+
name="woodchonk",
|
120
|
+
trigger=EventTrigger(
|
121
|
+
expect={"animal.walked"},
|
122
|
+
match={
|
123
|
+
"genus": "Marmota",
|
124
|
+
"species": "monax",
|
125
|
+
},
|
126
|
+
posture="Reactive",
|
127
|
+
threshold=3,
|
128
|
+
within=timedelta(seconds=10),
|
129
|
+
),
|
130
|
+
actions=[CancelFlowRun()]
|
131
|
+
)
|
132
|
+
created_automation = auto_to_create.create()
|
133
|
+
```
|
134
|
+
"""
|
135
|
+
with get_client(sync_client=True) as client:
|
136
|
+
automation = AutomationCore(**self.model_dump(exclude={"id"}))
|
137
|
+
self.id = client.create_automation(automation=automation)
|
138
|
+
return self
|
139
|
+
|
140
|
+
async def aupdate(self: Self) -> None:
|
109
141
|
"""
|
110
142
|
Updates an existing automation.
|
143
|
+
|
144
|
+
Examples:
|
145
|
+
|
146
|
+
```python
|
111
147
|
auto = Automation.read(id=123)
|
112
148
|
auto.name = "new name"
|
113
149
|
auto.update()
|
150
|
+
```
|
114
151
|
"""
|
115
152
|
assert self.id is not None
|
116
153
|
async with get_client() as client:
|
@@ -119,28 +156,51 @@ class Automation(AutomationCore):
|
|
119
156
|
)
|
120
157
|
await client.update_automation(automation_id=self.id, automation=automation)
|
121
158
|
|
159
|
+
@async_dispatch(aupdate)
|
160
|
+
def update(self: Self):
|
161
|
+
"""
|
162
|
+
Updates an existing automation.
|
163
|
+
|
164
|
+
Examples:
|
165
|
+
|
166
|
+
|
167
|
+
```python
|
168
|
+
auto = Automation.read(id=123)
|
169
|
+
auto.name = "new name"
|
170
|
+
auto.update()
|
171
|
+
```
|
172
|
+
"""
|
173
|
+
assert self.id is not None
|
174
|
+
with get_client(sync_client=True) as client:
|
175
|
+
automation = AutomationCore(
|
176
|
+
**self.model_dump(exclude={"id", "owner_resource"})
|
177
|
+
)
|
178
|
+
client.update_automation(automation_id=self.id, automation=automation)
|
179
|
+
|
122
180
|
@overload
|
123
181
|
@classmethod
|
124
|
-
async def
|
182
|
+
async def aread(cls, id: UUID, name: Optional[str] = ...) -> Self:
|
125
183
|
...
|
126
184
|
|
127
185
|
@overload
|
128
186
|
@classmethod
|
129
|
-
async def
|
187
|
+
async def aread(cls, id: None = None, name: str = ...) -> Self:
|
130
188
|
...
|
131
189
|
|
132
190
|
@classmethod
|
133
|
-
|
134
|
-
async def read(
|
135
|
-
cls, id: Optional[UUID] = None, name: Optional[str] = None
|
136
|
-
) -> Optional[Self]:
|
191
|
+
async def aread(cls, id: Optional[UUID] = None, name: Optional[str] = None) -> Self:
|
137
192
|
"""
|
138
|
-
|
139
|
-
automation = Automation.read(name="woodchonk")
|
193
|
+
Asynchronously read an automation by ID or name.
|
140
194
|
|
141
|
-
|
195
|
+
Examples:
|
142
196
|
|
143
|
-
|
197
|
+
```python
|
198
|
+
automation = await Automation.aread(name="woodchonk")
|
199
|
+
```
|
200
|
+
|
201
|
+
```python
|
202
|
+
automation = await Automation.aread(id=UUID("b3514963-02b1-47a5-93d1-6eeb131041cb"))
|
203
|
+
```
|
144
204
|
"""
|
145
205
|
if id and name:
|
146
206
|
raise ValueError("Only one of id or name can be provided")
|
@@ -162,15 +222,68 @@ class Automation(AutomationCore):
|
|
162
222
|
assert name is not None
|
163
223
|
automation = await client.read_automations_by_name(name=name)
|
164
224
|
if len(automation) > 0:
|
165
|
-
return cls(**automation[0].model_dump())
|
166
|
-
|
167
|
-
raise ValueError(f"Automation with name {name!r} not found")
|
225
|
+
return cls(**automation[0].model_dump())
|
226
|
+
raise ValueError(f"Automation with name {name!r} not found")
|
168
227
|
|
169
|
-
@
|
170
|
-
|
228
|
+
@overload
|
229
|
+
@classmethod
|
230
|
+
async def read(cls, id: UUID, name: Optional[str] = ...) -> Self:
|
231
|
+
...
|
232
|
+
|
233
|
+
@overload
|
234
|
+
@classmethod
|
235
|
+
async def read(cls, id: None = None, name: str = ...) -> Self:
|
236
|
+
...
|
237
|
+
|
238
|
+
@classmethod
|
239
|
+
@async_dispatch(aread)
|
240
|
+
def read(cls, id: Optional[UUID] = None, name: Optional[str] = None) -> Self:
|
171
241
|
"""
|
242
|
+
Read an automation by ID or name.
|
243
|
+
|
244
|
+
Examples:
|
245
|
+
|
246
|
+
```python
|
247
|
+
automation = Automation.read(name="woodchonk")
|
248
|
+
```
|
249
|
+
|
250
|
+
```python
|
251
|
+
automation = Automation.read(id=UUID("b3514963-02b1-47a5-93d1-6eeb131041cb"))
|
252
|
+
```
|
253
|
+
"""
|
254
|
+
if id and name:
|
255
|
+
raise ValueError("Only one of id or name can be provided")
|
256
|
+
if not id and not name:
|
257
|
+
raise ValueError("One of id or name must be provided")
|
258
|
+
with get_client(sync_client=True) as client:
|
259
|
+
if id:
|
260
|
+
try:
|
261
|
+
automation = client.read_automation(automation_id=id)
|
262
|
+
except PrefectHTTPStatusError as exc:
|
263
|
+
if exc.response.status_code == 404:
|
264
|
+
raise ValueError(f"Automation with ID {id!r} not found")
|
265
|
+
raise
|
266
|
+
if automation is None:
|
267
|
+
raise ValueError(f"Automation with ID {id!r} not found")
|
268
|
+
return cls(**automation.model_dump())
|
269
|
+
else:
|
270
|
+
if TYPE_CHECKING:
|
271
|
+
assert name is not None
|
272
|
+
automation = client.read_automations_by_name(name=name)
|
273
|
+
if len(automation) > 0:
|
274
|
+
return cls(**automation[0].model_dump())
|
275
|
+
raise ValueError(f"Automation with name {name!r} not found")
|
276
|
+
|
277
|
+
async def adelete(self: Self) -> bool:
|
278
|
+
"""
|
279
|
+
Asynchronously delete an automation.
|
280
|
+
|
281
|
+
Examples:
|
282
|
+
|
283
|
+
```python
|
172
284
|
auto = Automation.read(id = 123)
|
173
|
-
auto.
|
285
|
+
await auto.adelete()
|
286
|
+
```
|
174
287
|
"""
|
175
288
|
if self.id is None:
|
176
289
|
raise ValueError("Can't delete an automation without an id")
|
@@ -184,38 +297,131 @@ class Automation(AutomationCore):
|
|
184
297
|
return False
|
185
298
|
raise
|
186
299
|
|
187
|
-
@
|
188
|
-
|
300
|
+
@async_dispatch(adelete)
|
301
|
+
def delete(self: Self) -> bool:
|
302
|
+
"""
|
303
|
+
Delete an automation.
|
304
|
+
|
305
|
+
Examples:
|
306
|
+
|
307
|
+
```python
|
308
|
+
auto = Automation.read(id = 123)
|
309
|
+
auto.delete()
|
310
|
+
```
|
311
|
+
"""
|
312
|
+
if self.id is None:
|
313
|
+
raise ValueError("Can't delete an automation without an id")
|
314
|
+
|
315
|
+
with get_client(sync_client=True) as client:
|
316
|
+
try:
|
317
|
+
client.delete_automation(self.id)
|
318
|
+
return True
|
319
|
+
except PrefectHTTPStatusError as exc:
|
320
|
+
if exc.response.status_code == 404:
|
321
|
+
return False
|
322
|
+
raise
|
323
|
+
|
324
|
+
async def adisable(self: Self) -> bool:
|
325
|
+
"""
|
326
|
+
Asynchronously disable an automation.
|
327
|
+
|
328
|
+
Raises:
|
329
|
+
ValueError: If the automation does not have an id
|
330
|
+
PrefectHTTPStatusError: If the automation cannot be disabled
|
331
|
+
|
332
|
+
Example:
|
333
|
+
```python
|
334
|
+
auto = await Automation.aread(id = 123)
|
335
|
+
await auto.adisable()
|
336
|
+
```
|
337
|
+
"""
|
338
|
+
if self.id is None:
|
339
|
+
raise ValueError("Can't disable an automation without an id")
|
340
|
+
|
341
|
+
async with get_client() as client:
|
342
|
+
try:
|
343
|
+
await client.pause_automation(self.id)
|
344
|
+
return True
|
345
|
+
except PrefectHTTPStatusError as exc:
|
346
|
+
if exc.response.status_code == 404:
|
347
|
+
return False
|
348
|
+
raise
|
349
|
+
|
350
|
+
@async_dispatch(adisable)
|
351
|
+
def disable(self: Self) -> bool:
|
189
352
|
"""
|
190
353
|
Disable an automation.
|
354
|
+
|
355
|
+
|
356
|
+
Raises:
|
357
|
+
ValueError: If the automation does not have an id
|
358
|
+
PrefectHTTPStatusError: If the automation cannot be disabled
|
359
|
+
|
360
|
+
Example:
|
361
|
+
```python
|
191
362
|
auto = Automation.read(id = 123)
|
192
363
|
auto.disable()
|
364
|
+
```
|
193
365
|
"""
|
194
366
|
if self.id is None:
|
195
367
|
raise ValueError("Can't disable an automation without an id")
|
196
368
|
|
369
|
+
with get_client(sync_client=True) as client:
|
370
|
+
try:
|
371
|
+
client.pause_automation(self.id)
|
372
|
+
return True
|
373
|
+
except PrefectHTTPStatusError as exc:
|
374
|
+
if exc.response.status_code == 404:
|
375
|
+
return False
|
376
|
+
raise
|
377
|
+
|
378
|
+
async def aenable(self: Self) -> bool:
|
379
|
+
"""
|
380
|
+
Asynchronously enable an automation.
|
381
|
+
|
382
|
+
Raises:
|
383
|
+
ValueError: If the automation does not have an id
|
384
|
+
PrefectHTTPStatusError: If the automation cannot be enabled
|
385
|
+
|
386
|
+
Example:
|
387
|
+
```python
|
388
|
+
auto = await Automation.aread(id = 123)
|
389
|
+
await auto.aenable()
|
390
|
+
```
|
391
|
+
"""
|
392
|
+
if self.id is None:
|
393
|
+
raise ValueError("Can't enable an automation without an id")
|
394
|
+
|
197
395
|
async with get_client() as client:
|
198
396
|
try:
|
199
|
-
await client.
|
397
|
+
await client.resume_automation(self.id)
|
200
398
|
return True
|
201
399
|
except PrefectHTTPStatusError as exc:
|
202
400
|
if exc.response.status_code == 404:
|
203
401
|
return False
|
204
402
|
raise
|
205
403
|
|
206
|
-
@
|
207
|
-
|
404
|
+
@async_dispatch(aenable)
|
405
|
+
def enable(self: Self) -> bool:
|
208
406
|
"""
|
209
407
|
Enable an automation.
|
408
|
+
|
409
|
+
Raises:
|
410
|
+
ValueError: If the automation does not have an id
|
411
|
+
PrefectHTTPStatusError: If the automation cannot be enabled
|
412
|
+
|
413
|
+
Example:
|
414
|
+
```python
|
210
415
|
auto = Automation.read(id = 123)
|
211
416
|
auto.enable()
|
417
|
+
```
|
212
418
|
"""
|
213
419
|
if self.id is None:
|
214
420
|
raise ValueError("Can't enable an automation without an id")
|
215
421
|
|
216
|
-
|
422
|
+
with get_client(sync_client=True) as client:
|
217
423
|
try:
|
218
|
-
|
424
|
+
client.resume_automation(self.id)
|
219
425
|
return True
|
220
426
|
except PrefectHTTPStatusError as exc:
|
221
427
|
if exc.response.status_code == 404:
|
prefect/blocks/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# ensure core blocks are registered
|
2
2
|
|
3
|
-
import prefect.blocks.notifications
|
4
|
-
import prefect.blocks.system
|
5
|
-
import prefect.blocks.webhook
|
3
|
+
import prefect.blocks.notifications as notifications
|
4
|
+
import prefect.blocks.system as system
|
5
|
+
import prefect.blocks.webhook as webhook
|
6
6
|
|
7
7
|
__all__ = ["notifications", "system", "webhook"]
|