prefect 3.6.8__py3-none-any.whl → 3.6.8.dev3__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/_build_info.py +3 -3
- prefect/blocks/notifications.py +3 -9
- prefect/cli/flow_run.py +2 -250
- prefect/flows.py +0 -55
- prefect/futures.py +2 -2
- prefect/server/api/background_workers.py +19 -9
- prefect/workers/base.py +20 -44
- prefect/workers/process.py +7 -16
- {prefect-3.6.8.dist-info → prefect-3.6.8.dev3.dist-info}/METADATA +2 -2
- {prefect-3.6.8.dist-info → prefect-3.6.8.dev3.dist-info}/RECORD +13 -13
- {prefect-3.6.8.dist-info → prefect-3.6.8.dev3.dist-info}/WHEEL +0 -0
- {prefect-3.6.8.dist-info → prefect-3.6.8.dev3.dist-info}/entry_points.txt +0 -0
- {prefect-3.6.8.dist-info → prefect-3.6.8.dev3.dist-info}/licenses/LICENSE +0 -0
prefect/_build_info.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Generated by versioningit
|
|
2
|
-
__version__ = "3.6.8"
|
|
3
|
-
__build_date__ = "2025-12-24
|
|
4
|
-
__git_commit__ = "
|
|
2
|
+
__version__ = "3.6.8.dev3"
|
|
3
|
+
__build_date__ = "2025-12-24 08:10:40.420068+00:00"
|
|
4
|
+
__git_commit__ = "4baedb2efecfb4d82ce18b339ce16383da4a5754"
|
|
5
5
|
__dirty__ = False
|
prefect/blocks/notifications.py
CHANGED
|
@@ -844,12 +844,11 @@ class CustomWebhookNotificationBlock(NotificationBlock):
|
|
|
844
844
|
' "{{tokenFromSecrets}}"}'
|
|
845
845
|
],
|
|
846
846
|
)
|
|
847
|
-
form_data: Optional[dict[str, str]
|
|
847
|
+
form_data: Optional[dict[str, str]] = Field(
|
|
848
848
|
default=None,
|
|
849
849
|
title="Form Data",
|
|
850
850
|
description=(
|
|
851
|
-
"Send form data as payload. Should not be used together with _JSON Data_.
|
|
852
|
-
"Can be a dictionary for form-encoded data or a string for raw body content."
|
|
851
|
+
"Send form data as payload. Should not be used together with _JSON Data_."
|
|
853
852
|
),
|
|
854
853
|
examples=[
|
|
855
854
|
'{"text": "{{subject}}\\n{{body}}", "title": "{{name}}", "token":'
|
|
@@ -883,18 +882,13 @@ class CustomWebhookNotificationBlock(NotificationBlock):
|
|
|
883
882
|
"name": self.name,
|
|
884
883
|
}
|
|
885
884
|
)
|
|
886
|
-
# httpx uses 'data' for form-encoded dicts, 'content' for raw string/bytes
|
|
887
|
-
if isinstance(self.form_data, str):
|
|
888
|
-
data_key = "content"
|
|
889
|
-
else:
|
|
890
|
-
data_key = "data"
|
|
891
885
|
# do substution
|
|
892
886
|
return apply_values(
|
|
893
887
|
{
|
|
894
888
|
"method": self.method,
|
|
895
889
|
"url": self.url,
|
|
896
890
|
"params": self.params,
|
|
897
|
-
|
|
891
|
+
"data": self.form_data,
|
|
898
892
|
"json": self.json_data,
|
|
899
893
|
"headers": self.headers,
|
|
900
894
|
"cookies": self.cookies,
|
prefect/cli/flow_run.py
CHANGED
|
@@ -10,15 +10,9 @@ import signal
|
|
|
10
10
|
import threading
|
|
11
11
|
import webbrowser
|
|
12
12
|
from types import FrameType
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import List, Optional
|
|
14
14
|
from uuid import UUID
|
|
15
15
|
|
|
16
|
-
from prefect.utilities.callables import get_call_parameters, parameters_to_args_kwargs
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from prefect.client.orchestration import PrefectClient
|
|
20
|
-
from prefect.client.schemas.objects import FlowRun
|
|
21
|
-
|
|
22
16
|
import httpx
|
|
23
17
|
import orjson
|
|
24
18
|
import typer
|
|
@@ -38,7 +32,7 @@ from prefect.client.schemas.sorting import FlowRunSort, LogSort
|
|
|
38
32
|
from prefect.exceptions import ObjectNotFound
|
|
39
33
|
from prefect.logging import get_logger
|
|
40
34
|
from prefect.runner import Runner
|
|
41
|
-
from prefect.states import State
|
|
35
|
+
from prefect.states import State
|
|
42
36
|
from prefect.types._datetime import human_friendly_diff
|
|
43
37
|
from prefect.utilities.asyncutils import run_sync_in_worker_thread
|
|
44
38
|
from prefect.utilities.urls import url_for
|
|
@@ -49,65 +43,6 @@ flow_run_app: PrefectTyper = PrefectTyper(
|
|
|
49
43
|
app.add_typer(flow_run_app, aliases=["flow-runs"])
|
|
50
44
|
|
|
51
45
|
LOGS_DEFAULT_PAGE_SIZE = 200
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
async def _get_flow_run_by_id_or_name(
|
|
55
|
-
client: "PrefectClient",
|
|
56
|
-
id_or_name: str,
|
|
57
|
-
) -> "FlowRun":
|
|
58
|
-
"""
|
|
59
|
-
Resolve a flow run identifier that could be either a UUID or a name.
|
|
60
|
-
|
|
61
|
-
Flow run names are not guaranteed to be unique, so this function will
|
|
62
|
-
error if multiple flow runs match the given name.
|
|
63
|
-
|
|
64
|
-
Args:
|
|
65
|
-
client: The Prefect client to use for API calls
|
|
66
|
-
id_or_name: Either a UUID string or a flow run name
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
The matching FlowRun object
|
|
70
|
-
|
|
71
|
-
Raises:
|
|
72
|
-
typer.Exit: If flow run not found, or if multiple flow runs match the name
|
|
73
|
-
"""
|
|
74
|
-
from prefect.client.schemas.filters import FlowRunFilterName
|
|
75
|
-
|
|
76
|
-
# First, try parsing as UUID
|
|
77
|
-
try:
|
|
78
|
-
flow_run_id = UUID(id_or_name)
|
|
79
|
-
try:
|
|
80
|
-
return await client.read_flow_run(flow_run_id)
|
|
81
|
-
except ObjectNotFound:
|
|
82
|
-
exit_with_error(f"Flow run '{id_or_name}' not found!")
|
|
83
|
-
except ValueError:
|
|
84
|
-
# Not a valid UUID, treat as a name
|
|
85
|
-
pass
|
|
86
|
-
|
|
87
|
-
# Query by name (exact match)
|
|
88
|
-
flow_runs = await client.read_flow_runs(
|
|
89
|
-
flow_run_filter=FlowRunFilter(name=FlowRunFilterName(any_=[id_or_name])),
|
|
90
|
-
limit=100, # Reasonable limit for displaying matches
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
if not flow_runs:
|
|
94
|
-
exit_with_error(f"Flow run '{id_or_name}' not found!")
|
|
95
|
-
|
|
96
|
-
if len(flow_runs) == 1:
|
|
97
|
-
return flow_runs[0]
|
|
98
|
-
|
|
99
|
-
# Multiple matches - show all and exit with error
|
|
100
|
-
lines = [f"Multiple flow runs found with name '{id_or_name}':\n"]
|
|
101
|
-
for fr in flow_runs:
|
|
102
|
-
state_name = fr.state.name if fr.state else "unknown"
|
|
103
|
-
timestamp = fr.start_time or fr.created
|
|
104
|
-
timestamp_str = timestamp.strftime("%Y-%m-%d %H:%M:%S") if timestamp else "N/A"
|
|
105
|
-
lines.append(f" - {fr.id} ({state_name}, {timestamp_str})")
|
|
106
|
-
|
|
107
|
-
lines.append("\nPlease retry using an explicit flow run ID.")
|
|
108
|
-
exit_with_error("\n".join(lines))
|
|
109
|
-
|
|
110
|
-
|
|
111
46
|
LOGS_WITH_LIMIT_FLAG_DEFAULT_NUM_LOGS = 20
|
|
112
47
|
|
|
113
48
|
logger: "logging.Logger" = get_logger(__name__)
|
|
@@ -341,189 +276,6 @@ async def cancel(id: UUID):
|
|
|
341
276
|
exit_with_success(f"Flow run '{id}' was successfully scheduled for cancellation.")
|
|
342
277
|
|
|
343
278
|
|
|
344
|
-
@flow_run_app.command()
|
|
345
|
-
async def retry(
|
|
346
|
-
id_or_name: str = typer.Argument(
|
|
347
|
-
...,
|
|
348
|
-
help="The flow run ID (UUID) or name to retry.",
|
|
349
|
-
),
|
|
350
|
-
entrypoint: Optional[str] = typer.Option(
|
|
351
|
-
None,
|
|
352
|
-
"--entrypoint",
|
|
353
|
-
"-e",
|
|
354
|
-
help=(
|
|
355
|
-
"The path to a file containing the flow to run, and the name of the flow "
|
|
356
|
-
"function, in the format `path/to/file.py:flow_function_name`. "
|
|
357
|
-
"Required if the flow run does not have an associated deployment."
|
|
358
|
-
),
|
|
359
|
-
),
|
|
360
|
-
):
|
|
361
|
-
"""
|
|
362
|
-
Retry a failed or completed flow run.
|
|
363
|
-
|
|
364
|
-
The flow run can be specified by either its UUID or its name. If multiple
|
|
365
|
-
flow runs have the same name, you must use the UUID to disambiguate.
|
|
366
|
-
|
|
367
|
-
If the flow run has an associated deployment, it will be scheduled for retry
|
|
368
|
-
and a worker will pick it up. If there is no deployment, you must provide
|
|
369
|
-
an --entrypoint to the flow code, and the flow will execute locally.
|
|
370
|
-
|
|
371
|
-
\b
|
|
372
|
-
Examples:
|
|
373
|
-
$ prefect flow-run retry abc123-def456-7890-...
|
|
374
|
-
$ prefect flow-run retry my-flow-run-name
|
|
375
|
-
$ prefect flow-run retry abc123 --entrypoint ./flows/my_flow.py:my_flow
|
|
376
|
-
"""
|
|
377
|
-
from prefect.flow_engine import run_flow
|
|
378
|
-
from prefect.flows import load_flow_from_entrypoint
|
|
379
|
-
from prefect.states import Scheduled
|
|
380
|
-
|
|
381
|
-
terminal_states = {
|
|
382
|
-
StateType.COMPLETED,
|
|
383
|
-
StateType.FAILED,
|
|
384
|
-
StateType.CANCELLED,
|
|
385
|
-
StateType.CRASHED,
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
async with get_client() as client:
|
|
389
|
-
# Resolve flow run by ID or name
|
|
390
|
-
flow_run = await _get_flow_run_by_id_or_name(client, id_or_name)
|
|
391
|
-
flow_run_id = flow_run.id
|
|
392
|
-
|
|
393
|
-
# Validate flow run is in terminal state
|
|
394
|
-
if flow_run.state is None or flow_run.state.type not in terminal_states:
|
|
395
|
-
current_state = flow_run.state.type.value if flow_run.state else "unknown"
|
|
396
|
-
exit_with_error(
|
|
397
|
-
f"Flow run '{flow_run_id}' is in state '{current_state}' and cannot be retried. "
|
|
398
|
-
f"Only flow runs in terminal states (COMPLETED, FAILED, CANCELLED, CRASHED) can be retried."
|
|
399
|
-
)
|
|
400
|
-
|
|
401
|
-
# Branch based on deployment association
|
|
402
|
-
if flow_run.deployment_id:
|
|
403
|
-
# Deployment-based retry: set state to Scheduled and exit
|
|
404
|
-
# Use force=True to bypass orchestration rules that prevent state transitions
|
|
405
|
-
# from terminal states (e.g., CANCELLED -> SCHEDULED)
|
|
406
|
-
scheduled_state = Scheduled(message="Retried via CLI")
|
|
407
|
-
try:
|
|
408
|
-
result = await client.set_flow_run_state(
|
|
409
|
-
flow_run_id=flow_run_id, state=scheduled_state, force=True
|
|
410
|
-
)
|
|
411
|
-
except ObjectNotFound:
|
|
412
|
-
exit_with_error(f"Flow run '{flow_run_id}' not found!")
|
|
413
|
-
|
|
414
|
-
if result.status == SetStateStatus.ABORT:
|
|
415
|
-
exit_with_error(
|
|
416
|
-
f"Flow run '{flow_run_id}' could not be retried. Reason: '{result.details.reason}'"
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
exit_with_success(
|
|
420
|
-
f"Flow run '{flow_run_id}' has been scheduled for retry. "
|
|
421
|
-
"A worker will pick it up shortly."
|
|
422
|
-
)
|
|
423
|
-
else:
|
|
424
|
-
# Local retry: require entrypoint and execute synchronously
|
|
425
|
-
if not entrypoint:
|
|
426
|
-
exit_with_error(
|
|
427
|
-
f"Flow run '{flow_run_id}' does not have an associated deployment. "
|
|
428
|
-
"Please provide an --entrypoint to the flow code.\n\n"
|
|
429
|
-
f"Example: prefect flow-run retry {flow_run_id} --entrypoint ./flows/my_flow.py:my_flow"
|
|
430
|
-
)
|
|
431
|
-
|
|
432
|
-
# Load the flow from entrypoint
|
|
433
|
-
try:
|
|
434
|
-
flow = load_flow_from_entrypoint(entrypoint, use_placeholder_flow=False)
|
|
435
|
-
except Exception as exc:
|
|
436
|
-
exit_with_error(
|
|
437
|
-
f"Failed to load flow from entrypoint '{entrypoint}': {exc}"
|
|
438
|
-
)
|
|
439
|
-
|
|
440
|
-
# Check if this is an infrastructure-bound flow
|
|
441
|
-
from prefect.flows import InfrastructureBoundFlow
|
|
442
|
-
|
|
443
|
-
if isinstance(flow, InfrastructureBoundFlow):
|
|
444
|
-
app.console.print(
|
|
445
|
-
f"Retrying flow run '{flow_run_id}' on remote infrastructure "
|
|
446
|
-
f"(work pool: {flow.work_pool})..."
|
|
447
|
-
)
|
|
448
|
-
|
|
449
|
-
try:
|
|
450
|
-
# Use the retry method which handles remote execution
|
|
451
|
-
await flow.retry(flow_run)
|
|
452
|
-
except Exception as exc:
|
|
453
|
-
exit_with_error(f"Flow run failed: {exc}")
|
|
454
|
-
|
|
455
|
-
# Re-fetch to get final state
|
|
456
|
-
flow_run = await client.read_flow_run(flow_run_id)
|
|
457
|
-
final_state = flow_run.state.type.value if flow_run.state else "unknown"
|
|
458
|
-
|
|
459
|
-
if flow_run.state and flow_run.state.is_completed():
|
|
460
|
-
exit_with_success(
|
|
461
|
-
f"Flow run '{flow_run_id}' completed successfully."
|
|
462
|
-
)
|
|
463
|
-
else:
|
|
464
|
-
exit_with_error(
|
|
465
|
-
f"Flow run '{flow_run_id}' finished with state: {final_state}"
|
|
466
|
-
)
|
|
467
|
-
else:
|
|
468
|
-
# Regular local execution path
|
|
469
|
-
# Set state to Scheduled with force=True to bypass deployment check
|
|
470
|
-
scheduled_state = Scheduled(message="Retried via CLI (local execution)")
|
|
471
|
-
try:
|
|
472
|
-
result = await client.set_flow_run_state(
|
|
473
|
-
flow_run_id=flow_run_id, state=scheduled_state, force=True
|
|
474
|
-
)
|
|
475
|
-
except ObjectNotFound:
|
|
476
|
-
exit_with_error(f"Flow run '{flow_run_id}' not found!")
|
|
477
|
-
|
|
478
|
-
if result.status == SetStateStatus.ABORT:
|
|
479
|
-
exit_with_error(
|
|
480
|
-
f"Flow run '{flow_run_id}' could not be retried. Reason: '{result.details.reason}'"
|
|
481
|
-
)
|
|
482
|
-
|
|
483
|
-
app.console.print(f"Executing flow run '{flow_run_id}' locally...")
|
|
484
|
-
|
|
485
|
-
# Re-fetch the flow run to get updated state
|
|
486
|
-
flow_run = await client.read_flow_run(flow_run_id)
|
|
487
|
-
|
|
488
|
-
try:
|
|
489
|
-
call_args, call_kwargs = parameters_to_args_kwargs(
|
|
490
|
-
flow.fn, flow_run.parameters if flow_run else {}
|
|
491
|
-
)
|
|
492
|
-
parameters = get_call_parameters(flow.fn, call_args, call_kwargs)
|
|
493
|
-
except Exception as exc:
|
|
494
|
-
state = await exception_to_crashed_state(exc)
|
|
495
|
-
await client.set_flow_run_state(
|
|
496
|
-
flow_run_id=flow_run_id, state=state, force=True
|
|
497
|
-
)
|
|
498
|
-
exit_with_error(
|
|
499
|
-
"Failed to use parameters from previous attempt. Please ensure the flow signature has not changed since the last run."
|
|
500
|
-
)
|
|
501
|
-
|
|
502
|
-
# Execute the flow synchronously, reusing the existing flow run
|
|
503
|
-
try:
|
|
504
|
-
run_flow(
|
|
505
|
-
flow=flow,
|
|
506
|
-
flow_run=flow_run,
|
|
507
|
-
return_type="state",
|
|
508
|
-
parameters=parameters,
|
|
509
|
-
)
|
|
510
|
-
except Exception as exc:
|
|
511
|
-
exit_with_error(f"Flow run failed: {exc}")
|
|
512
|
-
|
|
513
|
-
# Re-fetch to get final state
|
|
514
|
-
flow_run = await client.read_flow_run(flow_run_id)
|
|
515
|
-
final_state = flow_run.state.type.value if flow_run.state else "unknown"
|
|
516
|
-
|
|
517
|
-
if flow_run.state and flow_run.state.is_completed():
|
|
518
|
-
exit_with_success(
|
|
519
|
-
f"Flow run '{flow_run_id}' completed successfully."
|
|
520
|
-
)
|
|
521
|
-
else:
|
|
522
|
-
exit_with_error(
|
|
523
|
-
f"Flow run '{flow_run_id}' finished with state: {final_state}"
|
|
524
|
-
)
|
|
525
|
-
|
|
526
|
-
|
|
527
279
|
@flow_run_app.command()
|
|
528
280
|
async def logs(
|
|
529
281
|
id: UUID,
|
prefect/flows.py
CHANGED
|
@@ -2204,61 +2204,6 @@ class InfrastructureBoundFlow(Flow[P, R]):
|
|
|
2204
2204
|
|
|
2205
2205
|
return run_coro_as_sync(submit_func())
|
|
2206
2206
|
|
|
2207
|
-
async def retry(
|
|
2208
|
-
self,
|
|
2209
|
-
flow_run: "FlowRun",
|
|
2210
|
-
) -> R | State[R]:
|
|
2211
|
-
"""
|
|
2212
|
-
EXPERIMENTAL: This method is experimental and may be removed or changed in future
|
|
2213
|
-
releases.
|
|
2214
|
-
|
|
2215
|
-
Retry an existing flow run on remote infrastructure.
|
|
2216
|
-
|
|
2217
|
-
This method allows retrying a flow run that was previously executed,
|
|
2218
|
-
reusing the same flow run ID and incrementing the run_count.
|
|
2219
|
-
|
|
2220
|
-
Args:
|
|
2221
|
-
flow_run: The existing flow run to retry
|
|
2222
|
-
return_state: If True, return the final state instead of the result
|
|
2223
|
-
|
|
2224
|
-
Returns:
|
|
2225
|
-
The flow result or final state
|
|
2226
|
-
|
|
2227
|
-
Example:
|
|
2228
|
-
```python
|
|
2229
|
-
from prefect import flow
|
|
2230
|
-
from prefect_aws.experimental import ecs
|
|
2231
|
-
|
|
2232
|
-
@ecs(work_pool="my-pool")
|
|
2233
|
-
@flow
|
|
2234
|
-
def my_flow():
|
|
2235
|
-
...
|
|
2236
|
-
|
|
2237
|
-
# Original run
|
|
2238
|
-
my_flow() # Creates flow run abc123
|
|
2239
|
-
|
|
2240
|
-
# Later, retry the same flow run
|
|
2241
|
-
flow_run = client.read_flow_run("abc123")
|
|
2242
|
-
await my_flow.retry(flow_run)
|
|
2243
|
-
```
|
|
2244
|
-
"""
|
|
2245
|
-
try:
|
|
2246
|
-
async with self.worker_cls(work_pool_name=self.work_pool) as worker:
|
|
2247
|
-
future = await worker.submit(
|
|
2248
|
-
flow=self,
|
|
2249
|
-
parameters=flow_run.parameters,
|
|
2250
|
-
job_variables=self.job_variables,
|
|
2251
|
-
flow_run=flow_run,
|
|
2252
|
-
)
|
|
2253
|
-
return await future.aresult()
|
|
2254
|
-
except (ExceptionGroup, BaseExceptionGroup) as exc:
|
|
2255
|
-
# For less verbose tracebacks
|
|
2256
|
-
exceptions = exc.exceptions
|
|
2257
|
-
if len(exceptions) == 1:
|
|
2258
|
-
raise exceptions[0] from None
|
|
2259
|
-
else:
|
|
2260
|
-
raise
|
|
2261
|
-
|
|
2262
2207
|
def submit_to_work_pool(
|
|
2263
2208
|
self, *args: P.args, **kwargs: P.kwargs
|
|
2264
2209
|
) -> PrefectFlowRunFuture[R]:
|
prefect/futures.py
CHANGED
|
@@ -373,7 +373,7 @@ class PrefectFlowRunFuture(PrefectFuture[R]):
|
|
|
373
373
|
async def wait_async(self, timeout: float | None = None) -> None:
|
|
374
374
|
if self._final_state:
|
|
375
375
|
logger.debug(
|
|
376
|
-
"Final state already set for %s. Returning...", self.
|
|
376
|
+
"Final state already set for %s. Returning...", self.task_run_id
|
|
377
377
|
)
|
|
378
378
|
return
|
|
379
379
|
|
|
@@ -425,7 +425,7 @@ class PrefectFlowRunFuture(PrefectFuture[R]):
|
|
|
425
425
|
await self.wait_async(timeout=timeout)
|
|
426
426
|
if not self._final_state:
|
|
427
427
|
raise TimeoutError(
|
|
428
|
-
f"
|
|
428
|
+
f"Task run {self.task_run_id} did not complete within {timeout} seconds"
|
|
429
429
|
)
|
|
430
430
|
|
|
431
431
|
return await self._final_state.aresult(raise_on_failure=raise_on_failure)
|
|
@@ -47,7 +47,7 @@ async def background_worker(
|
|
|
47
47
|
webserver_only: bool = False,
|
|
48
48
|
) -> AsyncGenerator[None, None]:
|
|
49
49
|
worker_task: asyncio.Task[None] | None = None
|
|
50
|
-
|
|
50
|
+
try:
|
|
51
51
|
# Register background task functions
|
|
52
52
|
docket.register_collection(
|
|
53
53
|
"prefect.server.api.background_workers:task_functions"
|
|
@@ -58,14 +58,24 @@ async def background_worker(
|
|
|
58
58
|
docket, ephemeral=ephemeral, webserver_only=webserver_only
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
async with Worker(docket) as worker:
|
|
62
62
|
worker_task = asyncio.create_task(worker.run_forever())
|
|
63
63
|
yield
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
finally:
|
|
66
|
+
if worker_task:
|
|
67
|
+
worker_task.cancel()
|
|
68
|
+
try:
|
|
69
|
+
logger.debug(
|
|
70
|
+
"Waiting for background worker to finish after cancellation..."
|
|
71
|
+
)
|
|
72
|
+
await asyncio.wait_for(worker_task, timeout=5.0)
|
|
73
|
+
logger.debug(
|
|
74
|
+
"Background worker finished successfully after cancellation"
|
|
75
|
+
)
|
|
76
|
+
except asyncio.TimeoutError:
|
|
77
|
+
logger.debug(
|
|
78
|
+
"Background worker did not finish within 5 seconds after cancellation. Proceeding with shutdown"
|
|
79
|
+
)
|
|
80
|
+
except asyncio.CancelledError:
|
|
81
|
+
pass
|
prefect/workers/base.py
CHANGED
|
@@ -766,7 +766,6 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
|
766
766
|
flow: "Flow[..., FR]",
|
|
767
767
|
parameters: dict[str, Any] | None = None,
|
|
768
768
|
job_variables: dict[str, Any] | None = None,
|
|
769
|
-
flow_run: "FlowRun | None" = None,
|
|
770
769
|
) -> "PrefectFlowRunFuture[FR]":
|
|
771
770
|
"""
|
|
772
771
|
EXPERIMENTAL: The interface for this method is subject to change.
|
|
@@ -776,11 +775,9 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
|
776
775
|
Args:
|
|
777
776
|
flow: The flow to submit
|
|
778
777
|
parameters: The parameters to pass to the flow
|
|
779
|
-
job_variables: Job variables for infrastructure configuration
|
|
780
|
-
flow_run: Optional existing flow run to retry (reuses ID instead of creating new)
|
|
781
778
|
|
|
782
779
|
Returns:
|
|
783
|
-
A flow run
|
|
780
|
+
A flow run object
|
|
784
781
|
"""
|
|
785
782
|
warnings.warn(
|
|
786
783
|
"Ad-hoc flow submission via workers is experimental. The interface "
|
|
@@ -796,7 +793,6 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
|
796
793
|
flow=flow,
|
|
797
794
|
parameters=parameters,
|
|
798
795
|
job_variables=job_variables,
|
|
799
|
-
flow_run=flow_run,
|
|
800
796
|
),
|
|
801
797
|
)
|
|
802
798
|
return PrefectFlowRunFuture(flow_run_id=flow_run.id)
|
|
@@ -807,17 +803,9 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
|
807
803
|
parameters: dict[str, Any] | None = None,
|
|
808
804
|
job_variables: dict[str, Any] | None = None,
|
|
809
805
|
task_status: anyio.abc.TaskStatus["FlowRun"] | None = None,
|
|
810
|
-
flow_run: "FlowRun | None" = None,
|
|
811
806
|
):
|
|
812
807
|
"""
|
|
813
808
|
Submits a flow for the worker to kick off execution for.
|
|
814
|
-
|
|
815
|
-
Args:
|
|
816
|
-
flow: The flow to submit
|
|
817
|
-
parameters: The parameters to pass to the flow
|
|
818
|
-
job_variables: Job variables for infrastructure configuration
|
|
819
|
-
task_status: Task status for signaling when the flow run is ready
|
|
820
|
-
flow_run: Optional existing flow run to retry (reuses ID instead of creating new)
|
|
821
809
|
"""
|
|
822
810
|
from prefect._experimental.bundles import (
|
|
823
811
|
aupload_bundle_to_storage,
|
|
@@ -874,40 +862,28 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
|
874
862
|
job_variables = (job_variables or {}) | {"command": " ".join(execute_command)}
|
|
875
863
|
parameters = parameters or {}
|
|
876
864
|
|
|
877
|
-
if
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
fn=flow.fn,
|
|
885
|
-
version=flow.version,
|
|
886
|
-
)
|
|
887
|
-
parent_task_run = await parent_task.create_run(
|
|
888
|
-
flow_run_context=flow_run_ctx,
|
|
889
|
-
parameters=parameters,
|
|
890
|
-
)
|
|
891
|
-
|
|
892
|
-
flow_run = await self.client.create_flow_run(
|
|
893
|
-
flow,
|
|
894
|
-
parameters=flow.serialize_parameters(parameters),
|
|
895
|
-
state=Pending(),
|
|
896
|
-
job_variables=job_variables,
|
|
897
|
-
work_pool_name=self.work_pool.name,
|
|
898
|
-
tags=TagsContext.get().current_tags,
|
|
899
|
-
parent_task_run_id=getattr(parent_task_run, "id", None),
|
|
865
|
+
# Create a parent task run if this is a child flow run to ensure it shows up as a child flow in the UI
|
|
866
|
+
parent_task_run = None
|
|
867
|
+
if flow_run_ctx := FlowRunContext.get():
|
|
868
|
+
parent_task = Task[Any, Any](
|
|
869
|
+
name=flow.name,
|
|
870
|
+
fn=flow.fn,
|
|
871
|
+
version=flow.version,
|
|
900
872
|
)
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
flow_run_id=flow_run.id,
|
|
905
|
-
state=Pending(message="Retrying on remote infrastructure"),
|
|
906
|
-
force=True,
|
|
873
|
+
parent_task_run = await parent_task.create_run(
|
|
874
|
+
flow_run_context=flow_run_ctx,
|
|
875
|
+
parameters=parameters,
|
|
907
876
|
)
|
|
908
|
-
# Re-fetch to get updated state
|
|
909
|
-
flow_run = await self.client.read_flow_run(flow_run.id)
|
|
910
877
|
|
|
878
|
+
flow_run = await self.client.create_flow_run(
|
|
879
|
+
flow,
|
|
880
|
+
parameters=flow.serialize_parameters(parameters),
|
|
881
|
+
state=Pending(),
|
|
882
|
+
job_variables=job_variables,
|
|
883
|
+
work_pool_name=self.work_pool.name,
|
|
884
|
+
tags=TagsContext.get().current_tags,
|
|
885
|
+
parent_task_run_id=getattr(parent_task_run, "id", None),
|
|
886
|
+
)
|
|
911
887
|
if task_status is not None:
|
|
912
888
|
# Emit the flow run object to .submit to allow it to return a future as soon as possible
|
|
913
889
|
task_status.started(flow_run)
|
prefect/workers/process.py
CHANGED
|
@@ -263,27 +263,18 @@ class ProcessWorker(
|
|
|
263
263
|
parameters: dict[str, Any] | None = None,
|
|
264
264
|
job_variables: dict[str, Any] | None = None,
|
|
265
265
|
task_status: anyio.abc.TaskStatus["FlowRun"] | None = None,
|
|
266
|
-
flow_run: "FlowRun | None" = None,
|
|
267
266
|
):
|
|
268
267
|
from prefect._experimental.bundles import (
|
|
269
268
|
create_bundle_for_flow_run,
|
|
270
269
|
)
|
|
271
270
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
)
|
|
280
|
-
else:
|
|
281
|
-
# Reuse existing flow run - set state to Pending for retry
|
|
282
|
-
await self.client.set_flow_run_state(
|
|
283
|
-
flow_run.id,
|
|
284
|
-
Pending(),
|
|
285
|
-
force=True,
|
|
286
|
-
)
|
|
271
|
+
flow_run = await self.client.create_flow_run(
|
|
272
|
+
flow,
|
|
273
|
+
parameters=parameters,
|
|
274
|
+
state=Pending(),
|
|
275
|
+
job_variables=job_variables,
|
|
276
|
+
work_pool_name=self.work_pool.name,
|
|
277
|
+
)
|
|
287
278
|
if task_status is not None:
|
|
288
279
|
# Emit the flow run object to .submit to allow it to return a future as soon as possible
|
|
289
280
|
task_status.started(flow_run)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prefect
|
|
3
|
-
Version: 3.6.8
|
|
3
|
+
Version: 3.6.8.dev3
|
|
4
4
|
Summary: Workflow orchestration and management.
|
|
5
5
|
Project-URL: Changelog, https://github.com/PrefectHQ/prefect/releases
|
|
6
6
|
Project-URL: Documentation, https://docs.prefect.io
|
|
@@ -57,7 +57,7 @@ Requires-Dist: pydantic!=2.11.0,!=2.11.1,!=2.11.2,!=2.11.3,!=2.11.4,<3.0.0,>=2.1
|
|
|
57
57
|
Requires-Dist: pydantic-core<3.0.0,>=2.12.0
|
|
58
58
|
Requires-Dist: pydantic-extra-types<3.0.0,>=2.8.2
|
|
59
59
|
Requires-Dist: pydantic-settings!=2.9.0,<3.0.0,>2.2.1
|
|
60
|
-
Requires-Dist: pydocket>=0.
|
|
60
|
+
Requires-Dist: pydocket>=0.13.0
|
|
61
61
|
Requires-Dist: python-dateutil<3.0.0,>=2.8.2
|
|
62
62
|
Requires-Dist: python-slugify<9.0,>=5.0
|
|
63
63
|
Requires-Dist: pytz<2026,>=2021.1
|
|
@@ -2,7 +2,7 @@ prefect/.prefectignore,sha256=awSprvKT0vI8a64mEOLrMxhxqcO-b0ERQeYpA2rNKVQ,390
|
|
|
2
2
|
prefect/AGENTS.md,sha256=qmCZAuKIF9jQyp5TrW_T8bsM_97-QaiCoQp71A_b2Lg,1008
|
|
3
3
|
prefect/__init__.py,sha256=-cFYePxZEyhJ4zSCwCu9QhTgvA6yRoUYMlAluL8voyE,5441
|
|
4
4
|
prefect/__main__.py,sha256=WFjw3kaYJY6pOTA7WDOgqjsz8zUEUZHCcj3P5wyVa-g,66
|
|
5
|
-
prefect/_build_info.py,sha256=
|
|
5
|
+
prefect/_build_info.py,sha256=pjqJwa2UD5QkleC2y5OsltOZypKLN4qhW-336oKE56Q,185
|
|
6
6
|
prefect/_result_records.py,sha256=S6QmsODkehGVSzbMm6ig022PYbI6gNKz671p_8kBYx4,7789
|
|
7
7
|
prefect/_states.py,sha256=_BIcTNbnExppu4sRIbeOHxdt5DeaIUAa9qKE8sNIkQ0,9775
|
|
8
8
|
prefect/_versioning.py,sha256=YqR5cxXrY4P6LM1Pmhd8iMo7v_G2KJpGNdsf4EvDFQ0,14132
|
|
@@ -17,8 +17,8 @@ prefect/exceptions.py,sha256=E67przt2JKW2hzXQGC2XMIMxM8PNtnDunwhW55VUsM8,12598
|
|
|
17
17
|
prefect/filesystems.py,sha256=PrDsWxT5mfmJSs3ib3pNaKkjEEBExY2kGrhh5fPWFIM,22393
|
|
18
18
|
prefect/flow_engine.py,sha256=bWGHTJ3WZs7taLAzbDvkr8Gvg_NgB7riHmjPtstj8xA,61774
|
|
19
19
|
prefect/flow_runs.py,sha256=gZBtxwSk2k-bUXUvb_Eipg00F2hEm5_batIse85ngXE,18495
|
|
20
|
-
prefect/flows.py,sha256=
|
|
21
|
-
prefect/futures.py,sha256=
|
|
20
|
+
prefect/flows.py,sha256=9lacxyuc2K16KcwcauOdsggt3f21NZDjJ4g8F9AxVI0,128521
|
|
21
|
+
prefect/futures.py,sha256=HwxR17RRuBHT_r5c7XuhmuYG8NnreFCUEuO_FpRBxPw,25670
|
|
22
22
|
prefect/main.py,sha256=swco0ugs87OPf0nVFtHSmFUAyyMkNFUKC_6Gncp-FFU,2547
|
|
23
23
|
prefect/plugins.py,sha256=6bPNLs5Cab1VqwFAlfwJi7Wj3ASx0ZLdT5_-s8_Blyo,2510
|
|
24
24
|
prefect/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -90,7 +90,7 @@ prefect/blocks/__init__.py,sha256=D0hB72qMfgqnBB2EMZRxUxlX9yLfkab5zDChOwJZmkY,22
|
|
|
90
90
|
prefect/blocks/abstract.py,sha256=mpOAWopSR_RrzdxeurBTXVSKisP8ne-k8LYos-tp7go,17021
|
|
91
91
|
prefect/blocks/core.py,sha256=V7a00ewzL37wPkSOrAtdty0gxpVxtqTt3RYOQUfiW9I,67055
|
|
92
92
|
prefect/blocks/fields.py,sha256=1m507VVmkpOnMF_7N-qboRjtw4_ceIuDneX3jZ3Jm54,63
|
|
93
|
-
prefect/blocks/notifications.py,sha256=
|
|
93
|
+
prefect/blocks/notifications.py,sha256=3Rbicq2IGBLM9B0nfHOLeEs9GXuVWW1VyOjmli6ieFs,37042
|
|
94
94
|
prefect/blocks/redis.py,sha256=fn6nR_ZoFfJqO_sWc-4WUY3D2No7RSXUntgZtRFNXYo,7682
|
|
95
95
|
prefect/blocks/system.py,sha256=9OE9gs0cH0bH6DV5dvf4oprthWBKA6Zh56aSnn2pIU4,2325
|
|
96
96
|
prefect/blocks/webhook.py,sha256=xylFigbDOsn-YzxahkTzNqYwrIA7wwS6204P0goLY3A,2907
|
|
@@ -109,7 +109,7 @@ prefect/cli/dev.py,sha256=1toGWrEvskTjir1agQXsCVWezFuvZjCkjNsFYExBmNE,11996
|
|
|
109
109
|
prefect/cli/events.py,sha256=E_QDm9eEDKxS64QRdmYsp7qPXQXfgIpNq2h575foDxw,6076
|
|
110
110
|
prefect/cli/experimental.py,sha256=skZ_c-agYqMT-YlDzfqjt6AvoXussbaIVGByYlUw12Q,5267
|
|
111
111
|
prefect/cli/flow.py,sha256=lBHC9y6ZeLgeZifsfd7CukGYqp7XrbaLBE7X1yh6OME,5789
|
|
112
|
-
prefect/cli/flow_run.py,sha256=
|
|
112
|
+
prefect/cli/flow_run.py,sha256=7Ju-3k8Px_-Xm0yRkHubtVu8gGbJdl-bMrY9INLBGEM,14684
|
|
113
113
|
prefect/cli/flow_runs_watching.py,sha256=nOeLoTn1a-jzR4A1PTjC6reaFO8Qds3bQhhQ40xlUFA,6805
|
|
114
114
|
prefect/cli/global_concurrency_limit.py,sha256=AiFBv2tqrs4QI5mASDa-TGodXSIpvpuRbwcJek8nmr0,14080
|
|
115
115
|
prefect/cli/profile.py,sha256=v1nc2oaWFAwlZNv3yKjcY3qMhencgHkN4-QF-AEQBsA,14712
|
|
@@ -290,7 +290,7 @@ prefect/server/api/__init__.py,sha256=SpRTXHC6ApqR4_Y3wNHM7TvvH9FZ8N3tH-RZIgmubg
|
|
|
290
290
|
prefect/server/api/admin.py,sha256=nINYSrux7XPAV4MMDQUts3X2dddrc3mJtd3iPl5N-jI,2644
|
|
291
291
|
prefect/server/api/artifacts.py,sha256=B19bxxnADiVTo0XtzvsbAHHtsJwr7S1IunjGl-qsSd4,7314
|
|
292
292
|
prefect/server/api/automations.py,sha256=KlQuhEOOKkxAI6f5aZedCKMELi99MdknulIleRP-JVE,8285
|
|
293
|
-
prefect/server/api/background_workers.py,sha256=
|
|
293
|
+
prefect/server/api/background_workers.py,sha256=02hwtFwWjcKK4U2QQMSCgoOyQ2CyrHYMVDbbajJcUzQ,2884
|
|
294
294
|
prefect/server/api/block_capabilities.py,sha256=0x1vtC2CtSRVcCWydgNbylmfv_LnJHIoPnt7hRiJkN4,828
|
|
295
295
|
prefect/server/api/block_documents.py,sha256=Hj-J10XMnfJGa2-AbJfZHun-iZrUwzxG8h7cXGfBXjg,5838
|
|
296
296
|
prefect/server/api/block_schemas.py,sha256=H9kxnKPlTvz62SMcX1ssHnQ8KACU2728ebCtSp2iLcs,5508
|
|
@@ -889,14 +889,14 @@ prefect/utilities/schema_tools/__init__.py,sha256=At3rMHd2g_Em2P3_dFQlFgqR_EpBwr
|
|
|
889
889
|
prefect/utilities/schema_tools/hydration.py,sha256=NkRhWkNfxxFmVGhNDfmxdK_xeKaEhs3a42q83Sg9cT4,9436
|
|
890
890
|
prefect/utilities/schema_tools/validation.py,sha256=UhRLWStdT9__u4yz-mnc3lcdt-VJmRGJBGV_f_9um-Y,10749
|
|
891
891
|
prefect/workers/__init__.py,sha256=EaM1F0RZ-XIJaGeTKLsXDnfOPHzVWk5bk0_c4BVS44M,64
|
|
892
|
-
prefect/workers/base.py,sha256=
|
|
892
|
+
prefect/workers/base.py,sha256=n0tcQWGg_gQX1zJ-0WHnSCQMzYyg69Lx8fuwSduoGik,64663
|
|
893
893
|
prefect/workers/block.py,sha256=dPvG1jDGD5HSH7aM2utwtk6RaJ9qg13XjkA0lAIgQmY,287
|
|
894
894
|
prefect/workers/cloud.py,sha256=dPvG1jDGD5HSH7aM2utwtk6RaJ9qg13XjkA0lAIgQmY,287
|
|
895
|
-
prefect/workers/process.py,sha256=
|
|
895
|
+
prefect/workers/process.py,sha256=jvxBQyR8-G4Svt0as-BNg94YpvRMYZBIXKtNwC0igNY,11902
|
|
896
896
|
prefect/workers/server.py,sha256=bWnYfMfJf5_IO3y3aJOpia7p9lFKC3ZZjiMvHox-UKY,1992
|
|
897
897
|
prefect/workers/utilities.py,sha256=VfPfAlGtTuDj0-Kb8WlMgAuOfgXCdrGAnKMapPSBrwc,2483
|
|
898
|
-
prefect-3.6.8.dist-info/METADATA,sha256=
|
|
899
|
-
prefect-3.6.8.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
900
|
-
prefect-3.6.8.dist-info/entry_points.txt,sha256=HlY8up83iIq2vU2r33a0qSis4eOFSyb1mRH4l7Xt9X8,126
|
|
901
|
-
prefect-3.6.8.dist-info/licenses/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
|
|
902
|
-
prefect-3.6.8.dist-info/RECORD,,
|
|
898
|
+
prefect-3.6.8.dev3.dist-info/METADATA,sha256=CIMb1Dqj_yKYfgBvjK3jW4ApcTb6ID-6iHukFw9PgLs,13659
|
|
899
|
+
prefect-3.6.8.dev3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
900
|
+
prefect-3.6.8.dev3.dist-info/entry_points.txt,sha256=HlY8up83iIq2vU2r33a0qSis4eOFSyb1mRH4l7Xt9X8,126
|
|
901
|
+
prefect-3.6.8.dev3.dist-info/licenses/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
|
|
902
|
+
prefect-3.6.8.dev3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|