prefect-client 2.20.2__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/__init__.py +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +423 -164
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +667 -440
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2466
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +124 -51
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +138 -48
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.2.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/context.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
"""
|
2
2
|
Async and thread safe models for passing runtime context data.
|
3
3
|
|
4
4
|
These contexts should never be directly mutated by the user.
|
@@ -9,50 +9,42 @@ For more user-accessible information about the current run, see [`prefect.runtim
|
|
9
9
|
import os
|
10
10
|
import sys
|
11
11
|
import warnings
|
12
|
-
|
13
|
-
from contextlib import contextmanager
|
12
|
+
import weakref
|
13
|
+
from contextlib import ExitStack, asynccontextmanager, contextmanager
|
14
14
|
from contextvars import ContextVar, Token
|
15
|
-
from functools import update_wrapper
|
16
15
|
from pathlib import Path
|
17
16
|
from typing import (
|
18
17
|
TYPE_CHECKING,
|
19
18
|
Any,
|
20
|
-
|
19
|
+
AsyncGenerator,
|
21
20
|
Dict,
|
22
21
|
Generator,
|
23
|
-
|
22
|
+
Mapping,
|
24
23
|
Optional,
|
25
24
|
Set,
|
26
|
-
Tuple,
|
27
25
|
Type,
|
28
26
|
TypeVar,
|
29
27
|
Union,
|
30
28
|
)
|
31
29
|
|
32
|
-
import anyio.abc
|
33
30
|
import pendulum
|
34
|
-
|
35
|
-
from
|
36
|
-
|
37
|
-
if HAS_PYDANTIC_V2:
|
38
|
-
from pydantic.v1 import BaseModel, Field, PrivateAttr
|
39
|
-
else:
|
40
|
-
from pydantic import BaseModel, Field, PrivateAttr
|
31
|
+
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
|
32
|
+
from pydantic_extra_types.pendulum_dt import DateTime
|
33
|
+
from typing_extensions import Self
|
41
34
|
|
42
35
|
import prefect.logging
|
43
36
|
import prefect.logging.configuration
|
44
37
|
import prefect.settings
|
45
|
-
from prefect._internal.
|
46
|
-
from prefect.client.orchestration import PrefectClient, SyncPrefectClient
|
38
|
+
from prefect._internal.compatibility.migration import getattr_migration
|
39
|
+
from prefect.client.orchestration import PrefectClient, SyncPrefectClient, get_client
|
47
40
|
from prefect.client.schemas import FlowRun, TaskRun
|
48
41
|
from prefect.events.worker import EventsWorker
|
49
42
|
from prefect.exceptions import MissingContextError
|
50
|
-
from prefect.
|
51
|
-
from prefect.results import ResultFactory
|
43
|
+
from prefect.results import ResultStore
|
52
44
|
from prefect.settings import PREFECT_HOME, Profile, Settings
|
53
45
|
from prefect.states import State
|
54
|
-
from prefect.task_runners import
|
55
|
-
from prefect.utilities.
|
46
|
+
from prefect.task_runners import TaskRunner
|
47
|
+
from prefect.utilities.services import start_client_metrics_server
|
56
48
|
|
57
49
|
T = TypeVar("T")
|
58
50
|
|
@@ -63,7 +55,60 @@ if TYPE_CHECKING:
|
|
63
55
|
# Define the global settings context variable
|
64
56
|
# This will be populated downstream but must be null here to facilitate loading the
|
65
57
|
# default settings.
|
66
|
-
GLOBAL_SETTINGS_CONTEXT = None
|
58
|
+
GLOBAL_SETTINGS_CONTEXT = None # type: ignore
|
59
|
+
|
60
|
+
|
61
|
+
def serialize_context() -> Dict[str, Any]:
|
62
|
+
"""
|
63
|
+
Serialize the current context for use in a remote execution environment.
|
64
|
+
"""
|
65
|
+
|
66
|
+
flow_run_context = EngineContext.get()
|
67
|
+
task_run_context = TaskRunContext.get()
|
68
|
+
tags_context = TagsContext.get()
|
69
|
+
settings_context = SettingsContext.get()
|
70
|
+
|
71
|
+
return {
|
72
|
+
"flow_run_context": flow_run_context.serialize() if flow_run_context else {},
|
73
|
+
"task_run_context": task_run_context.serialize() if task_run_context else {},
|
74
|
+
"tags_context": tags_context.serialize() if tags_context else {},
|
75
|
+
"settings_context": settings_context.serialize() if settings_context else {},
|
76
|
+
}
|
77
|
+
|
78
|
+
|
79
|
+
@contextmanager
|
80
|
+
def hydrated_context(
|
81
|
+
serialized_context: Optional[Dict[str, Any]] = None,
|
82
|
+
client: Union[PrefectClient, SyncPrefectClient, None] = None,
|
83
|
+
):
|
84
|
+
with ExitStack() as stack:
|
85
|
+
if serialized_context:
|
86
|
+
# Set up settings context
|
87
|
+
if settings_context := serialized_context.get("settings_context"):
|
88
|
+
stack.enter_context(SettingsContext(**settings_context))
|
89
|
+
# Set up parent flow run context
|
90
|
+
client = client or get_client(sync_client=True)
|
91
|
+
if flow_run_context := serialized_context.get("flow_run_context"):
|
92
|
+
flow = flow_run_context["flow"]
|
93
|
+
task_runner = stack.enter_context(flow.task_runner.duplicate())
|
94
|
+
flow_run_context = FlowRunContext(
|
95
|
+
**flow_run_context,
|
96
|
+
client=client,
|
97
|
+
task_runner=task_runner,
|
98
|
+
detached=True,
|
99
|
+
)
|
100
|
+
stack.enter_context(flow_run_context)
|
101
|
+
# Set up parent task run context
|
102
|
+
if parent_task_run_context := serialized_context.get("task_run_context"):
|
103
|
+
task_run_context = TaskRunContext(
|
104
|
+
**parent_task_run_context,
|
105
|
+
client=client,
|
106
|
+
)
|
107
|
+
stack.enter_context(task_run_context)
|
108
|
+
# Set up tags context
|
109
|
+
if tags_context := serialized_context.get("tags_context"):
|
110
|
+
stack.enter_context(tags(*tags_context["current_tags"]))
|
111
|
+
yield
|
67
112
|
|
68
113
|
|
69
114
|
class ContextModel(BaseModel):
|
@@ -74,14 +119,13 @@ class ContextModel(BaseModel):
|
|
74
119
|
|
75
120
|
# The context variable for storing data must be defined by the child class
|
76
121
|
__var__: ContextVar
|
77
|
-
_token: Token = PrivateAttr(None)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
extra = "forbid"
|
122
|
+
_token: Optional[Token] = PrivateAttr(None)
|
123
|
+
model_config = ConfigDict(
|
124
|
+
arbitrary_types_allowed=True,
|
125
|
+
extra="forbid",
|
126
|
+
)
|
83
127
|
|
84
|
-
def __enter__(self):
|
128
|
+
def __enter__(self) -> Self:
|
85
129
|
if self._token is not None:
|
86
130
|
raise RuntimeError(
|
87
131
|
"Context already entered. Context enter calls cannot be nested."
|
@@ -98,10 +142,13 @@ class ContextModel(BaseModel):
|
|
98
142
|
self._token = None
|
99
143
|
|
100
144
|
@classmethod
|
101
|
-
def get(cls: Type[
|
145
|
+
def get(cls: Type[Self]) -> Optional[Self]:
|
146
|
+
"""Get the current context instance"""
|
102
147
|
return cls.__var__.get(None)
|
103
148
|
|
104
|
-
def
|
149
|
+
def model_copy(
|
150
|
+
self: Self, *, update: Optional[Dict[str, Any]] = None, deep: bool = False
|
151
|
+
):
|
105
152
|
"""
|
106
153
|
Duplicate the context model, optionally choosing which fields to include, exclude, or change.
|
107
154
|
|
@@ -115,90 +162,130 @@ class ContextModel(BaseModel):
|
|
115
162
|
Returns:
|
116
163
|
A new model instance.
|
117
164
|
"""
|
165
|
+
new = super().model_copy(update=update, deep=deep)
|
118
166
|
# Remove the token on copy to avoid re-entrance errors
|
119
|
-
new = super().copy(**kwargs)
|
120
167
|
new._token = None
|
121
168
|
return new
|
122
169
|
|
170
|
+
def serialize(self) -> Dict[str, Any]:
|
171
|
+
"""
|
172
|
+
Serialize the context model to a dictionary that can be pickled with cloudpickle.
|
173
|
+
"""
|
174
|
+
return self.model_dump(exclude_unset=True)
|
123
175
|
|
124
|
-
class PrefectObjectRegistry(ContextModel):
|
125
|
-
"""
|
126
|
-
A context that acts as a registry for all Prefect objects that are
|
127
|
-
registered during load and execution.
|
128
176
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
177
|
+
class SyncClientContext(ContextModel):
|
178
|
+
"""
|
179
|
+
A context for managing the sync Prefect client instances.
|
180
|
+
|
181
|
+
Clients were formerly tracked on the TaskRunContext and FlowRunContext, but
|
182
|
+
having two separate places and the addition of both sync and async clients
|
183
|
+
made it difficult to manage. This context is intended to be the single
|
184
|
+
source for sync clients.
|
185
|
+
|
186
|
+
The client creates a sync client, which can either be read directly from
|
187
|
+
the context object OR loaded with get_client, inject_client, or other
|
188
|
+
Prefect utilities.
|
189
|
+
|
190
|
+
with SyncClientContext.get_or_create() as ctx:
|
191
|
+
c1 = get_client(sync_client=True)
|
192
|
+
c2 = get_client(sync_client=True)
|
193
|
+
assert c1 is c2
|
194
|
+
assert c1 is ctx.client
|
133
195
|
"""
|
134
196
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
)
|
197
|
+
__var__ = ContextVar("sync-client-context")
|
198
|
+
client: SyncPrefectClient
|
199
|
+
_httpx_settings: Optional[dict[str, Any]] = PrivateAttr(None)
|
200
|
+
_context_stack: int = PrivateAttr(0)
|
140
201
|
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
] = PrivateAttr(default_factory=lambda: defaultdict(list))
|
145
|
-
|
146
|
-
block_code_execution: bool = False
|
147
|
-
capture_failures: bool = False
|
148
|
-
|
149
|
-
__var__ = ContextVar("object_registry")
|
150
|
-
|
151
|
-
def get_instances(self, type_: Type[T]) -> List[T]:
|
152
|
-
instances = []
|
153
|
-
for registered_type, type_instances in self._instance_registry.items():
|
154
|
-
if type_ in registered_type.mro():
|
155
|
-
instances.extend(type_instances)
|
156
|
-
return instances
|
157
|
-
|
158
|
-
def get_instance_failures(
|
159
|
-
self, type_: Type[T]
|
160
|
-
) -> List[Tuple[Exception, T, Tuple, Dict]]:
|
161
|
-
failures = []
|
162
|
-
for type__ in type_.mro():
|
163
|
-
failures.extend(self._instance_init_failures[type__])
|
164
|
-
return failures
|
165
|
-
|
166
|
-
def register_instance(self, object):
|
167
|
-
# TODO: Consider using a 'Set' to avoid duplicate entries
|
168
|
-
self._instance_registry[type(object)].append(object)
|
169
|
-
|
170
|
-
def register_init_failure(
|
171
|
-
self, exc: Exception, object: Any, init_args: Tuple, init_kwargs: Dict
|
172
|
-
):
|
173
|
-
self._instance_init_failures[type(object)].append(
|
174
|
-
(exc, object, init_args, init_kwargs)
|
202
|
+
def __init__(self, httpx_settings: Optional[dict[str, Any]] = None):
|
203
|
+
super().__init__(
|
204
|
+
client=get_client(sync_client=True, httpx_settings=httpx_settings),
|
175
205
|
)
|
206
|
+
self._httpx_settings = httpx_settings
|
207
|
+
self._context_stack = 0
|
208
|
+
|
209
|
+
def __enter__(self):
|
210
|
+
self._context_stack += 1
|
211
|
+
if self._context_stack == 1:
|
212
|
+
self.client.__enter__()
|
213
|
+
return super().__enter__()
|
214
|
+
else:
|
215
|
+
return self
|
216
|
+
|
217
|
+
def __exit__(self, *exc_info):
|
218
|
+
self._context_stack -= 1
|
219
|
+
if self._context_stack == 0:
|
220
|
+
self.client.__exit__(*exc_info)
|
221
|
+
return super().__exit__(*exc_info)
|
176
222
|
|
177
223
|
@classmethod
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
224
|
+
@contextmanager
|
225
|
+
def get_or_create(cls) -> Generator["SyncClientContext", None, None]:
|
226
|
+
ctx = SyncClientContext.get()
|
227
|
+
if ctx:
|
228
|
+
yield ctx
|
229
|
+
else:
|
230
|
+
with SyncClientContext() as ctx:
|
231
|
+
yield ctx
|
184
232
|
|
185
|
-
def __register_init__(__self__: T, *args: Any, **kwargs: Any) -> None:
|
186
|
-
registry = cls.get()
|
187
|
-
try:
|
188
|
-
original_init(__self__, *args, **kwargs)
|
189
|
-
except Exception as exc:
|
190
|
-
if not registry or not registry.capture_failures:
|
191
|
-
raise
|
192
|
-
else:
|
193
|
-
registry.register_init_failure(exc, __self__, args, kwargs)
|
194
|
-
else:
|
195
|
-
if registry:
|
196
|
-
registry.register_instance(__self__)
|
197
233
|
|
198
|
-
|
234
|
+
class AsyncClientContext(ContextModel):
|
235
|
+
"""
|
236
|
+
A context for managing the async Prefect client instances.
|
237
|
+
|
238
|
+
Clients were formerly tracked on the TaskRunContext and FlowRunContext, but
|
239
|
+
having two separate places and the addition of both sync and async clients
|
240
|
+
made it difficult to manage. This context is intended to be the single
|
241
|
+
source for async clients.
|
242
|
+
|
243
|
+
The client creates an async client, which can either be read directly from
|
244
|
+
the context object OR loaded with get_client, inject_client, or other
|
245
|
+
Prefect utilities.
|
246
|
+
|
247
|
+
with AsyncClientContext.get_or_create() as ctx:
|
248
|
+
c1 = get_client(sync_client=False)
|
249
|
+
c2 = get_client(sync_client=False)
|
250
|
+
assert c1 is c2
|
251
|
+
assert c1 is ctx.client
|
252
|
+
"""
|
199
253
|
|
200
|
-
|
201
|
-
|
254
|
+
__var__ = ContextVar("async-client-context")
|
255
|
+
client: PrefectClient
|
256
|
+
_httpx_settings: Optional[dict[str, Any]] = PrivateAttr(None)
|
257
|
+
_context_stack: int = PrivateAttr(0)
|
258
|
+
|
259
|
+
def __init__(self, httpx_settings: Optional[dict[str, Any]] = None):
|
260
|
+
super().__init__(
|
261
|
+
client=get_client(sync_client=False, httpx_settings=httpx_settings),
|
262
|
+
)
|
263
|
+
self._httpx_settings = httpx_settings
|
264
|
+
self._context_stack = 0
|
265
|
+
|
266
|
+
async def __aenter__(self):
|
267
|
+
self._context_stack += 1
|
268
|
+
if self._context_stack == 1:
|
269
|
+
await self.client.__aenter__()
|
270
|
+
return super().__enter__()
|
271
|
+
else:
|
272
|
+
return self
|
273
|
+
|
274
|
+
async def __aexit__(self, *exc_info):
|
275
|
+
self._context_stack -= 1
|
276
|
+
if self._context_stack == 0:
|
277
|
+
await self.client.__aexit__(*exc_info)
|
278
|
+
return super().__exit__(*exc_info)
|
279
|
+
|
280
|
+
@classmethod
|
281
|
+
@asynccontextmanager
|
282
|
+
async def get_or_create(cls) -> AsyncGenerator[Self, None]:
|
283
|
+
ctx = cls.get()
|
284
|
+
if ctx:
|
285
|
+
yield ctx
|
286
|
+
else:
|
287
|
+
async with cls() as ctx:
|
288
|
+
yield ctx
|
202
289
|
|
203
290
|
|
204
291
|
class RunContext(ContextModel):
|
@@ -211,10 +298,21 @@ class RunContext(ContextModel):
|
|
211
298
|
client: The Prefect client instance being used for API communication
|
212
299
|
"""
|
213
300
|
|
214
|
-
|
301
|
+
def __init__(self, *args, **kwargs):
|
302
|
+
super().__init__(*args, **kwargs)
|
303
|
+
|
304
|
+
start_client_metrics_server()
|
305
|
+
|
306
|
+
start_time: DateTime = Field(default_factory=lambda: pendulum.now("UTC"))
|
215
307
|
input_keyset: Optional[Dict[str, Dict[str, str]]] = None
|
216
308
|
client: Union[PrefectClient, SyncPrefectClient]
|
217
309
|
|
310
|
+
def serialize(self):
|
311
|
+
return self.model_dump(
|
312
|
+
include={"start_time", "input_keyset"},
|
313
|
+
exclude_unset=True,
|
314
|
+
)
|
315
|
+
|
218
316
|
|
219
317
|
class EngineContext(RunContext):
|
220
318
|
"""
|
@@ -229,19 +327,20 @@ class EngineContext(RunContext):
|
|
229
327
|
task_run_states: A list of states for task runs created within this flow run
|
230
328
|
task_run_results: A mapping of result ids to task run states for this flow run
|
231
329
|
flow_run_states: A list of states for flow runs created within this flow run
|
232
|
-
sync_portal: A blocking portal for sync task/flow runs in an async flow
|
233
|
-
timeout_scope: The cancellation scope for flow level timeouts
|
234
330
|
"""
|
235
331
|
|
236
332
|
flow: Optional["Flow"] = None
|
237
333
|
flow_run: Optional[FlowRun] = None
|
238
|
-
|
239
|
-
task_runner: BaseTaskRunner
|
334
|
+
task_runner: TaskRunner
|
240
335
|
log_prints: bool = False
|
241
336
|
parameters: Optional[Dict[str, Any]] = None
|
242
337
|
|
338
|
+
# Flag signaling if the flow run context has been serialized and sent
|
339
|
+
# to remote infrastructure.
|
340
|
+
detached: bool = False
|
341
|
+
|
243
342
|
# Result handling
|
244
|
-
|
343
|
+
result_store: ResultStore
|
245
344
|
|
246
345
|
# Counter for task calls allowing unique
|
247
346
|
task_run_dynamic_keys: Dict[str, int] = Field(default_factory=dict)
|
@@ -249,24 +348,31 @@ class EngineContext(RunContext):
|
|
249
348
|
# Counter for flow pauses
|
250
349
|
observed_flow_pauses: Dict[str, int] = Field(default_factory=dict)
|
251
350
|
|
252
|
-
# Tracking for
|
253
|
-
|
254
|
-
|
255
|
-
task_run_results:
|
256
|
-
|
257
|
-
|
258
|
-
# The synchronous portal is only created for async flows for creating engine calls
|
259
|
-
# from synchronous task and subflow calls
|
260
|
-
sync_portal: Optional[anyio.abc.BlockingPortal] = None
|
261
|
-
timeout_scope: Optional[anyio.abc.CancelScope] = None
|
262
|
-
|
263
|
-
# Task group that can be used for background tasks during the flow run
|
264
|
-
background_tasks: anyio.abc.TaskGroup
|
351
|
+
# Tracking for result from task runs in this flow run for dependency tracking
|
352
|
+
# Holds the ID of the object returned by the task run and task run state
|
353
|
+
# This is a weakref dictionary to avoid undermining garbage collection
|
354
|
+
task_run_results: Mapping[int, State] = Field(
|
355
|
+
default_factory=weakref.WeakValueDictionary
|
356
|
+
)
|
265
357
|
|
266
|
-
# Events worker to emit events
|
358
|
+
# Events worker to emit events
|
267
359
|
events: Optional[EventsWorker] = None
|
268
360
|
|
269
|
-
__var__ = ContextVar("flow_run")
|
361
|
+
__var__: ContextVar = ContextVar("flow_run")
|
362
|
+
|
363
|
+
def serialize(self):
|
364
|
+
return self.model_dump(
|
365
|
+
include={
|
366
|
+
"flow_run",
|
367
|
+
"flow",
|
368
|
+
"parameters",
|
369
|
+
"log_prints",
|
370
|
+
"start_time",
|
371
|
+
"input_keyset",
|
372
|
+
"result_store",
|
373
|
+
},
|
374
|
+
exclude_unset=True,
|
375
|
+
)
|
270
376
|
|
271
377
|
|
272
378
|
FlowRunContext = EngineContext # for backwards compatibility
|
@@ -288,10 +394,24 @@ class TaskRunContext(RunContext):
|
|
288
394
|
parameters: Dict[str, Any]
|
289
395
|
|
290
396
|
# Result handling
|
291
|
-
|
397
|
+
result_store: ResultStore
|
292
398
|
|
293
399
|
__var__ = ContextVar("task_run")
|
294
400
|
|
401
|
+
def serialize(self):
|
402
|
+
return self.model_dump(
|
403
|
+
include={
|
404
|
+
"task_run",
|
405
|
+
"task",
|
406
|
+
"parameters",
|
407
|
+
"log_prints",
|
408
|
+
"start_time",
|
409
|
+
"input_keyset",
|
410
|
+
"result_store",
|
411
|
+
},
|
412
|
+
exclude_unset=True,
|
413
|
+
)
|
414
|
+
|
295
415
|
|
296
416
|
class TagsContext(ContextModel):
|
297
417
|
"""
|
@@ -452,23 +572,6 @@ def tags(*new_tags: str) -> Generator[Set[str], None, None]:
|
|
452
572
|
yield new_tags
|
453
573
|
|
454
574
|
|
455
|
-
def registry_from_script(
|
456
|
-
path: str,
|
457
|
-
block_code_execution: bool = True,
|
458
|
-
capture_failures: bool = True,
|
459
|
-
) -> PrefectObjectRegistry:
|
460
|
-
"""
|
461
|
-
Return a fresh registry with instances populated from execution of a script.
|
462
|
-
"""
|
463
|
-
with PrefectObjectRegistry(
|
464
|
-
block_code_execution=block_code_execution,
|
465
|
-
capture_failures=capture_failures,
|
466
|
-
) as registry:
|
467
|
-
load_script_as_module(path)
|
468
|
-
|
469
|
-
return registry
|
470
|
-
|
471
|
-
|
472
575
|
@contextmanager
|
473
576
|
def use_profile(
|
474
577
|
profile: Union[Profile, str],
|
@@ -555,7 +658,7 @@ def root_settings_context():
|
|
555
658
|
),
|
556
659
|
file=sys.stderr,
|
557
660
|
)
|
558
|
-
active_name = "
|
661
|
+
active_name = "ephemeral"
|
559
662
|
|
560
663
|
with use_profile(
|
561
664
|
profiles[active_name],
|
@@ -569,14 +672,8 @@ def root_settings_context():
|
|
569
672
|
|
570
673
|
|
571
674
|
GLOBAL_SETTINGS_CONTEXT: SettingsContext = root_settings_context()
|
572
|
-
GLOBAL_OBJECT_REGISTRY: ContextManager[PrefectObjectRegistry] = None
|
573
|
-
|
574
|
-
|
575
|
-
def initialize_object_registry():
|
576
|
-
global GLOBAL_OBJECT_REGISTRY
|
577
675
|
|
578
|
-
if GLOBAL_OBJECT_REGISTRY:
|
579
|
-
return
|
580
676
|
|
581
|
-
|
582
|
-
|
677
|
+
# 2024-07-02: This surfaces an actionable error message for removed objects
|
678
|
+
# in Prefect 3.0 upgrade.
|
679
|
+
__getattr__ = getattr_migration(__name__)
|
prefect/deployments/__init__.py
CHANGED
@@ -1,18 +1,33 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
from
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
1
|
+
from typing import TYPE_CHECKING
|
2
|
+
from prefect._internal.compatibility.migration import getattr_migration
|
3
|
+
|
4
|
+
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from .flow_runs import run_deployment
|
7
|
+
from .base import initialize_project
|
8
|
+
from .runner import deploy
|
9
|
+
|
10
|
+
_public_api: dict[str, tuple[str, str]] = {
|
11
|
+
"initialize_project": (__spec__.parent, ".base"),
|
12
|
+
"run_deployment": (__spec__.parent, ".flow_runs"),
|
13
|
+
"deploy": (__spec__.parent, ".runner"),
|
14
|
+
}
|
15
|
+
|
16
|
+
# Declare API for type-checkers
|
17
|
+
__all__ = ["initialize_project", "deploy", "run_deployment"]
|
18
|
+
|
19
|
+
|
20
|
+
def __getattr__(attr_name: str) -> object:
|
21
|
+
dynamic_attr = _public_api.get(attr_name)
|
22
|
+
if dynamic_attr is None:
|
23
|
+
return getattr_migration(__name__)(attr_name)
|
24
|
+
|
25
|
+
package, module_name = dynamic_attr
|
26
|
+
|
27
|
+
from importlib import import_module
|
28
|
+
|
29
|
+
if module_name == "__module__":
|
30
|
+
return import_module(f".{attr_name}", package=package)
|
31
|
+
else:
|
32
|
+
module = import_module(module_name, package=package)
|
33
|
+
return getattr(module, attr_name)
|
prefect/deployments/base.py
CHANGED
@@ -19,7 +19,7 @@ import anyio
|
|
19
19
|
import yaml
|
20
20
|
from ruamel.yaml import YAML
|
21
21
|
|
22
|
-
from prefect.client.schemas.
|
22
|
+
from prefect.client.schemas.actions import DeploymentScheduleCreate
|
23
23
|
from prefect.client.schemas.schedules import IntervalSchedule
|
24
24
|
from prefect.logging import get_logger
|
25
25
|
from prefect.settings import PREFECT_DEBUG_MODE
|
@@ -29,7 +29,7 @@ from prefect.utilities.templating import apply_values
|
|
29
29
|
|
30
30
|
|
31
31
|
def create_default_prefect_yaml(
|
32
|
-
path: str, name: str = None, contents: Optional[Dict[str, Any]] = None
|
32
|
+
path: str, name: Optional[str] = None, contents: Optional[Dict[str, Any]] = None
|
33
33
|
) -> bool:
|
34
34
|
"""
|
35
35
|
Creates default `prefect.yaml` file in the provided path if one does not already exist;
|
@@ -176,7 +176,9 @@ def _get_git_branch() -> Optional[str]:
|
|
176
176
|
|
177
177
|
|
178
178
|
def initialize_project(
|
179
|
-
name:
|
179
|
+
name: Optional[str] = None,
|
180
|
+
recipe: Optional[str] = None,
|
181
|
+
inputs: Optional[Dict[str, Any]] = None,
|
180
182
|
) -> List[str]:
|
181
183
|
"""
|
182
184
|
Initializes a basic project structure with base files. If no name is provided, the name
|
@@ -256,28 +258,21 @@ def _format_deployment_for_saving_to_prefect_file(
|
|
256
258
|
# Only want entrypoint to avoid errors
|
257
259
|
deployment.pop("flow_name", None)
|
258
260
|
|
259
|
-
if deployment.get("schedule"):
|
260
|
-
if isinstance(deployment["schedule"], IntervalSchedule):
|
261
|
-
deployment["schedule"] = _interval_schedule_to_dict(deployment["schedule"])
|
262
|
-
else: # all valid SCHEDULE_TYPES are subclasses of BaseModel
|
263
|
-
deployment["schedule"] = deployment["schedule"].dict()
|
264
|
-
|
265
|
-
if "is_schedule_active" in deployment:
|
266
|
-
deployment["schedule"]["active"] = deployment.pop("is_schedule_active")
|
267
|
-
|
268
261
|
if deployment.get("schedules"):
|
269
262
|
schedules = []
|
270
263
|
for deployment_schedule in cast(
|
271
|
-
List[
|
264
|
+
List[DeploymentScheduleCreate], deployment["schedules"]
|
272
265
|
):
|
273
266
|
if isinstance(deployment_schedule.schedule, IntervalSchedule):
|
274
267
|
schedule_config = _interval_schedule_to_dict(
|
275
268
|
deployment_schedule.schedule
|
276
269
|
)
|
277
270
|
else: # all valid SCHEDULE_TYPES are subclasses of BaseModel
|
278
|
-
schedule_config = deployment_schedule.schedule.
|
271
|
+
schedule_config = deployment_schedule.schedule.model_dump()
|
279
272
|
|
280
273
|
schedule_config["active"] = deployment_schedule.active
|
274
|
+
schedule_config["max_active_runs"] = deployment_schedule.max_active_runs
|
275
|
+
schedule_config["catchup"] = deployment_schedule.catchup
|
281
276
|
schedules.append(schedule_config)
|
282
277
|
|
283
278
|
deployment["schedules"] = schedules
|
@@ -295,7 +290,7 @@ def _interval_schedule_to_dict(schedule: IntervalSchedule) -> Dict:
|
|
295
290
|
Returns:
|
296
291
|
- Dict: the schedule as a dictionary
|
297
292
|
"""
|
298
|
-
schedule_config = schedule.
|
293
|
+
schedule_config = schedule.model_dump()
|
299
294
|
schedule_config["interval"] = schedule_config["interval"].total_seconds()
|
300
295
|
schedule_config["anchor_date"] = schedule_config["anchor_date"].isoformat()
|
301
296
|
|