prefect-client 3.1.14__py3-none-any.whl → 3.2.0__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/__main__.py +4 -0
- prefect/_experimental/lineage.py +40 -22
- prefect/_experimental/sla/objects.py +29 -1
- prefect/_internal/compatibility/deprecated.py +4 -4
- prefect/_internal/compatibility/migration.py +1 -1
- prefect/_internal/concurrency/calls.py +1 -2
- prefect/_internal/concurrency/cancellation.py +2 -4
- prefect/_internal/concurrency/services.py +1 -1
- prefect/_internal/concurrency/threads.py +3 -3
- prefect/_internal/schemas/bases.py +3 -11
- prefect/_internal/schemas/validators.py +36 -60
- prefect/_result_records.py +235 -0
- prefect/_version.py +3 -3
- prefect/agent.py +1 -0
- prefect/artifacts.py +408 -105
- prefect/automations.py +4 -8
- prefect/blocks/core.py +1 -1
- prefect/blocks/notifications.py +13 -8
- prefect/cache_policies.py +2 -0
- prefect/client/base.py +7 -8
- prefect/client/collections.py +3 -6
- prefect/client/orchestration/__init__.py +15 -263
- prefect/client/orchestration/_deployments/client.py +14 -6
- prefect/client/orchestration/_flow_runs/client.py +10 -6
- prefect/client/orchestration/_work_pools/__init__.py +0 -0
- prefect/client/orchestration/_work_pools/client.py +598 -0
- prefect/client/orchestration/base.py +9 -2
- prefect/client/schemas/actions.py +77 -3
- prefect/client/schemas/objects.py +22 -50
- prefect/client/schemas/schedules.py +11 -22
- prefect/client/types/flexible_schedule_list.py +2 -1
- prefect/context.py +2 -3
- prefect/deployments/base.py +13 -16
- prefect/deployments/flow_runs.py +1 -1
- prefect/deployments/runner.py +236 -47
- prefect/deployments/schedules.py +7 -1
- prefect/engine.py +4 -9
- prefect/events/clients.py +39 -0
- prefect/events/schemas/automations.py +4 -2
- prefect/events/utilities.py +15 -13
- prefect/exceptions.py +1 -1
- prefect/flow_engine.py +119 -0
- prefect/flow_runs.py +4 -8
- prefect/flows.py +282 -31
- prefect/infrastructure/__init__.py +1 -0
- prefect/infrastructure/base.py +1 -0
- prefect/infrastructure/provisioners/__init__.py +3 -6
- prefect/infrastructure/provisioners/coiled.py +3 -3
- prefect/infrastructure/provisioners/container_instance.py +1 -0
- prefect/infrastructure/provisioners/ecs.py +6 -6
- prefect/infrastructure/provisioners/modal.py +3 -3
- prefect/input/run_input.py +5 -7
- prefect/locking/filesystem.py +4 -3
- prefect/main.py +1 -1
- prefect/results.py +42 -249
- prefect/runner/runner.py +9 -4
- prefect/runner/server.py +5 -5
- prefect/runner/storage.py +12 -10
- prefect/runner/submit.py +2 -4
- prefect/runtime/task_run.py +37 -9
- prefect/schedules.py +231 -0
- prefect/serializers.py +5 -5
- prefect/settings/__init__.py +2 -1
- prefect/settings/base.py +3 -3
- prefect/settings/models/root.py +4 -0
- prefect/settings/models/server/services.py +50 -9
- prefect/settings/sources.py +4 -4
- prefect/states.py +42 -11
- prefect/task_engine.py +10 -10
- prefect/task_runners.py +11 -22
- prefect/task_worker.py +9 -9
- prefect/tasks.py +28 -45
- prefect/telemetry/bootstrap.py +4 -6
- prefect/telemetry/services.py +2 -4
- prefect/types/__init__.py +2 -1
- prefect/types/_datetime.py +28 -1
- prefect/utilities/_engine.py +0 -1
- prefect/utilities/asyncutils.py +4 -8
- prefect/utilities/collections.py +13 -22
- prefect/utilities/dispatch.py +2 -4
- prefect/utilities/dockerutils.py +6 -6
- prefect/utilities/importtools.py +1 -68
- prefect/utilities/names.py +1 -1
- prefect/utilities/processutils.py +3 -6
- prefect/utilities/pydantic.py +4 -6
- prefect/utilities/render_swagger.py +1 -1
- prefect/utilities/schema_tools/hydration.py +6 -5
- prefect/utilities/templating.py +21 -8
- prefect/utilities/visualization.py +2 -4
- prefect/workers/base.py +3 -3
- prefect/workers/block.py +1 -0
- prefect/workers/cloud.py +1 -0
- prefect/workers/process.py +1 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/METADATA +1 -1
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/RECORD +98 -93
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.14.dist-info → prefect_client-3.2.0.dist-info}/top_level.txt +0 -0
prefect/events/utilities.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from datetime import timedelta
|
2
|
-
from typing import Any
|
4
|
+
from typing import Any
|
3
5
|
from uuid import UUID
|
4
6
|
|
5
|
-
import
|
7
|
+
from prefect.types import DateTime
|
6
8
|
|
7
9
|
from .clients import (
|
8
10
|
AssertingEventsClient,
|
@@ -18,16 +20,16 @@ TIGHT_TIMING = timedelta(minutes=5)
|
|
18
20
|
|
19
21
|
def emit_event(
|
20
22
|
event: str,
|
21
|
-
resource:
|
22
|
-
occurred:
|
23
|
-
related:
|
24
|
-
payload:
|
25
|
-
id:
|
26
|
-
follows:
|
27
|
-
**kwargs:
|
28
|
-
) ->
|
23
|
+
resource: dict[str, str],
|
24
|
+
occurred: DateTime | None = None,
|
25
|
+
related: list[dict[str, str]] | list[RelatedResource] | None = None,
|
26
|
+
payload: dict[str, Any] | None = None,
|
27
|
+
id: UUID | None = None,
|
28
|
+
follows: Event | None = None,
|
29
|
+
**kwargs: dict[str, Any] | None,
|
30
|
+
) -> Event | None:
|
29
31
|
"""
|
30
|
-
Send an event to Prefect
|
32
|
+
Send an event to Prefect.
|
31
33
|
|
32
34
|
Args:
|
33
35
|
event: The name of the event that happened.
|
@@ -60,14 +62,14 @@ def emit_event(
|
|
60
62
|
if worker_instance.client_type not in operational_clients:
|
61
63
|
return None
|
62
64
|
|
63
|
-
event_kwargs:
|
65
|
+
event_kwargs: dict[str, Any] = {
|
64
66
|
"event": event,
|
65
67
|
"resource": resource,
|
66
68
|
**kwargs,
|
67
69
|
}
|
68
70
|
|
69
71
|
if occurred is None:
|
70
|
-
occurred =
|
72
|
+
occurred = DateTime.now("UTC")
|
71
73
|
event_kwargs["occurred"] = occurred
|
72
74
|
|
73
75
|
if related is not None:
|
prefect/exceptions.py
CHANGED
@@ -161,7 +161,7 @@ class ParameterTypeError(PrefectException):
|
|
161
161
|
@classmethod
|
162
162
|
def from_validation_error(cls, exc: ValidationError) -> Self:
|
163
163
|
bad_params = [
|
164
|
-
f'
|
164
|
+
f"{'.'.join(str(item) for item in err['loc'])}: {err['msg']}"
|
165
165
|
for err in exc.errors()
|
166
166
|
]
|
167
167
|
msg = "Flow run received invalid parameters:\n - " + "\n - ".join(bad_params)
|
prefect/flow_engine.py
CHANGED
@@ -2,10 +2,13 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import logging
|
5
|
+
import multiprocessing
|
6
|
+
import multiprocessing.context
|
5
7
|
import os
|
6
8
|
import time
|
7
9
|
from contextlib import ExitStack, asynccontextmanager, contextmanager, nullcontext
|
8
10
|
from dataclasses import dataclass, field
|
11
|
+
from functools import wraps
|
9
12
|
from typing import (
|
10
13
|
Any,
|
11
14
|
AsyncGenerator,
|
@@ -37,9 +40,12 @@ from prefect.concurrency.v1.context import ConcurrencyContext as ConcurrencyCont
|
|
37
40
|
from prefect.context import (
|
38
41
|
AsyncClientContext,
|
39
42
|
FlowRunContext,
|
43
|
+
SettingsContext,
|
40
44
|
SyncClientContext,
|
41
45
|
TagsContext,
|
46
|
+
get_settings_context,
|
42
47
|
hydrated_context,
|
48
|
+
serialize_context,
|
43
49
|
)
|
44
50
|
from prefect.exceptions import (
|
45
51
|
Abort,
|
@@ -62,6 +68,8 @@ from prefect.results import (
|
|
62
68
|
should_persist_result,
|
63
69
|
)
|
64
70
|
from prefect.settings import PREFECT_DEBUG_MODE
|
71
|
+
from prefect.settings.context import get_current_settings
|
72
|
+
from prefect.settings.models.root import Settings
|
65
73
|
from prefect.states import (
|
66
74
|
Failed,
|
67
75
|
Pending,
|
@@ -83,6 +91,7 @@ from prefect.utilities.annotations import NotSet
|
|
83
91
|
from prefect.utilities.asyncutils import run_coro_as_sync
|
84
92
|
from prefect.utilities.callables import (
|
85
93
|
call_with_parameters,
|
94
|
+
cloudpickle_wrapped_call,
|
86
95
|
get_call_parameters,
|
87
96
|
parameters_to_args_kwargs,
|
88
97
|
)
|
@@ -1509,6 +1518,8 @@ def run_flow(
|
|
1509
1518
|
ret_val = run_flow_async(**kwargs)
|
1510
1519
|
else:
|
1511
1520
|
ret_val = run_flow_sync(**kwargs)
|
1521
|
+
except (Abort, Pause):
|
1522
|
+
raise
|
1512
1523
|
except:
|
1513
1524
|
if error_logger:
|
1514
1525
|
error_logger.error(
|
@@ -1533,3 +1544,111 @@ def _flow_parameters(
|
|
1533
1544
|
parameters = flow_run.parameters if flow_run else {}
|
1534
1545
|
call_args, call_kwargs = parameters_to_args_kwargs(flow.fn, parameters)
|
1535
1546
|
return get_call_parameters(flow.fn, call_args, call_kwargs)
|
1547
|
+
|
1548
|
+
|
1549
|
+
def run_flow_in_subprocess(
|
1550
|
+
flow: "Flow[..., Any]",
|
1551
|
+
flow_run: "FlowRun | None" = None,
|
1552
|
+
parameters: dict[str, Any] | None = None,
|
1553
|
+
wait_for: Iterable[PrefectFuture[Any]] | None = None,
|
1554
|
+
context: dict[str, Any] | None = None,
|
1555
|
+
) -> multiprocessing.context.SpawnProcess:
|
1556
|
+
"""
|
1557
|
+
Run a flow in a subprocess.
|
1558
|
+
|
1559
|
+
Note the result of the flow will only be accessible if the flow is configured to
|
1560
|
+
persist its result.
|
1561
|
+
|
1562
|
+
Args:
|
1563
|
+
flow: The flow to run.
|
1564
|
+
flow_run: The flow run object containing run metadata.
|
1565
|
+
parameters: The parameters to use when invoking the flow.
|
1566
|
+
wait_for: The futures to wait for before starting the flow.
|
1567
|
+
context: A serialized context to hydrate before running the flow. If not provided,
|
1568
|
+
the current context will be used. A serialized context should be provided if
|
1569
|
+
this function is called in a separate memory space from the parent run (e.g.
|
1570
|
+
in a subprocess or on another machine).
|
1571
|
+
|
1572
|
+
Returns:
|
1573
|
+
A multiprocessing.context.SpawnProcess representing the process that is running the flow.
|
1574
|
+
"""
|
1575
|
+
from prefect.flow_engine import run_flow
|
1576
|
+
|
1577
|
+
@wraps(run_flow)
|
1578
|
+
def run_flow_with_env(
|
1579
|
+
*args: Any,
|
1580
|
+
env: dict[str, str] | None = None,
|
1581
|
+
**kwargs: Any,
|
1582
|
+
):
|
1583
|
+
"""
|
1584
|
+
Wrapper function to update environment variables and settings before running the flow.
|
1585
|
+
"""
|
1586
|
+
engine_logger = logging.getLogger("prefect.engine")
|
1587
|
+
|
1588
|
+
os.environ.update(env or {})
|
1589
|
+
settings_context = get_settings_context()
|
1590
|
+
# Create a new settings context with a new settings object to pick up the updated
|
1591
|
+
# environment variables
|
1592
|
+
with SettingsContext(
|
1593
|
+
profile=settings_context.profile,
|
1594
|
+
settings=Settings(),
|
1595
|
+
):
|
1596
|
+
try:
|
1597
|
+
maybe_coro = run_flow(*args, **kwargs)
|
1598
|
+
if asyncio.iscoroutine(maybe_coro):
|
1599
|
+
# This is running in a brand new process, so there won't be an existing
|
1600
|
+
# event loop.
|
1601
|
+
asyncio.run(maybe_coro)
|
1602
|
+
except Abort:
|
1603
|
+
if flow_run:
|
1604
|
+
msg = f"Execution of flow run '{flow_run.id}' aborted by orchestrator."
|
1605
|
+
else:
|
1606
|
+
msg = "Execution aborted by orchestrator."
|
1607
|
+
engine_logger.info(msg)
|
1608
|
+
exit(0)
|
1609
|
+
except Pause:
|
1610
|
+
if flow_run:
|
1611
|
+
msg = f"Execution of flow run '{flow_run.id}' is paused."
|
1612
|
+
else:
|
1613
|
+
msg = "Execution is paused."
|
1614
|
+
engine_logger.info(msg)
|
1615
|
+
exit(0)
|
1616
|
+
except Exception:
|
1617
|
+
if flow_run:
|
1618
|
+
msg = f"Execution of flow run '{flow_run.id}' exited with unexpected exception"
|
1619
|
+
else:
|
1620
|
+
msg = "Execution exited with unexpected exception"
|
1621
|
+
engine_logger.error(msg, exc_info=True)
|
1622
|
+
exit(1)
|
1623
|
+
except BaseException:
|
1624
|
+
if flow_run:
|
1625
|
+
msg = f"Execution of flow run '{flow_run.id}' interrupted by base exception"
|
1626
|
+
else:
|
1627
|
+
msg = "Execution interrupted by base exception"
|
1628
|
+
engine_logger.error(msg, exc_info=True)
|
1629
|
+
# Let the exit code be determined by the base exception type
|
1630
|
+
raise
|
1631
|
+
|
1632
|
+
ctx = multiprocessing.get_context("spawn")
|
1633
|
+
|
1634
|
+
context = context or serialize_context()
|
1635
|
+
|
1636
|
+
process = ctx.Process(
|
1637
|
+
target=cloudpickle_wrapped_call(
|
1638
|
+
run_flow_with_env,
|
1639
|
+
env=get_current_settings().to_environment_variables(exclude_unset=True)
|
1640
|
+
| os.environ
|
1641
|
+
| {
|
1642
|
+
# TODO: make this a thing we can pass into the engine
|
1643
|
+
"PREFECT__ENABLE_CANCELLATION_AND_CRASHED_HOOKS": "false",
|
1644
|
+
},
|
1645
|
+
flow=flow,
|
1646
|
+
flow_run=flow_run,
|
1647
|
+
parameters=parameters,
|
1648
|
+
wait_for=wait_for,
|
1649
|
+
context=context,
|
1650
|
+
),
|
1651
|
+
)
|
1652
|
+
process.start()
|
1653
|
+
|
1654
|
+
return process
|
prefect/flow_runs.py
CHANGED
@@ -139,8 +139,7 @@ async def pause_flow_run(
|
|
139
139
|
timeout: int = 3600,
|
140
140
|
poll_interval: int = 10,
|
141
141
|
key: Optional[str] = None,
|
142
|
-
) -> None:
|
143
|
-
...
|
142
|
+
) -> None: ...
|
144
143
|
|
145
144
|
|
146
145
|
@overload
|
@@ -149,8 +148,7 @@ async def pause_flow_run(
|
|
149
148
|
timeout: int = 3600,
|
150
149
|
poll_interval: int = 10,
|
151
150
|
key: Optional[str] = None,
|
152
|
-
) -> T:
|
153
|
-
...
|
151
|
+
) -> T: ...
|
154
152
|
|
155
153
|
|
156
154
|
@sync_compatible
|
@@ -308,8 +306,7 @@ async def suspend_flow_run(
|
|
308
306
|
timeout: Optional[int] = 3600,
|
309
307
|
key: Optional[str] = None,
|
310
308
|
client: Optional[PrefectClient] = None,
|
311
|
-
) -> None:
|
312
|
-
...
|
309
|
+
) -> None: ...
|
313
310
|
|
314
311
|
|
315
312
|
@overload
|
@@ -319,8 +316,7 @@ async def suspend_flow_run(
|
|
319
316
|
timeout: Optional[int] = 3600,
|
320
317
|
key: Optional[str] = None,
|
321
318
|
client: Optional[PrefectClient] = None,
|
322
|
-
) -> T:
|
323
|
-
...
|
319
|
+
) -> T: ...
|
324
320
|
|
325
321
|
|
326
322
|
@sync_compatible
|