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/schemas/events.py
CHANGED
@@ -10,21 +10,15 @@ from typing import (
|
|
10
10
|
Sequence,
|
11
11
|
Tuple,
|
12
12
|
Union,
|
13
|
-
cast,
|
14
13
|
)
|
15
14
|
from uuid import UUID, uuid4
|
16
15
|
|
17
16
|
import pendulum
|
18
|
-
|
19
|
-
from
|
20
|
-
|
21
|
-
if HAS_PYDANTIC_V2:
|
22
|
-
from pydantic.v1 import Field, root_validator, validator
|
23
|
-
else:
|
24
|
-
from pydantic import Field, root_validator, validator # type: ignore
|
17
|
+
from pydantic import ConfigDict, Field, RootModel, field_validator, model_validator
|
18
|
+
from pydantic_extra_types.pendulum_dt import DateTime
|
19
|
+
from typing_extensions import Self
|
25
20
|
|
26
21
|
from prefect._internal.schemas.bases import PrefectBaseModel
|
27
|
-
from prefect._internal.schemas.fields import DateTimeTZ
|
28
22
|
from prefect.logging import get_logger
|
29
23
|
from prefect.settings import (
|
30
24
|
PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE,
|
@@ -39,34 +33,24 @@ logger = get_logger(__name__)
|
|
39
33
|
class Resource(Labelled):
|
40
34
|
"""An observable business object of interest to the user"""
|
41
35
|
|
42
|
-
@
|
43
|
-
def enforce_maximum_labels(
|
44
|
-
|
45
|
-
if not isinstance(labels, dict):
|
46
|
-
return values
|
47
|
-
|
48
|
-
if len(labels) > PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE.value():
|
36
|
+
@model_validator(mode="after")
|
37
|
+
def enforce_maximum_labels(self) -> Self:
|
38
|
+
if len(self.root) > PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE.value():
|
49
39
|
raise ValueError(
|
50
40
|
"The maximum number of labels per resource "
|
51
41
|
f"is {PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE.value()}"
|
52
42
|
)
|
53
43
|
|
54
|
-
return
|
55
|
-
|
56
|
-
@root_validator(pre=True)
|
57
|
-
def requires_resource_id(cls, values: Dict[str, Any]):
|
58
|
-
labels = values.get("__root__")
|
59
|
-
if not isinstance(labels, dict):
|
60
|
-
return values
|
61
|
-
|
62
|
-
labels = cast(Dict[str, str], labels)
|
44
|
+
return self
|
63
45
|
|
64
|
-
|
46
|
+
@model_validator(mode="after")
|
47
|
+
def requires_resource_id(self) -> Self:
|
48
|
+
if "prefect.resource.id" not in self.root:
|
65
49
|
raise ValueError("Resources must include the prefect.resource.id label")
|
66
|
-
if not
|
50
|
+
if not self.root["prefect.resource.id"]:
|
67
51
|
raise ValueError("The prefect.resource.id label must be non-empty")
|
68
52
|
|
69
|
-
return
|
53
|
+
return self
|
70
54
|
|
71
55
|
@property
|
72
56
|
def id(self) -> str:
|
@@ -76,26 +60,30 @@ class Resource(Labelled):
|
|
76
60
|
def name(self) -> Optional[str]:
|
77
61
|
return self.get("prefect.resource.name")
|
78
62
|
|
63
|
+
def prefect_object_id(self, kind: str) -> UUID:
|
64
|
+
"""Extracts the UUID from an event's resource ID if it's the expected kind
|
65
|
+
of prefect resource"""
|
66
|
+
prefix = f"{kind}." if not kind.endswith(".") else kind
|
79
67
|
|
80
|
-
|
81
|
-
|
68
|
+
if not self.id.startswith(prefix):
|
69
|
+
raise ValueError(f"Resource ID {self.id} does not start with {prefix}")
|
82
70
|
|
83
|
-
|
84
|
-
def requires_resource_role(cls, values: Dict[str, Any]):
|
85
|
-
labels = values.get("__root__")
|
86
|
-
if not isinstance(labels, dict):
|
87
|
-
return values
|
71
|
+
return UUID(self.id[len(prefix) :])
|
88
72
|
|
89
|
-
labels = cast(Dict[str, str], labels)
|
90
73
|
|
91
|
-
|
74
|
+
class RelatedResource(Resource):
|
75
|
+
"""A Resource with a specific role in an Event"""
|
76
|
+
|
77
|
+
@model_validator(mode="after")
|
78
|
+
def requires_resource_role(self) -> Self:
|
79
|
+
if "prefect.resource.role" not in self.root:
|
92
80
|
raise ValueError(
|
93
81
|
"Related Resources must include the prefect.resource.role label"
|
94
82
|
)
|
95
|
-
if not
|
83
|
+
if not self.root["prefect.resource.role"]:
|
96
84
|
raise ValueError("The prefect.resource.role label must be non-empty")
|
97
85
|
|
98
|
-
return
|
86
|
+
return self
|
99
87
|
|
100
88
|
@property
|
101
89
|
def role(self) -> str:
|
@@ -105,7 +93,9 @@ class RelatedResource(Resource):
|
|
105
93
|
class Event(PrefectBaseModel):
|
106
94
|
"""The client-side view of an event that has happened to a Resource"""
|
107
95
|
|
108
|
-
|
96
|
+
model_config = ConfigDict(extra="ignore")
|
97
|
+
|
98
|
+
occurred: DateTime = Field(
|
109
99
|
default_factory=lambda: pendulum.now("UTC"),
|
110
100
|
description="When the event happened from the sender's perspective",
|
111
101
|
)
|
@@ -154,7 +144,8 @@ class Event(PrefectBaseModel):
|
|
154
144
|
resources[related.role].append(related)
|
155
145
|
return resources
|
156
146
|
|
157
|
-
@
|
147
|
+
@field_validator("related")
|
148
|
+
@classmethod
|
158
149
|
def enforce_maximum_related_resources(cls, value: List[RelatedResource]):
|
159
150
|
if len(value) > PREFECT_EVENTS_MAXIMUM_RELATED_RESOURCES.value():
|
160
151
|
raise ValueError(
|
@@ -181,10 +172,9 @@ class ReceivedEvent(Event):
|
|
181
172
|
"""The server-side view of an event that has happened to a Resource after it has
|
182
173
|
been received by the server"""
|
183
174
|
|
184
|
-
|
185
|
-
orm_mode = True
|
175
|
+
model_config = ConfigDict(from_attributes=True)
|
186
176
|
|
187
|
-
received:
|
177
|
+
received: DateTime = Field(
|
188
178
|
...,
|
189
179
|
description="When the event was received by Prefect Cloud",
|
190
180
|
)
|
@@ -210,35 +200,26 @@ def matches(expected: str, value: Optional[str]) -> bool:
|
|
210
200
|
return match if positive else not match
|
211
201
|
|
212
202
|
|
213
|
-
class ResourceSpecification(
|
214
|
-
"""A specification that may match zero, one, or many resources, used to target or
|
215
|
-
select a set of resources in a query or automation. A resource must match at least
|
216
|
-
one value of all of the provided labels"""
|
217
|
-
|
218
|
-
__root__: Dict[str, Union[str, List[str]]]
|
219
|
-
|
203
|
+
class ResourceSpecification(RootModel[Dict[str, Union[str, List[str]]]]):
|
220
204
|
def matches_every_resource(self) -> bool:
|
221
|
-
return len(self) == 0
|
205
|
+
return len(self.root) == 0
|
222
206
|
|
223
207
|
def matches_every_resource_of_kind(self, prefix: str) -> bool:
|
224
208
|
if self.matches_every_resource():
|
225
209
|
return True
|
226
|
-
|
227
|
-
|
228
|
-
if resource_id
|
210
|
+
if len(self.root) == 1:
|
211
|
+
resource_id = self.root.get("prefect.resource.id")
|
212
|
+
if resource_id:
|
229
213
|
values = [resource_id] if isinstance(resource_id, str) else resource_id
|
230
214
|
return any(value == f"{prefix}.*" for value in values)
|
231
|
-
|
232
215
|
return False
|
233
216
|
|
234
217
|
def includes(self, candidates: Iterable[Resource]) -> bool:
|
235
218
|
if self.matches_every_resource():
|
236
219
|
return True
|
237
|
-
|
238
220
|
for candidate in candidates:
|
239
221
|
if self.matches(candidate):
|
240
222
|
return True
|
241
|
-
|
242
223
|
return False
|
243
224
|
|
244
225
|
def matches(self, resource: Resource) -> bool:
|
@@ -251,14 +232,14 @@ class ResourceSpecification(PrefectBaseModel):
|
|
251
232
|
def items(self) -> Iterable[Tuple[str, List[str]]]:
|
252
233
|
return [
|
253
234
|
(label, [value] if isinstance(value, str) else value)
|
254
|
-
for label, value in self.
|
235
|
+
for label, value in self.root.items()
|
255
236
|
]
|
256
237
|
|
257
238
|
def __contains__(self, key: str) -> bool:
|
258
|
-
return self.
|
239
|
+
return key in self.root
|
259
240
|
|
260
241
|
def __getitem__(self, key: str) -> List[str]:
|
261
|
-
value = self.
|
242
|
+
value = self.root[key]
|
262
243
|
if not value:
|
263
244
|
return []
|
264
245
|
if not isinstance(value, list):
|
@@ -268,7 +249,7 @@ class ResourceSpecification(PrefectBaseModel):
|
|
268
249
|
def pop(
|
269
250
|
self, key: str, default: Optional[Union[str, List[str]]] = None
|
270
251
|
) -> Optional[List[str]]:
|
271
|
-
value = self.
|
252
|
+
value = self.root.pop(key, default)
|
272
253
|
if not value:
|
273
254
|
return []
|
274
255
|
if not isinstance(value, list):
|
@@ -278,7 +259,7 @@ class ResourceSpecification(PrefectBaseModel):
|
|
278
259
|
def get(
|
279
260
|
self, key: str, default: Optional[Union[str, List[str]]] = None
|
280
261
|
) -> Optional[List[str]]:
|
281
|
-
value = self.
|
262
|
+
value = self.root.get(key, default)
|
282
263
|
if not value:
|
283
264
|
return []
|
284
265
|
if not isinstance(value, list):
|
@@ -286,7 +267,7 @@ class ResourceSpecification(PrefectBaseModel):
|
|
286
267
|
return value
|
287
268
|
|
288
269
|
def __len__(self) -> int:
|
289
|
-
return len(self.
|
270
|
+
return len(self.root)
|
290
271
|
|
291
272
|
def deepcopy(self) -> "ResourceSpecification":
|
292
|
-
return ResourceSpecification
|
273
|
+
return ResourceSpecification(root=copy.deepcopy(self.root))
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import Dict, Iterable, Iterator, List, Optional, Tuple
|
2
2
|
|
3
|
-
from
|
3
|
+
from pydantic import RootModel
|
4
4
|
|
5
5
|
|
6
6
|
class LabelDiver:
|
@@ -71,36 +71,32 @@ class LabelDiver:
|
|
71
71
|
raise AttributeError
|
72
72
|
|
73
73
|
|
74
|
-
class Labelled(
|
75
|
-
"""An object defined by string labels and values"""
|
76
|
-
|
77
|
-
__root__: Dict[str, str]
|
78
|
-
|
74
|
+
class Labelled(RootModel[Dict[str, str]]):
|
79
75
|
def keys(self) -> Iterable[str]:
|
80
|
-
return self.
|
76
|
+
return self.root.keys()
|
81
77
|
|
82
78
|
def items(self) -> Iterable[Tuple[str, str]]:
|
83
|
-
return self.
|
79
|
+
return self.root.items()
|
84
80
|
|
85
81
|
def __getitem__(self, label: str) -> str:
|
86
|
-
return self.
|
82
|
+
return self.root[label]
|
87
83
|
|
88
84
|
def __setitem__(self, label: str, value: str) -> str:
|
89
|
-
self.
|
85
|
+
self.root[label] = value
|
90
86
|
return value
|
91
87
|
|
92
88
|
def __contains__(self, key: str) -> bool:
|
93
|
-
return key in self.
|
89
|
+
return key in self.root
|
94
90
|
|
95
91
|
def get(self, label: str, default: Optional[str] = None) -> Optional[str]:
|
96
|
-
return self.
|
92
|
+
return self.root.get(label, default)
|
97
93
|
|
98
94
|
def as_label_value_array(self) -> List[Dict[str, str]]:
|
99
95
|
return [{"label": label, "value": value} for label, value in self.items()]
|
100
96
|
|
101
97
|
@property
|
102
98
|
def labels(self) -> LabelDiver:
|
103
|
-
return LabelDiver(self.
|
99
|
+
return LabelDiver(self.root)
|
104
100
|
|
105
101
|
def has_all_labels(self, labels: Dict[str, str]) -> bool:
|
106
|
-
return all(self.
|
102
|
+
return all(self.root.get(label) == value for label, value in labels.items())
|
prefect/events/utilities.py
CHANGED
@@ -3,13 +3,12 @@ from typing import Any, Dict, List, Optional, Union
|
|
3
3
|
from uuid import UUID
|
4
4
|
|
5
5
|
import pendulum
|
6
|
-
|
7
|
-
from prefect._internal.schemas.fields import DateTimeTZ
|
6
|
+
from pydantic_extra_types.pendulum_dt import DateTime
|
8
7
|
|
9
8
|
from .clients import (
|
10
9
|
AssertingEventsClient,
|
10
|
+
AssertingPassthroughEventsClient,
|
11
11
|
PrefectCloudEventsClient,
|
12
|
-
PrefectEphemeralEventsClient,
|
13
12
|
PrefectEventsClient,
|
14
13
|
)
|
15
14
|
from .schemas.events import Event, RelatedResource
|
@@ -21,7 +20,7 @@ TIGHT_TIMING = timedelta(minutes=5)
|
|
21
20
|
def emit_event(
|
22
21
|
event: str,
|
23
22
|
resource: Dict[str, str],
|
24
|
-
occurred: Optional[
|
23
|
+
occurred: Optional[DateTime] = None,
|
25
24
|
related: Optional[Union[List[Dict[str, str]], List[RelatedResource]]] = None,
|
26
25
|
payload: Optional[Dict[str, Any]] = None,
|
27
26
|
id: Optional[UUID] = None,
|
@@ -51,10 +50,10 @@ def emit_event(
|
|
51
50
|
return None
|
52
51
|
|
53
52
|
operational_clients = [
|
53
|
+
AssertingPassthroughEventsClient,
|
54
54
|
AssertingEventsClient,
|
55
55
|
PrefectCloudEventsClient,
|
56
56
|
PrefectEventsClient,
|
57
|
-
PrefectEphemeralEventsClient,
|
58
57
|
]
|
59
58
|
worker_instance = EventsWorker.instance()
|
60
59
|
|
prefect/events/worker.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from contextlib import asynccontextmanager
|
2
2
|
from contextvars import Context, copy_context
|
3
|
-
from typing import Any, Dict, Optional, Tuple, Type
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type
|
4
4
|
from uuid import UUID
|
5
5
|
|
6
6
|
from typing_extensions import Self
|
@@ -10,7 +10,6 @@ from prefect.settings import (
|
|
10
10
|
PREFECT_API_KEY,
|
11
11
|
PREFECT_API_URL,
|
12
12
|
PREFECT_CLOUD_API_URL,
|
13
|
-
PREFECT_EXPERIMENTAL_EVENTS,
|
14
13
|
)
|
15
14
|
from prefect.utilities.context import temporary_context
|
16
15
|
|
@@ -18,12 +17,14 @@ from .clients import (
|
|
18
17
|
EventsClient,
|
19
18
|
NullEventsClient,
|
20
19
|
PrefectCloudEventsClient,
|
21
|
-
PrefectEphemeralEventsClient,
|
22
20
|
PrefectEventsClient,
|
23
21
|
)
|
24
22
|
from .related import related_resources_from_run_context
|
25
23
|
from .schemas.events import Event
|
26
24
|
|
25
|
+
if TYPE_CHECKING:
|
26
|
+
from prefect.client.orchestration import PrefectClient
|
27
|
+
|
27
28
|
|
28
29
|
def should_emit_events() -> bool:
|
29
30
|
return (
|
@@ -42,11 +43,11 @@ def emit_events_to_cloud() -> bool:
|
|
42
43
|
|
43
44
|
def should_emit_events_to_running_server() -> bool:
|
44
45
|
api_url = PREFECT_API_URL.value()
|
45
|
-
return isinstance(api_url, str)
|
46
|
+
return isinstance(api_url, str)
|
46
47
|
|
47
48
|
|
48
49
|
def should_emit_events_to_ephemeral_server() -> bool:
|
49
|
-
return PREFECT_API_KEY.value() is None
|
50
|
+
return PREFECT_API_KEY.value() is None
|
50
51
|
|
51
52
|
|
52
53
|
class EventsWorker(QueueService[Event]):
|
@@ -57,14 +58,18 @@ class EventsWorker(QueueService[Event]):
|
|
57
58
|
self.client_type = client_type
|
58
59
|
self.client_options = client_options
|
59
60
|
self._client: EventsClient
|
61
|
+
self._orchestration_client: "PrefectClient"
|
60
62
|
self._context_cache: Dict[UUID, Context] = {}
|
61
63
|
|
62
64
|
@asynccontextmanager
|
63
65
|
async def _lifespan(self):
|
64
66
|
self._client = self.client_type(**{k: v for k, v in self.client_options})
|
67
|
+
from prefect.client.orchestration import get_client
|
65
68
|
|
69
|
+
self._orchestration_client = get_client()
|
66
70
|
async with self._client:
|
67
|
-
|
71
|
+
async with self._orchestration_client:
|
72
|
+
yield
|
68
73
|
|
69
74
|
def _prepare_item(self, event: Event) -> Event:
|
70
75
|
self._context_cache[event.id] = copy_context()
|
@@ -79,7 +84,9 @@ class EventsWorker(QueueService[Event]):
|
|
79
84
|
|
80
85
|
async def attach_related_resources_from_context(self, event: Event):
|
81
86
|
exclude = {resource.id for resource in event.involved_resources}
|
82
|
-
event.related += await related_resources_from_run_context(
|
87
|
+
event.related += await related_resources_from_run_context(
|
88
|
+
client=self._orchestration_client, exclude=exclude
|
89
|
+
)
|
83
90
|
|
84
91
|
@classmethod
|
85
92
|
def instance(
|
@@ -98,7 +105,15 @@ class EventsWorker(QueueService[Event]):
|
|
98
105
|
elif should_emit_events_to_running_server():
|
99
106
|
client_type = PrefectEventsClient
|
100
107
|
elif should_emit_events_to_ephemeral_server():
|
101
|
-
|
108
|
+
# create an ephemeral API if none was provided
|
109
|
+
from prefect.server.api.server import SubprocessASGIServer
|
110
|
+
|
111
|
+
server = SubprocessASGIServer()
|
112
|
+
server.start()
|
113
|
+
assert server.server_process is not None, "Server process did not start"
|
114
|
+
|
115
|
+
client_kwargs = {"api_url": server.api_url}
|
116
|
+
client_type = PrefectEventsClient
|
102
117
|
else:
|
103
118
|
client_type = NullEventsClient
|
104
119
|
|
prefect/exceptions.py
CHANGED
@@ -403,3 +403,18 @@ class FlowPauseTimeout(PrefectException):
|
|
403
403
|
|
404
404
|
class FlowRunWaitTimeout(PrefectException):
|
405
405
|
"""Raised when a flow run takes longer than a given timeout"""
|
406
|
+
|
407
|
+
|
408
|
+
class PrefectImportError(ImportError):
|
409
|
+
"""
|
410
|
+
An error raised when a Prefect object cannot be imported due to a move or removal.
|
411
|
+
"""
|
412
|
+
|
413
|
+
def __init__(self, message: str) -> None:
|
414
|
+
super().__init__(message)
|
415
|
+
|
416
|
+
|
417
|
+
class SerializationError(PrefectException):
|
418
|
+
"""
|
419
|
+
Raised when an object cannot be serialized.
|
420
|
+
"""
|