prefect-client 2.18.0__py3-none-any.whl → 2.18.1__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/blocks/core.py +1 -1
- prefect/blocks/notifications.py +2 -2
- prefect/blocks/system.py +2 -3
- prefect/client/orchestration.py +283 -22
- 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 +47 -9
- prefect/events/clients.py +50 -18
- prefect/events/filters.py +30 -3
- prefect/events/instrument.py +40 -40
- prefect/events/related.py +2 -1
- prefect/events/schemas/automations.py +50 -5
- prefect/events/schemas/deployment_triggers.py +15 -227
- prefect/events/schemas/events.py +7 -7
- prefect/events/utilities.py +1 -1
- prefect/events/worker.py +10 -7
- prefect/flows.py +33 -18
- prefect/input/actions.py +9 -9
- prefect/input/run_input.py +49 -37
- prefect/new_flow_engine.py +293 -0
- prefect/new_task_engine.py +374 -0
- prefect/results.py +3 -2
- prefect/runner/runner.py +3 -2
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +44 -3
- prefect/settings.py +26 -0
- prefect/states.py +25 -19
- prefect/tasks.py +17 -0
- prefect/utilities/asyncutils.py +37 -0
- prefect/utilities/engine.py +6 -4
- prefect/utilities/schema_tools/validation.py +1 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/METADATA +1 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/RECORD +44 -43
- prefect/concurrency/common.py +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/LICENSE +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/WHEEL +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,374 @@
|
|
1
|
+
import asyncio
|
2
|
+
import logging
|
3
|
+
from contextlib import asynccontextmanager
|
4
|
+
from dataclasses import dataclass, field
|
5
|
+
from typing import (
|
6
|
+
Any,
|
7
|
+
AsyncGenerator,
|
8
|
+
Callable,
|
9
|
+
Coroutine,
|
10
|
+
Dict,
|
11
|
+
Generic,
|
12
|
+
Iterable,
|
13
|
+
Literal,
|
14
|
+
Optional,
|
15
|
+
TypeVar,
|
16
|
+
Union,
|
17
|
+
cast,
|
18
|
+
)
|
19
|
+
from uuid import uuid4
|
20
|
+
|
21
|
+
import pendulum
|
22
|
+
from typing_extensions import ParamSpec
|
23
|
+
|
24
|
+
from prefect import Task, get_client
|
25
|
+
from prefect.client.orchestration import PrefectClient
|
26
|
+
from prefect.client.schemas import TaskRun
|
27
|
+
from prefect.client.schemas.objects import TaskRunResult
|
28
|
+
from prefect.context import FlowRunContext, TaskRunContext
|
29
|
+
from prefect.futures import PrefectFuture, resolve_futures_to_states
|
30
|
+
from prefect.logging.loggers import get_logger, task_run_logger
|
31
|
+
from prefect.results import ResultFactory
|
32
|
+
from prefect.server.schemas.states import State
|
33
|
+
from prefect.settings import PREFECT_TASKS_REFRESH_CACHE
|
34
|
+
from prefect.states import (
|
35
|
+
Pending,
|
36
|
+
Retrying,
|
37
|
+
Running,
|
38
|
+
StateDetails,
|
39
|
+
exception_to_crashed_state,
|
40
|
+
exception_to_failed_state,
|
41
|
+
return_value_to_state,
|
42
|
+
)
|
43
|
+
from prefect.utilities.asyncutils import A, Async, is_async_fn
|
44
|
+
from prefect.utilities.engine import (
|
45
|
+
_dynamic_key_for_task_run,
|
46
|
+
_get_hook_name,
|
47
|
+
_resolve_custom_task_run_name,
|
48
|
+
collect_task_run_inputs,
|
49
|
+
propose_state,
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
@asynccontextmanager
|
54
|
+
async def timeout(
|
55
|
+
delay: Optional[float], *, loop: Optional[asyncio.AbstractEventLoop] = None
|
56
|
+
) -> AsyncGenerator[None, None]:
|
57
|
+
loop = loop or asyncio.get_running_loop()
|
58
|
+
task = asyncio.current_task(loop=loop)
|
59
|
+
timer_handle: Optional[asyncio.TimerHandle] = None
|
60
|
+
|
61
|
+
if delay is not None and task is not None:
|
62
|
+
timer_handle = loop.call_later(delay, task.cancel)
|
63
|
+
|
64
|
+
try:
|
65
|
+
yield
|
66
|
+
finally:
|
67
|
+
if timer_handle is not None:
|
68
|
+
timer_handle.cancel()
|
69
|
+
|
70
|
+
|
71
|
+
P = ParamSpec("P")
|
72
|
+
R = TypeVar("R")
|
73
|
+
|
74
|
+
|
75
|
+
@dataclass
|
76
|
+
class TaskRunEngine(Generic[P, R]):
|
77
|
+
task: Task[P, Coroutine[Any, Any, R]]
|
78
|
+
logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
|
79
|
+
parameters: Optional[Dict[str, Any]] = None
|
80
|
+
task_run: Optional[TaskRun] = None
|
81
|
+
retries: int = 0
|
82
|
+
_is_started: bool = False
|
83
|
+
_client: Optional[PrefectClient] = None
|
84
|
+
|
85
|
+
def __post_init__(self):
|
86
|
+
if self.parameters is None:
|
87
|
+
self.parameters = {}
|
88
|
+
|
89
|
+
@property
|
90
|
+
def client(self) -> PrefectClient:
|
91
|
+
if not self._is_started or self._client is None:
|
92
|
+
raise RuntimeError("Engine has not started.")
|
93
|
+
return self._client
|
94
|
+
|
95
|
+
@property
|
96
|
+
def state(self) -> State:
|
97
|
+
return self.task_run.state # type: ignore
|
98
|
+
|
99
|
+
@property
|
100
|
+
def can_retry(self) -> bool:
|
101
|
+
retry_condition: Optional[ # type: ignore
|
102
|
+
Callable[[Task[P, Coroutine[Any, Any, R]], TaskRun, State], bool]
|
103
|
+
] = self.task.retry_condition_fn # type: ignore
|
104
|
+
return not retry_condition or retry_condition(
|
105
|
+
self.task, self.task_run, self.state
|
106
|
+
) # type: ignore
|
107
|
+
|
108
|
+
async def _run_hooks(self, state: State) -> None:
|
109
|
+
"""Run the on_failure and on_completion hooks for a task, making sure to
|
110
|
+
catch and log any errors that occur.
|
111
|
+
"""
|
112
|
+
task = self.task
|
113
|
+
task_run = self.task_run
|
114
|
+
|
115
|
+
hooks = None
|
116
|
+
if state.is_failed() and task.on_failure:
|
117
|
+
hooks = task.on_failure
|
118
|
+
elif state.is_completed() and task.on_completion:
|
119
|
+
hooks = task.on_completion
|
120
|
+
|
121
|
+
if hooks:
|
122
|
+
for hook in hooks:
|
123
|
+
hook_name = _get_hook_name(hook)
|
124
|
+
try:
|
125
|
+
self.logger.info(
|
126
|
+
f"Running hook {hook_name!r} in response to entering state"
|
127
|
+
f" {state.name!r}"
|
128
|
+
)
|
129
|
+
if is_async_fn(hook):
|
130
|
+
await hook(task=task, task_run=task_run, state=state)
|
131
|
+
else:
|
132
|
+
hook(task=task, task_run=task_run, state=state)
|
133
|
+
except Exception:
|
134
|
+
self.logger.error(
|
135
|
+
f"An error was encountered while running hook {hook_name!r}",
|
136
|
+
exc_info=True,
|
137
|
+
)
|
138
|
+
else:
|
139
|
+
self.logger.info(
|
140
|
+
f"Hook {hook_name!r} finished running successfully"
|
141
|
+
)
|
142
|
+
|
143
|
+
def _compute_state_details(
|
144
|
+
self, include_cache_expiration: bool = False
|
145
|
+
) -> StateDetails:
|
146
|
+
## setup cache metadata
|
147
|
+
task_run_context = TaskRunContext.get()
|
148
|
+
cache_key = (
|
149
|
+
self.task.cache_key_fn(
|
150
|
+
task_run_context,
|
151
|
+
self.parameters,
|
152
|
+
)
|
153
|
+
if self.task.cache_key_fn
|
154
|
+
else None
|
155
|
+
)
|
156
|
+
# Ignore the cached results for a cache key, default = false
|
157
|
+
# Setting on task level overrules the Prefect setting (env var)
|
158
|
+
refresh_cache = (
|
159
|
+
self.task.refresh_cache
|
160
|
+
if self.task.refresh_cache is not None
|
161
|
+
else PREFECT_TASKS_REFRESH_CACHE.value()
|
162
|
+
)
|
163
|
+
|
164
|
+
if include_cache_expiration:
|
165
|
+
cache_expiration = (
|
166
|
+
(pendulum.now("utc") + self.task.cache_expiration)
|
167
|
+
if self.task.cache_expiration
|
168
|
+
else None
|
169
|
+
)
|
170
|
+
else:
|
171
|
+
cache_expiration = None
|
172
|
+
return StateDetails(
|
173
|
+
cache_key=cache_key,
|
174
|
+
refresh_cache=refresh_cache,
|
175
|
+
cache_expiration=cache_expiration,
|
176
|
+
)
|
177
|
+
|
178
|
+
async def begin_run(self) -> State:
|
179
|
+
state_details = self._compute_state_details()
|
180
|
+
new_state = Running(state_details=state_details)
|
181
|
+
state = await self.set_state(new_state)
|
182
|
+
while state.is_pending():
|
183
|
+
await asyncio.sleep(1)
|
184
|
+
state = await self.set_state(new_state)
|
185
|
+
|
186
|
+
async def set_state(self, state: State, force: bool = False) -> State:
|
187
|
+
new_state = await propose_state(
|
188
|
+
self.client, state, task_run_id=self.task_run.id, force=force
|
189
|
+
) # type: ignore
|
190
|
+
|
191
|
+
# currently this is a hack to keep a reference to the state object
|
192
|
+
# that has an in-memory result attached to it; using the API state
|
193
|
+
# could result in losing that reference
|
194
|
+
self.task_run.state = new_state
|
195
|
+
if new_state.is_final():
|
196
|
+
await self._run_hooks(new_state)
|
197
|
+
return new_state
|
198
|
+
|
199
|
+
async def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
|
200
|
+
return await self.state.result(raise_on_failure=raise_on_failure)
|
201
|
+
|
202
|
+
async def handle_success(self, result: R) -> R:
|
203
|
+
result_factory = getattr(TaskRunContext.get(), "result_factory", None)
|
204
|
+
terminal_state = await return_value_to_state(
|
205
|
+
await resolve_futures_to_states(result),
|
206
|
+
result_factory=result_factory,
|
207
|
+
)
|
208
|
+
terminal_state.state_details = self._compute_state_details(
|
209
|
+
include_cache_expiration=True
|
210
|
+
)
|
211
|
+
await self.set_state(terminal_state)
|
212
|
+
return result
|
213
|
+
|
214
|
+
async def handle_retry(self, exc: Exception) -> bool:
|
215
|
+
"""
|
216
|
+
If the task has retries left, and the retry condition is met, set the task to retrying.
|
217
|
+
- If the task has no retries left, or the retry condition is not met, return False.
|
218
|
+
- If the task has retries left, and the retry condition is met, return True.
|
219
|
+
"""
|
220
|
+
if self.retries < self.task.retries and self.can_retry:
|
221
|
+
await self.set_state(Retrying(), force=True)
|
222
|
+
self.retries = self.retries + 1
|
223
|
+
return True
|
224
|
+
return False
|
225
|
+
|
226
|
+
async def handle_exception(self, exc: Exception) -> None:
|
227
|
+
# If the task fails, and we have retries left, set the task to retrying.
|
228
|
+
if not await self.handle_retry(exc):
|
229
|
+
# If the task has no retries left, or the retry condition is not met, set the task to failed.
|
230
|
+
context = TaskRunContext.get()
|
231
|
+
state = await exception_to_failed_state(
|
232
|
+
exc,
|
233
|
+
message="Task run encountered an exception",
|
234
|
+
result_factory=getattr(context, "result_factory", None),
|
235
|
+
)
|
236
|
+
await self.set_state(state)
|
237
|
+
|
238
|
+
async def handle_crash(self, exc: BaseException) -> None:
|
239
|
+
state = await exception_to_crashed_state(exc)
|
240
|
+
self.logger.error(f"Crash detected! {state.message}")
|
241
|
+
self.logger.debug("Crash details:", exc_info=exc)
|
242
|
+
await self.set_state(state, force=True)
|
243
|
+
|
244
|
+
async def create_task_run(self, client: PrefectClient) -> TaskRun:
|
245
|
+
flow_run_ctx = FlowRunContext.get()
|
246
|
+
try:
|
247
|
+
task_run_name = _resolve_custom_task_run_name(self.task, self.parameters)
|
248
|
+
except TypeError:
|
249
|
+
task_run_name = None
|
250
|
+
|
251
|
+
# prep input tracking
|
252
|
+
task_inputs = {
|
253
|
+
k: await collect_task_run_inputs(v) for k, v in self.parameters.items()
|
254
|
+
}
|
255
|
+
|
256
|
+
# anticipate nested runs
|
257
|
+
task_run_ctx = TaskRunContext.get()
|
258
|
+
if task_run_ctx:
|
259
|
+
task_inputs["wait_for"] = [TaskRunResult(id=task_run_ctx.task_run.id)]
|
260
|
+
|
261
|
+
# TODO: implement wait_for
|
262
|
+
# if wait_for:
|
263
|
+
# task_inputs["wait_for"] = await collect_task_run_inputs(wait_for)
|
264
|
+
|
265
|
+
if flow_run_ctx:
|
266
|
+
dynamic_key = _dynamic_key_for_task_run(
|
267
|
+
context=flow_run_ctx, task=self.task
|
268
|
+
)
|
269
|
+
else:
|
270
|
+
dynamic_key = uuid4().hex
|
271
|
+
task_run = await client.create_task_run(
|
272
|
+
task=self.task,
|
273
|
+
name=task_run_name,
|
274
|
+
flow_run_id=(
|
275
|
+
getattr(flow_run_ctx.flow_run, "id", None)
|
276
|
+
if flow_run_ctx and flow_run_ctx.flow_run
|
277
|
+
else None
|
278
|
+
),
|
279
|
+
dynamic_key=dynamic_key,
|
280
|
+
state=Pending(),
|
281
|
+
task_inputs=task_inputs,
|
282
|
+
)
|
283
|
+
return task_run
|
284
|
+
|
285
|
+
@asynccontextmanager
|
286
|
+
async def enter_run_context(self, client: Optional[PrefectClient] = None):
|
287
|
+
if client is None:
|
288
|
+
client = self.client
|
289
|
+
|
290
|
+
self.task_run = await client.read_task_run(self.task_run.id)
|
291
|
+
|
292
|
+
with TaskRunContext(
|
293
|
+
task=self.task,
|
294
|
+
log_prints=self.task.log_prints or False,
|
295
|
+
task_run=self.task_run,
|
296
|
+
parameters=self.parameters,
|
297
|
+
result_factory=await ResultFactory.from_autonomous_task(self.task),
|
298
|
+
client=client,
|
299
|
+
):
|
300
|
+
self.logger = task_run_logger(task_run=self.task_run, task=self.task)
|
301
|
+
yield
|
302
|
+
|
303
|
+
@asynccontextmanager
|
304
|
+
async def start(self):
|
305
|
+
"""
|
306
|
+
Enters a client context and creates a task run if needed.
|
307
|
+
"""
|
308
|
+
async with get_client() as client:
|
309
|
+
self._client = client
|
310
|
+
self._is_started = True
|
311
|
+
try:
|
312
|
+
if not self.task_run:
|
313
|
+
self.task_run = await self.create_task_run(client)
|
314
|
+
|
315
|
+
yield self
|
316
|
+
except Exception:
|
317
|
+
# regular exceptions are caught and re-raised to the user
|
318
|
+
raise
|
319
|
+
except BaseException as exc:
|
320
|
+
# BaseExceptions are caught and handled as crashes
|
321
|
+
await self.handle_crash(exc)
|
322
|
+
raise
|
323
|
+
finally:
|
324
|
+
self._is_started = False
|
325
|
+
self._client = None
|
326
|
+
|
327
|
+
def is_running(self) -> bool:
|
328
|
+
if getattr(self, "task_run", None) is None:
|
329
|
+
return False
|
330
|
+
return getattr(self, "task_run").state.is_running()
|
331
|
+
|
332
|
+
def is_pending(self) -> bool:
|
333
|
+
if getattr(self, "task_run", None) is None:
|
334
|
+
return False # TODO: handle this differently?
|
335
|
+
return getattr(self, "task_run").state.is_pending()
|
336
|
+
|
337
|
+
|
338
|
+
async def run_task(
|
339
|
+
task: Task[P, Coroutine[Any, Any, R]],
|
340
|
+
task_run: Optional[TaskRun] = None,
|
341
|
+
parameters: Optional[Dict[str, Any]] = None,
|
342
|
+
wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
|
343
|
+
return_type: Literal["state", "result"] = "result",
|
344
|
+
) -> "Union[R, State, None]":
|
345
|
+
"""
|
346
|
+
Runs a task against the API.
|
347
|
+
|
348
|
+
We will most likely want to use this logic as a wrapper and return a coroutine for type inference.
|
349
|
+
"""
|
350
|
+
engine = TaskRunEngine[P, R](task=task, parameters=parameters, task_run=task_run)
|
351
|
+
async with engine.start() as run:
|
352
|
+
# This is a context manager that keeps track of the run of the task run.
|
353
|
+
await run.begin_run()
|
354
|
+
|
355
|
+
while run.is_running():
|
356
|
+
async with run.enter_run_context():
|
357
|
+
try:
|
358
|
+
# This is where the task is actually run.
|
359
|
+
async with timeout(run.task.timeout_seconds):
|
360
|
+
if task.isasync:
|
361
|
+
result = cast(R, await task.fn(**(parameters or {}))) # type: ignore
|
362
|
+
else:
|
363
|
+
result = cast(R, task.fn(**(parameters or {}))) # type: ignore
|
364
|
+
# If the task run is successful, finalize it.
|
365
|
+
await run.handle_success(result)
|
366
|
+
if return_type == "result":
|
367
|
+
return result
|
368
|
+
|
369
|
+
except Exception as exc:
|
370
|
+
await run.handle_exception(exc)
|
371
|
+
|
372
|
+
if return_type == "state":
|
373
|
+
return run.state
|
374
|
+
return await run.result()
|
prefect/results.py
CHANGED
@@ -16,7 +16,7 @@ from typing import (
|
|
16
16
|
)
|
17
17
|
from uuid import UUID
|
18
18
|
|
19
|
-
from typing_extensions import Self
|
19
|
+
from typing_extensions import ParamSpec, Self
|
20
20
|
|
21
21
|
import prefect
|
22
22
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
@@ -63,6 +63,7 @@ def DEFAULT_STORAGE_KEY_FN():
|
|
63
63
|
|
64
64
|
|
65
65
|
logger = get_logger("results")
|
66
|
+
P = ParamSpec("P")
|
66
67
|
R = TypeVar("R")
|
67
68
|
|
68
69
|
|
@@ -286,7 +287,7 @@ class ResultFactory(pydantic.BaseModel):
|
|
286
287
|
@classmethod
|
287
288
|
@inject_client
|
288
289
|
async def from_autonomous_task(
|
289
|
-
cls: Type[Self], task: "Task", client: "PrefectClient" = None
|
290
|
+
cls: Type[Self], task: "Task[P, R]", client: "PrefectClient" = None
|
290
291
|
) -> Self:
|
291
292
|
"""
|
292
293
|
Create a new result factory for an autonomous task.
|
prefect/runner/runner.py
CHANGED
@@ -29,6 +29,7 @@ Example:
|
|
29
29
|
```
|
30
30
|
|
31
31
|
"""
|
32
|
+
|
32
33
|
import asyncio
|
33
34
|
import datetime
|
34
35
|
import inspect
|
@@ -80,7 +81,7 @@ from prefect.deployments.runner import (
|
|
80
81
|
)
|
81
82
|
from prefect.deployments.schedules import FlexibleScheduleList
|
82
83
|
from prefect.engine import propose_state
|
83
|
-
from prefect.events import DeploymentTriggerTypes
|
84
|
+
from prefect.events import DeploymentTriggerTypes, TriggerTypes
|
84
85
|
from prefect.exceptions import (
|
85
86
|
Abort,
|
86
87
|
)
|
@@ -232,7 +233,7 @@ class Runner:
|
|
232
233
|
schedule: Optional[SCHEDULE_TYPES] = None,
|
233
234
|
is_schedule_active: Optional[bool] = None,
|
234
235
|
parameters: Optional[dict] = None,
|
235
|
-
triggers: Optional[List[DeploymentTriggerTypes]] = None,
|
236
|
+
triggers: Optional[List[Union[DeploymentTriggerTypes, TriggerTypes]]] = None,
|
236
237
|
description: Optional[str] = None,
|
237
238
|
tags: Optional[List[str]] = None,
|
238
239
|
version: Optional[str] = None,
|
@@ -111,10 +111,12 @@
|
|
111
111
|
"taskRoleArn": "{{ task_role_arn }}"
|
112
112
|
},
|
113
113
|
"tags": "{{ labels }}",
|
114
|
-
"taskDefinition": "{{ task_definition_arn }}"
|
114
|
+
"taskDefinition": "{{ task_definition_arn }}",
|
115
|
+
"capacityProviderStrategy": "{{ capacity_provider_strategy }}"
|
115
116
|
},
|
116
117
|
"configure_cloudwatch_logs": "{{ configure_cloudwatch_logs }}",
|
117
118
|
"cloudwatch_logs_options": "{{ cloudwatch_logs_options }}",
|
119
|
+
"cloudwatch_logs_prefix": "{{ cloudwatch_logs_prefix }}",
|
118
120
|
"network_configuration": "{{ network_configuration }}",
|
119
121
|
"stream_output": "{{ stream_output }}",
|
120
122
|
"task_start_timeout_seconds": "{{ task_start_timeout_seconds }}",
|
@@ -191,6 +193,14 @@
|
|
191
193
|
],
|
192
194
|
"type": "string"
|
193
195
|
},
|
196
|
+
"capacity_provider_strategy": {
|
197
|
+
"title": "Capacity Provider Strategy",
|
198
|
+
"description": "The capacity provider strategy to use when running the task. If a capacity provider strategy is specified, the selected launch type will be ignored.",
|
199
|
+
"type": "array",
|
200
|
+
"items": {
|
201
|
+
"$ref": "#/definitions/CapacityProvider"
|
202
|
+
}
|
203
|
+
},
|
194
204
|
"image": {
|
195
205
|
"title": "Image",
|
196
206
|
"description": "The image to use for the Prefect container in the task. If this value is not null, it will override the value in the task definition. This value defaults to a Prefect base image matching your local versions.",
|
@@ -239,6 +249,11 @@
|
|
239
249
|
"type": "string"
|
240
250
|
}
|
241
251
|
},
|
252
|
+
"cloudwatch_logs_prefix": {
|
253
|
+
"title": "Cloudwatch Logs Prefix",
|
254
|
+
"description": "When `configure_cloudwatch_logs` is enabled, this setting may be used to set a prefix for the log group. If not provided, the default prefix will be `prefect-logs_<work_pool_name>_<deployment_id>`. If `awslogs-stream-prefix` is present in `Cloudwatch logs options` this setting will be ignored.",
|
255
|
+
"type": "string"
|
256
|
+
},
|
242
257
|
"network_configuration": {
|
243
258
|
"title": "Network Configuration",
|
244
259
|
"description": "When `network_configuration` is supplied it will override ECS Worker'sawsvpcConfiguration that defined in the ECS task executing your workload. See the [AWS documentation](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ecs-service-awsvpcconfiguration.html) for available options.",
|
@@ -370,6 +385,30 @@
|
|
370
385
|
"aws_secret_access_key"
|
371
386
|
],
|
372
387
|
"block_schema_references": {}
|
388
|
+
},
|
389
|
+
"CapacityProvider": {
|
390
|
+
"title": "CapacityProvider",
|
391
|
+
"description": "The capacity provider strategy to use when running the task.",
|
392
|
+
"type": "object",
|
393
|
+
"properties": {
|
394
|
+
"capacityProvider": {
|
395
|
+
"title": "Capacityprovider",
|
396
|
+
"type": "string"
|
397
|
+
},
|
398
|
+
"weight": {
|
399
|
+
"title": "Weight",
|
400
|
+
"type": "integer"
|
401
|
+
},
|
402
|
+
"base": {
|
403
|
+
"title": "Base",
|
404
|
+
"type": "integer"
|
405
|
+
}
|
406
|
+
},
|
407
|
+
"required": [
|
408
|
+
"capacityProvider",
|
409
|
+
"weight",
|
410
|
+
"base"
|
411
|
+
]
|
373
412
|
}
|
374
413
|
}
|
375
414
|
}
|
@@ -1100,7 +1139,9 @@
|
|
1100
1139
|
"serviceAccount": "{{ service_account_name }}",
|
1101
1140
|
"maxRetries": "{{ max_retries }}",
|
1102
1141
|
"timeout": "{{ timeout }}",
|
1103
|
-
"vpcAccess":
|
1142
|
+
"vpcAccess": {
|
1143
|
+
"connector": "{{ vpc_connector_name }}"
|
1144
|
+
},
|
1104
1145
|
"containers": [
|
1105
1146
|
{
|
1106
1147
|
"env": [],
|
@@ -1647,4 +1688,4 @@
|
|
1647
1688
|
"type": "kubernetes"
|
1648
1689
|
}
|
1649
1690
|
}
|
1650
|
-
}
|
1691
|
+
}
|
prefect/settings.py
CHANGED
@@ -1214,6 +1214,20 @@ PREFECT_API_SERVICES_CANCELLATION_CLEANUP_LOOP_SECONDS = Setting(
|
|
1214
1214
|
this often. Defaults to `20`.
|
1215
1215
|
"""
|
1216
1216
|
|
1217
|
+
PREFECT_API_SERVICES_FOREMAN_ENABLED = Setting(bool, default=True)
|
1218
|
+
"""Whether or not to start the Foreman service in the server application."""
|
1219
|
+
|
1220
|
+
PREFECT_API_SERVICES_FOREMAN_LOOP_SECONDS = Setting(float, default=15)
|
1221
|
+
"""The number of seconds to wait between each iteration of the Foreman loop which checks
|
1222
|
+
for offline workers and updates work pool status."""
|
1223
|
+
|
1224
|
+
PREFECT_API_SERVICES_FOREMAN_DEPLOYMENT_LAST_POLLED_TIMEOUT_SECONDS = Setting(
|
1225
|
+
int, default=60
|
1226
|
+
)
|
1227
|
+
"""The number of seconds before a deployment is marked as not ready if it has not been
|
1228
|
+
polled."""
|
1229
|
+
|
1230
|
+
|
1217
1231
|
PREFECT_API_DEFAULT_LIMIT = Setting(
|
1218
1232
|
int,
|
1219
1233
|
default=200,
|
@@ -1579,6 +1593,11 @@ PREFECT_EXPERIMENTAL_ENABLE_WORK_QUEUE_STATUS = Setting(bool, default=True)
|
|
1579
1593
|
Whether or not to enable experimental work queue status in-place of work queue health.
|
1580
1594
|
"""
|
1581
1595
|
|
1596
|
+
PREFECT_EXPERIMENTAL_ENABLE_NEW_ENGINE = Setting(bool, default=False)
|
1597
|
+
"""
|
1598
|
+
Whether or not to enable experimental new engine.
|
1599
|
+
"""
|
1600
|
+
|
1582
1601
|
|
1583
1602
|
# Defaults -----------------------------------------------------------------------------
|
1584
1603
|
|
@@ -1698,6 +1717,13 @@ PREFECT_API_EVENTS_STREAM_OUT_ENABLED = Setting(bool, default=True)
|
|
1698
1717
|
Whether or not to allow streaming events out of via websockets.
|
1699
1718
|
"""
|
1700
1719
|
|
1720
|
+
PREFECT_API_EVENTS_RELATED_RESOURCE_CACHE_TTL = Setting(
|
1721
|
+
timedelta, default=timedelta(minutes=5)
|
1722
|
+
)
|
1723
|
+
"""
|
1724
|
+
How long to cache related resource data for emitting server-side vents
|
1725
|
+
"""
|
1726
|
+
|
1701
1727
|
# Deprecated settings ------------------------------------------------------------------
|
1702
1728
|
|
1703
1729
|
|
prefect/states.py
CHANGED
@@ -486,8 +486,10 @@ class StateGroup:
|
|
486
486
|
|
487
487
|
|
488
488
|
def Scheduled(
|
489
|
-
cls: Type[State] = State,
|
490
|
-
|
489
|
+
cls: Type[State[R]] = State,
|
490
|
+
scheduled_time: Optional[datetime.datetime] = None,
|
491
|
+
**kwargs: Any,
|
492
|
+
) -> State[R]:
|
491
493
|
"""Convenience function for creating `Scheduled` states.
|
492
494
|
|
493
495
|
Returns:
|
@@ -503,7 +505,7 @@ def Scheduled(
|
|
503
505
|
return cls(type=StateType.SCHEDULED, state_details=state_details, **kwargs)
|
504
506
|
|
505
507
|
|
506
|
-
def Completed(cls: Type[State] = State, **kwargs) -> State:
|
508
|
+
def Completed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
507
509
|
"""Convenience function for creating `Completed` states.
|
508
510
|
|
509
511
|
Returns:
|
@@ -512,7 +514,7 @@ def Completed(cls: Type[State] = State, **kwargs) -> State:
|
|
512
514
|
return cls(type=StateType.COMPLETED, **kwargs)
|
513
515
|
|
514
516
|
|
515
|
-
def Running(cls: Type[State] = State, **kwargs) -> State:
|
517
|
+
def Running(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
516
518
|
"""Convenience function for creating `Running` states.
|
517
519
|
|
518
520
|
Returns:
|
@@ -521,7 +523,7 @@ def Running(cls: Type[State] = State, **kwargs) -> State:
|
|
521
523
|
return cls(type=StateType.RUNNING, **kwargs)
|
522
524
|
|
523
525
|
|
524
|
-
def Failed(cls: Type[State] = State, **kwargs) -> State:
|
526
|
+
def Failed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
525
527
|
"""Convenience function for creating `Failed` states.
|
526
528
|
|
527
529
|
Returns:
|
@@ -530,7 +532,7 @@ def Failed(cls: Type[State] = State, **kwargs) -> State:
|
|
530
532
|
return cls(type=StateType.FAILED, **kwargs)
|
531
533
|
|
532
534
|
|
533
|
-
def Crashed(cls: Type[State] = State, **kwargs) -> State:
|
535
|
+
def Crashed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
534
536
|
"""Convenience function for creating `Crashed` states.
|
535
537
|
|
536
538
|
Returns:
|
@@ -539,7 +541,7 @@ def Crashed(cls: Type[State] = State, **kwargs) -> State:
|
|
539
541
|
return cls(type=StateType.CRASHED, **kwargs)
|
540
542
|
|
541
543
|
|
542
|
-
def Cancelling(cls: Type[State] = State, **kwargs) -> State:
|
544
|
+
def Cancelling(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
543
545
|
"""Convenience function for creating `Cancelling` states.
|
544
546
|
|
545
547
|
Returns:
|
@@ -548,7 +550,7 @@ def Cancelling(cls: Type[State] = State, **kwargs) -> State:
|
|
548
550
|
return cls(type=StateType.CANCELLING, **kwargs)
|
549
551
|
|
550
552
|
|
551
|
-
def Cancelled(cls: Type[State] = State, **kwargs) -> State:
|
553
|
+
def Cancelled(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
552
554
|
"""Convenience function for creating `Cancelled` states.
|
553
555
|
|
554
556
|
Returns:
|
@@ -557,7 +559,7 @@ def Cancelled(cls: Type[State] = State, **kwargs) -> State:
|
|
557
559
|
return cls(type=StateType.CANCELLED, **kwargs)
|
558
560
|
|
559
561
|
|
560
|
-
def Pending(cls: Type[State] = State, **kwargs) -> State:
|
562
|
+
def Pending(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
561
563
|
"""Convenience function for creating `Pending` states.
|
562
564
|
|
563
565
|
Returns:
|
@@ -567,13 +569,13 @@ def Pending(cls: Type[State] = State, **kwargs) -> State:
|
|
567
569
|
|
568
570
|
|
569
571
|
def Paused(
|
570
|
-
cls: Type[State] = State,
|
572
|
+
cls: Type[State[R]] = State,
|
571
573
|
timeout_seconds: Optional[int] = None,
|
572
574
|
pause_expiration_time: Optional[datetime.datetime] = None,
|
573
575
|
reschedule: bool = False,
|
574
576
|
pause_key: Optional[str] = None,
|
575
|
-
**kwargs,
|
576
|
-
) -> State:
|
577
|
+
**kwargs: Any,
|
578
|
+
) -> State[R]:
|
577
579
|
"""Convenience function for creating `Paused` states.
|
578
580
|
|
579
581
|
Returns:
|
@@ -603,11 +605,11 @@ def Paused(
|
|
603
605
|
|
604
606
|
|
605
607
|
def Suspended(
|
606
|
-
cls: Type[State] = State,
|
608
|
+
cls: Type[State[R]] = State,
|
607
609
|
timeout_seconds: Optional[int] = None,
|
608
610
|
pause_expiration_time: Optional[datetime.datetime] = None,
|
609
611
|
pause_key: Optional[str] = None,
|
610
|
-
**kwargs,
|
612
|
+
**kwargs: Any,
|
611
613
|
):
|
612
614
|
"""Convenience function for creating `Suspended` states.
|
613
615
|
|
@@ -626,8 +628,10 @@ def Suspended(
|
|
626
628
|
|
627
629
|
|
628
630
|
def AwaitingRetry(
|
629
|
-
cls: Type[State] = State,
|
630
|
-
|
631
|
+
cls: Type[State[R]] = State,
|
632
|
+
scheduled_time: Optional[datetime.datetime] = None,
|
633
|
+
**kwargs: Any,
|
634
|
+
) -> State[R]:
|
631
635
|
"""Convenience function for creating `AwaitingRetry` states.
|
632
636
|
|
633
637
|
Returns:
|
@@ -638,7 +642,7 @@ def AwaitingRetry(
|
|
638
642
|
)
|
639
643
|
|
640
644
|
|
641
|
-
def Retrying(cls: Type[State] = State, **kwargs) -> State:
|
645
|
+
def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
642
646
|
"""Convenience function for creating `Retrying` states.
|
643
647
|
|
644
648
|
Returns:
|
@@ -648,8 +652,10 @@ def Retrying(cls: Type[State] = State, **kwargs) -> State:
|
|
648
652
|
|
649
653
|
|
650
654
|
def Late(
|
651
|
-
cls: Type[State] = State,
|
652
|
-
|
655
|
+
cls: Type[State[R]] = State,
|
656
|
+
scheduled_time: Optional[datetime.datetime] = None,
|
657
|
+
**kwargs: Any,
|
658
|
+
) -> State[R]:
|
653
659
|
"""Convenience function for creating `Late` states.
|
654
660
|
|
655
661
|
Returns:
|