prefect-client 2.20.4__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/__init__.py +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/utilities/dockerutils.py
CHANGED
@@ -9,6 +9,7 @@ from tempfile import TemporaryDirectory
|
|
9
9
|
from types import TracebackType
|
10
10
|
from typing import (
|
11
11
|
TYPE_CHECKING,
|
12
|
+
Any,
|
12
13
|
Generator,
|
13
14
|
Iterable,
|
14
15
|
List,
|
@@ -27,6 +28,10 @@ import prefect
|
|
27
28
|
from prefect.utilities.importtools import lazy_import
|
28
29
|
from prefect.utilities.slugify import slugify
|
29
30
|
|
31
|
+
CONTAINER_LABELS = {
|
32
|
+
"io.prefect.version": prefect.__version__,
|
33
|
+
}
|
34
|
+
|
30
35
|
|
31
36
|
def python_version_minor() -> str:
|
32
37
|
return f"{sys.version_info.major}.{sys.version_info.minor}"
|
@@ -37,7 +42,9 @@ def python_version_micro() -> str:
|
|
37
42
|
|
38
43
|
|
39
44
|
def get_prefect_image_name(
|
40
|
-
prefect_version: str = None,
|
45
|
+
prefect_version: Optional[str] = None,
|
46
|
+
python_version: Optional[str] = None,
|
47
|
+
flavor: Optional[str] = None,
|
41
48
|
) -> str:
|
42
49
|
"""
|
43
50
|
Get the Prefect image name matching the current Prefect and Python versions.
|
@@ -134,9 +141,9 @@ def build_image(
|
|
134
141
|
dockerfile: str = "Dockerfile",
|
135
142
|
tag: Optional[str] = None,
|
136
143
|
pull: bool = False,
|
137
|
-
platform: str = None,
|
144
|
+
platform: Optional[str] = None,
|
138
145
|
stream_progress_to: Optional[TextIO] = None,
|
139
|
-
**kwargs,
|
146
|
+
**kwargs: Any,
|
140
147
|
) -> str:
|
141
148
|
"""Builds a Docker image, returning the image ID
|
142
149
|
|
@@ -205,7 +212,7 @@ class ImageBuilder:
|
|
205
212
|
self,
|
206
213
|
base_image: str,
|
207
214
|
base_directory: Path = None,
|
208
|
-
platform: str = None,
|
215
|
+
platform: Optional[str] = None,
|
209
216
|
context: Path = None,
|
210
217
|
):
|
211
218
|
"""Create an ImageBuilder
|
prefect/utilities/engine.py
CHANGED
@@ -4,13 +4,12 @@ import inspect
|
|
4
4
|
import os
|
5
5
|
import signal
|
6
6
|
import time
|
7
|
-
from contextlib import contextmanager
|
8
7
|
from functools import partial
|
9
8
|
from typing import (
|
9
|
+
TYPE_CHECKING,
|
10
10
|
Any,
|
11
11
|
Callable,
|
12
12
|
Dict,
|
13
|
-
Generator,
|
14
13
|
Iterable,
|
15
14
|
Optional,
|
16
15
|
Set,
|
@@ -20,14 +19,12 @@ from typing import (
|
|
20
19
|
from uuid import UUID, uuid4
|
21
20
|
|
22
21
|
import anyio
|
23
|
-
from exceptiongroup import BaseExceptionGroup # novermin
|
24
22
|
from typing_extensions import Literal
|
25
23
|
|
26
24
|
import prefect
|
27
25
|
import prefect.context
|
28
26
|
import prefect.plugins
|
29
27
|
from prefect._internal.concurrency.cancellation import get_deadline
|
30
|
-
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
31
28
|
from prefect.client.schemas import OrchestrationResult, TaskRun
|
32
29
|
from prefect.client.schemas.objects import (
|
33
30
|
StateType,
|
@@ -47,6 +44,7 @@ from prefect.exceptions import (
|
|
47
44
|
)
|
48
45
|
from prefect.flows import Flow
|
49
46
|
from prefect.futures import PrefectFuture
|
47
|
+
from prefect.futures import PrefectFuture as NewPrefectFuture
|
50
48
|
from prefect.logging.loggers import (
|
51
49
|
get_logger,
|
52
50
|
task_run_logger,
|
@@ -58,17 +56,19 @@ from prefect.settings import (
|
|
58
56
|
from prefect.states import (
|
59
57
|
State,
|
60
58
|
get_state_exception,
|
61
|
-
is_state,
|
62
59
|
)
|
63
60
|
from prefect.tasks import Task
|
64
61
|
from prefect.utilities.annotations import allow_failure, quote
|
65
62
|
from prefect.utilities.asyncutils import (
|
66
63
|
gather,
|
67
|
-
|
64
|
+
run_coro_as_sync,
|
68
65
|
)
|
69
66
|
from prefect.utilities.collections import StopVisiting, visit_collection
|
70
67
|
from prefect.utilities.text import truncated_to
|
71
68
|
|
69
|
+
if TYPE_CHECKING:
|
70
|
+
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
71
|
+
|
72
72
|
API_HEALTHCHECKS = {}
|
73
73
|
UNTRACKABLE_TYPES = {bool, type(None), type(...), type(NotImplemented)}
|
74
74
|
engine_logger = get_logger("engine")
|
@@ -96,7 +96,7 @@ async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> Set[TaskRun
|
|
96
96
|
# We need to wait for futures to be submitted before we can get the task
|
97
97
|
# run id but we want to do so asynchronously
|
98
98
|
futures.add(obj)
|
99
|
-
elif
|
99
|
+
elif isinstance(obj, State):
|
100
100
|
if obj.state_details.task_run_id:
|
101
101
|
inputs.add(TaskRunResult(id=obj.state_details.task_run_id))
|
102
102
|
# Expressions inside quotes should not be traversed
|
@@ -121,8 +121,49 @@ async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> Set[TaskRun
|
|
121
121
|
return inputs
|
122
122
|
|
123
123
|
|
124
|
+
def collect_task_run_inputs_sync(
|
125
|
+
expr: Any, future_cls: Any = NewPrefectFuture, max_depth: int = -1
|
126
|
+
) -> Set[TaskRunInput]:
|
127
|
+
"""
|
128
|
+
This function recurses through an expression to generate a set of any discernible
|
129
|
+
task run inputs it finds in the data structure. It produces a set of all inputs
|
130
|
+
found.
|
131
|
+
|
132
|
+
Examples:
|
133
|
+
>>> task_inputs = {
|
134
|
+
>>> k: collect_task_run_inputs(v) for k, v in parameters.items()
|
135
|
+
>>> }
|
136
|
+
"""
|
137
|
+
# TODO: This function needs to be updated to detect parameters and constants
|
138
|
+
|
139
|
+
inputs = set()
|
140
|
+
|
141
|
+
def add_futures_and_states_to_inputs(obj):
|
142
|
+
if isinstance(obj, future_cls) and hasattr(obj, "task_run_id"):
|
143
|
+
inputs.add(TaskRunResult(id=obj.task_run_id))
|
144
|
+
elif isinstance(obj, State):
|
145
|
+
if obj.state_details.task_run_id:
|
146
|
+
inputs.add(TaskRunResult(id=obj.state_details.task_run_id))
|
147
|
+
# Expressions inside quotes should not be traversed
|
148
|
+
elif isinstance(obj, quote):
|
149
|
+
raise StopVisiting
|
150
|
+
else:
|
151
|
+
state = get_state_for_result(obj)
|
152
|
+
if state and state.state_details.task_run_id:
|
153
|
+
inputs.add(TaskRunResult(id=state.state_details.task_run_id))
|
154
|
+
|
155
|
+
visit_collection(
|
156
|
+
expr,
|
157
|
+
visit_fn=add_futures_and_states_to_inputs,
|
158
|
+
return_data=False,
|
159
|
+
max_depth=max_depth,
|
160
|
+
)
|
161
|
+
|
162
|
+
return inputs
|
163
|
+
|
164
|
+
|
124
165
|
async def wait_for_task_runs_and_report_crashes(
|
125
|
-
task_run_futures: Iterable[PrefectFuture], client: PrefectClient
|
166
|
+
task_run_futures: Iterable[PrefectFuture], client: "PrefectClient"
|
126
167
|
) -> Literal[True]:
|
127
168
|
crash_exceptions = []
|
128
169
|
|
@@ -228,7 +269,7 @@ async def resolve_inputs(
|
|
228
269
|
|
229
270
|
if isinstance(expr, PrefectFuture):
|
230
271
|
futures.add(expr)
|
231
|
-
if
|
272
|
+
if isinstance(expr, State):
|
232
273
|
states.add(expr)
|
233
274
|
|
234
275
|
return expr
|
@@ -267,7 +308,7 @@ async def resolve_inputs(
|
|
267
308
|
|
268
309
|
if isinstance(expr, PrefectFuture):
|
269
310
|
state = expr._final_state
|
270
|
-
elif
|
311
|
+
elif isinstance(expr, State):
|
271
312
|
state = expr
|
272
313
|
else:
|
273
314
|
return expr
|
@@ -314,7 +355,7 @@ async def resolve_inputs(
|
|
314
355
|
|
315
356
|
|
316
357
|
async def propose_state(
|
317
|
-
client: PrefectClient,
|
358
|
+
client: "PrefectClient",
|
318
359
|
state: State[object],
|
319
360
|
force: bool = False,
|
320
361
|
task_run_id: Optional[UUID] = None,
|
@@ -415,7 +456,7 @@ async def propose_state(
|
|
415
456
|
|
416
457
|
|
417
458
|
def propose_state_sync(
|
418
|
-
client: SyncPrefectClient,
|
459
|
+
client: "SyncPrefectClient",
|
419
460
|
state: State[object],
|
420
461
|
force: bool = False,
|
421
462
|
task_run_id: Optional[UUID] = None,
|
@@ -462,7 +503,7 @@ def propose_state_sync(
|
|
462
503
|
# the purpose of disabling `cache_result_in_memory`
|
463
504
|
result = state.result(raise_on_failure=False, fetch=True)
|
464
505
|
if inspect.isawaitable(result):
|
465
|
-
result =
|
506
|
+
result = run_coro_as_sync(result)
|
466
507
|
else:
|
467
508
|
result = state.data
|
468
509
|
|
@@ -517,8 +558,14 @@ def propose_state_sync(
|
|
517
558
|
)
|
518
559
|
|
519
560
|
|
520
|
-
def _dynamic_key_for_task_run(
|
521
|
-
|
561
|
+
def _dynamic_key_for_task_run(
|
562
|
+
context: FlowRunContext, task: Task, stable: bool = True
|
563
|
+
) -> Union[int, str]:
|
564
|
+
if (
|
565
|
+
stable is False or context.detached
|
566
|
+
): # this task is running on remote infrastructure
|
567
|
+
return str(uuid4())
|
568
|
+
elif context.flow_run is None: # this is an autonomous task run
|
522
569
|
context.task_run_dynamic_keys[task.task_key] = getattr(
|
523
570
|
task, "dynamic_key", str(uuid4())
|
524
571
|
)
|
@@ -531,14 +578,6 @@ def _dynamic_key_for_task_run(context: FlowRunContext, task: Task) -> int:
|
|
531
578
|
return context.task_run_dynamic_keys[task.task_key]
|
532
579
|
|
533
580
|
|
534
|
-
def _observed_flow_pauses(context: FlowRunContext) -> int:
|
535
|
-
if "counter" not in context.observed_flow_pauses:
|
536
|
-
context.observed_flow_pauses["counter"] = 1
|
537
|
-
else:
|
538
|
-
context.observed_flow_pauses["counter"] += 1
|
539
|
-
return context.observed_flow_pauses["counter"]
|
540
|
-
|
541
|
-
|
542
581
|
def get_state_for_result(obj: Any) -> Optional[State]:
|
543
582
|
"""
|
544
583
|
Get the state related to a result object.
|
@@ -667,7 +706,7 @@ def _get_hook_name(hook: Callable) -> str:
|
|
667
706
|
)
|
668
707
|
|
669
708
|
|
670
|
-
async def check_api_reachable(client: PrefectClient, fail_message: str):
|
709
|
+
async def check_api_reachable(client: "PrefectClient", fail_message: str):
|
671
710
|
# Do not perform a healthcheck if it exists and is not expired
|
672
711
|
api_url = str(client.api_url)
|
673
712
|
if api_url in API_HEALTHCHECKS:
|
@@ -709,6 +748,12 @@ def emit_task_run_state_change_event(
|
|
709
748
|
"message": truncated_to(
|
710
749
|
state_message_truncation_length, initial_state.message
|
711
750
|
),
|
751
|
+
"state_details": initial_state.state_details.model_dump(
|
752
|
+
mode="json",
|
753
|
+
exclude_none=True,
|
754
|
+
exclude_unset=True,
|
755
|
+
exclude={"flow_run_id", "task_run_id"},
|
756
|
+
),
|
712
757
|
}
|
713
758
|
if initial_state
|
714
759
|
else None
|
@@ -719,7 +764,33 @@ def emit_task_run_state_change_event(
|
|
719
764
|
"message": truncated_to(
|
720
765
|
state_message_truncation_length, validated_state.message
|
721
766
|
),
|
767
|
+
"state_details": validated_state.state_details.model_dump(
|
768
|
+
mode="json",
|
769
|
+
exclude_none=True,
|
770
|
+
exclude_unset=True,
|
771
|
+
exclude={"flow_run_id", "task_run_id"},
|
772
|
+
),
|
773
|
+
"data": validated_state.data.model_dump(mode="json")
|
774
|
+
if isinstance(validated_state.data, BaseResult)
|
775
|
+
else None,
|
722
776
|
},
|
777
|
+
"task_run": task_run.model_dump(
|
778
|
+
mode="json",
|
779
|
+
exclude_none=True,
|
780
|
+
exclude={
|
781
|
+
"id",
|
782
|
+
"created",
|
783
|
+
"updated",
|
784
|
+
"flow_run_id",
|
785
|
+
"state_id",
|
786
|
+
"state_type",
|
787
|
+
"state_name",
|
788
|
+
"state",
|
789
|
+
# server materialized fields
|
790
|
+
"estimated_start_time_delta",
|
791
|
+
"estimated_run_time",
|
792
|
+
},
|
793
|
+
),
|
723
794
|
},
|
724
795
|
resource={
|
725
796
|
"prefect.resource.id": f"prefect.task-run.{task_run.id}",
|
@@ -734,17 +805,100 @@ def emit_task_run_state_change_event(
|
|
734
805
|
else ""
|
735
806
|
),
|
736
807
|
"prefect.state-type": str(validated_state.type.value),
|
808
|
+
"prefect.orchestration": "client",
|
737
809
|
},
|
738
810
|
follows=follows,
|
739
811
|
)
|
740
812
|
|
741
813
|
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
814
|
+
def resolve_to_final_result(expr, context):
|
815
|
+
"""
|
816
|
+
Resolve any `PrefectFuture`, or `State` types nested in parameters into
|
817
|
+
data. Designed to be use with `visit_collection`.
|
818
|
+
"""
|
819
|
+
state = None
|
820
|
+
|
821
|
+
# Expressions inside quotes should not be modified
|
822
|
+
if isinstance(context.get("annotation"), quote):
|
823
|
+
raise StopVisiting()
|
824
|
+
|
825
|
+
if isinstance(expr, NewPrefectFuture):
|
826
|
+
upstream_task_run = context.get("current_task_run")
|
827
|
+
upstream_task = context.get("current_task")
|
828
|
+
if (
|
829
|
+
upstream_task
|
830
|
+
and upstream_task_run
|
831
|
+
and expr.task_run_id == upstream_task_run.id
|
832
|
+
):
|
833
|
+
raise ValueError(
|
834
|
+
f"Discovered a task depending on itself. Raising to avoid a deadlock. Please inspect the inputs and dependencies of {upstream_task.name}."
|
835
|
+
)
|
836
|
+
|
837
|
+
expr.wait()
|
838
|
+
state = expr.state
|
839
|
+
elif isinstance(expr, State):
|
840
|
+
state = expr
|
841
|
+
else:
|
842
|
+
return expr
|
843
|
+
|
844
|
+
assert state
|
845
|
+
|
846
|
+
# Do not allow uncompleted upstreams except failures when `allow_failure` has
|
847
|
+
# been used
|
848
|
+
if not state.is_completed() and not (
|
849
|
+
# TODO: Note that the contextual annotation here is only at the current level
|
850
|
+
# if `allow_failure` is used then another annotation is used, this will
|
851
|
+
# incorrectly evaluate to false — to resolve this, we must track all
|
852
|
+
# annotations wrapping the current expression but this is not yet
|
853
|
+
# implemented.
|
854
|
+
isinstance(context.get("annotation"), allow_failure) and state.is_failed()
|
855
|
+
):
|
856
|
+
raise UpstreamTaskError(
|
857
|
+
f"Upstream task run '{state.state_details.task_run_id}' did not reach a"
|
858
|
+
" 'COMPLETED' state."
|
859
|
+
)
|
860
|
+
|
861
|
+
_result = state.result(raise_on_failure=False, fetch=True)
|
862
|
+
if inspect.isawaitable(_result):
|
863
|
+
_result = run_coro_as_sync(_result)
|
864
|
+
return _result
|
865
|
+
|
866
|
+
|
867
|
+
def resolve_inputs_sync(
|
868
|
+
parameters: Dict[str, Any], return_data: bool = True, max_depth: int = -1
|
869
|
+
) -> Dict[str, Any]:
|
870
|
+
"""
|
871
|
+
Resolve any `Quote`, `PrefectFuture`, or `State` types nested in parameters into
|
872
|
+
data.
|
873
|
+
|
874
|
+
Returns:
|
875
|
+
A copy of the parameters with resolved data
|
876
|
+
|
877
|
+
Raises:
|
878
|
+
UpstreamTaskError: If any of the upstream states are not `COMPLETED`
|
879
|
+
"""
|
749
880
|
|
750
|
-
|
881
|
+
if not parameters:
|
882
|
+
return {}
|
883
|
+
|
884
|
+
resolved_parameters = {}
|
885
|
+
for parameter, value in parameters.items():
|
886
|
+
try:
|
887
|
+
resolved_parameters[parameter] = visit_collection(
|
888
|
+
value,
|
889
|
+
visit_fn=resolve_to_final_result,
|
890
|
+
return_data=return_data,
|
891
|
+
max_depth=max_depth,
|
892
|
+
remove_annotations=True,
|
893
|
+
context={},
|
894
|
+
)
|
895
|
+
except UpstreamTaskError:
|
896
|
+
raise
|
897
|
+
except Exception as exc:
|
898
|
+
raise PrefectException(
|
899
|
+
f"Failed to resolve inputs in parameter {parameter!r}. If your"
|
900
|
+
" parameter type is not supported, consider using the `quote`"
|
901
|
+
" annotation to skip resolution of inputs."
|
902
|
+
) from exc
|
903
|
+
|
904
|
+
return resolved_parameters
|
prefect/utilities/filesystem.py
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
"""
|
2
2
|
Utilities for working with file systems
|
3
3
|
"""
|
4
|
+
|
4
5
|
import os
|
5
6
|
import pathlib
|
6
7
|
import threading
|
7
8
|
from contextlib import contextmanager
|
8
9
|
from pathlib import Path, PureWindowsPath
|
9
|
-
from typing import Union
|
10
|
+
from typing import Optional, Union
|
10
11
|
|
11
12
|
import fsspec
|
12
13
|
import pathspec
|
@@ -32,7 +33,7 @@ def create_default_ignore_file(path: str) -> bool:
|
|
32
33
|
|
33
34
|
|
34
35
|
def filter_files(
|
35
|
-
root: str = ".", ignore_patterns: list = None, include_dirs: bool = True
|
36
|
+
root: str = ".", ignore_patterns: Optional[list] = None, include_dirs: bool = True
|
36
37
|
) -> set:
|
37
38
|
"""
|
38
39
|
This function accepts a root directory path and a list of file patterns to ignore, and returns
|
@@ -40,9 +41,7 @@ def filter_files(
|
|
40
41
|
|
41
42
|
The specification matches that of [.gitignore files](https://git-scm.com/docs/gitignore).
|
42
43
|
"""
|
43
|
-
|
44
|
-
ignore_patterns = []
|
45
|
-
spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_patterns)
|
44
|
+
spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_patterns or [])
|
46
45
|
ignored_files = {p.path for p in spec.match_tree_entries(root)}
|
47
46
|
if include_dirs:
|
48
47
|
all_files = {p.path for p in pathspec.util.iter_tree_entries(root)}
|
prefect/utilities/importtools.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import ast
|
2
2
|
import importlib
|
3
3
|
import importlib.util
|
4
|
-
import inspect
|
5
4
|
import os
|
6
5
|
import runpy
|
7
6
|
import sys
|
7
|
+
import warnings
|
8
8
|
from importlib.abc import Loader, MetaPathFinder
|
9
9
|
from importlib.machinery import ModuleSpec
|
10
10
|
from pathlib import Path
|
@@ -228,24 +228,18 @@ class DelayedImportErrorModule(ModuleType):
|
|
228
228
|
[1]: https://github.com/scientific-python/lazy_loader
|
229
229
|
"""
|
230
230
|
|
231
|
-
def __init__(self,
|
232
|
-
self.
|
231
|
+
def __init__(self, error_message, help_message, *args, **kwargs):
|
232
|
+
self.__error_message = error_message
|
233
233
|
self.__help_message = (
|
234
234
|
help_message or "Import errors for this module are only reported when used."
|
235
235
|
)
|
236
236
|
super().__init__(*args, **kwargs)
|
237
237
|
|
238
238
|
def __getattr__(self, attr):
|
239
|
-
if attr in ("__class__", "__file__", "
|
239
|
+
if attr in ("__class__", "__file__", "__help_message"):
|
240
240
|
super().__getattr__(attr)
|
241
241
|
else:
|
242
|
-
|
243
|
-
raise ModuleNotFoundError(
|
244
|
-
f"No module named '{fd['spec']}'\n\nThis module was originally imported"
|
245
|
-
f" at:\n File \"{fd['filename']}\", line {fd['lineno']}, in"
|
246
|
-
f" {fd['function']}\n\n {''.join(fd['code_context']).strip()}\n"
|
247
|
-
+ self.__help_message
|
248
|
-
)
|
242
|
+
raise ModuleNotFoundError(self.__error_message)
|
249
243
|
|
250
244
|
|
251
245
|
def lazy_import(
|
@@ -256,6 +250,13 @@ def lazy_import(
|
|
256
250
|
Use this to retain module-level imports for libraries that we don't want to
|
257
251
|
actually import until they are needed.
|
258
252
|
|
253
|
+
NOTE: Lazy-loading a subpackage can cause the subpackage to be imported
|
254
|
+
twice if another non-lazy import also imports the subpackage. For example,
|
255
|
+
using both `lazy_import("docker.errors")` and `import docker.errors` in the
|
256
|
+
same codebase will import `docker.errors` twice and can lead to unexpected
|
257
|
+
behavior, e.g. type check failures and import-time side effects running
|
258
|
+
twice.
|
259
|
+
|
259
260
|
Adapted from the [Python documentation][1] and [lazy_loader][2]
|
260
261
|
|
261
262
|
[1]: https://docs.python.org/3/library/importlib.html#implementing-lazy-imports
|
@@ -267,25 +268,23 @@ def lazy_import(
|
|
267
268
|
except KeyError:
|
268
269
|
pass
|
269
270
|
|
271
|
+
if "." in name:
|
272
|
+
warnings.warn(
|
273
|
+
"Lazy importing subpackages can lead to unexpected behavior.",
|
274
|
+
RuntimeWarning,
|
275
|
+
)
|
276
|
+
|
270
277
|
spec = importlib.util.find_spec(name)
|
278
|
+
|
271
279
|
if spec is None:
|
280
|
+
import_error_message = f"No module named '{name}'.\n{help_message}"
|
281
|
+
|
272
282
|
if error_on_import:
|
273
|
-
raise ModuleNotFoundError(
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
"spec": name,
|
279
|
-
"filename": parent.filename,
|
280
|
-
"lineno": parent.lineno,
|
281
|
-
"function": parent.function,
|
282
|
-
"code_context": parent.code_context,
|
283
|
-
}
|
284
|
-
return DelayedImportErrorModule(
|
285
|
-
frame_data, help_message, "DelayedImportErrorModule"
|
286
|
-
)
|
287
|
-
finally:
|
288
|
-
del parent
|
283
|
+
raise ModuleNotFoundError(import_error_message)
|
284
|
+
|
285
|
+
return DelayedImportErrorModule(
|
286
|
+
import_error_message, help_message, "DelayedImportErrorModule"
|
287
|
+
)
|
289
288
|
|
290
289
|
module = importlib.util.module_from_spec(spec)
|
291
290
|
sys.modules[name] = module
|