prefect-client 2.18.1__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/automations.py +162 -0
- prefect/client/orchestration.py +29 -11
- prefect/client/schemas/objects.py +11 -8
- prefect/events/cli/automations.py +157 -34
- prefect/events/clients.py +3 -2
- prefect/events/filters.py +1 -1
- prefect/events/schemas/automations.py +2 -2
- prefect/events/schemas/deployment_triggers.py +1 -1
- prefect/events/schemas/events.py +11 -4
- prefect/events/schemas/labelling.py +1 -1
- prefect/flows.py +14 -11
- prefect/input/run_input.py +3 -1
- prefect/new_flow_engine.py +206 -55
- prefect/new_task_engine.py +159 -45
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +1 -1
- prefect/settings.py +21 -0
- prefect/tasks.py +134 -24
- prefect/utilities/asyncutils.py +16 -12
- prefect/workers/process.py +2 -1
- {prefect_client-2.18.1.dist-info → prefect_client-2.18.2.dist-info}/METADATA +1 -1
- {prefect_client-2.18.1.dist-info → prefect_client-2.18.2.dist-info}/RECORD +24 -23
- {prefect_client-2.18.1.dist-info → prefect_client-2.18.2.dist-info}/LICENSE +0 -0
- {prefect_client-2.18.1.dist-info → prefect_client-2.18.2.dist-info}/WHEEL +0 -0
- {prefect_client-2.18.1.dist-info → prefect_client-2.18.2.dist-info}/top_level.txt +0 -0
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/client/orchestration.py
CHANGED
@@ -623,12 +623,12 @@ class PrefectClient:
|
|
623
623
|
async def create_flow_run(
|
624
624
|
self,
|
625
625
|
flow: "FlowObject",
|
626
|
-
name: str = None,
|
626
|
+
name: Optional[str] = None,
|
627
627
|
parameters: Optional[Dict[str, Any]] = None,
|
628
628
|
context: Optional[Dict[str, Any]] = None,
|
629
|
-
tags: Iterable[str] = None,
|
630
|
-
parent_task_run_id: UUID = None,
|
631
|
-
state: "prefect.states.State" = None,
|
629
|
+
tags: Optional[Iterable[str]] = None,
|
630
|
+
parent_task_run_id: Optional[UUID] = None,
|
631
|
+
state: Optional["prefect.states.State"] = None,
|
632
632
|
) -> FlowRun:
|
633
633
|
"""
|
634
634
|
Create a flow run for a flow.
|
@@ -3161,6 +3161,16 @@ class PrefectClient:
|
|
3161
3161
|
|
3162
3162
|
return UUID(response.json()["id"])
|
3163
3163
|
|
3164
|
+
async def update_automation(self, automation_id: UUID, automation: AutomationCore):
|
3165
|
+
"""Updates an automation in Prefect Cloud."""
|
3166
|
+
if not self.server_type.supports_automations():
|
3167
|
+
self._raise_for_unsupported_automations()
|
3168
|
+
response = await self._client.put(
|
3169
|
+
f"/automations/{automation_id}",
|
3170
|
+
json=automation.dict(json_compatible=True, exclude_unset=True),
|
3171
|
+
)
|
3172
|
+
response.raise_for_status
|
3173
|
+
|
3164
3174
|
async def read_automations(self) -> List[Automation]:
|
3165
3175
|
if not self.server_type.supports_automations():
|
3166
3176
|
self._raise_for_unsupported_automations()
|
@@ -3169,16 +3179,24 @@ class PrefectClient:
|
|
3169
3179
|
response.raise_for_status()
|
3170
3180
|
return pydantic.parse_obj_as(List[Automation], response.json())
|
3171
3181
|
|
3172
|
-
async def find_automation(
|
3173
|
-
|
3174
|
-
|
3175
|
-
|
3176
|
-
|
3182
|
+
async def find_automation(
|
3183
|
+
self, id_or_name: Union[str, UUID], exit_if_not_found: bool = True
|
3184
|
+
) -> Optional[Automation]:
|
3185
|
+
if isinstance(id_or_name, str):
|
3186
|
+
try:
|
3187
|
+
id = UUID(id_or_name)
|
3188
|
+
except ValueError:
|
3189
|
+
id = None
|
3190
|
+
elif isinstance(id_or_name, UUID):
|
3191
|
+
id = id_or_name
|
3177
3192
|
|
3178
3193
|
if id:
|
3179
|
-
|
3180
|
-
|
3194
|
+
try:
|
3195
|
+
automation = await self.read_automation(id)
|
3181
3196
|
return automation
|
3197
|
+
except prefect.exceptions.HTTPStatusError as e:
|
3198
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
3199
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
3182
3200
|
|
3183
3201
|
automations = await self.read_automations()
|
3184
3202
|
|
@@ -53,6 +53,7 @@ if TYPE_CHECKING:
|
|
53
53
|
from prefect.deprecated.data_documents import DataDocument
|
54
54
|
from prefect.results import BaseResult
|
55
55
|
|
56
|
+
|
56
57
|
R = TypeVar("R")
|
57
58
|
|
58
59
|
|
@@ -120,20 +121,20 @@ class WorkQueueStatus(AutoEnum):
|
|
120
121
|
|
121
122
|
|
122
123
|
class StateDetails(PrefectBaseModel):
|
123
|
-
flow_run_id: UUID = None
|
124
|
-
task_run_id: UUID = None
|
124
|
+
flow_run_id: Optional[UUID] = None
|
125
|
+
task_run_id: Optional[UUID] = None
|
125
126
|
# for task runs that represent subflows, the subflow's run ID
|
126
|
-
child_flow_run_id: UUID = None
|
127
|
+
child_flow_run_id: Optional[UUID] = None
|
127
128
|
scheduled_time: DateTimeTZ = None
|
128
|
-
cache_key: str = None
|
129
|
+
cache_key: Optional[str] = None
|
129
130
|
cache_expiration: DateTimeTZ = None
|
130
131
|
untrackable_result: bool = False
|
131
132
|
pause_timeout: DateTimeTZ = None
|
132
133
|
pause_reschedule: bool = False
|
133
|
-
pause_key: str = None
|
134
|
+
pause_key: Optional[str] = None
|
134
135
|
run_input_keyset: Optional[Dict[str, str]] = None
|
135
|
-
refresh_cache: bool = None
|
136
|
-
retriable: bool = None
|
136
|
+
refresh_cache: Optional[bool] = None
|
137
|
+
retriable: Optional[bool] = None
|
137
138
|
transition_id: Optional[UUID] = None
|
138
139
|
task_parameters_id: Optional[UUID] = None
|
139
140
|
|
@@ -160,7 +161,9 @@ class State(ObjectBaseModel, Generic[R]):
|
|
160
161
|
def result(self: "State[R]", raise_on_failure: bool = False) -> Union[R, Exception]:
|
161
162
|
...
|
162
163
|
|
163
|
-
def result(
|
164
|
+
def result(
|
165
|
+
self, raise_on_failure: bool = True, fetch: Optional[bool] = None
|
166
|
+
) -> Union[R, Exception]:
|
164
167
|
"""
|
165
168
|
Retrieve the result attached to this state.
|
166
169
|
|
@@ -4,6 +4,7 @@ Command line interface for working with automations.
|
|
4
4
|
|
5
5
|
import functools
|
6
6
|
from typing import Optional
|
7
|
+
from uuid import UUID
|
7
8
|
|
8
9
|
import orjson
|
9
10
|
import typer
|
@@ -16,6 +17,8 @@ from prefect.cli._types import PrefectTyper
|
|
16
17
|
from prefect.cli._utilities import exit_with_error, exit_with_success
|
17
18
|
from prefect.cli.root import app
|
18
19
|
from prefect.client.orchestration import get_client
|
20
|
+
from prefect.events.schemas.automations import Automation
|
21
|
+
from prefect.exceptions import PrefectHTTPStatusError
|
19
22
|
|
20
23
|
automations_app = PrefectTyper(
|
21
24
|
name="automation",
|
@@ -98,55 +101,175 @@ async def ls():
|
|
98
101
|
|
99
102
|
@automations_app.command()
|
100
103
|
@requires_automations
|
101
|
-
async def inspect(
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
async def inspect(
|
105
|
+
name: Optional[str] = typer.Argument(None, help="An automation's name"),
|
106
|
+
id: Optional[str] = typer.Option(None, "--id", help="An automation's id"),
|
107
|
+
yaml: bool = typer.Option(False, "--yaml", help="Output as YAML"),
|
108
|
+
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
109
|
+
):
|
110
|
+
"""
|
111
|
+
Inspect an automation.
|
107
112
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
113
|
+
Arguments:
|
114
|
+
|
115
|
+
name: the name of the automation to inspect
|
116
|
+
|
117
|
+
id: the id of the automation to inspect
|
118
|
+
|
119
|
+
yaml: output as YAML
|
120
|
+
|
121
|
+
json: output as JSON
|
122
|
+
|
123
|
+
Examples:
|
124
|
+
|
125
|
+
$ prefect automation inspect "my-automation"
|
126
|
+
|
127
|
+
$ prefect automation inspect --id "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
128
|
+
|
129
|
+
$ prefect automation inspect "my-automation" --yaml
|
130
|
+
|
131
|
+
$ prefect automation inspect "my-automation" --json
|
132
|
+
"""
|
133
|
+
if not id and not name:
|
134
|
+
exit_with_error("Please provide either a name or an id.")
|
135
|
+
|
136
|
+
if name:
|
137
|
+
async with get_client() as client:
|
138
|
+
automation = await client.read_automations_by_name(name=name)
|
139
|
+
if not automation:
|
140
|
+
exit_with_error(f"Automation {name!r} not found.")
|
141
|
+
|
142
|
+
elif id:
|
143
|
+
async with get_client() as client:
|
144
|
+
try:
|
145
|
+
uuid_id = UUID(id)
|
146
|
+
automation = await client.read_automation(uuid_id)
|
147
|
+
except (PrefectHTTPStatusError, ValueError):
|
148
|
+
exit_with_error(f"Automation with id {id!r} not found.")
|
149
|
+
|
150
|
+
if yaml or json:
|
151
|
+
if isinstance(automation, list):
|
152
|
+
automation = [a.dict(json_compatible=True) for a in automation]
|
153
|
+
elif isinstance(automation, Automation):
|
154
|
+
automation = automation.dict(json_compatible=True)
|
155
|
+
if yaml:
|
156
|
+
app.console.print(pyyaml.dump(automation, sort_keys=False))
|
157
|
+
elif json:
|
158
|
+
app.console.print(
|
159
|
+
orjson.dumps(automation, option=orjson.OPT_INDENT_2).decode()
|
160
|
+
)
|
118
161
|
else:
|
119
162
|
app.console.print(Pretty(automation))
|
120
163
|
|
121
164
|
|
122
165
|
@automations_app.command(aliases=["enable"])
|
123
166
|
@requires_automations
|
124
|
-
async def resume(
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
167
|
+
async def resume(
|
168
|
+
name: Optional[str] = typer.Argument(None, help="An automation's name"),
|
169
|
+
id: Optional[str] = typer.Option(None, "--id", help="An automation's id"),
|
170
|
+
):
|
171
|
+
"""
|
172
|
+
Resume an automation.
|
130
173
|
|
131
|
-
|
132
|
-
await client.resume_automation(automation.id)
|
174
|
+
Arguments:
|
133
175
|
|
134
|
-
|
176
|
+
name: the name of the automation to resume
|
177
|
+
|
178
|
+
id: the id of the automation to resume
|
179
|
+
|
180
|
+
Examples:
|
181
|
+
|
182
|
+
$ prefect automation resume "my-automation"
|
183
|
+
|
184
|
+
$ prefect automation resume --id "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
185
|
+
"""
|
186
|
+
if not id and not name:
|
187
|
+
exit_with_error("Please provide either a name or an id.")
|
188
|
+
|
189
|
+
if name:
|
190
|
+
async with get_client() as client:
|
191
|
+
automation = await client.read_automations_by_name(name=name)
|
192
|
+
if not automation:
|
193
|
+
exit_with_error(
|
194
|
+
f"Automation with name {name!r} not found. You can also specify an id with the `--id` flag."
|
195
|
+
)
|
196
|
+
if len(automation) > 1:
|
197
|
+
if not typer.confirm(
|
198
|
+
f"Multiple automations found with name {name!r}. Do you want to resume all of them?",
|
199
|
+
default=False,
|
200
|
+
):
|
201
|
+
exit_with_error("Resume aborted.")
|
202
|
+
|
203
|
+
for a in automation:
|
204
|
+
await client.resume_automation(a.id)
|
205
|
+
exit_with_success(
|
206
|
+
f"Resumed automation(s) with name {name!r} and id(s) {', '.join([repr(str(a.id)) for a in automation])}."
|
207
|
+
)
|
208
|
+
|
209
|
+
elif id:
|
210
|
+
async with get_client() as client:
|
211
|
+
try:
|
212
|
+
uuid_id = UUID(id)
|
213
|
+
automation = await client.read_automation(uuid_id)
|
214
|
+
except (PrefectHTTPStatusError, ValueError):
|
215
|
+
exit_with_error(f"Automation with id {id!r} not found.")
|
216
|
+
await client.resume_automation(automation.id)
|
217
|
+
exit_with_success(f"Resumed automation with id {str(automation.id)!r}.")
|
135
218
|
|
136
219
|
|
137
220
|
@automations_app.command(aliases=["disable"])
|
138
221
|
@requires_automations
|
139
|
-
async def pause(
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
222
|
+
async def pause(
|
223
|
+
name: Optional[str] = typer.Argument(None, help="An automation's name"),
|
224
|
+
id: Optional[str] = typer.Option(None, "--id", help="An automation's id"),
|
225
|
+
):
|
226
|
+
"""
|
227
|
+
Pause an automation.
|
145
228
|
|
146
|
-
|
147
|
-
|
229
|
+
Arguments:
|
230
|
+
|
231
|
+
name: the name of the automation to pause
|
232
|
+
|
233
|
+
id: the id of the automation to pause
|
234
|
+
|
235
|
+
Examples:
|
236
|
+
|
237
|
+
$ prefect automation pause "my-automation"
|
238
|
+
|
239
|
+
$ prefect automation pause --id "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
|
240
|
+
"""
|
241
|
+
if not id and not name:
|
242
|
+
exit_with_error("Please provide either a name or an id.")
|
243
|
+
|
244
|
+
if name:
|
245
|
+
async with get_client() as client:
|
246
|
+
automation = await client.read_automations_by_name(name=name)
|
247
|
+
if not automation:
|
248
|
+
exit_with_error(
|
249
|
+
f"Automation with name {name!r} not found. You can also specify an id with the `--id` flag."
|
250
|
+
)
|
251
|
+
if len(automation) > 1:
|
252
|
+
if not typer.confirm(
|
253
|
+
f"Multiple automations found with name {name!r}. Do you want to pause all of them?",
|
254
|
+
default=False,
|
255
|
+
):
|
256
|
+
exit_with_error("Pause aborted.")
|
257
|
+
|
258
|
+
for a in automation:
|
259
|
+
await client.pause_automation(a.id)
|
260
|
+
exit_with_success(
|
261
|
+
f"Paused automation(s) with name {name!r} and id(s) {', '.join([repr(str(a.id)) for a in automation])}."
|
262
|
+
)
|
148
263
|
|
149
|
-
|
264
|
+
elif id:
|
265
|
+
async with get_client() as client:
|
266
|
+
try:
|
267
|
+
uuid_id = UUID(id)
|
268
|
+
automation = await client.read_automation(uuid_id)
|
269
|
+
except (PrefectHTTPStatusError, ValueError):
|
270
|
+
exit_with_error(f"Automation with id {id!r} not found.")
|
271
|
+
await client.pause_automation(automation.id)
|
272
|
+
exit_with_success(f"Paused automation with id {str(automation.id)!r}.")
|
150
273
|
|
151
274
|
|
152
275
|
@automations_app.command()
|
prefect/events/clients.py
CHANGED
@@ -11,6 +11,7 @@ from typing import (
|
|
11
11
|
Optional,
|
12
12
|
Tuple,
|
13
13
|
Type,
|
14
|
+
cast,
|
14
15
|
)
|
15
16
|
from uuid import UUID
|
16
17
|
|
@@ -377,7 +378,7 @@ SEEN_EVENTS_TTL = 120
|
|
377
378
|
|
378
379
|
class PrefectEventSubscriber:
|
379
380
|
"""
|
380
|
-
Subscribes to a Prefect
|
381
|
+
Subscribes to a Prefect event stream, yielding events as they occur.
|
381
382
|
|
382
383
|
Example:
|
383
384
|
|
@@ -412,7 +413,7 @@ class PrefectEventSubscriber:
|
|
412
413
|
the client should attempt to reconnect
|
413
414
|
"""
|
414
415
|
if not api_url:
|
415
|
-
api_url = PREFECT_API_URL.value()
|
416
|
+
api_url = cast(str, PREFECT_API_URL.value())
|
416
417
|
self._api_key = None
|
417
418
|
|
418
419
|
from prefect.events.filters import EventFilter
|
prefect/events/filters.py
CHANGED
@@ -43,7 +43,7 @@ class AutomationFilter(PrefectBaseModel):
|
|
43
43
|
)
|
44
44
|
|
45
45
|
|
46
|
-
class EventDataFilter(PrefectBaseModel, extra="forbid"):
|
46
|
+
class EventDataFilter(PrefectBaseModel, extra="forbid"): # type: ignore[call-arg]
|
47
47
|
"""A base class for filtering event data."""
|
48
48
|
|
49
49
|
_top_level_filter: "EventFilter | None" = PrivateAttr(None)
|
@@ -39,7 +39,7 @@ class Posture(AutoEnum):
|
|
39
39
|
Metric = "Metric"
|
40
40
|
|
41
41
|
|
42
|
-
class Trigger(PrefectBaseModel, abc.ABC, extra="ignore"):
|
42
|
+
class Trigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-arg]
|
43
43
|
"""
|
44
44
|
Base class describing a set of criteria that must be satisfied in order to trigger
|
45
45
|
an automation.
|
@@ -391,7 +391,7 @@ CompoundTrigger.update_forward_refs()
|
|
391
391
|
SequenceTrigger.update_forward_refs()
|
392
392
|
|
393
393
|
|
394
|
-
class AutomationCore(PrefectBaseModel, extra="ignore"):
|
394
|
+
class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg]
|
395
395
|
"""Defines an action a user wants to take when a certain number of events
|
396
396
|
do or don't happen to the matching resources"""
|
397
397
|
|
@@ -48,7 +48,7 @@ from .automations import (
|
|
48
48
|
from .events import ResourceSpecification
|
49
49
|
|
50
50
|
|
51
|
-
class BaseDeploymentTrigger(PrefectBaseModel, abc.ABC, extra="ignore"):
|
51
|
+
class BaseDeploymentTrigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-arg]
|
52
52
|
"""
|
53
53
|
Base class describing a set of criteria that must be satisfied in order to trigger
|
54
54
|
an automation.
|
prefect/events/schemas/events.py
CHANGED
@@ -192,15 +192,22 @@ class ReceivedEvent(Event):
|
|
192
192
|
|
193
193
|
def matches(expected: str, value: Optional[str]) -> bool:
|
194
194
|
"""Returns true if the given value matches the expected string, which may
|
195
|
-
include
|
195
|
+
include a a negation prefix ("!this-value") or a wildcard suffix
|
196
|
+
("any-value-starting-with*")"""
|
196
197
|
if value is None:
|
197
198
|
return False
|
198
199
|
|
199
|
-
|
200
|
+
positive = True
|
201
|
+
if expected.startswith("!"):
|
202
|
+
expected = expected[1:]
|
203
|
+
positive = False
|
204
|
+
|
200
205
|
if expected.endswith("*"):
|
201
|
-
|
206
|
+
match = value.startswith(expected[:-1])
|
207
|
+
else:
|
208
|
+
match = value == expected
|
202
209
|
|
203
|
-
return
|
210
|
+
return match if positive else not match
|
204
211
|
|
205
212
|
|
206
213
|
class ResourceSpecification(PrefectBaseModel):
|
@@ -71,7 +71,7 @@ class LabelDiver:
|
|
71
71
|
raise AttributeError
|
72
72
|
|
73
73
|
|
74
|
-
class Labelled(PrefectBaseModel, extra="ignore"):
|
74
|
+
class Labelled(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg]
|
75
75
|
"""An object defined by string labels and values"""
|
76
76
|
|
77
77
|
__root__: Dict[str, str]
|
prefect/flows.py
CHANGED
@@ -733,7 +733,7 @@ class Flow(Generic[P, R]):
|
|
733
733
|
@sync_compatible
|
734
734
|
async def serve(
|
735
735
|
self,
|
736
|
-
name: str,
|
736
|
+
name: Optional[str] = None,
|
737
737
|
interval: Optional[
|
738
738
|
Union[
|
739
739
|
Iterable[Union[int, float, datetime.timedelta]],
|
@@ -764,7 +764,7 @@ class Flow(Generic[P, R]):
|
|
764
764
|
Creates a deployment for this flow and starts a runner to monitor for scheduled work.
|
765
765
|
|
766
766
|
Args:
|
767
|
-
name: The name to give the created deployment.
|
767
|
+
name: The name to give the created deployment. Defaults to the name of the flow.
|
768
768
|
interval: An interval on which to execute the deployment. Accepts a number or a
|
769
769
|
timedelta object to create a single schedule. If a number is given, it will be
|
770
770
|
interpreted as seconds. Also accepts an iterable of numbers or timedelta to create
|
@@ -827,10 +827,13 @@ class Flow(Generic[P, R]):
|
|
827
827
|
"""
|
828
828
|
from prefect.runner import Runner
|
829
829
|
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
830
|
+
if not name:
|
831
|
+
name = self.name
|
832
|
+
else:
|
833
|
+
# Handling for my_flow.serve(__file__)
|
834
|
+
# Will set name to name of file where my_flow.serve() without the extension
|
835
|
+
# Non filepath strings will pass through unchanged
|
836
|
+
name = Path(name).stem
|
834
837
|
|
835
838
|
runner = Runner(name=name, pause_on_shutdown=pause_on_shutdown, limit=limit)
|
836
839
|
deployment_id = await runner.add_flow(
|
@@ -1226,19 +1229,19 @@ class Flow(Generic[P, R]):
|
|
1226
1229
|
return track_viz_task(self.isasync, self.name, parameters)
|
1227
1230
|
|
1228
1231
|
if PREFECT_EXPERIMENTAL_ENABLE_NEW_ENGINE.value():
|
1229
|
-
from prefect.new_flow_engine import run_flow
|
1230
|
-
from prefect.utilities.asyncutils import run_sync
|
1232
|
+
from prefect.new_flow_engine import run_flow, run_flow_sync
|
1231
1233
|
|
1232
|
-
|
1234
|
+
run_kwargs = dict(
|
1233
1235
|
flow=self,
|
1234
1236
|
parameters=parameters,
|
1235
1237
|
wait_for=wait_for,
|
1236
1238
|
return_type=return_type,
|
1237
1239
|
)
|
1238
1240
|
if self.isasync:
|
1239
|
-
|
1241
|
+
# this returns an awaitable coroutine
|
1242
|
+
return run_flow(**run_kwargs)
|
1240
1243
|
else:
|
1241
|
-
return
|
1244
|
+
return run_flow_sync(**run_kwargs)
|
1242
1245
|
|
1243
1246
|
return enter_flow_run_engine_from_flow_call(
|
1244
1247
|
self,
|
prefect/input/run_input.py
CHANGED
@@ -582,7 +582,9 @@ def receive_input(
|
|
582
582
|
# the signature is the same as here:
|
583
583
|
# Union[Type[R], Type[T], pydantic.BaseModel],
|
584
584
|
# Seems like a possible mypy bug, so we'll ignore the type check here.
|
585
|
-
input_cls
|
585
|
+
input_cls: Union[
|
586
|
+
Type[AutomaticRunInput[T]], Type[R]
|
587
|
+
] = run_input_subclass_from_type(input_type) # type: ignore[arg-type]
|
586
588
|
|
587
589
|
if issubclass(input_cls, AutomaticRunInput):
|
588
590
|
return input_cls.receive(
|