prefect-client 2.20.4__py3-none-any.whl → 3.0.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 +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/new_flow_engine.py
DELETED
@@ -1,449 +0,0 @@
|
|
1
|
-
import inspect
|
2
|
-
import logging
|
3
|
-
import os
|
4
|
-
import time
|
5
|
-
from contextlib import contextmanager
|
6
|
-
from dataclasses import dataclass, field
|
7
|
-
from typing import (
|
8
|
-
Any,
|
9
|
-
Coroutine,
|
10
|
-
Dict,
|
11
|
-
Generic,
|
12
|
-
Iterable,
|
13
|
-
Literal,
|
14
|
-
Optional,
|
15
|
-
Tuple,
|
16
|
-
TypeVar,
|
17
|
-
Union,
|
18
|
-
cast,
|
19
|
-
)
|
20
|
-
from uuid import UUID
|
21
|
-
|
22
|
-
import anyio
|
23
|
-
import anyio._backends._asyncio
|
24
|
-
from sniffio import AsyncLibraryNotFoundError
|
25
|
-
from typing_extensions import ParamSpec
|
26
|
-
|
27
|
-
from prefect import Task, get_client
|
28
|
-
from prefect.client.orchestration import SyncPrefectClient
|
29
|
-
from prefect.client.schemas import FlowRun, TaskRun
|
30
|
-
from prefect.client.schemas.filters import FlowRunFilter
|
31
|
-
from prefect.client.schemas.sorting import FlowRunSort
|
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
|
36
|
-
from prefect.futures import PrefectFuture, resolve_futures_to_states
|
37
|
-
from prefect.logging.loggers import flow_run_logger, get_logger
|
38
|
-
from prefect.results import ResultFactory
|
39
|
-
from prefect.states import (
|
40
|
-
Pending,
|
41
|
-
Running,
|
42
|
-
State,
|
43
|
-
exception_to_crashed_state,
|
44
|
-
exception_to_failed_state,
|
45
|
-
return_value_to_state,
|
46
|
-
)
|
47
|
-
from prefect.utilities.asyncutils import A, Async, run_sync
|
48
|
-
from prefect.utilities.callables import parameters_to_args_kwargs
|
49
|
-
from prefect.utilities.engine import (
|
50
|
-
_resolve_custom_flow_run_name,
|
51
|
-
propose_state_sync,
|
52
|
-
)
|
53
|
-
|
54
|
-
P = ParamSpec("P")
|
55
|
-
R = TypeVar("R")
|
56
|
-
|
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
|
-
|
73
|
-
@dataclass
|
74
|
-
class FlowRunEngine(Generic[P, R]):
|
75
|
-
flow: Optional[Union[Flow[P, R], Flow[P, Coroutine[Any, Any, R]]]] = None
|
76
|
-
parameters: Optional[Dict[str, Any]] = None
|
77
|
-
flow_run: Optional[FlowRun] = None
|
78
|
-
flow_run_id: Optional[UUID] = None
|
79
|
-
logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
|
80
|
-
_is_started: bool = False
|
81
|
-
_client: Optional[SyncPrefectClient] = None
|
82
|
-
short_circuit: bool = False
|
83
|
-
|
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
|
-
|
88
|
-
if self.parameters is None:
|
89
|
-
self.parameters = {}
|
90
|
-
|
91
|
-
@property
|
92
|
-
def client(self) -> SyncPrefectClient:
|
93
|
-
if not self._is_started or self._client is None:
|
94
|
-
raise RuntimeError("Engine has not started.")
|
95
|
-
return self._client
|
96
|
-
|
97
|
-
@property
|
98
|
-
def state(self) -> State:
|
99
|
-
return self.flow_run.state # type: ignore
|
100
|
-
|
101
|
-
def begin_run(self) -> State:
|
102
|
-
new_state = Running()
|
103
|
-
state = self.set_state(new_state)
|
104
|
-
while state.is_pending():
|
105
|
-
time.sleep(0.2)
|
106
|
-
state = self.set_state(new_state)
|
107
|
-
return state
|
108
|
-
|
109
|
-
def set_state(self, state: State, force: bool = False) -> State:
|
110
|
-
""" """
|
111
|
-
# prevents any state-setting activity
|
112
|
-
if self.short_circuit:
|
113
|
-
return self.state
|
114
|
-
|
115
|
-
state = propose_state_sync(
|
116
|
-
self.client, state, flow_run_id=self.flow_run.id, force=force
|
117
|
-
) # type: ignore
|
118
|
-
self.flow_run.state = state # type: ignore
|
119
|
-
self.flow_run.state_name = state.name # type: ignore
|
120
|
-
self.flow_run.state_type = state.type # type: ignore
|
121
|
-
return state
|
122
|
-
|
123
|
-
def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
|
124
|
-
_result = self.state.result(raise_on_failure=raise_on_failure, fetch=True) # type: ignore
|
125
|
-
# state.result is a `sync_compatible` function that may or may not return an awaitable
|
126
|
-
# depending on whether the parent frame is sync or not
|
127
|
-
if inspect.isawaitable(_result):
|
128
|
-
_result = run_sync(_result)
|
129
|
-
return _result
|
130
|
-
|
131
|
-
def handle_success(self, result: R) -> R:
|
132
|
-
result_factory = getattr(FlowRunContext.get(), "result_factory", None)
|
133
|
-
if result_factory is None:
|
134
|
-
raise ValueError("Result factory is not set")
|
135
|
-
terminal_state = run_sync(
|
136
|
-
return_value_to_state(
|
137
|
-
run_sync(resolve_futures_to_states(result)),
|
138
|
-
result_factory=result_factory,
|
139
|
-
)
|
140
|
-
)
|
141
|
-
self.set_state(terminal_state)
|
142
|
-
return result
|
143
|
-
|
144
|
-
def handle_exception(
|
145
|
-
self,
|
146
|
-
exc: Exception,
|
147
|
-
msg: Optional[str] = None,
|
148
|
-
result_factory: Optional[ResultFactory] = None,
|
149
|
-
) -> State:
|
150
|
-
context = FlowRunContext.get()
|
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
|
-
)
|
158
|
-
)
|
159
|
-
state = self.set_state(state)
|
160
|
-
if self.state.is_scheduled():
|
161
|
-
state = self.set_state(Running())
|
162
|
-
return state
|
163
|
-
|
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,
|
175
|
-
) -> Union[FlowRun, None]:
|
176
|
-
"""
|
177
|
-
This method attempts to load an existing flow run for a subflow task
|
178
|
-
run, if appropriate.
|
179
|
-
|
180
|
-
If the parent task run is in a final but not COMPLETED state, and not
|
181
|
-
being rerun, then we attempt to load an existing flow run instead of
|
182
|
-
creating a new one. This will prevent the engine from running the
|
183
|
-
subflow again.
|
184
|
-
|
185
|
-
If no existing flow run is found, or if the subflow should be rerun,
|
186
|
-
then no flow run is returned.
|
187
|
-
"""
|
188
|
-
|
189
|
-
# check if the parent flow run is rerunning
|
190
|
-
rerunning = (
|
191
|
-
context.flow_run.run_count > 1
|
192
|
-
if getattr(context, "flow_run", None)
|
193
|
-
and isinstance(context.flow_run, FlowRun)
|
194
|
-
else False
|
195
|
-
)
|
196
|
-
|
197
|
-
# if the parent task run is in a final but not completed state, and
|
198
|
-
# not rerunning, then retrieve the most recent flow run instead of
|
199
|
-
# creating a new one. This effectively loads a cached flow run for
|
200
|
-
# situations where we are confident the flow should not be run
|
201
|
-
# again.
|
202
|
-
assert isinstance(parent_task_run.state, State)
|
203
|
-
if parent_task_run.state.is_final() and not (
|
204
|
-
rerunning and not parent_task_run.state.is_completed()
|
205
|
-
):
|
206
|
-
# return the most recent flow run, if it exists
|
207
|
-
flow_runs = client.read_flow_runs(
|
208
|
-
flow_run_filter=FlowRunFilter(
|
209
|
-
parent_task_run_id={"any_": [parent_task_run.id]}
|
210
|
-
),
|
211
|
-
sort=FlowRunSort.EXPECTED_START_TIME_ASC,
|
212
|
-
limit=1,
|
213
|
-
)
|
214
|
-
if flow_runs:
|
215
|
-
return flow_runs[-1]
|
216
|
-
|
217
|
-
def create_flow_run(self, client: SyncPrefectClient) -> FlowRun:
|
218
|
-
flow_run_ctx = FlowRunContext.get()
|
219
|
-
parameters = self.parameters or {}
|
220
|
-
|
221
|
-
parent_task_run = None
|
222
|
-
|
223
|
-
# this is a subflow run
|
224
|
-
if flow_run_ctx:
|
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
|
-
)
|
236
|
-
)
|
237
|
-
|
238
|
-
# check if there is already a flow run for this subflow
|
239
|
-
if subflow_run := self.load_subflow_run(
|
240
|
-
parent_task_run=parent_task_run, client=client, context=flow_run_ctx
|
241
|
-
):
|
242
|
-
return subflow_run
|
243
|
-
|
244
|
-
try:
|
245
|
-
flow_run_name = _resolve_custom_flow_run_name(
|
246
|
-
flow=self.flow, parameters=parameters
|
247
|
-
)
|
248
|
-
except TypeError:
|
249
|
-
flow_run_name = None
|
250
|
-
|
251
|
-
flow_run = client.create_flow_run(
|
252
|
-
flow=self.flow,
|
253
|
-
name=flow_run_name,
|
254
|
-
parameters=self.flow.serialize_parameters(parameters),
|
255
|
-
state=Pending(),
|
256
|
-
parent_task_run_id=getattr(parent_task_run, "id", None),
|
257
|
-
)
|
258
|
-
return flow_run
|
259
|
-
|
260
|
-
@contextmanager
|
261
|
-
def enter_run_context(self, client: Optional[SyncPrefectClient] = None):
|
262
|
-
if client is None:
|
263
|
-
client = self.client
|
264
|
-
if not self.flow_run:
|
265
|
-
raise ValueError("Flow run not set")
|
266
|
-
|
267
|
-
self.flow_run = client.read_flow_run(self.flow_run.id)
|
268
|
-
|
269
|
-
# if running in a completely synchronous frame, anyio will not detect the
|
270
|
-
# backend to use for the task group
|
271
|
-
try:
|
272
|
-
task_group = anyio.create_task_group()
|
273
|
-
except AsyncLibraryNotFoundError:
|
274
|
-
task_group = anyio._backends._asyncio.TaskGroup()
|
275
|
-
|
276
|
-
with FlowRunContext(
|
277
|
-
flow=self.flow,
|
278
|
-
log_prints=self.flow.log_prints or False,
|
279
|
-
flow_run=self.flow_run,
|
280
|
-
parameters=self.parameters,
|
281
|
-
client=client,
|
282
|
-
background_tasks=task_group,
|
283
|
-
result_factory=run_sync(ResultFactory.from_flow(self.flow)),
|
284
|
-
task_runner=self.flow.task_runner.duplicate(),
|
285
|
-
):
|
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
|
293
|
-
|
294
|
-
@contextmanager
|
295
|
-
def start(self):
|
296
|
-
"""
|
297
|
-
Enters a client context and creates a flow run if needed.
|
298
|
-
"""
|
299
|
-
|
300
|
-
with get_client(sync_client=True) as client:
|
301
|
-
self._client = client
|
302
|
-
self._is_started = True
|
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
|
-
|
316
|
-
if not self.flow_run:
|
317
|
-
self.flow_run = self.create_flow_run(client)
|
318
|
-
self.logger.debug(f'Created flow run "{self.flow_run.id}"')
|
319
|
-
|
320
|
-
# validate prior to context so that context receives validated params
|
321
|
-
if self.flow.should_validate_parameters:
|
322
|
-
try:
|
323
|
-
self.parameters = self.flow.validate_parameters(
|
324
|
-
self.parameters or {}
|
325
|
-
)
|
326
|
-
except Exception as exc:
|
327
|
-
self.handle_exception(
|
328
|
-
exc,
|
329
|
-
msg="Validation of flow parameters failed with error",
|
330
|
-
result_factory=run_sync(ResultFactory.from_flow(self.flow)),
|
331
|
-
)
|
332
|
-
self.short_circuit = True
|
333
|
-
try:
|
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
|
344
|
-
finally:
|
345
|
-
self._is_started = False
|
346
|
-
self._client = None
|
347
|
-
|
348
|
-
def is_running(self) -> bool:
|
349
|
-
if getattr(self, "flow_run", None) is None:
|
350
|
-
return False
|
351
|
-
return getattr(self, "flow_run").state.is_running()
|
352
|
-
|
353
|
-
def is_pending(self) -> bool:
|
354
|
-
if getattr(self, "flow_run", None) is None:
|
355
|
-
return False # TODO: handle this differently?
|
356
|
-
return getattr(self, "flow_run").state.is_pending()
|
357
|
-
|
358
|
-
|
359
|
-
async def run_flow_async(
|
360
|
-
flow: Optional[Flow[P, Coroutine[Any, Any, R]]] = None,
|
361
|
-
flow_run: Optional[FlowRun] = None,
|
362
|
-
flow_run_id: Optional[UUID] = None,
|
363
|
-
parameters: Optional[Dict[str, Any]] = None,
|
364
|
-
wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
|
365
|
-
return_type: Literal["state", "result"] = "result",
|
366
|
-
) -> Union[R, None]:
|
367
|
-
"""
|
368
|
-
Runs a flow against the API.
|
369
|
-
|
370
|
-
We will most likely want to use this logic as a wrapper and return a coroutine for type inference.
|
371
|
-
"""
|
372
|
-
|
373
|
-
engine = FlowRunEngine[P, R](flow, parameters, flow_run, flow_run_id)
|
374
|
-
|
375
|
-
# This is a context manager that keeps track of the state of the flow run.
|
376
|
-
with engine.start() as run:
|
377
|
-
run.begin_run()
|
378
|
-
|
379
|
-
while run.is_running():
|
380
|
-
with run.enter_run_context():
|
381
|
-
try:
|
382
|
-
# This is where the flow is actually run.
|
383
|
-
call_args, call_kwargs = parameters_to_args_kwargs(
|
384
|
-
flow.fn, run.parameters or {}
|
385
|
-
)
|
386
|
-
result = cast(R, await flow.fn(*call_args, **call_kwargs)) # type: ignore
|
387
|
-
# If the flow run is successful, finalize it.
|
388
|
-
run.handle_success(result)
|
389
|
-
|
390
|
-
except Exception as exc:
|
391
|
-
# If the flow fails, and we have retries left, set the flow to retrying.
|
392
|
-
run.handle_exception(exc)
|
393
|
-
|
394
|
-
if return_type == "state":
|
395
|
-
return run.state
|
396
|
-
return run.result()
|
397
|
-
|
398
|
-
|
399
|
-
def run_flow_sync(
|
400
|
-
flow: Flow[P, R],
|
401
|
-
flow_run: Optional[FlowRun] = None,
|
402
|
-
parameters: Optional[Dict[str, Any]] = None,
|
403
|
-
wait_for: Optional[Iterable[PrefectFuture[A, Async]]] = None,
|
404
|
-
return_type: Literal["state", "result"] = "result",
|
405
|
-
) -> Union[R, State, None]:
|
406
|
-
engine = FlowRunEngine[P, R](flow, parameters, flow_run)
|
407
|
-
|
408
|
-
# This is a context manager that keeps track of the state of the flow run.
|
409
|
-
with engine.start() as run:
|
410
|
-
run.begin_run()
|
411
|
-
|
412
|
-
while run.is_running():
|
413
|
-
with run.enter_run_context():
|
414
|
-
try:
|
415
|
-
# This is where the flow is actually run.
|
416
|
-
call_args, call_kwargs = parameters_to_args_kwargs(
|
417
|
-
flow.fn, run.parameters or {}
|
418
|
-
)
|
419
|
-
result = cast(R, flow.fn(*call_args, **call_kwargs)) # type: ignore
|
420
|
-
# If the flow run is successful, finalize it.
|
421
|
-
run.handle_success(result)
|
422
|
-
|
423
|
-
except Exception as exc:
|
424
|
-
# If the flow fails, and we have retries left, set the flow to retrying.
|
425
|
-
run.handle_exception(exc)
|
426
|
-
|
427
|
-
if return_type == "state":
|
428
|
-
return run.state
|
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)
|