prefect-client 2.19.2__py3-none-any.whl → 3.0.0rc1__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/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 +347 -341
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +151 -145
- 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 +3 -1
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +6 -3
- 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 +34 -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} +313 -72
- prefect/flow_runs.py +377 -5
- prefect/flows.py +307 -166
- prefect/futures.py +186 -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 +5 -7
- 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/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +19 -15
- 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 +70 -133
- prefect/states.py +17 -47
- prefect/task_engine.py +697 -58
- prefect/task_runners.py +269 -301
- prefect/task_server.py +53 -34
- prefect/tasks.py +327 -337
- prefect/transactions.py +220 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +311 -43
- 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 +97 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +78 -61
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +15 -17
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
- prefect_client-3.0.0rc1.dist-info/RECORD +176 -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.2.dist-info/RECORD +0 -292
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
prefect/futures.py
CHANGED
@@ -1,375 +1,241 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
"""
|
7
|
-
import asyncio
|
8
|
-
import warnings
|
1
|
+
import abc
|
2
|
+
import concurrent.futures
|
3
|
+
import inspect
|
4
|
+
import time
|
5
|
+
import uuid
|
9
6
|
from functools import partial
|
10
|
-
from typing import
|
11
|
-
TYPE_CHECKING,
|
12
|
-
Any,
|
13
|
-
Awaitable,
|
14
|
-
Callable,
|
15
|
-
Generic,
|
16
|
-
Optional,
|
17
|
-
Set,
|
18
|
-
TypeVar,
|
19
|
-
Union,
|
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
|
32
|
-
from prefect.utilities.annotations import quote
|
33
|
-
from prefect.utilities.asyncutils import A, Async, Sync, sync
|
34
|
-
from prefect.utilities.collections import StopVisiting, visit_collection
|
7
|
+
from typing import Any, Generic, Optional, Set, Union, cast
|
35
8
|
|
36
|
-
|
37
|
-
from prefect.task_runners import BaseTaskRunner
|
9
|
+
from typing_extensions import TypeVar
|
38
10
|
|
11
|
+
from prefect.client.orchestration import get_client
|
12
|
+
from prefect.client.schemas.objects import TaskRun
|
13
|
+
from prefect.exceptions import ObjectNotFound
|
14
|
+
from prefect.states import Pending, State
|
15
|
+
from prefect.utilities.annotations import quote
|
16
|
+
from prefect.utilities.asyncutils import run_coro_as_sync
|
17
|
+
from prefect.utilities.collections import StopVisiting, visit_collection
|
39
18
|
|
40
|
-
|
19
|
+
F = TypeVar("F")
|
41
20
|
|
42
21
|
|
43
|
-
class PrefectFuture(
|
22
|
+
class PrefectFuture(abc.ABC):
|
44
23
|
"""
|
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()
|
24
|
+
Abstract base class for Prefect futures. A Prefect future is a handle to the
|
25
|
+
asynchronous execution of a task run. It provides methods to wait for the task
|
26
|
+
to complete and to retrieve the result of the task run.
|
105
27
|
"""
|
106
28
|
|
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
|
-
...
|
29
|
+
def __init__(self, task_run_id: uuid.UUID):
|
30
|
+
self._task_run_id = task_run_id
|
31
|
+
self._final_state = None
|
135
32
|
|
136
|
-
@
|
137
|
-
def
|
138
|
-
|
139
|
-
|
140
|
-
...
|
33
|
+
@property
|
34
|
+
def task_run_id(self) -> uuid.UUID:
|
35
|
+
"""The ID of the task run associated with this future"""
|
36
|
+
return self._task_run_id
|
141
37
|
|
142
|
-
@
|
143
|
-
def
|
38
|
+
@property
|
39
|
+
def state(self) -> State:
|
40
|
+
"""The current state of the task run associated with this future"""
|
41
|
+
if self._final_state:
|
42
|
+
return self._final_state
|
43
|
+
client = get_client(sync_client=True)
|
44
|
+
try:
|
45
|
+
task_run = cast(TaskRun, client.read_task_run(task_run_id=self.task_run_id))
|
46
|
+
except ObjectNotFound:
|
47
|
+
# We'll be optimistic and assume this task will eventually start
|
48
|
+
# TODO: Consider using task run events to wait for the task to start
|
49
|
+
return Pending()
|
50
|
+
return task_run.state or Pending()
|
51
|
+
|
52
|
+
@abc.abstractmethod
|
53
|
+
def wait(self, timeout: Optional[float] = None) -> None:
|
144
54
|
...
|
145
|
-
|
146
|
-
def wait(self, timeout=None):
|
147
|
-
"""
|
148
|
-
Wait for the run to finish and return the final state
|
149
|
-
|
150
|
-
If the timeout is reached before the run reaches a final state,
|
151
|
-
`None` is returned.
|
152
55
|
"""
|
153
|
-
|
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
|
56
|
+
Wait for the task run to complete.
|
159
57
|
|
160
|
-
|
161
|
-
async def _wait(self, timeout: None = None) -> State[R]:
|
162
|
-
...
|
163
|
-
|
164
|
-
@overload
|
165
|
-
async def _wait(self, timeout: float) -> Optional[State[R]]:
|
166
|
-
...
|
58
|
+
If the task run has already completed, this method will return immediately.
|
167
59
|
|
168
|
-
|
169
|
-
|
170
|
-
|
60
|
+
Args:
|
61
|
+
- timeout: The maximum number of seconds to wait for the task run to complete.
|
62
|
+
If the task run has not completed after the timeout has elapsed, this method will return.
|
171
63
|
"""
|
172
|
-
await self._wait_for_submission()
|
173
64
|
|
174
|
-
|
175
|
-
return self._final_state
|
176
|
-
|
177
|
-
self._final_state = await self._task_runner.wait(self.key, timeout)
|
178
|
-
return self._final_state
|
179
|
-
|
180
|
-
@overload
|
65
|
+
@abc.abstractmethod
|
181
66
|
def result(
|
182
|
-
self
|
183
|
-
timeout: float = None,
|
67
|
+
self,
|
68
|
+
timeout: Optional[float] = None,
|
184
69
|
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]:
|
70
|
+
) -> Any:
|
194
71
|
...
|
72
|
+
"""
|
73
|
+
Get the result of the task run associated with this future.
|
195
74
|
|
196
|
-
|
197
|
-
def result(
|
198
|
-
self: "PrefectFuture[R, Async]",
|
199
|
-
timeout: float = None,
|
200
|
-
raise_on_failure: bool = True,
|
201
|
-
) -> Awaitable[R]:
|
202
|
-
...
|
75
|
+
If the task run has not completed, this method will wait for the task run to complete.
|
203
76
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
raise_on_failure: bool = False,
|
209
|
-
) -> Awaitable[Union[R, Exception]]:
|
210
|
-
...
|
77
|
+
Args:
|
78
|
+
- timeout: The maximum number of seconds to wait for the task run to complete.
|
79
|
+
If the task run has not completed after the timeout has elapsed, this method will return.
|
80
|
+
- raise_on_failure: If `True`, an exception will be raised if the task run fails.
|
211
81
|
|
212
|
-
|
82
|
+
Returns:
|
83
|
+
The result of the task run.
|
213
84
|
"""
|
214
|
-
Wait for the run to finish and return the final state.
|
215
85
|
|
216
|
-
If the timeout is reached before the run reaches a final state, a `TimeoutError`
|
217
|
-
will be raised.
|
218
86
|
|
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()
|
87
|
+
class PrefectWrappedFuture(PrefectFuture, abc.ABC, Generic[F]):
|
88
|
+
"""
|
89
|
+
A Prefect future that wraps another future object.
|
90
|
+
"""
|
229
91
|
|
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
|
-
...
|
92
|
+
def __init__(self, task_run_id: uuid.UUID, wrapped_future: F):
|
93
|
+
self._wrapped_future = wrapped_future
|
94
|
+
super().__init__(task_run_id)
|
244
95
|
|
245
|
-
@
|
246
|
-
def
|
247
|
-
|
248
|
-
|
249
|
-
...
|
96
|
+
@property
|
97
|
+
def wrapped_future(self) -> F:
|
98
|
+
"""The underlying future object wrapped by this Prefect future"""
|
99
|
+
return self._wrapped_future
|
250
100
|
|
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
101
|
|
260
|
-
|
261
|
-
|
262
|
-
|
102
|
+
class PrefectConcurrentFuture(PrefectWrappedFuture[concurrent.futures.Future]):
|
103
|
+
"""
|
104
|
+
A Prefect future that wraps a concurrent.futures.Future. This future is used
|
105
|
+
when the task run is submitted to a ThreadPoolExecutor.
|
106
|
+
"""
|
263
107
|
|
264
|
-
|
265
|
-
|
108
|
+
def wait(self, timeout: Optional[float] = None) -> None:
|
109
|
+
try:
|
110
|
+
result = self._wrapped_future.result(timeout=timeout)
|
111
|
+
except concurrent.futures.TimeoutError:
|
112
|
+
return
|
113
|
+
if isinstance(result, State):
|
114
|
+
self._final_state = result
|
266
115
|
|
267
|
-
|
116
|
+
def result(
|
117
|
+
self,
|
118
|
+
timeout: Optional[float] = None,
|
119
|
+
raise_on_failure: bool = True,
|
120
|
+
) -> Any:
|
121
|
+
if not self._final_state:
|
122
|
+
try:
|
123
|
+
future_result = self._wrapped_future.result(timeout=timeout)
|
124
|
+
except concurrent.futures.TimeoutError as exc:
|
125
|
+
raise TimeoutError(
|
126
|
+
f"Task run {self.task_run_id} did not complete within {timeout} seconds"
|
127
|
+
) from exc
|
128
|
+
|
129
|
+
if isinstance(future_result, State):
|
130
|
+
self._final_state = future_result
|
131
|
+
else:
|
132
|
+
return future_result
|
133
|
+
|
134
|
+
_result = self._final_state.result(
|
135
|
+
raise_on_failure=raise_on_failure, fetch=True
|
136
|
+
)
|
137
|
+
# state.result is a `sync_compatible` function that may or may not return an awaitable
|
138
|
+
# depending on whether the parent frame is sync or not
|
139
|
+
if inspect.isawaitable(_result):
|
140
|
+
_result = run_coro_as_sync(_result)
|
141
|
+
return _result
|
268
142
|
|
269
|
-
if not task_run:
|
270
|
-
raise RuntimeError("Future has no associated task run in the server.")
|
271
143
|
|
272
|
-
|
273
|
-
|
274
|
-
|
144
|
+
class PrefectDistributedFuture(PrefectFuture):
|
145
|
+
"""
|
146
|
+
Represents the result of a computation happening anywhere.
|
275
147
|
|
276
|
-
|
277
|
-
|
148
|
+
This class is typically used to interact with the result of a task run
|
149
|
+
scheduled to run in a Prefect task server but can be used to interact with
|
150
|
+
any task run scheduled in Prefect's API.
|
151
|
+
"""
|
278
152
|
|
279
|
-
def
|
280
|
-
|
153
|
+
def __init__(self, task_run_id: uuid.UUID):
|
154
|
+
self._task_run = None
|
155
|
+
self._client = None
|
156
|
+
super().__init__(task_run_id=task_run_id)
|
281
157
|
|
282
|
-
|
283
|
-
|
158
|
+
@property
|
159
|
+
def client(self):
|
160
|
+
if self._client is None:
|
161
|
+
self._client = get_client(sync_client=True)
|
162
|
+
return self._client
|
284
163
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
"e.g. `if my_task().result(): ...`"
|
291
|
-
),
|
292
|
-
stacklevel=2,
|
293
|
-
)
|
294
|
-
return True
|
164
|
+
@property
|
165
|
+
def task_run(self):
|
166
|
+
if self._task_run is None:
|
167
|
+
self._task_run = self.client.read_task_run(task_run_id=self.task_run_id)
|
168
|
+
return self._task_run
|
295
169
|
|
170
|
+
@task_run.setter
|
171
|
+
def task_run(self, task_run):
|
172
|
+
self._task_run = task_run
|
296
173
|
|
297
|
-
def
|
298
|
-
|
299
|
-
|
300
|
-
|
174
|
+
def wait(
|
175
|
+
self, timeout: Optional[float] = None, polling_interval: Optional[float] = 0.2
|
176
|
+
) -> None:
|
177
|
+
start_time = time.time()
|
178
|
+
# TODO: Websocket implementation?
|
179
|
+
while True:
|
180
|
+
self.task_run = cast(
|
181
|
+
TaskRun, self.client.read_task_run(task_run_id=self.task_run_id)
|
182
|
+
)
|
183
|
+
if self.task_run.state and self.task_run.state.is_final():
|
184
|
+
self._final_state = self.task_run.state
|
185
|
+
return
|
186
|
+
if timeout is not None and (time.time() - start_time) > timeout:
|
187
|
+
return
|
188
|
+
time.sleep(polling_interval)
|
301
189
|
|
302
|
-
|
303
|
-
|
190
|
+
def result(
|
191
|
+
self,
|
192
|
+
timeout: Optional[float] = None,
|
193
|
+
raise_on_failure: bool = True,
|
194
|
+
polling_interval: Optional[float] = 0.2,
|
195
|
+
) -> Any:
|
196
|
+
if not self._final_state:
|
197
|
+
self.wait(timeout=timeout)
|
198
|
+
if not self._final_state:
|
199
|
+
raise TimeoutError(
|
200
|
+
f"Task run {self.task_run_id} did not complete within {timeout} seconds"
|
201
|
+
)
|
202
|
+
|
203
|
+
_result = self._final_state.result(
|
204
|
+
raise_on_failure=raise_on_failure, fetch=True
|
205
|
+
)
|
206
|
+
# state.result is a `sync_compatible` function that may or may not return an awaitable
|
207
|
+
# depending on whether the parent frame is sync or not
|
208
|
+
if inspect.isawaitable(_result):
|
209
|
+
_result = run_coro_as_sync(_result)
|
210
|
+
return _result
|
304
211
|
|
305
|
-
|
212
|
+
def __eq__(self, other):
|
213
|
+
if not isinstance(other, PrefectDistributedFuture):
|
214
|
+
return False
|
215
|
+
return self.task_run_id == other.task_run_id
|
306
216
|
|
307
217
|
|
308
|
-
|
309
|
-
expr: Union[PrefectFuture
|
310
|
-
|
311
|
-
) -> Union[R, Any]:
|
218
|
+
def resolve_futures_to_states(
|
219
|
+
expr: Union[PrefectFuture, Any],
|
220
|
+
) -> Union[State, Any]:
|
312
221
|
"""
|
313
222
|
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.
|
223
|
+
new collection with the same structure with futures resolved to their final states.
|
224
|
+
Resolving futures to their final states may wait for execution to complete.
|
317
225
|
|
318
226
|
Unsupported object types will be returned without modification.
|
319
227
|
"""
|
320
228
|
futures: Set[PrefectFuture] = set()
|
321
229
|
|
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
|
230
|
+
def _collect_futures(futures, expr, context):
|
231
|
+
# Expressions inside quotes should not be traversed
|
346
232
|
if isinstance(context.get("annotation"), quote):
|
347
233
|
raise StopVisiting()
|
348
234
|
|
349
235
|
if isinstance(expr, PrefectFuture):
|
350
|
-
|
351
|
-
else:
|
352
|
-
return expr
|
236
|
+
futures.add(expr)
|
353
237
|
|
354
|
-
|
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.
|
369
|
-
|
370
|
-
Unsupported object types will be returned without modification.
|
371
|
-
"""
|
372
|
-
futures: Set[PrefectFuture] = set()
|
238
|
+
return expr
|
373
239
|
|
374
240
|
visit_collection(
|
375
241
|
expr,
|
@@ -379,13 +245,10 @@ async def resolve_futures_to_states(
|
|
379
245
|
)
|
380
246
|
|
381
247
|
# Get final states for each future
|
382
|
-
states =
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
for future in futures
|
387
|
-
]
|
388
|
-
)
|
248
|
+
states = []
|
249
|
+
for future in futures:
|
250
|
+
future.wait()
|
251
|
+
states.append(future.state)
|
389
252
|
|
390
253
|
states_by_future = dict(zip(futures, states))
|
391
254
|
|
@@ -405,25 +268,3 @@ async def resolve_futures_to_states(
|
|
405
268
|
return_data=True,
|
406
269
|
context={},
|
407
270
|
)
|
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
|
|