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/events/related.py
CHANGED
@@ -21,6 +21,7 @@ from .schemas.events import RelatedResource
|
|
21
21
|
|
22
22
|
if TYPE_CHECKING:
|
23
23
|
from prefect._internal.schemas.bases import ObjectBaseModel
|
24
|
+
from prefect.client.orchestration import PrefectClient
|
24
25
|
|
25
26
|
ResourceCacheEntry = Dict[str, Union[str, "ObjectBaseModel", None]]
|
26
27
|
RelatedResourceCache = Dict[str, Tuple[ResourceCacheEntry, DateTime]]
|
@@ -31,7 +32,7 @@ RESOURCE_CACHE: RelatedResourceCache = {}
|
|
31
32
|
|
32
33
|
def tags_as_related_resources(tags: Iterable[str]) -> List[RelatedResource]:
|
33
34
|
return [
|
34
|
-
RelatedResource.
|
35
|
+
RelatedResource.model_validate(
|
35
36
|
{
|
36
37
|
"prefect.resource.id": f"prefect.tag.{tag}",
|
37
38
|
"prefect.resource.role": "tag",
|
@@ -44,7 +45,7 @@ def tags_as_related_resources(tags: Iterable[str]) -> List[RelatedResource]:
|
|
44
45
|
def object_as_related_resource(kind: str, role: str, object: Any) -> RelatedResource:
|
45
46
|
resource_id = f"prefect.{kind}.{object.id}"
|
46
47
|
|
47
|
-
return RelatedResource.
|
48
|
+
return RelatedResource.model_validate(
|
48
49
|
{
|
49
50
|
"prefect.resource.id": resource_id,
|
50
51
|
"prefect.resource.role": role,
|
@@ -54,9 +55,9 @@ def object_as_related_resource(kind: str, role: str, object: Any) -> RelatedReso
|
|
54
55
|
|
55
56
|
|
56
57
|
async def related_resources_from_run_context(
|
58
|
+
client: "PrefectClient",
|
57
59
|
exclude: Optional[Set[str]] = None,
|
58
60
|
) -> List[RelatedResource]:
|
59
|
-
from prefect.client.orchestration import get_client
|
60
61
|
from prefect.client.schemas.objects import FlowRun
|
61
62
|
from prefect.context import FlowRunContext, TaskRunContext
|
62
63
|
|
@@ -77,86 +78,84 @@ async def related_resources_from_run_context(
|
|
77
78
|
|
78
79
|
related_objects: List[ResourceCacheEntry] = []
|
79
80
|
|
80
|
-
async
|
81
|
+
async def dummy_read():
|
82
|
+
return {}
|
81
83
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
client_method=client.read_flow_run,
|
99
|
-
obj_id=flow_run_id,
|
100
|
-
cache=RESOURCE_CACHE,
|
101
|
-
)
|
84
|
+
if flow_run_context:
|
85
|
+
related_objects.append(
|
86
|
+
{
|
87
|
+
"kind": "flow-run",
|
88
|
+
"role": "flow-run",
|
89
|
+
"object": flow_run_context.flow_run,
|
90
|
+
},
|
91
|
+
)
|
92
|
+
else:
|
93
|
+
related_objects.append(
|
94
|
+
await _get_and_cache_related_object(
|
95
|
+
kind="flow-run",
|
96
|
+
role="flow-run",
|
97
|
+
client_method=client.read_flow_run,
|
98
|
+
obj_id=flow_run_id,
|
99
|
+
cache=RESOURCE_CACHE,
|
102
100
|
)
|
101
|
+
)
|
103
102
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
103
|
+
if task_run_context:
|
104
|
+
related_objects.append(
|
105
|
+
{
|
106
|
+
"kind": "task-run",
|
107
|
+
"role": "task-run",
|
108
|
+
"object": task_run_context.task_run,
|
109
|
+
},
|
110
|
+
)
|
112
111
|
|
113
|
-
|
112
|
+
flow_run = related_objects[0]["object"]
|
114
113
|
|
115
|
-
|
116
|
-
|
117
|
-
|
114
|
+
if isinstance(flow_run, FlowRun):
|
115
|
+
related_objects += list(
|
116
|
+
await asyncio.gather(
|
117
|
+
_get_and_cache_related_object(
|
118
|
+
kind="flow",
|
119
|
+
role="flow",
|
120
|
+
client_method=client.read_flow,
|
121
|
+
obj_id=flow_run.flow_id,
|
122
|
+
cache=RESOURCE_CACHE,
|
123
|
+
),
|
124
|
+
(
|
125
|
+
_get_and_cache_related_object(
|
126
|
+
kind="deployment",
|
127
|
+
role="deployment",
|
128
|
+
client_method=client.read_deployment,
|
129
|
+
obj_id=flow_run.deployment_id,
|
130
|
+
cache=RESOURCE_CACHE,
|
131
|
+
)
|
132
|
+
if flow_run.deployment_id
|
133
|
+
else dummy_read()
|
134
|
+
),
|
135
|
+
(
|
136
|
+
_get_and_cache_related_object(
|
137
|
+
kind="work-queue",
|
138
|
+
role="work-queue",
|
139
|
+
client_method=client.read_work_queue,
|
140
|
+
obj_id=flow_run.work_queue_id,
|
141
|
+
cache=RESOURCE_CACHE,
|
142
|
+
)
|
143
|
+
if flow_run.work_queue_id
|
144
|
+
else dummy_read()
|
145
|
+
),
|
146
|
+
(
|
118
147
|
_get_and_cache_related_object(
|
119
|
-
kind="
|
120
|
-
role="
|
121
|
-
client_method=client.
|
122
|
-
obj_id=flow_run.
|
148
|
+
kind="work-pool",
|
149
|
+
role="work-pool",
|
150
|
+
client_method=client.read_work_pool,
|
151
|
+
obj_id=flow_run.work_pool_name,
|
123
152
|
cache=RESOURCE_CACHE,
|
124
|
-
)
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
role="deployment",
|
129
|
-
client_method=client.read_deployment,
|
130
|
-
obj_id=flow_run.deployment_id,
|
131
|
-
cache=RESOURCE_CACHE,
|
132
|
-
)
|
133
|
-
if flow_run.deployment_id
|
134
|
-
else dummy_read()
|
135
|
-
),
|
136
|
-
(
|
137
|
-
_get_and_cache_related_object(
|
138
|
-
kind="work-queue",
|
139
|
-
role="work-queue",
|
140
|
-
client_method=client.read_work_queue,
|
141
|
-
obj_id=flow_run.work_queue_id,
|
142
|
-
cache=RESOURCE_CACHE,
|
143
|
-
)
|
144
|
-
if flow_run.work_queue_id
|
145
|
-
else dummy_read()
|
146
|
-
),
|
147
|
-
(
|
148
|
-
_get_and_cache_related_object(
|
149
|
-
kind="work-pool",
|
150
|
-
role="work-pool",
|
151
|
-
client_method=client.read_work_pool,
|
152
|
-
obj_id=flow_run.work_pool_name,
|
153
|
-
cache=RESOURCE_CACHE,
|
154
|
-
)
|
155
|
-
if flow_run.work_pool_name
|
156
|
-
else dummy_read()
|
157
|
-
),
|
158
|
-
)
|
153
|
+
)
|
154
|
+
if flow_run.work_pool_name
|
155
|
+
else dummy_read()
|
156
|
+
),
|
159
157
|
)
|
158
|
+
)
|
160
159
|
|
161
160
|
related = []
|
162
161
|
tags = set()
|
@@ -14,19 +14,15 @@ from typing import (
|
|
14
14
|
)
|
15
15
|
from uuid import UUID
|
16
16
|
|
17
|
-
from
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
else:
|
25
|
-
from pydantic import Field, PrivateAttr, root_validator, validator # type: ignore
|
26
|
-
from pydantic.fields import ModelField # type: ignore
|
17
|
+
from pydantic import (
|
18
|
+
Field,
|
19
|
+
PrivateAttr,
|
20
|
+
field_validator,
|
21
|
+
model_validator,
|
22
|
+
)
|
23
|
+
from typing_extensions import Self, TypeAlias
|
27
24
|
|
28
25
|
from prefect._internal.schemas.bases import PrefectBaseModel
|
29
|
-
from prefect._internal.schemas.validators import validate_trigger_within
|
30
26
|
from prefect.events.actions import ActionTypes, RunDeployment
|
31
27
|
from prefect.utilities.collections import AutoEnum
|
32
28
|
|
@@ -81,7 +77,7 @@ class Trigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-a
|
|
81
77
|
# This is one of the Deployment*Trigger classes, so translate it over to a
|
82
78
|
# plain Trigger
|
83
79
|
if hasattr(self, "trigger_type"):
|
84
|
-
trigger = self.trigger_type(**self.
|
80
|
+
trigger = self.trigger_type(**self.model_dump())
|
85
81
|
|
86
82
|
return AutomationCore(
|
87
83
|
name=(
|
@@ -104,11 +100,11 @@ class ResourceTrigger(Trigger, abc.ABC):
|
|
104
100
|
type: str
|
105
101
|
|
106
102
|
match: ResourceSpecification = Field(
|
107
|
-
default_factory=lambda: ResourceSpecification.
|
103
|
+
default_factory=lambda: ResourceSpecification.model_validate({}),
|
108
104
|
description="Labels for resources which this trigger will match.",
|
109
105
|
)
|
110
106
|
match_related: ResourceSpecification = Field(
|
111
|
-
default_factory=lambda: ResourceSpecification.
|
107
|
+
default_factory=lambda: ResourceSpecification.model_validate({}),
|
112
108
|
description="Labels for related resources which this trigger will match.",
|
113
109
|
)
|
114
110
|
|
@@ -167,9 +163,8 @@ class EventTrigger(ResourceTrigger):
|
|
167
163
|
),
|
168
164
|
)
|
169
165
|
within: timedelta = Field(
|
170
|
-
timedelta(0),
|
171
|
-
|
172
|
-
exclusiveMinimum=False,
|
166
|
+
timedelta(seconds=0),
|
167
|
+
ge=timedelta(seconds=0),
|
173
168
|
description=(
|
174
169
|
"The time period over which the events must occur. For Reactive triggers, "
|
175
170
|
"this may be as low as 0 seconds, but must be at least 10 seconds for "
|
@@ -177,26 +172,33 @@ class EventTrigger(ResourceTrigger):
|
|
177
172
|
),
|
178
173
|
)
|
179
174
|
|
180
|
-
@
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
175
|
+
@model_validator(mode="before")
|
176
|
+
@classmethod
|
177
|
+
def enforce_minimum_within_for_proactive_triggers(
|
178
|
+
cls, data: Dict[str, Any]
|
179
|
+
) -> Dict[str, Any]:
|
180
|
+
if not isinstance(data, dict):
|
181
|
+
return data
|
182
|
+
|
183
|
+
if "within" in data and data["within"] is None:
|
184
|
+
raise ValueError("`within` should be a valid timedelta")
|
185
185
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
within
|
186
|
+
posture: Optional[Posture] = data.get("posture")
|
187
|
+
within: Optional[timedelta] = data.get("within")
|
188
|
+
|
189
|
+
if isinstance(within, (int, float)):
|
190
|
+
within = timedelta(seconds=within)
|
190
191
|
|
191
192
|
if posture == Posture.Proactive:
|
192
193
|
if not within or within == timedelta(0):
|
193
|
-
|
194
|
+
within = timedelta(seconds=10.0)
|
194
195
|
elif within < timedelta(seconds=10.0):
|
195
196
|
raise ValueError(
|
196
|
-
"
|
197
|
+
"`within` for Proactive triggers must be greater than or equal to "
|
198
|
+
"10 seconds"
|
197
199
|
)
|
198
200
|
|
199
|
-
return
|
201
|
+
return data | {"within": within} if within else data
|
200
202
|
|
201
203
|
def describe_for_cli(self, indent: int = 0) -> str:
|
202
204
|
"""Return a human-readable description of this trigger for the CLI"""
|
@@ -258,8 +260,6 @@ class MetricTriggerQuery(PrefectBaseModel):
|
|
258
260
|
)
|
259
261
|
range: timedelta = Field(
|
260
262
|
timedelta(seconds=300), # defaults to 5 minutes
|
261
|
-
minimum=300.0,
|
262
|
-
exclusiveMinimum=False,
|
263
263
|
description=(
|
264
264
|
"The lookback duration (seconds) for a metric query. This duration is "
|
265
265
|
"used to determine the time range over which the query will be executed. "
|
@@ -268,8 +268,6 @@ class MetricTriggerQuery(PrefectBaseModel):
|
|
268
268
|
)
|
269
269
|
firing_for: timedelta = Field(
|
270
270
|
timedelta(seconds=300), # defaults to 5 minutes
|
271
|
-
minimum=300.0,
|
272
|
-
exclusiveMinimum=False,
|
273
271
|
description=(
|
274
272
|
"The duration (seconds) for which the metric query must breach "
|
275
273
|
"or resolve continuously before the state is updated and the "
|
@@ -278,6 +276,12 @@ class MetricTriggerQuery(PrefectBaseModel):
|
|
278
276
|
),
|
279
277
|
)
|
280
278
|
|
279
|
+
@field_validator("range", "firing_for")
|
280
|
+
def enforce_minimum_range(cls, value: timedelta):
|
281
|
+
if value < timedelta(seconds=300):
|
282
|
+
raise ValueError("The minimum range is 300 seconds (5 minutes)")
|
283
|
+
return value
|
284
|
+
|
281
285
|
|
282
286
|
class MetricTrigger(ResourceTrigger):
|
283
287
|
"""
|
@@ -316,7 +320,14 @@ class CompositeTrigger(Trigger, abc.ABC):
|
|
316
320
|
|
317
321
|
type: Literal["compound", "sequence"]
|
318
322
|
triggers: List["TriggerTypes"]
|
319
|
-
within: Optional[timedelta]
|
323
|
+
within: Optional[timedelta] = Field(
|
324
|
+
None,
|
325
|
+
description=(
|
326
|
+
"The time period over which the events must occur. For Reactive triggers, "
|
327
|
+
"this may be as low as 0 seconds, but must be at least 10 seconds for "
|
328
|
+
"Proactive triggers"
|
329
|
+
),
|
330
|
+
)
|
320
331
|
|
321
332
|
|
322
333
|
class CompoundTrigger(CompositeTrigger):
|
@@ -326,19 +337,17 @@ class CompoundTrigger(CompositeTrigger):
|
|
326
337
|
type: Literal["compound"] = "compound"
|
327
338
|
require: Union[int, Literal["any", "all"]]
|
328
339
|
|
329
|
-
@
|
330
|
-
def validate_require(
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
if require
|
335
|
-
raise ValueError("required must be at least 1")
|
336
|
-
if require > len(values["triggers"]):
|
340
|
+
@model_validator(mode="after")
|
341
|
+
def validate_require(self) -> Self:
|
342
|
+
if isinstance(self.require, int):
|
343
|
+
if self.require < 1:
|
344
|
+
raise ValueError("require must be at least 1")
|
345
|
+
if self.require > len(self.triggers):
|
337
346
|
raise ValueError(
|
338
|
-
"
|
347
|
+
"require must be less than or equal to the number of triggers"
|
339
348
|
)
|
340
349
|
|
341
|
-
return
|
350
|
+
return self
|
342
351
|
|
343
352
|
def describe_for_cli(self, indent: int = 0) -> str:
|
344
353
|
"""Return a human-readable description of this trigger for the CLI"""
|
@@ -387,8 +396,8 @@ TriggerTypes: TypeAlias = Union[
|
|
387
396
|
]
|
388
397
|
"""The union of all concrete trigger types that a user may actually create"""
|
389
398
|
|
390
|
-
CompoundTrigger.
|
391
|
-
SequenceTrigger.
|
399
|
+
CompoundTrigger.model_rebuild()
|
400
|
+
SequenceTrigger.model_rebuild()
|
392
401
|
|
393
402
|
|
394
403
|
class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg]
|
@@ -10,42 +10,27 @@ create them from YAML.
|
|
10
10
|
"""
|
11
11
|
|
12
12
|
import abc
|
13
|
-
import textwrap
|
14
|
-
from datetime import timedelta
|
15
13
|
from typing import (
|
16
14
|
Any,
|
15
|
+
ClassVar,
|
17
16
|
Dict,
|
18
|
-
List,
|
19
17
|
Optional,
|
20
|
-
|
18
|
+
Type,
|
21
19
|
Union,
|
22
20
|
)
|
23
|
-
from uuid import UUID
|
24
21
|
|
22
|
+
from pydantic import Field
|
25
23
|
from typing_extensions import TypeAlias
|
26
24
|
|
27
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
28
|
-
|
29
|
-
if HAS_PYDANTIC_V2:
|
30
|
-
from pydantic.v1 import Field, PrivateAttr
|
31
|
-
else:
|
32
|
-
from pydantic import Field, PrivateAttr # type: ignore
|
33
|
-
|
34
|
-
from prefect._internal.compatibility.deprecated import deprecated_class
|
35
25
|
from prefect._internal.schemas.bases import PrefectBaseModel
|
36
|
-
from prefect.events.actions import ActionTypes, RunDeployment
|
37
26
|
|
38
27
|
from .automations import (
|
39
|
-
AutomationCore,
|
40
28
|
CompoundTrigger,
|
41
29
|
EventTrigger,
|
42
30
|
MetricTrigger,
|
43
|
-
MetricTriggerQuery,
|
44
|
-
Posture,
|
45
31
|
SequenceTrigger,
|
46
32
|
TriggerTypes,
|
47
33
|
)
|
48
|
-
from .events import ResourceSpecification
|
49
34
|
|
50
35
|
|
51
36
|
class BaseDeploymentTrigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-arg]
|
@@ -86,7 +71,7 @@ class DeploymentEventTrigger(BaseDeploymentTrigger, EventTrigger):
|
|
86
71
|
period of time.
|
87
72
|
"""
|
88
73
|
|
89
|
-
trigger_type = EventTrigger
|
74
|
+
trigger_type: ClassVar[Type[TriggerTypes]] = EventTrigger
|
90
75
|
|
91
76
|
|
92
77
|
class DeploymentMetricTrigger(BaseDeploymentTrigger, MetricTrigger):
|
@@ -94,21 +79,21 @@ class DeploymentMetricTrigger(BaseDeploymentTrigger, MetricTrigger):
|
|
94
79
|
A trigger that fires based on the results of a metric query.
|
95
80
|
"""
|
96
81
|
|
97
|
-
trigger_type = MetricTrigger
|
82
|
+
trigger_type: ClassVar[Type[TriggerTypes]] = MetricTrigger
|
98
83
|
|
99
84
|
|
100
85
|
class DeploymentCompoundTrigger(BaseDeploymentTrigger, CompoundTrigger):
|
101
86
|
"""A composite trigger that requires some number of triggers to have
|
102
87
|
fired within the given time period"""
|
103
88
|
|
104
|
-
trigger_type = CompoundTrigger
|
89
|
+
trigger_type: ClassVar[Type[TriggerTypes]] = CompoundTrigger
|
105
90
|
|
106
91
|
|
107
92
|
class DeploymentSequenceTrigger(BaseDeploymentTrigger, SequenceTrigger):
|
108
93
|
"""A composite trigger that requires some number of triggers to have fired
|
109
94
|
within the given time period in a specific order"""
|
110
95
|
|
111
|
-
trigger_type = SequenceTrigger
|
96
|
+
trigger_type: ClassVar[Type[TriggerTypes]] = SequenceTrigger
|
112
97
|
|
113
98
|
|
114
99
|
# Concrete deployment trigger types
|
@@ -118,178 +103,3 @@ DeploymentTriggerTypes: TypeAlias = Union[
|
|
118
103
|
DeploymentCompoundTrigger,
|
119
104
|
DeploymentSequenceTrigger,
|
120
105
|
]
|
121
|
-
|
122
|
-
|
123
|
-
# The deprecated all-in-one DeploymentTrigger
|
124
|
-
|
125
|
-
|
126
|
-
@deprecated_class(
|
127
|
-
start_date="Mar 2024",
|
128
|
-
help=textwrap.dedent(
|
129
|
-
"""
|
130
|
-
To associate a specific kind of trigger with a Deployment, use one of the more
|
131
|
-
specific deployment trigger types instead:
|
132
|
-
|
133
|
-
* DeploymentEventTrigger to associate an EventTrigger with a deployment
|
134
|
-
* DeploymentMetricTrigger to associate an MetricTrigger with a deployment
|
135
|
-
* DeploymentCompoundTrigger to associate an CompoundTrigger with a deployment
|
136
|
-
* DeploymentSequenceTrigger to associate an SequenceTrigger with a deployment
|
137
|
-
|
138
|
-
In other cases, use the Automation and Trigger class hierarchy directly.
|
139
|
-
"""
|
140
|
-
),
|
141
|
-
)
|
142
|
-
class DeploymentTrigger(PrefectBaseModel):
|
143
|
-
name: Optional[str] = Field(
|
144
|
-
None, description="The name to give to the automation created for this trigger."
|
145
|
-
)
|
146
|
-
description: str = Field("", description="A longer description of this automation")
|
147
|
-
enabled: bool = Field(True, description="Whether this automation will be evaluated")
|
148
|
-
job_variables: Optional[Dict[str, Any]] = Field(
|
149
|
-
None,
|
150
|
-
description=(
|
151
|
-
"Job variables to pass to the run, or None to use the "
|
152
|
-
"deployment's default job variables"
|
153
|
-
),
|
154
|
-
)
|
155
|
-
|
156
|
-
# from ResourceTrigger
|
157
|
-
|
158
|
-
match: ResourceSpecification = Field(
|
159
|
-
default_factory=lambda: ResourceSpecification.parse_obj({}),
|
160
|
-
description="Labels for resources which this trigger will match.",
|
161
|
-
)
|
162
|
-
match_related: ResourceSpecification = Field(
|
163
|
-
default_factory=lambda: ResourceSpecification.parse_obj({}),
|
164
|
-
description="Labels for related resources which this trigger will match.",
|
165
|
-
)
|
166
|
-
|
167
|
-
# from both EventTrigger and MetricTrigger
|
168
|
-
|
169
|
-
posture: Posture = Field(
|
170
|
-
Posture.Reactive,
|
171
|
-
description=(
|
172
|
-
"The posture of this trigger, either Reactive, Proactive, or Metric. "
|
173
|
-
"Reactive triggers respond to the _presence_ of the expected events, while "
|
174
|
-
"Proactive triggers respond to the _absence_ of those expected events. "
|
175
|
-
"Metric triggers periodically evaluate the configured metric query."
|
176
|
-
),
|
177
|
-
)
|
178
|
-
|
179
|
-
# from EventTrigger
|
180
|
-
|
181
|
-
after: Set[str] = Field(
|
182
|
-
default_factory=set,
|
183
|
-
description=(
|
184
|
-
"The event(s) which must first been seen to fire this trigger. If "
|
185
|
-
"empty, then fire this trigger immediately. Events may include "
|
186
|
-
"trailing wildcards, like `prefect.flow-run.*`"
|
187
|
-
),
|
188
|
-
)
|
189
|
-
expect: Set[str] = Field(
|
190
|
-
default_factory=set,
|
191
|
-
description=(
|
192
|
-
"The event(s) this trigger is expecting to see. If empty, this "
|
193
|
-
"trigger will match any event. Events may include trailing wildcards, "
|
194
|
-
"like `prefect.flow-run.*`"
|
195
|
-
),
|
196
|
-
)
|
197
|
-
|
198
|
-
for_each: Set[str] = Field(
|
199
|
-
default_factory=set,
|
200
|
-
description=(
|
201
|
-
"Evaluate the trigger separately for each distinct value of these labels "
|
202
|
-
"on the resource. By default, labels refer to the primary resource of the "
|
203
|
-
"triggering event. You may also refer to labels from related "
|
204
|
-
"resources by specifying `related:<role>:<label>`. This will use the "
|
205
|
-
"value of that label for the first related resource in that role. For "
|
206
|
-
'example, `"for_each": ["related:flow:prefect.resource.id"]` would '
|
207
|
-
"evaluate the trigger for each flow."
|
208
|
-
),
|
209
|
-
)
|
210
|
-
threshold: int = Field(
|
211
|
-
1,
|
212
|
-
description=(
|
213
|
-
"The number of events required for this trigger to fire (for "
|
214
|
-
"Reactive triggers), or the number of events expected (for Proactive "
|
215
|
-
"triggers)"
|
216
|
-
),
|
217
|
-
)
|
218
|
-
within: timedelta = Field(
|
219
|
-
timedelta(0),
|
220
|
-
minimum=0.0,
|
221
|
-
exclusiveMinimum=False,
|
222
|
-
description=(
|
223
|
-
"The time period over which the events must occur. For Reactive triggers, "
|
224
|
-
"this may be as low as 0 seconds, but must be at least 10 seconds for "
|
225
|
-
"Proactive triggers"
|
226
|
-
),
|
227
|
-
)
|
228
|
-
|
229
|
-
# from MetricTrigger
|
230
|
-
|
231
|
-
metric: Optional[MetricTriggerQuery] = Field(
|
232
|
-
None,
|
233
|
-
description="The metric query to evaluate for this trigger. ",
|
234
|
-
)
|
235
|
-
|
236
|
-
_deployment_id: Optional[UUID] = PrivateAttr(default=None)
|
237
|
-
parameters: Optional[Dict[str, Any]] = Field(
|
238
|
-
None,
|
239
|
-
description=(
|
240
|
-
"The parameters to pass to the deployment, or None to use the "
|
241
|
-
"deployment's default parameters"
|
242
|
-
),
|
243
|
-
)
|
244
|
-
|
245
|
-
def as_automation(self) -> AutomationCore:
|
246
|
-
assert self.name
|
247
|
-
|
248
|
-
trigger: TriggerTypes
|
249
|
-
|
250
|
-
if self.posture == Posture.Metric:
|
251
|
-
assert self.metric
|
252
|
-
trigger = MetricTrigger(
|
253
|
-
type="metric",
|
254
|
-
match=self.match,
|
255
|
-
match_related=self.match_related,
|
256
|
-
posture=self.posture,
|
257
|
-
metric=self.metric,
|
258
|
-
)
|
259
|
-
else:
|
260
|
-
trigger = EventTrigger(
|
261
|
-
match=self.match,
|
262
|
-
match_related=self.match_related,
|
263
|
-
after=self.after,
|
264
|
-
expect=self.expect,
|
265
|
-
for_each=self.for_each,
|
266
|
-
posture=self.posture,
|
267
|
-
threshold=self.threshold,
|
268
|
-
within=self.within,
|
269
|
-
)
|
270
|
-
|
271
|
-
return AutomationCore(
|
272
|
-
name=self.name,
|
273
|
-
description=self.description,
|
274
|
-
enabled=self.enabled,
|
275
|
-
trigger=trigger,
|
276
|
-
actions=self.actions(),
|
277
|
-
owner_resource=self.owner_resource(),
|
278
|
-
)
|
279
|
-
|
280
|
-
def set_deployment_id(self, deployment_id: UUID):
|
281
|
-
self._deployment_id = deployment_id
|
282
|
-
|
283
|
-
def owner_resource(self) -> Optional[str]:
|
284
|
-
return f"prefect.deployment.{self._deployment_id}"
|
285
|
-
|
286
|
-
def actions(self) -> List[ActionTypes]:
|
287
|
-
assert self._deployment_id
|
288
|
-
return [
|
289
|
-
RunDeployment(
|
290
|
-
source="selected",
|
291
|
-
deployment_id=self._deployment_id,
|
292
|
-
parameters=self.parameters,
|
293
|
-
job_variables=self.job_variables,
|
294
|
-
)
|
295
|
-
]
|