prefect-client 2.20.2__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 +423 -164
- 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 +667 -440
- 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 -2466
- 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 +124 -51
- 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 +138 -48
- 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.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.2.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.2.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.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/states.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import asyncio
|
1
2
|
import datetime
|
2
3
|
import sys
|
3
4
|
import traceback
|
@@ -13,28 +14,31 @@ from typing_extensions import TypeGuard
|
|
13
14
|
|
14
15
|
from prefect.client.schemas import State as State
|
15
16
|
from prefect.client.schemas import StateDetails, StateType
|
16
|
-
from prefect.deprecated.data_documents import (
|
17
|
-
DataDocument,
|
18
|
-
result_from_state_with_data_document,
|
19
|
-
)
|
20
17
|
from prefect.exceptions import (
|
21
18
|
CancelledRun,
|
22
19
|
CrashedRun,
|
23
20
|
FailedRun,
|
21
|
+
MissingContextError,
|
24
22
|
MissingResult,
|
25
23
|
PausedRun,
|
26
24
|
TerminationSignal,
|
27
25
|
UnfinishedRun,
|
28
26
|
)
|
29
|
-
from prefect.
|
27
|
+
from prefect.logging.loggers import get_logger, get_run_logger
|
28
|
+
from prefect.results import BaseResult, R, ResultStore
|
30
29
|
from prefect.settings import PREFECT_ASYNC_FETCH_STATE_RESULT
|
31
30
|
from prefect.utilities.annotations import BaseAnnotation
|
32
31
|
from prefect.utilities.asyncutils import in_async_main_thread, sync_compatible
|
33
32
|
from prefect.utilities.collections import ensure_iterable
|
34
33
|
|
34
|
+
logger = get_logger("states")
|
35
|
+
|
35
36
|
|
36
37
|
def get_state_result(
|
37
|
-
state: State[R],
|
38
|
+
state: State[R],
|
39
|
+
raise_on_failure: bool = True,
|
40
|
+
fetch: Optional[bool] = None,
|
41
|
+
retry_result_failure: bool = True,
|
38
42
|
) -> R:
|
39
43
|
"""
|
40
44
|
Get the result from a state.
|
@@ -47,7 +51,6 @@ def get_state_result(
|
|
47
51
|
):
|
48
52
|
# Fetch defaults to `True` for sync users or async users who have opted in
|
49
53
|
fetch = True
|
50
|
-
|
51
54
|
if not fetch:
|
52
55
|
if fetch is None and in_async_main_thread():
|
53
56
|
warnings.warn(
|
@@ -60,19 +63,53 @@ def get_state_result(
|
|
60
63
|
DeprecationWarning,
|
61
64
|
stacklevel=2,
|
62
65
|
)
|
63
|
-
|
64
|
-
|
65
|
-
return result_from_state_with_data_document(
|
66
|
-
state, raise_on_failure=raise_on_failure
|
67
|
-
)
|
68
|
-
else:
|
69
|
-
return state.data
|
66
|
+
|
67
|
+
return state.data
|
70
68
|
else:
|
71
|
-
return _get_state_result(
|
69
|
+
return _get_state_result(
|
70
|
+
state,
|
71
|
+
raise_on_failure=raise_on_failure,
|
72
|
+
retry_result_failure=retry_result_failure,
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
RESULT_READ_MAXIMUM_ATTEMPTS = 10
|
77
|
+
RESULT_READ_RETRY_DELAY = 0.25
|
78
|
+
|
79
|
+
|
80
|
+
async def _get_state_result_data_with_retries(
|
81
|
+
state: State[R], retry_result_failure: bool = True
|
82
|
+
) -> R:
|
83
|
+
# Results may be written asynchronously, possibly after their corresponding
|
84
|
+
# state has been written and events have been emitted, so we should give some
|
85
|
+
# grace here about missing results. The exception below could come in the form
|
86
|
+
# of a missing file, a short read, or other types of errors depending on the
|
87
|
+
# result storage backend.
|
88
|
+
if retry_result_failure is False:
|
89
|
+
max_attempts = 1
|
90
|
+
else:
|
91
|
+
max_attempts = RESULT_READ_MAXIMUM_ATTEMPTS
|
92
|
+
|
93
|
+
for i in range(1, max_attempts + 1):
|
94
|
+
try:
|
95
|
+
return await state.data.get()
|
96
|
+
except Exception as e:
|
97
|
+
if i == max_attempts:
|
98
|
+
raise
|
99
|
+
logger.debug(
|
100
|
+
"Exception %r while reading result, retry %s/%s in %ss...",
|
101
|
+
e,
|
102
|
+
i,
|
103
|
+
max_attempts,
|
104
|
+
RESULT_READ_RETRY_DELAY,
|
105
|
+
)
|
106
|
+
await asyncio.sleep(RESULT_READ_RETRY_DELAY)
|
72
107
|
|
73
108
|
|
74
109
|
@sync_compatible
|
75
|
-
async def _get_state_result(
|
110
|
+
async def _get_state_result(
|
111
|
+
state: State[R], raise_on_failure: bool, retry_result_failure: bool = True
|
112
|
+
) -> R:
|
76
113
|
"""
|
77
114
|
Internal implementation for `get_state_result` without async backwards compatibility
|
78
115
|
"""
|
@@ -90,12 +127,11 @@ async def _get_state_result(state: State[R], raise_on_failure: bool) -> R:
|
|
90
127
|
):
|
91
128
|
raise await get_state_exception(state)
|
92
129
|
|
93
|
-
if isinstance(state.data,
|
94
|
-
result =
|
95
|
-
state,
|
130
|
+
if isinstance(state.data, BaseResult):
|
131
|
+
result = await _get_state_result_data_with_retries(
|
132
|
+
state, retry_result_failure=retry_result_failure
|
96
133
|
)
|
97
|
-
|
98
|
-
result = await state.data.get()
|
134
|
+
|
99
135
|
elif state.data is None:
|
100
136
|
if state.is_failed() or state.is_crashed() or state.is_cancelled():
|
101
137
|
return await get_state_exception(state)
|
@@ -131,7 +167,7 @@ def format_exception(exc: BaseException, tb: TracebackType = None) -> str:
|
|
131
167
|
|
132
168
|
async def exception_to_crashed_state(
|
133
169
|
exc: BaseException,
|
134
|
-
|
170
|
+
result_store: Optional[ResultStore] = None,
|
135
171
|
) -> State:
|
136
172
|
"""
|
137
173
|
Takes an exception that occurs _outside_ of user code and converts it to a
|
@@ -170,8 +206,8 @@ async def exception_to_crashed_state(
|
|
170
206
|
f" {format_exception(exc)}"
|
171
207
|
)
|
172
208
|
|
173
|
-
if
|
174
|
-
data = await
|
209
|
+
if result_store:
|
210
|
+
data = await result_store.create_result(exc)
|
175
211
|
else:
|
176
212
|
# Attach the exception for local usage, will not be available when retrieved
|
177
213
|
# from the API
|
@@ -182,12 +218,18 @@ async def exception_to_crashed_state(
|
|
182
218
|
|
183
219
|
async def exception_to_failed_state(
|
184
220
|
exc: Optional[BaseException] = None,
|
185
|
-
|
221
|
+
result_store: Optional[ResultStore] = None,
|
222
|
+
write_result: bool = False,
|
186
223
|
**kwargs,
|
187
224
|
) -> State:
|
188
225
|
"""
|
189
226
|
Convenience function for creating `Failed` states from exceptions
|
190
227
|
"""
|
228
|
+
try:
|
229
|
+
local_logger = get_run_logger()
|
230
|
+
except MissingContextError:
|
231
|
+
local_logger = logger
|
232
|
+
|
191
233
|
if not exc:
|
192
234
|
_, exc, _ = sys.exc_info()
|
193
235
|
if exc is None:
|
@@ -197,8 +239,16 @@ async def exception_to_failed_state(
|
|
197
239
|
else:
|
198
240
|
pass
|
199
241
|
|
200
|
-
if
|
201
|
-
data = await
|
242
|
+
if result_store:
|
243
|
+
data = await result_store.create_result(exc)
|
244
|
+
if write_result:
|
245
|
+
try:
|
246
|
+
await data.write()
|
247
|
+
except Exception as exc:
|
248
|
+
local_logger.warning(
|
249
|
+
"Failed to write result: %s Execution will continue, but the result has not been written",
|
250
|
+
exc,
|
251
|
+
)
|
202
252
|
else:
|
203
253
|
# Attach the exception for local usage, will not be available when retrieved
|
204
254
|
# from the API
|
@@ -212,10 +262,19 @@ async def exception_to_failed_state(
|
|
212
262
|
# excluded from messages for now
|
213
263
|
message = existing_message + format_exception(exc)
|
214
264
|
|
215
|
-
|
265
|
+
state = Failed(data=data, message=message, **kwargs)
|
266
|
+
state.state_details.retriable = False
|
267
|
+
|
268
|
+
return state
|
216
269
|
|
217
270
|
|
218
|
-
async def return_value_to_state(
|
271
|
+
async def return_value_to_state(
|
272
|
+
retval: R,
|
273
|
+
result_store: ResultStore,
|
274
|
+
key: Optional[str] = None,
|
275
|
+
expiration: Optional[datetime.datetime] = None,
|
276
|
+
write_result: bool = False,
|
277
|
+
) -> State[R]:
|
219
278
|
"""
|
220
279
|
Given a return value from a user's function, create a `State` the run should
|
221
280
|
be placed in.
|
@@ -236,28 +295,39 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
|
|
236
295
|
Callers should resolve all futures into states before passing return values to this
|
237
296
|
function.
|
238
297
|
"""
|
298
|
+
try:
|
299
|
+
local_logger = get_run_logger()
|
300
|
+
except MissingContextError:
|
301
|
+
local_logger = logger
|
239
302
|
|
240
303
|
if (
|
241
|
-
|
304
|
+
isinstance(retval, State)
|
242
305
|
# Check for manual creation
|
243
306
|
and not retval.state_details.flow_run_id
|
244
307
|
and not retval.state_details.task_run_id
|
245
308
|
):
|
246
309
|
state = retval
|
247
|
-
|
248
|
-
# Do not modify states with data documents attached; backwards compatibility
|
249
|
-
if isinstance(state.data, DataDocument):
|
250
|
-
return state
|
251
|
-
|
252
|
-
# Unless the user has already constructed a result explicitly, use the factory
|
310
|
+
# Unless the user has already constructed a result explicitly, use the store
|
253
311
|
# to update the data to the correct type
|
254
312
|
if not isinstance(state.data, BaseResult):
|
255
|
-
|
256
|
-
|
313
|
+
result = await result_store.create_result(
|
314
|
+
state.data,
|
315
|
+
key=key,
|
316
|
+
expiration=expiration,
|
317
|
+
)
|
318
|
+
if write_result:
|
319
|
+
try:
|
320
|
+
await result.write()
|
321
|
+
except Exception as exc:
|
322
|
+
local_logger.warning(
|
323
|
+
"Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
|
324
|
+
exc,
|
325
|
+
)
|
326
|
+
state.data = result
|
257
327
|
return state
|
258
328
|
|
259
329
|
# Determine a new state from the aggregate of contained states
|
260
|
-
if
|
330
|
+
if isinstance(retval, State) or is_state_iterable(retval):
|
261
331
|
states = StateGroup(ensure_iterable(retval))
|
262
332
|
|
263
333
|
# Determine the new state type
|
@@ -289,10 +359,23 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
|
|
289
359
|
# TODO: We may actually want to set the data to a `StateGroup` object and just
|
290
360
|
# allow it to be unpacked into a tuple and such so users can interact with
|
291
361
|
# it
|
362
|
+
result = await result_store.create_result(
|
363
|
+
retval,
|
364
|
+
key=key,
|
365
|
+
expiration=expiration,
|
366
|
+
)
|
367
|
+
if write_result:
|
368
|
+
try:
|
369
|
+
await result.write()
|
370
|
+
except Exception as exc:
|
371
|
+
local_logger.warning(
|
372
|
+
"Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
|
373
|
+
exc,
|
374
|
+
)
|
292
375
|
return State(
|
293
376
|
type=new_state_type,
|
294
377
|
message=message,
|
295
|
-
data=
|
378
|
+
data=result,
|
296
379
|
)
|
297
380
|
|
298
381
|
# Generators aren't portable, implicitly convert them to a list.
|
@@ -302,7 +385,23 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
|
|
302
385
|
data = retval
|
303
386
|
|
304
387
|
# Otherwise, they just gave data and this is a completed retval
|
305
|
-
|
388
|
+
if isinstance(data, BaseResult):
|
389
|
+
return Completed(data=data)
|
390
|
+
else:
|
391
|
+
result = await result_store.create_result(
|
392
|
+
data,
|
393
|
+
key=key,
|
394
|
+
expiration=expiration,
|
395
|
+
)
|
396
|
+
if write_result:
|
397
|
+
try:
|
398
|
+
await result.write()
|
399
|
+
except Exception as exc:
|
400
|
+
local_logger.warning(
|
401
|
+
"Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
|
402
|
+
exc,
|
403
|
+
)
|
404
|
+
return Completed(data=result)
|
306
405
|
|
307
406
|
|
308
407
|
@sync_compatible
|
@@ -342,7 +441,7 @@ async def get_state_exception(state: State) -> BaseException:
|
|
342
441
|
raise ValueError(f"Expected failed or crashed state got {state!r}.")
|
343
442
|
|
344
443
|
if isinstance(state.data, BaseResult):
|
345
|
-
result = await state
|
444
|
+
result = await _get_state_result_data_with_retries(state)
|
346
445
|
elif state.data is None:
|
347
446
|
result = None
|
348
447
|
else:
|
@@ -360,7 +459,7 @@ async def get_state_exception(state: State) -> BaseException:
|
|
360
459
|
elif isinstance(result, str):
|
361
460
|
return wrapper(result)
|
362
461
|
|
363
|
-
elif
|
462
|
+
elif isinstance(result, State):
|
364
463
|
# Return the exception from the inner state
|
365
464
|
return await get_state_exception(result)
|
366
465
|
|
@@ -392,23 +491,6 @@ async def raise_state_exception(state: State) -> None:
|
|
392
491
|
raise await get_state_exception(state)
|
393
492
|
|
394
493
|
|
395
|
-
def is_state(obj: Any) -> TypeGuard[State]:
|
396
|
-
"""
|
397
|
-
Check if the given object is a state instance
|
398
|
-
"""
|
399
|
-
# We may want to narrow this to client-side state types but for now this provides
|
400
|
-
# backwards compatibility
|
401
|
-
try:
|
402
|
-
from prefect.server.schemas.states import State as State_
|
403
|
-
|
404
|
-
classes_ = (State, State_)
|
405
|
-
except ImportError:
|
406
|
-
classes_ = State
|
407
|
-
|
408
|
-
# return isinstance(obj, (State, State_))
|
409
|
-
return isinstance(obj, classes_)
|
410
|
-
|
411
|
-
|
412
494
|
def is_state_iterable(obj: Any) -> TypeGuard[Iterable[State]]:
|
413
495
|
"""
|
414
496
|
Check if a the given object is an iterable of states types
|
@@ -427,7 +509,7 @@ def is_state_iterable(obj: Any) -> TypeGuard[Iterable[State]]:
|
|
427
509
|
and isinstance(obj, (list, set, tuple))
|
428
510
|
and obj
|
429
511
|
):
|
430
|
-
return all([
|
512
|
+
return all([isinstance(o, State) for o in obj])
|
431
513
|
else:
|
432
514
|
return False
|
433
515
|
|
@@ -495,7 +577,7 @@ def Scheduled(
|
|
495
577
|
Returns:
|
496
578
|
State: a Scheduled state
|
497
579
|
"""
|
498
|
-
state_details = StateDetails.
|
580
|
+
state_details = StateDetails.model_validate(kwargs.pop("state_details", {}))
|
499
581
|
if scheduled_time is None:
|
500
582
|
scheduled_time = pendulum.now("UTC")
|
501
583
|
elif state_details.scheduled_time:
|
@@ -581,7 +663,7 @@ def Paused(
|
|
581
663
|
Returns:
|
582
664
|
State: a Paused state
|
583
665
|
"""
|
584
|
-
state_details = StateDetails.
|
666
|
+
state_details = StateDetails.model_validate(kwargs.pop("state_details", {}))
|
585
667
|
|
586
668
|
if state_details.pause_timeout:
|
587
669
|
raise ValueError("An extra pause timeout was provided in state_details")
|
@@ -642,6 +724,21 @@ def AwaitingRetry(
|
|
642
724
|
)
|
643
725
|
|
644
726
|
|
727
|
+
def AwaitingConcurrencySlot(
|
728
|
+
cls: Type[State[R]] = State,
|
729
|
+
scheduled_time: Optional[datetime.datetime] = None,
|
730
|
+
**kwargs: Any,
|
731
|
+
) -> State[R]:
|
732
|
+
"""Convenience function for creating `AwaitingConcurrencySlot` states.
|
733
|
+
|
734
|
+
Returns:
|
735
|
+
State: a AwaitingConcurrencySlot state
|
736
|
+
"""
|
737
|
+
return Scheduled(
|
738
|
+
cls=cls, scheduled_time=scheduled_time, name="AwaitingConcurrencySlot", **kwargs
|
739
|
+
)
|
740
|
+
|
741
|
+
|
645
742
|
def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
646
743
|
"""Convenience function for creating `Retrying` states.
|
647
744
|
|