prefect-client 2.18.0__py3-none-any.whl → 2.18.2__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/_internal/schemas/fields.py +31 -12
- prefect/automations.py +162 -0
- prefect/blocks/core.py +1 -1
- prefect/blocks/notifications.py +2 -2
- prefect/blocks/system.py +2 -3
- prefect/client/orchestration.py +309 -30
- prefect/client/schemas/objects.py +11 -8
- prefect/client/schemas/sorting.py +9 -0
- prefect/client/utilities.py +25 -3
- prefect/concurrency/asyncio.py +11 -5
- prefect/concurrency/events.py +3 -3
- prefect/concurrency/services.py +1 -1
- prefect/concurrency/sync.py +9 -5
- prefect/deployments/deployments.py +27 -18
- prefect/deployments/runner.py +34 -26
- prefect/engine.py +3 -1
- prefect/events/actions.py +2 -1
- prefect/events/cli/automations.py +207 -46
- prefect/events/clients.py +53 -20
- prefect/events/filters.py +31 -4
- prefect/events/instrument.py +40 -40
- prefect/events/related.py +2 -1
- prefect/events/schemas/automations.py +52 -7
- prefect/events/schemas/deployment_triggers.py +16 -228
- prefect/events/schemas/events.py +18 -11
- prefect/events/schemas/labelling.py +1 -1
- prefect/events/utilities.py +1 -1
- prefect/events/worker.py +10 -7
- prefect/flows.py +42 -24
- prefect/input/actions.py +9 -9
- prefect/input/run_input.py +51 -37
- prefect/new_flow_engine.py +444 -0
- prefect/new_task_engine.py +488 -0
- prefect/results.py +3 -2
- prefect/runner/runner.py +3 -2
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +45 -4
- prefect/settings.py +47 -0
- prefect/states.py +25 -19
- prefect/tasks.py +146 -19
- prefect/utilities/asyncutils.py +41 -0
- prefect/utilities/engine.py +6 -4
- prefect/utilities/schema_tools/validation.py +1 -1
- prefect/workers/process.py +2 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/METADATA +1 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/RECORD +48 -46
- prefect/concurrency/common.py +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/LICENSE +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/WHEEL +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ from typing import Optional
|
|
3
3
|
from uuid import UUID
|
4
4
|
|
5
5
|
import pendulum
|
6
|
+
from typing_extensions import TypeAlias
|
6
7
|
|
7
8
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
8
9
|
|
@@ -12,19 +13,37 @@ else:
|
|
12
13
|
from pydantic import BaseModel, Field
|
13
14
|
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
# Rather than subclassing pendulum.DateTime to add our pydantic-specific validation,
|
17
|
+
# which will lead to a lot of funky typing issues, we'll just monkeypatch the pydantic
|
18
|
+
# validators onto the class. Retaining this type alias means that we can still use it
|
19
|
+
# as we have been in class definitions, also guaranteeing that we'll be applying these
|
20
|
+
# validators by importing this module.
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
DateTimeTZ: TypeAlias = pendulum.DateTime
|
23
|
+
|
24
|
+
|
25
|
+
def _datetime_patched_classmethod(function):
|
26
|
+
if hasattr(DateTimeTZ, function.__name__):
|
27
|
+
return function
|
28
|
+
setattr(DateTimeTZ, function.__name__, classmethod(function))
|
29
|
+
return function
|
30
|
+
|
31
|
+
|
32
|
+
@_datetime_patched_classmethod
|
33
|
+
def __get_validators__(cls):
|
34
|
+
yield getattr(cls, "validate")
|
35
|
+
|
36
|
+
|
37
|
+
@_datetime_patched_classmethod
|
38
|
+
def validate(cls, v) -> pendulum.DateTime:
|
39
|
+
if isinstance(v, str):
|
40
|
+
parsed = pendulum.parse(v)
|
41
|
+
assert isinstance(parsed, pendulum.DateTime)
|
42
|
+
return parsed
|
43
|
+
elif isinstance(v, datetime.datetime):
|
44
|
+
return pendulum.instance(v)
|
45
|
+
else:
|
46
|
+
raise ValueError("Unrecognized datetime.")
|
28
47
|
|
29
48
|
|
30
49
|
class CreatedBy(BaseModel):
|
prefect/automations.py
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
from uuid import UUID
|
3
|
+
|
4
|
+
from pydantic import Field
|
5
|
+
from typing_extensions import Self
|
6
|
+
|
7
|
+
from prefect.client.utilities import get_or_create_client
|
8
|
+
from prefect.events.schemas.automations import (
|
9
|
+
AutomationCore,
|
10
|
+
CompositeTrigger,
|
11
|
+
CompoundTrigger,
|
12
|
+
EventTrigger,
|
13
|
+
MetricTrigger,
|
14
|
+
MetricTriggerOperator,
|
15
|
+
MetricTriggerQuery,
|
16
|
+
Posture,
|
17
|
+
PrefectMetric,
|
18
|
+
ResourceSpecification,
|
19
|
+
ResourceTrigger,
|
20
|
+
SequenceTrigger,
|
21
|
+
Trigger,
|
22
|
+
)
|
23
|
+
from prefect.exceptions import PrefectHTTPStatusError
|
24
|
+
from prefect.utilities.asyncutils import sync_compatible
|
25
|
+
|
26
|
+
__all__ = [
|
27
|
+
"AutomationCore",
|
28
|
+
"EventTrigger",
|
29
|
+
"ResourceTrigger",
|
30
|
+
"Posture",
|
31
|
+
"Trigger",
|
32
|
+
"ResourceSpecification",
|
33
|
+
"MetricTriggerOperator",
|
34
|
+
"MetricTrigger",
|
35
|
+
"PrefectMetric",
|
36
|
+
"CompositeTrigger",
|
37
|
+
"SequenceTrigger",
|
38
|
+
"CompoundTrigger",
|
39
|
+
"MetricTriggerQuery",
|
40
|
+
]
|
41
|
+
|
42
|
+
|
43
|
+
class Automation(AutomationCore):
|
44
|
+
id: Optional[UUID] = Field(default=None, description="The ID of this automation")
|
45
|
+
|
46
|
+
@sync_compatible
|
47
|
+
async def create(self: Self) -> Self:
|
48
|
+
"""
|
49
|
+
Create a new automation.
|
50
|
+
|
51
|
+
auto_to_create = Automation(
|
52
|
+
name="woodchonk",
|
53
|
+
trigger=EventTrigger(
|
54
|
+
expect={"animal.walked"},
|
55
|
+
match={
|
56
|
+
"genus": "Marmota",
|
57
|
+
"species": "monax",
|
58
|
+
},
|
59
|
+
posture="Reactive",
|
60
|
+
threshold=3,
|
61
|
+
within=timedelta(seconds=10),
|
62
|
+
),
|
63
|
+
actions=[CancelFlowRun()]
|
64
|
+
)
|
65
|
+
created_automation = auto_to_create.create()
|
66
|
+
"""
|
67
|
+
client, _ = get_or_create_client()
|
68
|
+
automation = AutomationCore(**self.dict(exclude={"id"}))
|
69
|
+
self.id = await client.create_automation(automation=automation)
|
70
|
+
return self
|
71
|
+
|
72
|
+
@sync_compatible
|
73
|
+
async def update(self: Self):
|
74
|
+
"""
|
75
|
+
Updates an existing automation.
|
76
|
+
auto = Automation.read(id=123)
|
77
|
+
auto.name = "new name"
|
78
|
+
auto.update()
|
79
|
+
"""
|
80
|
+
|
81
|
+
client, _ = get_or_create_client()
|
82
|
+
automation = AutomationCore(**self.dict(exclude={"id", "owner_resource"}))
|
83
|
+
await client.update_automation(automation_id=self.id, automation=automation)
|
84
|
+
|
85
|
+
@classmethod
|
86
|
+
@sync_compatible
|
87
|
+
async def read(
|
88
|
+
cls: Self, id: Optional[UUID] = None, name: Optional[str] = None
|
89
|
+
) -> Self:
|
90
|
+
"""
|
91
|
+
Read an automation by ID or name.
|
92
|
+
automation = Automation.read(name="woodchonk")
|
93
|
+
|
94
|
+
or
|
95
|
+
|
96
|
+
automation = Automation.read(id=UUID("b3514963-02b1-47a5-93d1-6eeb131041cb"))
|
97
|
+
"""
|
98
|
+
if id and name:
|
99
|
+
raise ValueError("Only one of id or name can be provided")
|
100
|
+
if not id and not name:
|
101
|
+
raise ValueError("One of id or name must be provided")
|
102
|
+
client, _ = get_or_create_client()
|
103
|
+
if id:
|
104
|
+
try:
|
105
|
+
automation = await client.read_automation(automation_id=id)
|
106
|
+
except PrefectHTTPStatusError as exc:
|
107
|
+
if exc.response.status_code == 404:
|
108
|
+
raise ValueError(f"Automation with ID {id!r} not found")
|
109
|
+
return Automation(**automation.dict())
|
110
|
+
else:
|
111
|
+
automation = await client.read_automations_by_name(name=name)
|
112
|
+
if len(automation) > 0:
|
113
|
+
return Automation(**automation[0].dict()) if automation else None
|
114
|
+
else:
|
115
|
+
raise ValueError(f"Automation with name {name!r} not found")
|
116
|
+
|
117
|
+
@sync_compatible
|
118
|
+
async def delete(self: Self) -> bool:
|
119
|
+
"""
|
120
|
+
auto = Automation.read(id = 123)
|
121
|
+
auto.delete()
|
122
|
+
"""
|
123
|
+
try:
|
124
|
+
client, _ = get_or_create_client()
|
125
|
+
await client.delete_automation(self.id)
|
126
|
+
return True
|
127
|
+
except PrefectHTTPStatusError as exc:
|
128
|
+
if exc.response.status_code == 404:
|
129
|
+
return False
|
130
|
+
raise
|
131
|
+
|
132
|
+
@sync_compatible
|
133
|
+
async def disable(self: Self) -> bool:
|
134
|
+
"""
|
135
|
+
Disable an automation.
|
136
|
+
auto = Automation.read(id = 123)
|
137
|
+
auto.disable()
|
138
|
+
"""
|
139
|
+
try:
|
140
|
+
client, _ = get_or_create_client()
|
141
|
+
await client.pause_automation(self.id)
|
142
|
+
return True
|
143
|
+
except PrefectHTTPStatusError as exc:
|
144
|
+
if exc.response.status_code == 404:
|
145
|
+
return False
|
146
|
+
raise
|
147
|
+
|
148
|
+
@sync_compatible
|
149
|
+
async def enable(self: Self) -> bool:
|
150
|
+
"""
|
151
|
+
Enable an automation.
|
152
|
+
auto = Automation.read(id = 123)
|
153
|
+
auto.enable()
|
154
|
+
"""
|
155
|
+
try:
|
156
|
+
client, _ = get_or_create_client()
|
157
|
+
await client.resume_automation("asd")
|
158
|
+
return True
|
159
|
+
except PrefectHTTPStatusError as exc:
|
160
|
+
if exc.response.status_code == 404:
|
161
|
+
return False
|
162
|
+
raise
|
prefect/blocks/core.py
CHANGED
prefect/blocks/notifications.py
CHANGED
@@ -62,7 +62,7 @@ class AbstractAppriseNotificationBlock(NotificationBlock, ABC):
|
|
62
62
|
self._start_apprise_client(self.url)
|
63
63
|
|
64
64
|
@sync_compatible
|
65
|
-
@instrument_instance_method_call
|
65
|
+
@instrument_instance_method_call
|
66
66
|
async def notify(
|
67
67
|
self,
|
68
68
|
body: str,
|
@@ -717,7 +717,7 @@ class CustomWebhookNotificationBlock(NotificationBlock):
|
|
717
717
|
raise KeyError(f"{name}/{placeholder}")
|
718
718
|
|
719
719
|
@sync_compatible
|
720
|
-
@instrument_instance_method_call
|
720
|
+
@instrument_instance_method_call
|
721
721
|
async def notify(self, body: str, subject: Optional[str] = None):
|
722
722
|
import httpx
|
723
723
|
|
prefect/blocks/system.py
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
from typing import Any
|
2
2
|
|
3
|
-
import pendulum
|
4
|
-
|
5
3
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
6
4
|
|
7
5
|
if HAS_PYDANTIC_V2:
|
@@ -9,6 +7,7 @@ if HAS_PYDANTIC_V2:
|
|
9
7
|
else:
|
10
8
|
from pydantic import Field, SecretStr
|
11
9
|
|
10
|
+
from prefect._internal.schemas.fields import DateTimeTZ
|
12
11
|
from prefect.blocks.core import Block
|
13
12
|
|
14
13
|
|
@@ -76,7 +75,7 @@ class DateTime(Block):
|
|
76
75
|
_logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/8b3da9a6621e92108b8e6a75b82e15374e170ff7-48x48.png"
|
77
76
|
_documentation_url = "https://docs.prefect.io/api-ref/prefect/blocks/system/#prefect.blocks.system.DateTime"
|
78
77
|
|
79
|
-
value:
|
78
|
+
value: DateTimeTZ = Field(
|
80
79
|
default=...,
|
81
80
|
description="An ISO 8601-compatible datetime value.",
|
82
81
|
)
|