prefect-client 2.19.3__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 +147 -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 +248 -165
- 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 +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 +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 +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 +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.3.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.3.dist-info/RECORD +0 -292
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.3.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
prefect/utilities/collections.py
CHANGED
@@ -4,6 +4,7 @@ Utilities for extensions of and operations on Python collections.
|
|
4
4
|
|
5
5
|
import io
|
6
6
|
import itertools
|
7
|
+
import warnings
|
7
8
|
from collections import OrderedDict, defaultdict
|
8
9
|
from collections.abc import Iterator as IteratorABC
|
9
10
|
from collections.abc import Sequence
|
@@ -28,12 +29,7 @@ from typing import (
|
|
28
29
|
)
|
29
30
|
from unittest.mock import Mock
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
if HAS_PYDANTIC_V2:
|
34
|
-
import pydantic.v1 as pydantic
|
35
|
-
else:
|
36
|
-
import pydantic
|
32
|
+
import pydantic
|
37
33
|
|
38
34
|
# Quote moved to `prefect.utilities.annotations` but preserved here for compatibility
|
39
35
|
from prefect.utilities.annotations import BaseAnnotation, Quote, quote # noqa
|
@@ -225,7 +221,7 @@ class StopVisiting(BaseException):
|
|
225
221
|
|
226
222
|
def visit_collection(
|
227
223
|
expr,
|
228
|
-
visit_fn: Callable[[Any], Any],
|
224
|
+
visit_fn: Union[Callable[[Any, dict], Any], Callable[[Any], Any]],
|
229
225
|
return_data: bool = False,
|
230
226
|
max_depth: int = -1,
|
231
227
|
context: Optional[dict] = None,
|
@@ -252,8 +248,10 @@ def visit_collection(
|
|
252
248
|
|
253
249
|
Args:
|
254
250
|
expr (Any): a Python object or expression
|
255
|
-
visit_fn (Callable[[Any], Awaitable[Any]]):
|
256
|
-
will be applied to every non-collection element of expr.
|
251
|
+
visit_fn (Callable[[Any, Optional[dict]], Awaitable[Any]]): a function that
|
252
|
+
will be applied to every non-collection element of expr. The function can
|
253
|
+
accept one or two arguments. If two arguments are accepted, the second
|
254
|
+
argument will be the context dictionary.
|
257
255
|
return_data (bool): if `True`, a copy of `expr` containing data modified
|
258
256
|
by `visit_fn` will be returned. This is slower than `return_data=False`
|
259
257
|
(the default).
|
@@ -343,39 +341,26 @@ def visit_collection(
|
|
343
341
|
result = typ(**items) if return_data else None
|
344
342
|
|
345
343
|
elif isinstance(expr, pydantic.BaseModel):
|
346
|
-
|
347
|
-
# Pydantic does not expose extras in `__fields__` so we use `__fields_set__`
|
348
|
-
# as well to get all of the relevant attributes
|
349
|
-
# Check for presence of attrs even if they're in the field set due to pydantic#4916
|
350
|
-
model_fields = {
|
351
|
-
f for f in expr.__fields_set__.union(expr.__fields__) if hasattr(expr, f)
|
352
|
-
}
|
353
|
-
items = [visit_nested(getattr(expr, key)) for key in model_fields]
|
344
|
+
typ = cast(Type[pydantic.BaseModel], typ)
|
354
345
|
|
355
|
-
|
356
|
-
|
357
|
-
aliases = {
|
358
|
-
key: value.alias
|
359
|
-
for key, value in expr.__fields__.items()
|
360
|
-
if value.has_alias
|
361
|
-
}
|
346
|
+
# when extra=allow, fields not in model_fields may be in model_fields_set
|
347
|
+
model_fields = expr.model_fields_set.union(expr.model_fields.keys())
|
362
348
|
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
for key, value in zip(model_fields, items)
|
367
|
-
}
|
368
|
-
)
|
349
|
+
# We may encounter a deprecated field here, but this isn't the caller's fault
|
350
|
+
with warnings.catch_warnings():
|
351
|
+
warnings.simplefilter("ignore", category=DeprecationWarning)
|
369
352
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
for attr in expr.__private_attributes__:
|
374
|
-
# Use `object.__setattr__` to avoid errors on immutable models
|
375
|
-
object.__setattr__(model_instance, attr, getattr(expr, attr))
|
353
|
+
updated_data = {
|
354
|
+
field: visit_nested(getattr(expr, field)) for field in model_fields
|
355
|
+
}
|
376
356
|
|
377
|
-
|
378
|
-
|
357
|
+
if return_data:
|
358
|
+
# Use construct to avoid validation and handle immutability
|
359
|
+
model_instance = typ.model_construct(
|
360
|
+
_fields_set=expr.model_fields_set, **updated_data
|
361
|
+
)
|
362
|
+
for private_attr in expr.__private_attributes__:
|
363
|
+
setattr(model_instance, private_attr, getattr(expr, private_attr))
|
379
364
|
result = model_instance
|
380
365
|
else:
|
381
366
|
result = None
|
prefect/utilities/dispatch.py
CHANGED
@@ -90,8 +90,10 @@ def get_dispatch_key(
|
|
90
90
|
|
91
91
|
@classmethod
|
92
92
|
def _register_subclass_of_base_type(cls, **kwargs):
|
93
|
-
if cls
|
93
|
+
if hasattr(cls, "__init_subclass_original__"):
|
94
94
|
cls.__init_subclass_original__(**kwargs)
|
95
|
+
elif hasattr(cls, "__pydantic_init_subclass_original__"):
|
96
|
+
cls.__pydantic_init_subclass_original__(**kwargs)
|
95
97
|
|
96
98
|
# Do not register abstract base classes
|
97
99
|
if abc.ABC in getattr(cls, "__bases__", []):
|
@@ -114,8 +116,14 @@ def register_base_type(cls: T) -> T:
|
|
114
116
|
registry[base_key] = cls
|
115
117
|
|
116
118
|
# Add automatic subtype registration
|
117
|
-
|
118
|
-
|
119
|
+
if hasattr(cls, "__pydantic_init_subclass__"):
|
120
|
+
cls.__pydantic_init_subclass_original__ = getattr(
|
121
|
+
cls, "__pydantic_init_subclass__"
|
122
|
+
)
|
123
|
+
cls.__pydantic_init_subclass__ = _register_subclass_of_base_type
|
124
|
+
else:
|
125
|
+
cls.__init_subclass_original__ = getattr(cls, "__init_subclass__")
|
126
|
+
cls.__init_subclass__ = _register_subclass_of_base_type
|
119
127
|
|
120
128
|
return cls
|
121
129
|
|
prefect/utilities/dockerutils.py
CHANGED
@@ -27,6 +27,10 @@ import prefect
|
|
27
27
|
from prefect.utilities.importtools import lazy_import
|
28
28
|
from prefect.utilities.slugify import slugify
|
29
29
|
|
30
|
+
CONTAINER_LABELS = {
|
31
|
+
"io.prefect.version": prefect.__version__,
|
32
|
+
}
|
33
|
+
|
30
34
|
|
31
35
|
def python_version_minor() -> str:
|
32
36
|
return f"{sys.version_info.major}.{sys.version_info.minor}"
|
prefect/utilities/engine.py
CHANGED
@@ -6,6 +6,7 @@ import signal
|
|
6
6
|
import time
|
7
7
|
from functools import partial
|
8
8
|
from typing import (
|
9
|
+
TYPE_CHECKING,
|
9
10
|
Any,
|
10
11
|
Callable,
|
11
12
|
Dict,
|
@@ -24,7 +25,6 @@ import prefect
|
|
24
25
|
import prefect.context
|
25
26
|
import prefect.plugins
|
26
27
|
from prefect._internal.concurrency.cancellation import get_deadline
|
27
|
-
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
28
28
|
from prefect.client.schemas import OrchestrationResult, TaskRun
|
29
29
|
from prefect.client.schemas.objects import (
|
30
30
|
StateType,
|
@@ -44,6 +44,7 @@ from prefect.exceptions import (
|
|
44
44
|
)
|
45
45
|
from prefect.flows import Flow
|
46
46
|
from prefect.futures import PrefectFuture
|
47
|
+
from prefect.futures import PrefectFuture as NewPrefectFuture
|
47
48
|
from prefect.logging.loggers import (
|
48
49
|
get_logger,
|
49
50
|
task_run_logger,
|
@@ -55,17 +56,19 @@ from prefect.settings import (
|
|
55
56
|
from prefect.states import (
|
56
57
|
State,
|
57
58
|
get_state_exception,
|
58
|
-
is_state,
|
59
59
|
)
|
60
60
|
from prefect.tasks import Task
|
61
61
|
from prefect.utilities.annotations import allow_failure, quote
|
62
62
|
from prefect.utilities.asyncutils import (
|
63
63
|
gather,
|
64
|
-
|
64
|
+
run_coro_as_sync,
|
65
65
|
)
|
66
66
|
from prefect.utilities.collections import StopVisiting, visit_collection
|
67
67
|
from prefect.utilities.text import truncated_to
|
68
68
|
|
69
|
+
if TYPE_CHECKING:
|
70
|
+
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
71
|
+
|
69
72
|
API_HEALTHCHECKS = {}
|
70
73
|
UNTRACKABLE_TYPES = {bool, type(None), type(...), type(NotImplemented)}
|
71
74
|
engine_logger = get_logger("engine")
|
@@ -93,7 +96,7 @@ async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> Set[TaskRun
|
|
93
96
|
# We need to wait for futures to be submitted before we can get the task
|
94
97
|
# run id but we want to do so asynchronously
|
95
98
|
futures.add(obj)
|
96
|
-
elif
|
99
|
+
elif isinstance(obj, State):
|
97
100
|
if obj.state_details.task_run_id:
|
98
101
|
inputs.add(TaskRunResult(id=obj.state_details.task_run_id))
|
99
102
|
# Expressions inside quotes should not be traversed
|
@@ -118,8 +121,49 @@ async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> Set[TaskRun
|
|
118
121
|
return inputs
|
119
122
|
|
120
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
|
+
|
121
165
|
async def wait_for_task_runs_and_report_crashes(
|
122
|
-
task_run_futures: Iterable[PrefectFuture], client: PrefectClient
|
166
|
+
task_run_futures: Iterable[PrefectFuture], client: "PrefectClient"
|
123
167
|
) -> Literal[True]:
|
124
168
|
crash_exceptions = []
|
125
169
|
|
@@ -225,7 +269,7 @@ async def resolve_inputs(
|
|
225
269
|
|
226
270
|
if isinstance(expr, PrefectFuture):
|
227
271
|
futures.add(expr)
|
228
|
-
if
|
272
|
+
if isinstance(expr, State):
|
229
273
|
states.add(expr)
|
230
274
|
|
231
275
|
return expr
|
@@ -264,7 +308,7 @@ async def resolve_inputs(
|
|
264
308
|
|
265
309
|
if isinstance(expr, PrefectFuture):
|
266
310
|
state = expr._final_state
|
267
|
-
elif
|
311
|
+
elif isinstance(expr, State):
|
268
312
|
state = expr
|
269
313
|
else:
|
270
314
|
return expr
|
@@ -311,7 +355,7 @@ async def resolve_inputs(
|
|
311
355
|
|
312
356
|
|
313
357
|
async def propose_state(
|
314
|
-
client: PrefectClient,
|
358
|
+
client: "PrefectClient",
|
315
359
|
state: State[object],
|
316
360
|
force: bool = False,
|
317
361
|
task_run_id: Optional[UUID] = None,
|
@@ -412,7 +456,7 @@ async def propose_state(
|
|
412
456
|
|
413
457
|
|
414
458
|
def propose_state_sync(
|
415
|
-
client: SyncPrefectClient,
|
459
|
+
client: "SyncPrefectClient",
|
416
460
|
state: State[object],
|
417
461
|
force: bool = False,
|
418
462
|
task_run_id: Optional[UUID] = None,
|
@@ -459,7 +503,7 @@ def propose_state_sync(
|
|
459
503
|
# the purpose of disabling `cache_result_in_memory`
|
460
504
|
result = state.result(raise_on_failure=False, fetch=True)
|
461
505
|
if inspect.isawaitable(result):
|
462
|
-
result =
|
506
|
+
result = run_coro_as_sync(result)
|
463
507
|
else:
|
464
508
|
result = state.data
|
465
509
|
|
@@ -515,7 +559,9 @@ def propose_state_sync(
|
|
515
559
|
|
516
560
|
|
517
561
|
def _dynamic_key_for_task_run(context: FlowRunContext, task: Task) -> int:
|
518
|
-
if context.
|
562
|
+
if context.detached: # this task is running on remote infrastructure
|
563
|
+
return str(uuid4())
|
564
|
+
elif context.flow_run is None: # this is an autonomous task run
|
519
565
|
context.task_run_dynamic_keys[task.task_key] = getattr(
|
520
566
|
task, "dynamic_key", str(uuid4())
|
521
567
|
)
|
@@ -528,14 +574,6 @@ def _dynamic_key_for_task_run(context: FlowRunContext, task: Task) -> int:
|
|
528
574
|
return context.task_run_dynamic_keys[task.task_key]
|
529
575
|
|
530
576
|
|
531
|
-
def _observed_flow_pauses(context: FlowRunContext) -> int:
|
532
|
-
if "counter" not in context.observed_flow_pauses:
|
533
|
-
context.observed_flow_pauses["counter"] = 1
|
534
|
-
else:
|
535
|
-
context.observed_flow_pauses["counter"] += 1
|
536
|
-
return context.observed_flow_pauses["counter"]
|
537
|
-
|
538
|
-
|
539
577
|
def get_state_for_result(obj: Any) -> Optional[State]:
|
540
578
|
"""
|
541
579
|
Get the state related to a result object.
|
@@ -664,7 +702,7 @@ def _get_hook_name(hook: Callable) -> str:
|
|
664
702
|
)
|
665
703
|
|
666
704
|
|
667
|
-
async def check_api_reachable(client: PrefectClient, fail_message: str):
|
705
|
+
async def check_api_reachable(client: "PrefectClient", fail_message: str):
|
668
706
|
# Do not perform a healthcheck if it exists and is not expired
|
669
707
|
api_url = str(client.api_url)
|
670
708
|
if api_url in API_HEALTHCHECKS:
|
@@ -734,3 +772,85 @@ def emit_task_run_state_change_event(
|
|
734
772
|
},
|
735
773
|
follows=follows,
|
736
774
|
)
|
775
|
+
|
776
|
+
|
777
|
+
def resolve_to_final_result(expr, context):
|
778
|
+
"""
|
779
|
+
Resolve any `PrefectFuture`, or `State` types nested in parameters into
|
780
|
+
data. Designed to be use with `visit_collection`.
|
781
|
+
"""
|
782
|
+
state = None
|
783
|
+
|
784
|
+
# Expressions inside quotes should not be modified
|
785
|
+
if isinstance(context.get("annotation"), quote):
|
786
|
+
raise StopVisiting()
|
787
|
+
|
788
|
+
if isinstance(expr, NewPrefectFuture):
|
789
|
+
expr.wait()
|
790
|
+
state = expr.state
|
791
|
+
elif isinstance(expr, State):
|
792
|
+
state = expr
|
793
|
+
else:
|
794
|
+
return expr
|
795
|
+
|
796
|
+
assert state
|
797
|
+
|
798
|
+
# Do not allow uncompleted upstreams except failures when `allow_failure` has
|
799
|
+
# been used
|
800
|
+
if not state.is_completed() and not (
|
801
|
+
# TODO: Note that the contextual annotation here is only at the current level
|
802
|
+
# if `allow_failure` is used then another annotation is used, this will
|
803
|
+
# incorrectly evaluate to false — to resolve this, we must track all
|
804
|
+
# annotations wrapping the current expression but this is not yet
|
805
|
+
# implemented.
|
806
|
+
isinstance(context.get("annotation"), allow_failure) and state.is_failed()
|
807
|
+
):
|
808
|
+
raise UpstreamTaskError(
|
809
|
+
f"Upstream task run '{state.state_details.task_run_id}' did not reach a"
|
810
|
+
" 'COMPLETED' state."
|
811
|
+
)
|
812
|
+
|
813
|
+
_result = state.result(raise_on_failure=False, fetch=True)
|
814
|
+
if inspect.isawaitable(_result):
|
815
|
+
_result = run_coro_as_sync(_result)
|
816
|
+
return _result
|
817
|
+
|
818
|
+
|
819
|
+
def resolve_inputs_sync(
|
820
|
+
parameters: Dict[str, Any], return_data: bool = True, max_depth: int = -1
|
821
|
+
) -> Dict[str, Any]:
|
822
|
+
"""
|
823
|
+
Resolve any `Quote`, `PrefectFuture`, or `State` types nested in parameters into
|
824
|
+
data.
|
825
|
+
|
826
|
+
Returns:
|
827
|
+
A copy of the parameters with resolved data
|
828
|
+
|
829
|
+
Raises:
|
830
|
+
UpstreamTaskError: If any of the upstream states are not `COMPLETED`
|
831
|
+
"""
|
832
|
+
|
833
|
+
if not parameters:
|
834
|
+
return {}
|
835
|
+
|
836
|
+
resolved_parameters = {}
|
837
|
+
for parameter, value in parameters.items():
|
838
|
+
try:
|
839
|
+
resolved_parameters[parameter] = visit_collection(
|
840
|
+
value,
|
841
|
+
visit_fn=resolve_to_final_result,
|
842
|
+
return_data=return_data,
|
843
|
+
max_depth=max_depth,
|
844
|
+
remove_annotations=True,
|
845
|
+
context={},
|
846
|
+
)
|
847
|
+
except UpstreamTaskError:
|
848
|
+
raise
|
849
|
+
except Exception as exc:
|
850
|
+
raise PrefectException(
|
851
|
+
f"Failed to resolve inputs in parameter {parameter!r}. If your"
|
852
|
+
" parameter type is not supported, consider using the `quote`"
|
853
|
+
" annotation to skip resolution of inputs."
|
854
|
+
) from exc
|
855
|
+
|
856
|
+
return resolved_parameters
|
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
|