prefect-client 2.19.4__py3-none-any.whl → 3.0.0rc2__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 +8 -56
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/concurrency/api.py +0 -34
- 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/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/pydantic/__init__.py +0 -45
- 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/schemas/bases.py +44 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +60 -158
- prefect/artifacts.py +161 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +268 -148
- prefect/blocks/fields.py +2 -57
- prefect/blocks/kubernetes.py +8 -12
- prefect/blocks/notifications.py +40 -20
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +22 -11
- prefect/blocks/webhook.py +2 -9
- prefect/client/base.py +4 -4
- prefect/client/cloud.py +8 -13
- prefect/client/orchestration.py +362 -340
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +158 -152
- prefect/client/schemas/responses.py +16 -24
- prefect/client/schemas/schedules.py +47 -35
- prefect/client/subscriptions.py +2 -2
- prefect/client/utilities.py +5 -2
- prefect/concurrency/asyncio.py +4 -2
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +7 -4
- prefect/context.py +195 -27
- prefect/deployments/__init__.py +5 -6
- prefect/deployments/base.py +7 -5
- prefect/deployments/flow_runs.py +185 -0
- prefect/deployments/runner.py +50 -45
- prefect/deployments/schedules.py +28 -23
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +1 -0
- prefect/deployments/steps/pull.py +7 -21
- prefect/engine.py +12 -2422
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +19 -6
- prefect/events/clients.py +14 -37
- prefect/events/filters.py +14 -18
- prefect/events/related.py +2 -2
- 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 +36 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +2 -3
- prefect/events/worker.py +2 -3
- prefect/filesystems.py +6 -517
- prefect/{new_flow_engine.py → flow_engine.py} +315 -74
- prefect/flow_runs.py +379 -7
- prefect/flows.py +248 -165
- prefect/futures.py +187 -345
- prefect/infrastructure/__init__.py +0 -27
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +11 -6
- prefect/infrastructure/provisioners/container_instance.py +11 -7
- prefect/infrastructure/provisioners/ecs.py +6 -4
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +0 -2
- prefect/logging/handlers.py +3 -11
- prefect/logging/loggers.py +2 -2
- prefect/manifests.py +2 -1
- prefect/records/__init__.py +1 -0
- prefect/records/cache_policies.py +179 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +9 -9
- prefect/runner/server.py +6 -10
- prefect/runner/storage.py +3 -8
- prefect/runner/submit.py +2 -2
- prefect/runner/utils.py +2 -2
- prefect/serializers.py +24 -35
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +76 -136
- prefect/states.py +22 -50
- prefect/task_engine.py +666 -56
- prefect/task_runners.py +272 -300
- prefect/task_runs.py +203 -0
- prefect/{task_server.py → task_worker.py} +89 -60
- prefect/tasks.py +358 -341
- prefect/transactions.py +224 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +121 -41
- prefect/utilities/collections.py +23 -38
- prefect/utilities/dispatch.py +11 -3
- prefect/utilities/dockerutils.py +4 -0
- prefect/utilities/engine.py +140 -20
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +84 -62
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +26 -18
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/METADATA +23 -21
- prefect_client-3.0.0rc2.dist-info/RECORD +179 -0
- 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/__init__.py +0 -0
- 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/__init__.py +0 -0
- 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/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/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/agent.py +0 -698
- prefect/deployments/deployments.py +0 -1042
- 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/base.py +0 -323
- prefect/infrastructure/container.py +0 -818
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- 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/workers/block.py +0 -218
- prefect_client-2.19.4.dist-info/RECORD +0 -292
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/top_level.txt +0 -0
prefect/futures.py
CHANGED
@@ -1,375 +1,242 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
futures in nested data structures.
|
6
|
-
"""
|
7
|
-
import asyncio
|
8
|
-
import warnings
|
1
|
+
import abc
|
2
|
+
import concurrent.futures
|
3
|
+
import inspect
|
4
|
+
import uuid
|
9
5
|
from functools import partial
|
10
|
-
from typing import
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
cast,
|
21
|
-
overload,
|
22
|
-
)
|
23
|
-
from uuid import UUID
|
24
|
-
|
25
|
-
import anyio
|
26
|
-
|
27
|
-
from prefect._internal.concurrency.api import create_call, from_async, from_sync
|
28
|
-
from prefect._internal.concurrency.event_loop import run_coroutine_in_loop_from_async
|
29
|
-
from prefect.client.orchestration import PrefectClient
|
30
|
-
from prefect.client.utilities import inject_client
|
31
|
-
from prefect.states import State
|
6
|
+
from typing import Any, Generic, Optional, Set, Union, cast
|
7
|
+
|
8
|
+
from typing_extensions import TypeVar
|
9
|
+
|
10
|
+
from prefect.client.orchestration import get_client
|
11
|
+
from prefect.client.schemas.objects import TaskRun
|
12
|
+
from prefect.exceptions import ObjectNotFound
|
13
|
+
from prefect.logging.loggers import get_logger
|
14
|
+
from prefect.states import Pending, State
|
15
|
+
from prefect.task_runs import TaskRunWaiter
|
32
16
|
from prefect.utilities.annotations import quote
|
33
|
-
from prefect.utilities.asyncutils import
|
17
|
+
from prefect.utilities.asyncutils import run_coro_as_sync
|
34
18
|
from prefect.utilities.collections import StopVisiting, visit_collection
|
35
19
|
|
36
|
-
|
37
|
-
from prefect.task_runners import BaseTaskRunner
|
20
|
+
F = TypeVar("F")
|
38
21
|
|
22
|
+
logger = get_logger(__name__)
|
39
23
|
|
40
|
-
R = TypeVar("R")
|
41
24
|
|
42
|
-
|
43
|
-
class PrefectFuture(Generic[R, A]):
|
25
|
+
class PrefectFuture(abc.ABC):
|
44
26
|
"""
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
for access to the state and result of the task.
|
49
|
-
|
50
|
-
Examples:
|
51
|
-
Define a task that returns a string
|
52
|
-
|
53
|
-
>>> from prefect import flow, task
|
54
|
-
>>> @task
|
55
|
-
>>> def my_task() -> str:
|
56
|
-
>>> return "hello"
|
57
|
-
|
58
|
-
Calls of this task in a flow will return a future
|
59
|
-
|
60
|
-
>>> @flow
|
61
|
-
>>> def my_flow():
|
62
|
-
>>> future = my_task.submit() # PrefectFuture[str, Sync] includes result type
|
63
|
-
>>> future.task_run.id # UUID for the task run
|
64
|
-
|
65
|
-
Wait for the task to complete
|
66
|
-
|
67
|
-
>>> @flow
|
68
|
-
>>> def my_flow():
|
69
|
-
>>> future = my_task.submit()
|
70
|
-
>>> final_state = future.wait()
|
71
|
-
|
72
|
-
Wait N seconds for the task to complete
|
73
|
-
|
74
|
-
>>> @flow
|
75
|
-
>>> def my_flow():
|
76
|
-
>>> future = my_task.submit()
|
77
|
-
>>> final_state = future.wait(0.1)
|
78
|
-
>>> if final_state:
|
79
|
-
>>> ... # Task done
|
80
|
-
>>> else:
|
81
|
-
>>> ... # Task not done yet
|
82
|
-
|
83
|
-
Wait for a task to complete and retrieve its result
|
84
|
-
|
85
|
-
>>> @flow
|
86
|
-
>>> def my_flow():
|
87
|
-
>>> future = my_task.submit()
|
88
|
-
>>> result = future.result()
|
89
|
-
>>> assert result == "hello"
|
90
|
-
|
91
|
-
Wait N seconds for a task to complete and retrieve its result
|
92
|
-
|
93
|
-
>>> @flow
|
94
|
-
>>> def my_flow():
|
95
|
-
>>> future = my_task.submit()
|
96
|
-
>>> result = future.result(timeout=5)
|
97
|
-
>>> assert result == "hello"
|
98
|
-
|
99
|
-
Retrieve the state of a task without waiting for completion
|
100
|
-
|
101
|
-
>>> @flow
|
102
|
-
>>> def my_flow():
|
103
|
-
>>> future = my_task.submit()
|
104
|
-
>>> state = future.get_state()
|
27
|
+
Abstract base class for Prefect futures. A Prefect future is a handle to the
|
28
|
+
asynchronous execution of a task run. It provides methods to wait for the task
|
29
|
+
to complete and to retrieve the result of the task run.
|
105
30
|
"""
|
106
31
|
|
107
|
-
def __init__(
|
108
|
-
self
|
109
|
-
|
110
|
-
key: UUID,
|
111
|
-
task_runner: "BaseTaskRunner",
|
112
|
-
asynchronous: A = True,
|
113
|
-
_final_state: State[R] = None, # Exposed for testing
|
114
|
-
) -> None:
|
115
|
-
self.key = key
|
116
|
-
self.name = name
|
117
|
-
self.asynchronous = asynchronous
|
118
|
-
self.task_run = None
|
119
|
-
self._final_state = _final_state
|
120
|
-
self._exception: Optional[Exception] = None
|
121
|
-
self._task_runner = task_runner
|
122
|
-
self._submitted = anyio.Event()
|
123
|
-
|
124
|
-
self._loop = asyncio.get_running_loop()
|
125
|
-
|
126
|
-
@overload
|
127
|
-
def wait(
|
128
|
-
self: "PrefectFuture[R, Async]", timeout: None = None
|
129
|
-
) -> Awaitable[State[R]]:
|
130
|
-
...
|
131
|
-
|
132
|
-
@overload
|
133
|
-
def wait(self: "PrefectFuture[R, Sync]", timeout: None = None) -> State[R]:
|
134
|
-
...
|
32
|
+
def __init__(self, task_run_id: uuid.UUID):
|
33
|
+
self._task_run_id = task_run_id
|
34
|
+
self._final_state = None
|
135
35
|
|
136
|
-
@
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
...
|
36
|
+
@property
|
37
|
+
def task_run_id(self) -> uuid.UUID:
|
38
|
+
"""The ID of the task run associated with this future"""
|
39
|
+
return self._task_run_id
|
141
40
|
|
142
|
-
@
|
143
|
-
def
|
41
|
+
@property
|
42
|
+
def state(self) -> State:
|
43
|
+
"""The current state of the task run associated with this future"""
|
44
|
+
if self._final_state:
|
45
|
+
return self._final_state
|
46
|
+
client = get_client(sync_client=True)
|
47
|
+
try:
|
48
|
+
task_run = cast(TaskRun, client.read_task_run(task_run_id=self.task_run_id))
|
49
|
+
except ObjectNotFound:
|
50
|
+
# We'll be optimistic and assume this task will eventually start
|
51
|
+
# TODO: Consider using task run events to wait for the task to start
|
52
|
+
return Pending()
|
53
|
+
return task_run.state or Pending()
|
54
|
+
|
55
|
+
@abc.abstractmethod
|
56
|
+
def wait(self, timeout: Optional[float] = None) -> None:
|
144
57
|
...
|
145
|
-
|
146
|
-
def wait(self, timeout=None):
|
147
58
|
"""
|
148
|
-
Wait for the run to
|
59
|
+
Wait for the task run to complete.
|
149
60
|
|
150
|
-
If the
|
151
|
-
`None` is returned.
|
152
|
-
"""
|
153
|
-
wait = create_call(self._wait, timeout=timeout)
|
154
|
-
if self.asynchronous:
|
155
|
-
return from_async.call_soon_in_loop_thread(wait).aresult()
|
156
|
-
else:
|
157
|
-
# type checking cannot handle the overloaded timeout passing
|
158
|
-
return from_sync.call_soon_in_loop_thread(wait).result() # type: ignore
|
61
|
+
If the task run has already completed, this method will return immediately.
|
159
62
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
@overload
|
165
|
-
async def _wait(self, timeout: float) -> Optional[State[R]]:
|
166
|
-
...
|
167
|
-
|
168
|
-
async def _wait(self, timeout=None):
|
169
|
-
"""
|
170
|
-
Async implementation for `wait`
|
63
|
+
Args:
|
64
|
+
- timeout: The maximum number of seconds to wait for the task run to complete.
|
65
|
+
If the task run has not completed after the timeout has elapsed, this method will return.
|
171
66
|
"""
|
172
|
-
await self._wait_for_submission()
|
173
|
-
|
174
|
-
if self._final_state:
|
175
|
-
return self._final_state
|
176
67
|
|
177
|
-
|
178
|
-
return self._final_state
|
179
|
-
|
180
|
-
@overload
|
68
|
+
@abc.abstractmethod
|
181
69
|
def result(
|
182
|
-
self
|
183
|
-
timeout: float = None,
|
70
|
+
self,
|
71
|
+
timeout: Optional[float] = None,
|
184
72
|
raise_on_failure: bool = True,
|
185
|
-
) ->
|
186
|
-
...
|
187
|
-
|
188
|
-
@overload
|
189
|
-
def result(
|
190
|
-
self: "PrefectFuture[R, Sync]",
|
191
|
-
timeout: float = None,
|
192
|
-
raise_on_failure: bool = False,
|
193
|
-
) -> Union[R, Exception]:
|
73
|
+
) -> Any:
|
194
74
|
...
|
75
|
+
"""
|
76
|
+
Get the result of the task run associated with this future.
|
195
77
|
|
196
|
-
|
197
|
-
def result(
|
198
|
-
self: "PrefectFuture[R, Async]",
|
199
|
-
timeout: float = None,
|
200
|
-
raise_on_failure: bool = True,
|
201
|
-
) -> Awaitable[R]:
|
202
|
-
...
|
78
|
+
If the task run has not completed, this method will wait for the task run to complete.
|
203
79
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
raise_on_failure: bool = False,
|
209
|
-
) -> Awaitable[Union[R, Exception]]:
|
210
|
-
...
|
80
|
+
Args:
|
81
|
+
- timeout: The maximum number of seconds to wait for the task run to complete.
|
82
|
+
If the task run has not completed after the timeout has elapsed, this method will return.
|
83
|
+
- raise_on_failure: If `True`, an exception will be raised if the task run fails.
|
211
84
|
|
212
|
-
|
85
|
+
Returns:
|
86
|
+
The result of the task run.
|
213
87
|
"""
|
214
|
-
Wait for the run to finish and return the final state.
|
215
88
|
|
216
|
-
If the timeout is reached before the run reaches a final state, a `TimeoutError`
|
217
|
-
will be raised.
|
218
89
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
self._result, timeout=timeout, raise_on_failure=raise_on_failure
|
224
|
-
)
|
225
|
-
if self.asynchronous:
|
226
|
-
return from_async.call_soon_in_loop_thread(result).aresult()
|
227
|
-
else:
|
228
|
-
return from_sync.call_soon_in_loop_thread(result).result()
|
90
|
+
class PrefectWrappedFuture(PrefectFuture, abc.ABC, Generic[F]):
|
91
|
+
"""
|
92
|
+
A Prefect future that wraps another future object.
|
93
|
+
"""
|
229
94
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
"""
|
234
|
-
final_state = await self._wait(timeout=timeout)
|
235
|
-
if not final_state:
|
236
|
-
raise TimeoutError("Call timed out before task finished.")
|
237
|
-
return await final_state.result(raise_on_failure=raise_on_failure, fetch=True)
|
238
|
-
|
239
|
-
@overload
|
240
|
-
def get_state(
|
241
|
-
self: "PrefectFuture[R, Async]", client: PrefectClient = None
|
242
|
-
) -> Awaitable[State[R]]:
|
243
|
-
...
|
95
|
+
def __init__(self, task_run_id: uuid.UUID, wrapped_future: F):
|
96
|
+
self._wrapped_future = wrapped_future
|
97
|
+
super().__init__(task_run_id)
|
244
98
|
|
245
|
-
@
|
246
|
-
def
|
247
|
-
|
248
|
-
|
249
|
-
...
|
99
|
+
@property
|
100
|
+
def wrapped_future(self) -> F:
|
101
|
+
"""The underlying future object wrapped by this Prefect future"""
|
102
|
+
return self._wrapped_future
|
250
103
|
|
251
|
-
def get_state(self, client: PrefectClient = None):
|
252
|
-
"""
|
253
|
-
Get the current state of the task run.
|
254
|
-
"""
|
255
|
-
if self.asynchronous:
|
256
|
-
return cast(Awaitable[State[R]], self._get_state(client=client))
|
257
|
-
else:
|
258
|
-
return cast(State[R], sync(self._get_state, client=client))
|
259
104
|
|
260
|
-
|
261
|
-
|
262
|
-
|
105
|
+
class PrefectConcurrentFuture(PrefectWrappedFuture[concurrent.futures.Future]):
|
106
|
+
"""
|
107
|
+
A Prefect future that wraps a concurrent.futures.Future. This future is used
|
108
|
+
when the task run is submitted to a ThreadPoolExecutor.
|
109
|
+
"""
|
263
110
|
|
264
|
-
|
265
|
-
|
111
|
+
def wait(self, timeout: Optional[float] = None) -> None:
|
112
|
+
try:
|
113
|
+
result = self._wrapped_future.result(timeout=timeout)
|
114
|
+
except concurrent.futures.TimeoutError:
|
115
|
+
return
|
116
|
+
if isinstance(result, State):
|
117
|
+
self._final_state = result
|
266
118
|
|
267
|
-
|
119
|
+
def result(
|
120
|
+
self,
|
121
|
+
timeout: Optional[float] = None,
|
122
|
+
raise_on_failure: bool = True,
|
123
|
+
) -> Any:
|
124
|
+
if not self._final_state:
|
125
|
+
try:
|
126
|
+
future_result = self._wrapped_future.result(timeout=timeout)
|
127
|
+
except concurrent.futures.TimeoutError as exc:
|
128
|
+
raise TimeoutError(
|
129
|
+
f"Task run {self.task_run_id} did not complete within {timeout} seconds"
|
130
|
+
) from exc
|
131
|
+
|
132
|
+
if isinstance(future_result, State):
|
133
|
+
self._final_state = future_result
|
134
|
+
else:
|
135
|
+
return future_result
|
136
|
+
|
137
|
+
_result = self._final_state.result(
|
138
|
+
raise_on_failure=raise_on_failure, fetch=True
|
139
|
+
)
|
140
|
+
# state.result is a `sync_compatible` function that may or may not return an awaitable
|
141
|
+
# depending on whether the parent frame is sync or not
|
142
|
+
if inspect.isawaitable(_result):
|
143
|
+
_result = run_coro_as_sync(_result)
|
144
|
+
return _result
|
268
145
|
|
269
|
-
if not task_run:
|
270
|
-
raise RuntimeError("Future has no associated task run in the server.")
|
271
146
|
|
272
|
-
|
273
|
-
|
274
|
-
|
147
|
+
class PrefectDistributedFuture(PrefectFuture):
|
148
|
+
"""
|
149
|
+
Represents the result of a computation happening anywhere.
|
275
150
|
|
276
|
-
|
277
|
-
|
151
|
+
This class is typically used to interact with the result of a task run
|
152
|
+
scheduled to run in a Prefect task worker but can be used to interact with
|
153
|
+
any task run scheduled in Prefect's API.
|
154
|
+
"""
|
278
155
|
|
279
|
-
def
|
280
|
-
return
|
156
|
+
def wait(self, timeout: Optional[float] = None) -> None:
|
157
|
+
return run_coro_as_sync(self.wait_async(timeout=timeout))
|
281
158
|
|
282
|
-
def
|
283
|
-
|
159
|
+
async def wait_async(self, timeout: Optional[float] = None):
|
160
|
+
if self._final_state:
|
161
|
+
logger.debug(
|
162
|
+
"Final state already set for %s. Returning...", self.task_run_id
|
163
|
+
)
|
164
|
+
return
|
165
|
+
|
166
|
+
# Read task run to see if it is still running
|
167
|
+
async with get_client() as client:
|
168
|
+
task_run = await client.read_task_run(task_run_id=self._task_run_id)
|
169
|
+
if task_run.state.is_final():
|
170
|
+
logger.debug(
|
171
|
+
"Task run %s already finished. Returning...",
|
172
|
+
self.task_run_id,
|
173
|
+
)
|
174
|
+
self._final_state = task_run.state
|
175
|
+
return
|
176
|
+
|
177
|
+
# If still running, wait for a completed event from the server
|
178
|
+
logger.debug(
|
179
|
+
"Waiting for completed event for task run %s...",
|
180
|
+
self.task_run_id,
|
181
|
+
)
|
182
|
+
await TaskRunWaiter.wait_for_task_run(self._task_run_id, timeout=timeout)
|
183
|
+
task_run = await client.read_task_run(task_run_id=self._task_run_id)
|
184
|
+
if task_run.state.is_final():
|
185
|
+
self._final_state = task_run.state
|
186
|
+
return
|
284
187
|
|
285
|
-
def
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
)
|
292
|
-
stacklevel=2,
|
188
|
+
def result(
|
189
|
+
self,
|
190
|
+
timeout: Optional[float] = None,
|
191
|
+
raise_on_failure: bool = True,
|
192
|
+
) -> Any:
|
193
|
+
return run_coro_as_sync(
|
194
|
+
self.result_async(timeout=timeout, raise_on_failure=raise_on_failure)
|
293
195
|
)
|
294
|
-
return True
|
295
|
-
|
296
196
|
|
297
|
-
def
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
197
|
+
async def result_async(
|
198
|
+
self,
|
199
|
+
timeout: Optional[float] = None,
|
200
|
+
raise_on_failure: bool = True,
|
201
|
+
):
|
202
|
+
if not self._final_state:
|
203
|
+
await self.wait_async(timeout=timeout)
|
204
|
+
if not self._final_state:
|
205
|
+
raise TimeoutError(
|
206
|
+
f"Task run {self.task_run_id} did not complete within {timeout} seconds"
|
207
|
+
)
|
208
|
+
|
209
|
+
return await self._final_state.result(
|
210
|
+
raise_on_failure=raise_on_failure, fetch=True
|
211
|
+
)
|
304
212
|
|
305
|
-
|
213
|
+
def __eq__(self, other):
|
214
|
+
if not isinstance(other, PrefectDistributedFuture):
|
215
|
+
return False
|
216
|
+
return self.task_run_id == other.task_run_id
|
306
217
|
|
307
218
|
|
308
|
-
|
309
|
-
expr: Union[PrefectFuture
|
310
|
-
|
311
|
-
) -> Union[R, Any]:
|
219
|
+
def resolve_futures_to_states(
|
220
|
+
expr: Union[PrefectFuture, Any],
|
221
|
+
) -> Union[State, Any]:
|
312
222
|
"""
|
313
223
|
Given a Python built-in collection, recursively find `PrefectFutures` and build a
|
314
|
-
new collection with the same structure with futures resolved to their
|
315
|
-
Resolving futures to their
|
316
|
-
communication with the API.
|
224
|
+
new collection with the same structure with futures resolved to their final states.
|
225
|
+
Resolving futures to their final states may wait for execution to complete.
|
317
226
|
|
318
227
|
Unsupported object types will be returned without modification.
|
319
228
|
"""
|
320
229
|
futures: Set[PrefectFuture] = set()
|
321
230
|
|
322
|
-
|
323
|
-
|
324
|
-
visit_fn=partial(_collect_futures, futures),
|
325
|
-
return_data=False,
|
326
|
-
context={},
|
327
|
-
)
|
328
|
-
if maybe_expr is not None:
|
329
|
-
expr = maybe_expr
|
330
|
-
|
331
|
-
# Get results
|
332
|
-
results = await asyncio.gather(
|
333
|
-
*[
|
334
|
-
# We must wait for the future in the thread it was created in
|
335
|
-
from_async.call_soon_in_loop_thread(
|
336
|
-
create_call(future._result, raise_on_failure=raise_on_failure)
|
337
|
-
).aresult()
|
338
|
-
for future in futures
|
339
|
-
]
|
340
|
-
)
|
341
|
-
|
342
|
-
results_by_future = dict(zip(futures, results))
|
343
|
-
|
344
|
-
def replace_futures_with_results(expr, context):
|
345
|
-
# Expressions inside quotes should not be modified
|
231
|
+
def _collect_futures(futures, expr, context):
|
232
|
+
# Expressions inside quotes should not be traversed
|
346
233
|
if isinstance(context.get("annotation"), quote):
|
347
234
|
raise StopVisiting()
|
348
235
|
|
349
236
|
if isinstance(expr, PrefectFuture):
|
350
|
-
|
351
|
-
else:
|
352
|
-
return expr
|
353
|
-
|
354
|
-
return visit_collection(
|
355
|
-
expr,
|
356
|
-
visit_fn=replace_futures_with_results,
|
357
|
-
return_data=True,
|
358
|
-
context={},
|
359
|
-
)
|
360
|
-
|
361
|
-
|
362
|
-
async def resolve_futures_to_states(
|
363
|
-
expr: Union[PrefectFuture[R, Any], Any],
|
364
|
-
) -> Union[State[R], Any]:
|
365
|
-
"""
|
366
|
-
Given a Python built-in collection, recursively find `PrefectFutures` and build a
|
367
|
-
new collection with the same structure with futures resolved to their final states.
|
368
|
-
Resolving futures to their final states may wait for execution to complete.
|
237
|
+
futures.add(expr)
|
369
238
|
|
370
|
-
|
371
|
-
"""
|
372
|
-
futures: Set[PrefectFuture] = set()
|
239
|
+
return expr
|
373
240
|
|
374
241
|
visit_collection(
|
375
242
|
expr,
|
@@ -379,13 +246,10 @@ async def resolve_futures_to_states(
|
|
379
246
|
)
|
380
247
|
|
381
248
|
# Get final states for each future
|
382
|
-
states =
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
for future in futures
|
387
|
-
]
|
388
|
-
)
|
249
|
+
states = []
|
250
|
+
for future in futures:
|
251
|
+
future.wait()
|
252
|
+
states.append(future.state)
|
389
253
|
|
390
254
|
states_by_future = dict(zip(futures, states))
|
391
255
|
|
@@ -405,25 +269,3 @@ async def resolve_futures_to_states(
|
|
405
269
|
return_data=True,
|
406
270
|
context={},
|
407
271
|
)
|
408
|
-
|
409
|
-
|
410
|
-
def call_repr(__fn: Callable, *args: Any, **kwargs: Any) -> str:
|
411
|
-
"""
|
412
|
-
Generate a repr for a function call as "fn_name(arg_value, kwarg_name=kwarg_value)"
|
413
|
-
"""
|
414
|
-
|
415
|
-
name = __fn.__name__
|
416
|
-
|
417
|
-
# TODO: If this computation is concerningly expensive, we can iterate checking the
|
418
|
-
# length at each arg or avoid calling `repr` on args with large amounts of
|
419
|
-
# data
|
420
|
-
call_args = ", ".join(
|
421
|
-
[repr(arg) for arg in args]
|
422
|
-
+ [f"{key}={repr(val)}" for key, val in kwargs.items()]
|
423
|
-
)
|
424
|
-
|
425
|
-
# Enforce a maximum length
|
426
|
-
if len(call_args) > 100:
|
427
|
-
call_args = call_args[:100] + "..."
|
428
|
-
|
429
|
-
return f"{name}({call_args})"
|
@@ -1,27 +0,0 @@
|
|
1
|
-
from prefect.infrastructure.base import Infrastructure, InfrastructureResult
|
2
|
-
from prefect.infrastructure.container import DockerContainer, DockerContainerResult
|
3
|
-
from prefect.infrastructure.kubernetes import (
|
4
|
-
KubernetesClusterConfig,
|
5
|
-
KubernetesImagePullPolicy,
|
6
|
-
KubernetesJob,
|
7
|
-
KubernetesJobResult,
|
8
|
-
KubernetesManifest,
|
9
|
-
KubernetesRestartPolicy,
|
10
|
-
)
|
11
|
-
from prefect.infrastructure.process import Process, ProcessResult
|
12
|
-
|
13
|
-
# Declare API
|
14
|
-
__all__ = [
|
15
|
-
"DockerContainer",
|
16
|
-
"DockerContainerResult",
|
17
|
-
"Infrastructure",
|
18
|
-
"InfrastructureResult",
|
19
|
-
"KubernetesClusterConfig",
|
20
|
-
"KubernetesImagePullPolicy",
|
21
|
-
"KubernetesJob",
|
22
|
-
"KubernetesJobResult",
|
23
|
-
"KubernetesManifest",
|
24
|
-
"KubernetesRestartPolicy",
|
25
|
-
"Process",
|
26
|
-
"ProcessResult",
|
27
|
-
]
|
@@ -1,12 +1,14 @@
|
|
1
|
-
from typing import Any, Dict, Optional, Protocol, Type
|
1
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Protocol, Type
|
2
2
|
|
3
|
-
from prefect.client.orchestration import PrefectClient
|
4
3
|
from prefect.infrastructure.provisioners.modal import ModalPushProvisioner
|
5
4
|
from .cloud_run import CloudRunPushProvisioner
|
6
5
|
from .container_instance import ContainerInstancePushProvisioner
|
7
6
|
from .ecs import ElasticContainerServicePushProvisioner
|
8
7
|
import rich.console
|
9
8
|
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from prefect.client.orchestration import PrefectClient
|
11
|
+
|
10
12
|
_provisioners = {
|
11
13
|
"cloud-run:push": CloudRunPushProvisioner,
|
12
14
|
"cloud-run-v2:push": CloudRunPushProvisioner,
|
@@ -29,7 +31,7 @@ class Provisioner(Protocol):
|
|
29
31
|
self,
|
30
32
|
work_pool_name: str,
|
31
33
|
base_job_template: Dict[str, Any],
|
32
|
-
client: Optional[PrefectClient] = None,
|
34
|
+
client: Optional["PrefectClient"] = None,
|
33
35
|
) -> Dict[str, Any]:
|
34
36
|
...
|
35
37
|
|