prefect-client 2.18.2__py3-none-any.whl → 2.19.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/__init__.py +1 -15
- prefect/_internal/concurrency/cancellation.py +2 -0
- prefect/_internal/schemas/validators.py +10 -0
- prefect/_vendor/starlette/testclient.py +1 -1
- prefect/blocks/notifications.py +6 -6
- prefect/client/base.py +244 -1
- prefect/client/cloud.py +4 -2
- prefect/client/orchestration.py +515 -106
- prefect/client/schemas/actions.py +58 -8
- prefect/client/schemas/objects.py +15 -1
- prefect/client/schemas/responses.py +19 -0
- prefect/client/schemas/schedules.py +1 -1
- prefect/client/utilities.py +2 -2
- prefect/concurrency/asyncio.py +34 -4
- prefect/concurrency/sync.py +40 -6
- prefect/context.py +2 -2
- prefect/engine.py +17 -1
- prefect/events/clients.py +2 -2
- prefect/flows.py +91 -17
- prefect/infrastructure/process.py +0 -17
- prefect/logging/formatters.py +1 -4
- prefect/new_flow_engine.py +166 -161
- prefect/new_task_engine.py +137 -202
- prefect/runner/__init__.py +1 -1
- prefect/runner/runner.py +2 -107
- prefect/settings.py +11 -0
- prefect/tasks.py +76 -57
- prefect/types/__init__.py +27 -5
- prefect/utilities/annotations.py +1 -8
- prefect/utilities/asyncutils.py +4 -0
- prefect/utilities/engine.py +106 -1
- prefect/utilities/schema_tools/__init__.py +6 -1
- prefect/utilities/schema_tools/validation.py +25 -8
- prefect/utilities/timeout.py +34 -0
- prefect/workers/base.py +7 -3
- prefect/workers/process.py +0 -17
- {prefect_client-2.18.2.dist-info → prefect_client-2.19.0.dist-info}/METADATA +1 -1
- {prefect_client-2.18.2.dist-info → prefect_client-2.19.0.dist-info}/RECORD +41 -40
- {prefect_client-2.18.2.dist-info → prefect_client-2.19.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.18.2.dist-info → prefect_client-2.19.0.dist-info}/WHEEL +0 -0
- {prefect_client-2.18.2.dist-info → prefect_client-2.19.0.dist-info}/top_level.txt +0 -0
prefect/new_flow_engine.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
import asyncio
|
2
1
|
import inspect
|
3
|
-
|
4
|
-
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
import time
|
5
|
+
from contextlib import contextmanager
|
6
|
+
from dataclasses import dataclass, field
|
5
7
|
from typing import (
|
6
8
|
Any,
|
7
9
|
Coroutine,
|
@@ -10,60 +12,84 @@ from typing import (
|
|
10
12
|
Iterable,
|
11
13
|
Literal,
|
12
14
|
Optional,
|
15
|
+
Tuple,
|
13
16
|
TypeVar,
|
14
17
|
Union,
|
15
18
|
cast,
|
16
19
|
)
|
20
|
+
from uuid import UUID
|
17
21
|
|
18
22
|
import anyio
|
19
23
|
import anyio._backends._asyncio
|
20
24
|
from sniffio import AsyncLibraryNotFoundError
|
21
25
|
from typing_extensions import ParamSpec
|
22
26
|
|
23
|
-
from prefect import
|
24
|
-
from prefect.client.orchestration import
|
27
|
+
from prefect import Task, get_client
|
28
|
+
from prefect.client.orchestration import SyncPrefectClient
|
25
29
|
from prefect.client.schemas import FlowRun, TaskRun
|
26
30
|
from prefect.client.schemas.filters import FlowRunFilter
|
27
31
|
from prefect.client.schemas.sorting import FlowRunSort
|
28
32
|
from prefect.context import FlowRunContext
|
33
|
+
from prefect.deployments import load_flow_from_flow_run
|
34
|
+
from prefect.exceptions import Abort, Pause
|
35
|
+
from prefect.flows import Flow, load_flow_from_entrypoint
|
29
36
|
from prefect.futures import PrefectFuture, resolve_futures_to_states
|
30
|
-
from prefect.logging.loggers import flow_run_logger
|
37
|
+
from prefect.logging.loggers import flow_run_logger, get_logger
|
31
38
|
from prefect.results import ResultFactory
|
32
39
|
from prefect.states import (
|
33
40
|
Pending,
|
34
41
|
Running,
|
35
42
|
State,
|
43
|
+
exception_to_crashed_state,
|
36
44
|
exception_to_failed_state,
|
37
45
|
return_value_to_state,
|
38
46
|
)
|
39
47
|
from prefect.utilities.asyncutils import A, Async, run_sync
|
40
48
|
from prefect.utilities.callables import parameters_to_args_kwargs
|
41
49
|
from prefect.utilities.engine import (
|
42
|
-
_dynamic_key_for_task_run,
|
43
50
|
_resolve_custom_flow_run_name,
|
44
|
-
|
45
|
-
propose_state,
|
51
|
+
propose_state_sync,
|
46
52
|
)
|
47
53
|
|
48
54
|
P = ParamSpec("P")
|
49
55
|
R = TypeVar("R")
|
50
56
|
|
51
57
|
|
58
|
+
def load_flow_and_flow_run(flow_run_id: UUID) -> Tuple[FlowRun, Flow]:
|
59
|
+
## TODO: add error handling to update state and log tracebacks
|
60
|
+
entrypoint = os.environ.get("PREFECT__FLOW_ENTRYPOINT")
|
61
|
+
|
62
|
+
client = get_client(sync_client=True)
|
63
|
+
flow_run = client.read_flow_run(flow_run_id)
|
64
|
+
flow = (
|
65
|
+
load_flow_from_entrypoint(entrypoint)
|
66
|
+
if entrypoint
|
67
|
+
else run_sync(load_flow_from_flow_run(flow_run, client=client))
|
68
|
+
)
|
69
|
+
|
70
|
+
return flow_run, flow
|
71
|
+
|
72
|
+
|
52
73
|
@dataclass
|
53
74
|
class FlowRunEngine(Generic[P, R]):
|
54
|
-
flow: Union[Flow[P, R], Flow[P, Coroutine[Any, Any, R]]]
|
75
|
+
flow: Optional[Union[Flow[P, R], Flow[P, Coroutine[Any, Any, R]]]] = None
|
55
76
|
parameters: Optional[Dict[str, Any]] = None
|
56
77
|
flow_run: Optional[FlowRun] = None
|
78
|
+
flow_run_id: Optional[UUID] = None
|
79
|
+
logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
|
57
80
|
_is_started: bool = False
|
58
|
-
_client: Optional[
|
81
|
+
_client: Optional[SyncPrefectClient] = None
|
59
82
|
short_circuit: bool = False
|
60
83
|
|
61
84
|
def __post_init__(self):
|
85
|
+
if self.flow is None and self.flow_run_id is None:
|
86
|
+
raise ValueError("Either a flow or a flow_run_id must be provided.")
|
87
|
+
|
62
88
|
if self.parameters is None:
|
63
89
|
self.parameters = {}
|
64
90
|
|
65
91
|
@property
|
66
|
-
def client(self) ->
|
92
|
+
def client(self) -> SyncPrefectClient:
|
67
93
|
if not self._is_started or self._client is None:
|
68
94
|
raise RuntimeError("Engine has not started.")
|
69
95
|
return self._client
|
@@ -72,64 +98,80 @@ class FlowRunEngine(Generic[P, R]):
|
|
72
98
|
def state(self) -> State:
|
73
99
|
return self.flow_run.state # type: ignore
|
74
100
|
|
75
|
-
|
101
|
+
def begin_run(self) -> State:
|
76
102
|
new_state = Running()
|
77
|
-
state =
|
103
|
+
state = self.set_state(new_state)
|
78
104
|
while state.is_pending():
|
79
|
-
|
80
|
-
state =
|
105
|
+
time.sleep(0.2)
|
106
|
+
state = self.set_state(new_state)
|
81
107
|
return state
|
82
108
|
|
83
|
-
|
109
|
+
def set_state(self, state: State, force: bool = False) -> State:
|
84
110
|
""" """
|
85
111
|
# prevents any state-setting activity
|
86
112
|
if self.short_circuit:
|
87
113
|
return self.state
|
88
114
|
|
89
|
-
state =
|
115
|
+
state = propose_state_sync(
|
116
|
+
self.client, state, flow_run_id=self.flow_run.id, force=force
|
117
|
+
) # type: ignore
|
90
118
|
self.flow_run.state = state # type: ignore
|
91
119
|
self.flow_run.state_name = state.name # type: ignore
|
92
120
|
self.flow_run.state_type = state.type # type: ignore
|
93
121
|
return state
|
94
122
|
|
95
|
-
|
123
|
+
def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
|
96
124
|
_result = self.state.result(raise_on_failure=raise_on_failure, fetch=True) # type: ignore
|
97
125
|
# state.result is a `sync_compatible` function that may or may not return an awaitable
|
98
126
|
# depending on whether the parent frame is sync or not
|
99
127
|
if inspect.isawaitable(_result):
|
100
|
-
_result =
|
128
|
+
_result = run_sync(_result)
|
101
129
|
return _result
|
102
130
|
|
103
|
-
|
131
|
+
def handle_success(self, result: R) -> R:
|
104
132
|
result_factory = getattr(FlowRunContext.get(), "result_factory", None)
|
105
133
|
if result_factory is None:
|
106
134
|
raise ValueError("Result factory is not set")
|
107
|
-
terminal_state =
|
108
|
-
|
109
|
-
|
135
|
+
terminal_state = run_sync(
|
136
|
+
return_value_to_state(
|
137
|
+
run_sync(resolve_futures_to_states(result)),
|
138
|
+
result_factory=result_factory,
|
139
|
+
)
|
110
140
|
)
|
111
|
-
|
141
|
+
self.set_state(terminal_state)
|
112
142
|
return result
|
113
143
|
|
114
|
-
|
144
|
+
def handle_exception(
|
115
145
|
self,
|
116
146
|
exc: Exception,
|
117
147
|
msg: Optional[str] = None,
|
118
148
|
result_factory: Optional[ResultFactory] = None,
|
119
149
|
) -> State:
|
120
150
|
context = FlowRunContext.get()
|
121
|
-
state =
|
122
|
-
|
123
|
-
|
124
|
-
|
151
|
+
state = run_sync(
|
152
|
+
exception_to_failed_state(
|
153
|
+
exc,
|
154
|
+
message=msg or "Flow run encountered an exception:",
|
155
|
+
result_factory=result_factory
|
156
|
+
or getattr(context, "result_factory", None),
|
157
|
+
)
|
125
158
|
)
|
126
|
-
state =
|
159
|
+
state = self.set_state(state)
|
127
160
|
if self.state.is_scheduled():
|
128
|
-
state =
|
161
|
+
state = self.set_state(Running())
|
129
162
|
return state
|
130
163
|
|
131
|
-
|
132
|
-
|
164
|
+
def handle_crash(self, exc: BaseException) -> None:
|
165
|
+
state = run_sync(exception_to_crashed_state(exc))
|
166
|
+
self.logger.error(f"Crash detected! {state.message}")
|
167
|
+
self.logger.debug("Crash details:", exc_info=exc)
|
168
|
+
self.set_state(state, force=True)
|
169
|
+
|
170
|
+
def load_subflow_run(
|
171
|
+
self,
|
172
|
+
parent_task_run: TaskRun,
|
173
|
+
client: SyncPrefectClient,
|
174
|
+
context: FlowRunContext,
|
133
175
|
) -> Union[FlowRun, None]:
|
134
176
|
"""
|
135
177
|
This method attempts to load an existing flow run for a subflow task
|
@@ -162,7 +204,7 @@ class FlowRunEngine(Generic[P, R]):
|
|
162
204
|
rerunning and not parent_task_run.state.is_completed()
|
163
205
|
):
|
164
206
|
# return the most recent flow run, if it exists
|
165
|
-
flow_runs =
|
207
|
+
flow_runs = client.read_flow_runs(
|
166
208
|
flow_run_filter=FlowRunFilter(
|
167
209
|
parent_task_run_id={"any_": [parent_task_run.id]}
|
168
210
|
),
|
@@ -172,37 +214,7 @@ class FlowRunEngine(Generic[P, R]):
|
|
172
214
|
if flow_runs:
|
173
215
|
return flow_runs[-1]
|
174
216
|
|
175
|
-
|
176
|
-
self, client: PrefectClient, context: FlowRunContext
|
177
|
-
) -> TaskRun:
|
178
|
-
"""
|
179
|
-
Adds a task to a parent flow run that represents the execution of a subflow run.
|
180
|
-
|
181
|
-
The task run is referred to as the "parent task run" of the subflow and will be kept
|
182
|
-
in sync with the subflow run's state by the orchestration engine.
|
183
|
-
"""
|
184
|
-
dummy_task = Task(
|
185
|
-
name=self.flow.name, fn=self.flow.fn, version=self.flow.version
|
186
|
-
)
|
187
|
-
task_inputs = {
|
188
|
-
k: await collect_task_run_inputs(v)
|
189
|
-
for k, v in (self.parameters or {}).items()
|
190
|
-
}
|
191
|
-
parent_task_run = await client.create_task_run(
|
192
|
-
task=dummy_task,
|
193
|
-
flow_run_id=(
|
194
|
-
context.flow_run.id
|
195
|
-
if getattr(context, "flow_run", None)
|
196
|
-
and isinstance(context.flow_run, FlowRun)
|
197
|
-
else None
|
198
|
-
),
|
199
|
-
dynamic_key=_dynamic_key_for_task_run(context, dummy_task), # type: ignore
|
200
|
-
task_inputs=task_inputs, # type: ignore
|
201
|
-
state=Pending(),
|
202
|
-
)
|
203
|
-
return parent_task_run
|
204
|
-
|
205
|
-
async def create_flow_run(self, client: PrefectClient) -> FlowRun:
|
217
|
+
def create_flow_run(self, client: SyncPrefectClient) -> FlowRun:
|
206
218
|
flow_run_ctx = FlowRunContext.get()
|
207
219
|
parameters = self.parameters or {}
|
208
220
|
|
@@ -210,13 +222,21 @@ class FlowRunEngine(Generic[P, R]):
|
|
210
222
|
|
211
223
|
# this is a subflow run
|
212
224
|
if flow_run_ctx:
|
213
|
-
#
|
214
|
-
|
215
|
-
|
225
|
+
# add a task to a parent flow run that represents the execution of a subflow run
|
226
|
+
# reuse the logic from the TaskRunEngine to ensure parents are created correctly
|
227
|
+
parent_task = Task(
|
228
|
+
name=self.flow.name, fn=self.flow.fn, version=self.flow.version
|
229
|
+
)
|
230
|
+
parent_task_run = run_sync(
|
231
|
+
parent_task.create_run(
|
232
|
+
client=self.client,
|
233
|
+
flow_run_context=flow_run_ctx,
|
234
|
+
parameters=self.parameters,
|
235
|
+
)
|
216
236
|
)
|
217
237
|
|
218
238
|
# check if there is already a flow run for this subflow
|
219
|
-
if subflow_run :=
|
239
|
+
if subflow_run := self.load_subflow_run(
|
220
240
|
parent_task_run=parent_task_run, client=client, context=flow_run_ctx
|
221
241
|
):
|
222
242
|
return subflow_run
|
@@ -228,7 +248,7 @@ class FlowRunEngine(Generic[P, R]):
|
|
228
248
|
except TypeError:
|
229
249
|
flow_run_name = None
|
230
250
|
|
231
|
-
flow_run =
|
251
|
+
flow_run = client.create_flow_run(
|
232
252
|
flow=self.flow,
|
233
253
|
name=flow_run_name,
|
234
254
|
parameters=self.flow.serialize_parameters(parameters),
|
@@ -237,43 +257,14 @@ class FlowRunEngine(Generic[P, R]):
|
|
237
257
|
)
|
238
258
|
return flow_run
|
239
259
|
|
240
|
-
@asynccontextmanager
|
241
|
-
async def enter_run_context(self, client: Optional[PrefectClient] = None):
|
242
|
-
if client is None:
|
243
|
-
client = self.client
|
244
|
-
if not self.flow_run:
|
245
|
-
raise ValueError("Flow run not set")
|
246
|
-
|
247
|
-
self.flow_run = await client.read_flow_run(self.flow_run.id)
|
248
|
-
task_runner = self.flow.task_runner.duplicate()
|
249
|
-
|
250
|
-
async with AsyncExitStack() as stack:
|
251
|
-
task_runner = await stack.enter_async_context(
|
252
|
-
self.flow.task_runner.duplicate().start()
|
253
|
-
)
|
254
|
-
stack.enter_context(
|
255
|
-
FlowRunContext(
|
256
|
-
flow=self.flow,
|
257
|
-
log_prints=self.flow.log_prints or False,
|
258
|
-
flow_run=self.flow_run,
|
259
|
-
parameters=self.parameters,
|
260
|
-
client=client,
|
261
|
-
background_tasks=anyio.create_task_group(),
|
262
|
-
result_factory=await ResultFactory.from_flow(self.flow),
|
263
|
-
task_runner=task_runner,
|
264
|
-
)
|
265
|
-
)
|
266
|
-
self.logger = flow_run_logger(flow_run=self.flow_run, flow=self.flow)
|
267
|
-
yield
|
268
|
-
|
269
260
|
@contextmanager
|
270
|
-
def
|
261
|
+
def enter_run_context(self, client: Optional[SyncPrefectClient] = None):
|
271
262
|
if client is None:
|
272
263
|
client = self.client
|
273
264
|
if not self.flow_run:
|
274
265
|
raise ValueError("Flow run not set")
|
275
266
|
|
276
|
-
self.flow_run =
|
267
|
+
self.flow_run = client.read_flow_run(self.flow_run.id)
|
277
268
|
|
278
269
|
# if running in a completely synchronous frame, anyio will not detect the
|
279
270
|
# backend to use for the task group
|
@@ -290,22 +281,41 @@ class FlowRunEngine(Generic[P, R]):
|
|
290
281
|
client=client,
|
291
282
|
background_tasks=task_group,
|
292
283
|
result_factory=run_sync(ResultFactory.from_flow(self.flow)),
|
293
|
-
task_runner=self.flow.task_runner,
|
284
|
+
task_runner=self.flow.task_runner.duplicate(),
|
294
285
|
):
|
295
|
-
|
296
|
-
|
286
|
+
# set the logger to the flow run logger
|
287
|
+
current_logger = self.logger
|
288
|
+
try:
|
289
|
+
self.logger = flow_run_logger(flow_run=self.flow_run, flow=self.flow)
|
290
|
+
yield
|
291
|
+
finally:
|
292
|
+
self.logger = current_logger
|
297
293
|
|
298
|
-
@
|
299
|
-
|
294
|
+
@contextmanager
|
295
|
+
def start(self):
|
300
296
|
"""
|
301
297
|
Enters a client context and creates a flow run if needed.
|
302
298
|
"""
|
303
|
-
|
299
|
+
|
300
|
+
with get_client(sync_client=True) as client:
|
304
301
|
self._client = client
|
305
302
|
self._is_started = True
|
306
303
|
|
304
|
+
# this conditional is engaged whenever a run is triggered via deployment
|
305
|
+
if self.flow_run_id and not self.flow:
|
306
|
+
self.flow_run = client.read_flow_run(self.flow_run_id)
|
307
|
+
try:
|
308
|
+
self.flow = self.load_flow(client)
|
309
|
+
except Exception as exc:
|
310
|
+
self.handle_exception(
|
311
|
+
exc,
|
312
|
+
msg="Failed to load flow from entrypoint.",
|
313
|
+
)
|
314
|
+
self.short_circuit = True
|
315
|
+
|
307
316
|
if not self.flow_run:
|
308
|
-
self.flow_run =
|
317
|
+
self.flow_run = self.create_flow_run(client)
|
318
|
+
self.logger.debug(f'Created flow run "{self.flow_run.id}"')
|
309
319
|
|
310
320
|
# validate prior to context so that context receives validated params
|
311
321
|
if self.flow.should_validate_parameters:
|
@@ -314,53 +324,27 @@ class FlowRunEngine(Generic[P, R]):
|
|
314
324
|
self.parameters or {}
|
315
325
|
)
|
316
326
|
except Exception as exc:
|
317
|
-
|
327
|
+
self.handle_exception(
|
318
328
|
exc,
|
319
329
|
msg="Validation of flow parameters failed with error",
|
320
|
-
result_factory=
|
330
|
+
result_factory=run_sync(ResultFactory.from_flow(self.flow)),
|
321
331
|
)
|
322
332
|
self.short_circuit = True
|
323
333
|
try:
|
324
334
|
yield self
|
335
|
+
except Exception:
|
336
|
+
# regular exceptions are caught and re-raised to the user
|
337
|
+
raise
|
338
|
+
except (Abort, Pause):
|
339
|
+
raise
|
340
|
+
except BaseException as exc:
|
341
|
+
# BaseExceptions are caught and handled as crashes
|
342
|
+
self.handle_crash(exc)
|
343
|
+
raise
|
325
344
|
finally:
|
326
345
|
self._is_started = False
|
327
346
|
self._client = None
|
328
347
|
|
329
|
-
@contextmanager
|
330
|
-
def start_sync(self):
|
331
|
-
"""
|
332
|
-
Enters a client context and creates a flow run if needed.
|
333
|
-
"""
|
334
|
-
|
335
|
-
client = get_client()
|
336
|
-
run_sync(client.__aenter__())
|
337
|
-
self._client = client
|
338
|
-
self._is_started = True
|
339
|
-
|
340
|
-
if not self.flow_run:
|
341
|
-
self.flow_run = run_sync(self.create_flow_run(client))
|
342
|
-
|
343
|
-
# validate prior to context so that context receives validated params
|
344
|
-
if self.flow.should_validate_parameters:
|
345
|
-
try:
|
346
|
-
self.parameters = self.flow.validate_parameters(self.parameters or {})
|
347
|
-
except Exception as exc:
|
348
|
-
run_sync(
|
349
|
-
self.handle_exception(
|
350
|
-
exc,
|
351
|
-
msg="Validation of flow parameters failed with error",
|
352
|
-
result_factory=run_sync(ResultFactory.from_flow(self.flow)),
|
353
|
-
)
|
354
|
-
)
|
355
|
-
self.short_circuit = True
|
356
|
-
try:
|
357
|
-
yield self
|
358
|
-
finally:
|
359
|
-
# quickly close client
|
360
|
-
run_sync(client.__aexit__(None, None, None))
|
361
|
-
self._is_started = False
|
362
|
-
self._client = None
|
363
|
-
|
364
348
|
def is_running(self) -> bool:
|
365
349
|
if getattr(self, "flow_run", None) is None:
|
366
350
|
return False
|
@@ -372,9 +356,10 @@ class FlowRunEngine(Generic[P, R]):
|
|
372
356
|
return getattr(self, "flow_run").state.is_pending()
|
373
357
|
|
374
358
|
|
375
|
-
async def
|
376
|
-
flow: Flow[P, Coroutine[Any, Any, R]],
|
359
|
+
async def run_flow_async(
|
360
|
+
flow: Optional[Flow[P, Coroutine[Any, Any, R]]] = None,
|
377
361
|
flow_run: Optional[FlowRun] = None,
|
362
|
+
flow_run_id: Optional[UUID] = None,
|
378
363
|
parameters: Optional[Dict[str, Any]] = None,
|
379
364
|
wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
|
380
365
|
return_type: Literal["state", "result"] = "result",
|
@@ -385,14 +370,14 @@ async def run_flow(
|
|
385
370
|
We will most likely want to use this logic as a wrapper and return a coroutine for type inference.
|
386
371
|
"""
|
387
372
|
|
388
|
-
engine = FlowRunEngine[P, R](flow, parameters, flow_run)
|
373
|
+
engine = FlowRunEngine[P, R](flow, parameters, flow_run, flow_run_id)
|
389
374
|
|
390
375
|
# This is a context manager that keeps track of the state of the flow run.
|
391
|
-
|
392
|
-
|
376
|
+
with engine.start() as run:
|
377
|
+
run.begin_run()
|
393
378
|
|
394
379
|
while run.is_running():
|
395
|
-
|
380
|
+
with run.enter_run_context():
|
396
381
|
try:
|
397
382
|
# This is where the flow is actually run.
|
398
383
|
call_args, call_kwargs = parameters_to_args_kwargs(
|
@@ -400,15 +385,15 @@ async def run_flow(
|
|
400
385
|
)
|
401
386
|
result = cast(R, await flow.fn(*call_args, **call_kwargs)) # type: ignore
|
402
387
|
# If the flow run is successful, finalize it.
|
403
|
-
|
388
|
+
run.handle_success(result)
|
404
389
|
|
405
390
|
except Exception as exc:
|
406
391
|
# If the flow fails, and we have retries left, set the flow to retrying.
|
407
|
-
|
392
|
+
run.handle_exception(exc)
|
408
393
|
|
409
394
|
if return_type == "state":
|
410
395
|
return run.state
|
411
|
-
return
|
396
|
+
return run.result()
|
412
397
|
|
413
398
|
|
414
399
|
def run_flow_sync(
|
@@ -421,11 +406,11 @@ def run_flow_sync(
|
|
421
406
|
engine = FlowRunEngine[P, R](flow, parameters, flow_run)
|
422
407
|
|
423
408
|
# This is a context manager that keeps track of the state of the flow run.
|
424
|
-
with engine.
|
425
|
-
|
409
|
+
with engine.start() as run:
|
410
|
+
run.begin_run()
|
426
411
|
|
427
412
|
while run.is_running():
|
428
|
-
with run.
|
413
|
+
with run.enter_run_context():
|
429
414
|
try:
|
430
415
|
# This is where the flow is actually run.
|
431
416
|
call_args, call_kwargs = parameters_to_args_kwargs(
|
@@ -433,12 +418,32 @@ def run_flow_sync(
|
|
433
418
|
)
|
434
419
|
result = cast(R, flow.fn(*call_args, **call_kwargs)) # type: ignore
|
435
420
|
# If the flow run is successful, finalize it.
|
436
|
-
|
421
|
+
run.handle_success(result)
|
437
422
|
|
438
423
|
except Exception as exc:
|
439
424
|
# If the flow fails, and we have retries left, set the flow to retrying.
|
440
|
-
|
425
|
+
run.handle_exception(exc)
|
441
426
|
|
442
427
|
if return_type == "state":
|
443
428
|
return run.state
|
444
|
-
return
|
429
|
+
return run.result()
|
430
|
+
|
431
|
+
|
432
|
+
def run_flow(
|
433
|
+
flow: Flow[P, R],
|
434
|
+
flow_run: Optional[FlowRun] = None,
|
435
|
+
parameters: Optional[Dict[str, Any]] = None,
|
436
|
+
wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
|
437
|
+
return_type: Literal["state", "result"] = "result",
|
438
|
+
) -> Union[R, State, None]:
|
439
|
+
kwargs = dict(
|
440
|
+
flow=flow,
|
441
|
+
flow_run=flow_run,
|
442
|
+
parameters=parameters,
|
443
|
+
wait_for=wait_for,
|
444
|
+
return_type=return_type,
|
445
|
+
)
|
446
|
+
if flow.isasync:
|
447
|
+
return run_flow_async(**kwargs)
|
448
|
+
else:
|
449
|
+
return run_flow_sync(**kwargs)
|