prefect-client 3.1.11__py3-none-any.whl → 3.1.13__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/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +92 -0
- prefect/_experimental/sla/objects.py +61 -0
- prefect/_internal/concurrency/services.py +2 -2
- prefect/_internal/concurrency/threads.py +6 -0
- prefect/_internal/retries.py +6 -3
- prefect/_internal/schemas/validators.py +6 -4
- prefect/_version.py +3 -3
- prefect/artifacts.py +4 -1
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +57 -31
- prefect/blocks/core.py +181 -82
- prefect/blocks/notifications.py +134 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +23 -22
- prefect/client/orchestration/__init__.py +103 -2006
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/client.py +16 -14
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/objects.py +5 -5
- prefect/client/utilities.py +3 -3
- prefect/context.py +15 -1
- prefect/deployments/base.py +13 -4
- prefect/deployments/flow_runs.py +5 -1
- prefect/deployments/runner.py +37 -1
- prefect/deployments/steps/core.py +1 -1
- prefect/deployments/steps/pull.py +8 -3
- prefect/deployments/steps/utility.py +2 -2
- prefect/docker/docker_image.py +13 -9
- prefect/engine.py +33 -11
- prefect/events/cli/automations.py +4 -4
- prefect/events/clients.py +17 -14
- prefect/events/schemas/automations.py +12 -8
- prefect/events/schemas/events.py +5 -1
- prefect/events/worker.py +1 -1
- prefect/filesystems.py +7 -3
- prefect/flow_engine.py +64 -47
- prefect/flows.py +128 -74
- prefect/futures.py +14 -7
- prefect/infrastructure/provisioners/__init__.py +2 -0
- prefect/infrastructure/provisioners/cloud_run.py +4 -4
- prefect/infrastructure/provisioners/coiled.py +249 -0
- prefect/infrastructure/provisioners/container_instance.py +4 -3
- prefect/infrastructure/provisioners/ecs.py +55 -43
- prefect/infrastructure/provisioners/modal.py +5 -4
- prefect/input/actions.py +5 -1
- prefect/input/run_input.py +157 -43
- prefect/logging/configuration.py +3 -3
- prefect/logging/filters.py +2 -2
- prefect/logging/formatters.py +15 -11
- prefect/logging/handlers.py +24 -14
- prefect/logging/highlighters.py +5 -5
- prefect/logging/loggers.py +28 -18
- prefect/logging/logging.yml +1 -1
- prefect/main.py +3 -1
- prefect/results.py +166 -86
- prefect/runner/runner.py +38 -29
- prefect/runner/server.py +3 -1
- prefect/runner/storage.py +18 -18
- prefect/runner/submit.py +19 -12
- prefect/runtime/deployment.py +15 -8
- prefect/runtime/flow_run.py +19 -6
- prefect/runtime/task_run.py +7 -3
- prefect/settings/base.py +17 -7
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +4 -3
- prefect/settings/models/cli.py +4 -3
- prefect/settings/models/client.py +7 -4
- prefect/settings/models/cloud.py +9 -3
- prefect/settings/models/deployments.py +4 -3
- prefect/settings/models/experiments.py +4 -8
- prefect/settings/models/flows.py +4 -3
- prefect/settings/models/internal.py +4 -3
- prefect/settings/models/logging.py +8 -6
- prefect/settings/models/results.py +4 -3
- prefect/settings/models/root.py +11 -16
- prefect/settings/models/runner.py +8 -5
- prefect/settings/models/server/api.py +6 -3
- prefect/settings/models/server/database.py +120 -25
- prefect/settings/models/server/deployments.py +4 -3
- prefect/settings/models/server/ephemeral.py +7 -4
- prefect/settings/models/server/events.py +6 -3
- prefect/settings/models/server/flow_run_graph.py +4 -3
- prefect/settings/models/server/root.py +4 -3
- prefect/settings/models/server/services.py +15 -12
- prefect/settings/models/server/tasks.py +7 -4
- prefect/settings/models/server/ui.py +4 -3
- prefect/settings/models/tasks.py +10 -5
- prefect/settings/models/testing.py +4 -3
- prefect/settings/models/worker.py +7 -4
- prefect/settings/profiles.py +13 -12
- prefect/settings/sources.py +20 -19
- prefect/states.py +74 -51
- prefect/task_engine.py +43 -33
- prefect/task_runners.py +85 -72
- prefect/task_runs.py +20 -11
- prefect/task_worker.py +14 -9
- prefect/tasks.py +36 -28
- prefect/telemetry/bootstrap.py +13 -9
- prefect/telemetry/run_telemetry.py +15 -13
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +3 -3
- prefect/types/__init__.py +3 -1
- prefect/utilities/_deprecated.py +38 -0
- prefect/utilities/engine.py +11 -4
- prefect/utilities/filesystem.py +2 -2
- prefect/utilities/generics.py +1 -1
- prefect/utilities/pydantic.py +21 -36
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +58 -33
- prefect/workers/process.py +20 -15
- prefect/workers/server.py +4 -5
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
prefect/deployments/flow_runs.py
CHANGED
prefect/deployments/runner.py
CHANGED
@@ -48,12 +48,14 @@ from rich.console import Console
|
|
48
48
|
from rich.progress import Progress, SpinnerColumn, TextColumn, track
|
49
49
|
from rich.table import Table
|
50
50
|
|
51
|
+
from prefect._experimental.sla.objects import SlaTypes
|
51
52
|
from prefect._internal.concurrency.api import create_call, from_async
|
52
53
|
from prefect._internal.schemas.validators import (
|
53
54
|
reconcile_paused_deployment,
|
54
55
|
reconcile_schedules_runner,
|
55
56
|
)
|
56
|
-
from prefect.client.
|
57
|
+
from prefect.client.base import ServerType
|
58
|
+
from prefect.client.orchestration import PrefectClient, get_client
|
57
59
|
from prefect.client.schemas.actions import DeploymentScheduleCreate
|
58
60
|
from prefect.client.schemas.filters import WorkerFilter, WorkerFilterStatus
|
59
61
|
from prefect.client.schemas.objects import (
|
@@ -128,6 +130,7 @@ class RunnerDeployment(BaseModel):
|
|
128
130
|
job_variables: Settings used to override the values specified default base job template
|
129
131
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
130
132
|
available settings.
|
133
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
131
134
|
"""
|
132
135
|
|
133
136
|
model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
|
@@ -207,6 +210,10 @@ class RunnerDeployment(BaseModel):
|
|
207
210
|
" a built runner."
|
208
211
|
),
|
209
212
|
)
|
213
|
+
# (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
214
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = PrivateAttr(
|
215
|
+
default=None,
|
216
|
+
)
|
210
217
|
_entrypoint_type: EntrypointType = PrivateAttr(
|
211
218
|
default=EntrypointType.FILE_PATH,
|
212
219
|
)
|
@@ -351,8 +358,28 @@ class RunnerDeployment(BaseModel):
|
|
351
358
|
trigger.set_deployment_id(deployment_id)
|
352
359
|
await client.create_automation(trigger.as_automation())
|
353
360
|
|
361
|
+
# We plan to support SLA configuration on the Prefect Server in the future.
|
362
|
+
# For now, we only support it on Prefect Cloud.
|
363
|
+
|
364
|
+
# If we're provided with an empty list, we will call the apply endpoint
|
365
|
+
# to remove existing SLAs for the deployment. If the argument is not provided,
|
366
|
+
# we will not call the endpoint.
|
367
|
+
if self._sla or self._sla == []:
|
368
|
+
await self._create_slas(deployment_id, client)
|
369
|
+
|
354
370
|
return deployment_id
|
355
371
|
|
372
|
+
async def _create_slas(self, deployment_id: UUID, client: PrefectClient):
|
373
|
+
if not isinstance(self._sla, list):
|
374
|
+
self._sla = [self._sla]
|
375
|
+
|
376
|
+
if client.server_type == ServerType.CLOUD:
|
377
|
+
await client.apply_slas_for_deployment(deployment_id, self._sla)
|
378
|
+
else:
|
379
|
+
raise ValueError(
|
380
|
+
"SLA configuration is currently only supported on Prefect Cloud."
|
381
|
+
)
|
382
|
+
|
356
383
|
@staticmethod
|
357
384
|
def _construct_deployment_schedules(
|
358
385
|
interval: Optional[
|
@@ -467,6 +494,7 @@ class RunnerDeployment(BaseModel):
|
|
467
494
|
work_queue_name: Optional[str] = None,
|
468
495
|
job_variables: Optional[dict[str, Any]] = None,
|
469
496
|
entrypoint_type: EntrypointType = EntrypointType.FILE_PATH,
|
497
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
470
498
|
) -> "RunnerDeployment":
|
471
499
|
"""
|
472
500
|
Configure a deployment for a given flow.
|
@@ -497,6 +525,7 @@ class RunnerDeployment(BaseModel):
|
|
497
525
|
job_variables: Settings used to override the values specified default base job template
|
498
526
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
499
527
|
available settings.
|
528
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
500
529
|
"""
|
501
530
|
constructed_schedules = cls._construct_deployment_schedules(
|
502
531
|
interval=interval,
|
@@ -532,6 +561,7 @@ class RunnerDeployment(BaseModel):
|
|
532
561
|
work_queue_name=work_queue_name,
|
533
562
|
job_variables=job_variables,
|
534
563
|
)
|
564
|
+
deployment._sla = _sla
|
535
565
|
|
536
566
|
if not deployment.entrypoint:
|
537
567
|
no_file_location_error = (
|
@@ -607,6 +637,7 @@ class RunnerDeployment(BaseModel):
|
|
607
637
|
work_pool_name: Optional[str] = None,
|
608
638
|
work_queue_name: Optional[str] = None,
|
609
639
|
job_variables: Optional[dict[str, Any]] = None,
|
640
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
610
641
|
) -> "RunnerDeployment":
|
611
642
|
"""
|
612
643
|
Configure a deployment for a given flow located at a given entrypoint.
|
@@ -638,6 +669,7 @@ class RunnerDeployment(BaseModel):
|
|
638
669
|
job_variables: Settings used to override the values specified default base job template
|
639
670
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
640
671
|
available settings.
|
672
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
641
673
|
"""
|
642
674
|
from prefect.flows import load_flow_from_entrypoint
|
643
675
|
|
@@ -677,6 +709,7 @@ class RunnerDeployment(BaseModel):
|
|
677
709
|
work_queue_name=work_queue_name,
|
678
710
|
job_variables=job_variables,
|
679
711
|
)
|
712
|
+
deployment._sla = _sla
|
680
713
|
deployment._path = str(Path.cwd())
|
681
714
|
|
682
715
|
cls._set_defaults_from_flow(deployment, flow)
|
@@ -708,6 +741,7 @@ class RunnerDeployment(BaseModel):
|
|
708
741
|
work_pool_name: Optional[str] = None,
|
709
742
|
work_queue_name: Optional[str] = None,
|
710
743
|
job_variables: Optional[dict[str, Any]] = None,
|
744
|
+
_sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
|
711
745
|
):
|
712
746
|
"""
|
713
747
|
Create a RunnerDeployment from a flow located at a given entrypoint and stored in a
|
@@ -739,6 +773,7 @@ class RunnerDeployment(BaseModel):
|
|
739
773
|
job_variables: Settings used to override the values specified default base job template
|
740
774
|
of the chosen work pool. Refer to the base job template of the chosen work pool for
|
741
775
|
available settings.
|
776
|
+
_sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
|
742
777
|
"""
|
743
778
|
from prefect.flows import load_flow_from_entrypoint
|
744
779
|
|
@@ -787,6 +822,7 @@ class RunnerDeployment(BaseModel):
|
|
787
822
|
work_queue_name=work_queue_name,
|
788
823
|
job_variables=job_variables,
|
789
824
|
)
|
825
|
+
deployment._sla = _sla
|
790
826
|
deployment._path = str(storage.destination).replace(
|
791
827
|
tmpdir, "$STORAGE_BASE_PATH"
|
792
828
|
)
|
@@ -141,7 +141,7 @@ async def run_steps(
|
|
141
141
|
steps: List[Dict[str, Any]],
|
142
142
|
upstream_outputs: Optional[Dict[str, Any]] = None,
|
143
143
|
print_function: Any = print,
|
144
|
-
):
|
144
|
+
) -> dict[str, Any]:
|
145
145
|
upstream_outputs = deepcopy(upstream_outputs) if upstream_outputs else {}
|
146
146
|
for step in steps:
|
147
147
|
if not step:
|
@@ -12,7 +12,10 @@ from prefect.logging.loggers import get_logger
|
|
12
12
|
from prefect.runner.storage import BlockStorageAdapter, GitRepository, RemoteStorage
|
13
13
|
from prefect.utilities.asyncutils import run_coro_as_sync
|
14
14
|
|
15
|
-
|
15
|
+
if TYPE_CHECKING:
|
16
|
+
import logging
|
17
|
+
|
18
|
+
deployment_logger: "logging.Logger" = get_logger("deployment")
|
16
19
|
|
17
20
|
if TYPE_CHECKING:
|
18
21
|
from prefect.blocks.core import Block
|
@@ -197,7 +200,7 @@ def git_clone(
|
|
197
200
|
return dict(directory=str(storage.destination.relative_to(Path.cwd())))
|
198
201
|
|
199
202
|
|
200
|
-
async def pull_from_remote_storage(url: str, **settings: Any):
|
203
|
+
async def pull_from_remote_storage(url: str, **settings: Any) -> dict[str, Any]:
|
201
204
|
"""
|
202
205
|
Pulls code from a remote storage location into the current working directory.
|
203
206
|
|
@@ -239,7 +242,9 @@ async def pull_from_remote_storage(url: str, **settings: Any):
|
|
239
242
|
return {"directory": directory}
|
240
243
|
|
241
244
|
|
242
|
-
async def pull_with_block(
|
245
|
+
async def pull_with_block(
|
246
|
+
block_document_name: str, block_type_slug: str
|
247
|
+
) -> dict[str, Any]:
|
243
248
|
"""
|
244
249
|
Pulls code using a block.
|
245
250
|
|
@@ -26,7 +26,7 @@ import shlex
|
|
26
26
|
import string
|
27
27
|
import subprocess
|
28
28
|
import sys
|
29
|
-
from typing import Dict, Optional
|
29
|
+
from typing import Any, Dict, Optional
|
30
30
|
|
31
31
|
from anyio import create_task_group
|
32
32
|
from anyio.streams.text import TextReceiveStream
|
@@ -205,7 +205,7 @@ async def pip_install_requirements(
|
|
205
205
|
directory: Optional[str] = None,
|
206
206
|
requirements_file: str = "requirements.txt",
|
207
207
|
stream_output: bool = True,
|
208
|
-
):
|
208
|
+
) -> dict[str, Any]:
|
209
209
|
"""
|
210
210
|
Installs dependencies from a requirements.txt file.
|
211
211
|
|
prefect/docker/docker_image.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from pathlib import Path
|
2
|
-
from typing import Optional
|
2
|
+
from typing import Any, Optional
|
3
3
|
|
4
4
|
from pendulum import now as pendulum_now
|
5
5
|
|
@@ -34,7 +34,11 @@ class DockerImage:
|
|
34
34
|
"""
|
35
35
|
|
36
36
|
def __init__(
|
37
|
-
self,
|
37
|
+
self,
|
38
|
+
name: str,
|
39
|
+
tag: Optional[str] = None,
|
40
|
+
dockerfile: str = "auto",
|
41
|
+
**build_kwargs: Any,
|
38
42
|
):
|
39
43
|
image_name, image_tag = parse_image_tag(name)
|
40
44
|
if tag and image_tag:
|
@@ -49,16 +53,16 @@ class DockerImage:
|
|
49
53
|
namespace = PREFECT_DEFAULT_DOCKER_BUILD_NAMESPACE.value()
|
50
54
|
# join the namespace and repository to create the full image name
|
51
55
|
# ignore namespace if it is None
|
52
|
-
self.name = "/".join(filter(None, [namespace, repository]))
|
53
|
-
self.tag = tag or image_tag or slugify(pendulum_now("utc").isoformat())
|
54
|
-
self.dockerfile = dockerfile
|
55
|
-
self.build_kwargs = build_kwargs
|
56
|
+
self.name: str = "/".join(filter(None, [namespace, repository]))
|
57
|
+
self.tag: str = tag or image_tag or slugify(pendulum_now("utc").isoformat())
|
58
|
+
self.dockerfile: str = dockerfile
|
59
|
+
self.build_kwargs: dict[str, Any] = build_kwargs
|
56
60
|
|
57
61
|
@property
|
58
|
-
def reference(self):
|
62
|
+
def reference(self) -> str:
|
59
63
|
return f"{self.name}:{self.tag}"
|
60
64
|
|
61
|
-
def build(self):
|
65
|
+
def build(self) -> None:
|
62
66
|
full_image_name = self.reference
|
63
67
|
build_kwargs = self.build_kwargs.copy()
|
64
68
|
build_kwargs["context"] = Path.cwd()
|
@@ -72,7 +76,7 @@ class DockerImage:
|
|
72
76
|
build_kwargs["dockerfile"] = self.dockerfile
|
73
77
|
build_image(**build_kwargs)
|
74
78
|
|
75
|
-
def push(self):
|
79
|
+
def push(self) -> None:
|
76
80
|
with docker_client() as client:
|
77
81
|
events = client.api.push(
|
78
82
|
repository=self.name, tag=self.tag, stream=True, decode=True
|
prefect/engine.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import sys
|
3
|
-
from typing import Any, Callable
|
3
|
+
from typing import TYPE_CHECKING, Any, Callable
|
4
4
|
from uuid import UUID
|
5
5
|
|
6
6
|
from prefect._internal.compatibility.migration import getattr_migration
|
@@ -15,12 +15,19 @@ from prefect.utilities.asyncutils import (
|
|
15
15
|
run_coro_as_sync,
|
16
16
|
)
|
17
17
|
|
18
|
-
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
import logging
|
20
|
+
|
21
|
+
from prefect.flow_engine import FlowRun
|
22
|
+
from prefect.flows import Flow
|
23
|
+
from prefect.logging.loggers import LoggingAdapter
|
24
|
+
|
25
|
+
engine_logger: "logging.Logger" = get_logger("engine")
|
19
26
|
|
20
27
|
|
21
28
|
if __name__ == "__main__":
|
22
29
|
try:
|
23
|
-
flow_run_id = UUID(
|
30
|
+
flow_run_id: UUID = UUID(
|
24
31
|
sys.argv[1] if len(sys.argv) > 1 else os.environ.get("PREFECT__FLOW_RUN_ID")
|
25
32
|
)
|
26
33
|
except Exception:
|
@@ -31,26 +38,41 @@ if __name__ == "__main__":
|
|
31
38
|
|
32
39
|
try:
|
33
40
|
from prefect.flow_engine import (
|
34
|
-
|
41
|
+
flow_run_logger,
|
42
|
+
load_flow,
|
43
|
+
load_flow_run,
|
35
44
|
run_flow,
|
36
45
|
)
|
37
46
|
|
38
|
-
flow_run
|
47
|
+
flow_run: "FlowRun" = load_flow_run(flow_run_id=flow_run_id)
|
48
|
+
run_logger: "LoggingAdapter" = flow_run_logger(flow_run=flow_run)
|
49
|
+
|
50
|
+
try:
|
51
|
+
flow: "Flow[..., Any]" = load_flow(flow_run)
|
52
|
+
except Exception:
|
53
|
+
run_logger.error(
|
54
|
+
"Unexpected exception encountered when trying to load flow",
|
55
|
+
exc_info=True,
|
56
|
+
)
|
57
|
+
raise
|
58
|
+
|
39
59
|
# run the flow
|
40
60
|
if flow.isasync:
|
41
|
-
run_coro_as_sync(run_flow(flow, flow_run=flow_run))
|
61
|
+
run_coro_as_sync(run_flow(flow, flow_run=flow_run, error_logger=run_logger))
|
42
62
|
else:
|
43
|
-
run_flow(flow, flow_run=flow_run)
|
63
|
+
run_flow(flow, flow_run=flow_run, error_logger=run_logger)
|
44
64
|
|
45
|
-
except Abort as
|
65
|
+
except Abort as abort_signal:
|
66
|
+
abort_signal: Abort
|
46
67
|
engine_logger.info(
|
47
68
|
f"Engine execution of flow run '{flow_run_id}' aborted by orchestrator:"
|
48
|
-
f" {
|
69
|
+
f" {abort_signal}"
|
49
70
|
)
|
50
71
|
exit(0)
|
51
|
-
except Pause as
|
72
|
+
except Pause as pause_signal:
|
73
|
+
pause_signal: Pause
|
52
74
|
engine_logger.info(
|
53
|
-
f"Engine execution of flow run '{flow_run_id}' is paused: {
|
75
|
+
f"Engine execution of flow run '{flow_run_id}' is paused: {pause_signal}"
|
54
76
|
)
|
55
77
|
exit(0)
|
56
78
|
except Exception:
|
@@ -3,7 +3,7 @@ Command line interface for working with automations.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import functools
|
6
|
-
from typing import Optional, Type
|
6
|
+
from typing import Any, Callable, Optional, Type
|
7
7
|
from uuid import UUID
|
8
8
|
|
9
9
|
import orjson
|
@@ -21,16 +21,16 @@ from prefect.client.orchestration import get_client
|
|
21
21
|
from prefect.events.schemas.automations import Automation
|
22
22
|
from prefect.exceptions import PrefectHTTPStatusError
|
23
23
|
|
24
|
-
automations_app = PrefectTyper(
|
24
|
+
automations_app: PrefectTyper = PrefectTyper(
|
25
25
|
name="automation",
|
26
26
|
help="Manage automations.",
|
27
27
|
)
|
28
28
|
app.add_typer(automations_app, aliases=["automations"])
|
29
29
|
|
30
30
|
|
31
|
-
def requires_automations(func):
|
31
|
+
def requires_automations(func: Callable[..., Any]) -> Callable[..., Any]:
|
32
32
|
@functools.wraps(func)
|
33
|
-
async def wrapper(*args, **kwargs):
|
33
|
+
async def wrapper(*args: Any, **kwargs: Any) -> Any:
|
34
34
|
try:
|
35
35
|
return await func(*args, **kwargs)
|
36
36
|
except RuntimeError as exc:
|
prefect/events/clients.py
CHANGED
@@ -70,18 +70,21 @@ EVENT_WEBSOCKET_CHECKPOINTS = Counter(
|
|
70
70
|
labelnames=["client"],
|
71
71
|
)
|
72
72
|
|
73
|
-
|
73
|
+
if TYPE_CHECKING:
|
74
|
+
import logging
|
75
|
+
|
76
|
+
logger: "logging.Logger" = get_logger(__name__)
|
74
77
|
|
75
78
|
|
76
|
-
def http_to_ws(url: str):
|
79
|
+
def http_to_ws(url: str) -> str:
|
77
80
|
return url.replace("https://", "wss://").replace("http://", "ws://").rstrip("/")
|
78
81
|
|
79
82
|
|
80
|
-
def events_in_socket_from_api_url(url: str):
|
83
|
+
def events_in_socket_from_api_url(url: str) -> str:
|
81
84
|
return http_to_ws(url) + "/events/in"
|
82
85
|
|
83
86
|
|
84
|
-
def events_out_socket_from_api_url(url: str):
|
87
|
+
def events_out_socket_from_api_url(url: str) -> str:
|
85
88
|
return http_to_ws(url) + "/events/out"
|
86
89
|
|
87
90
|
|
@@ -250,11 +253,11 @@ class AssertingEventsClient(EventsClient):
|
|
250
253
|
last: ClassVar["Optional[AssertingEventsClient]"] = None
|
251
254
|
all: ClassVar[List["AssertingEventsClient"]] = []
|
252
255
|
|
253
|
-
args:
|
254
|
-
kwargs:
|
255
|
-
events:
|
256
|
+
args: tuple[Any, ...]
|
257
|
+
kwargs: dict[str, Any]
|
258
|
+
events: list[Event]
|
256
259
|
|
257
|
-
def __init__(self, *args, **kwargs):
|
260
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
258
261
|
AssertingEventsClient.last = self
|
259
262
|
AssertingEventsClient.all.append(self)
|
260
263
|
self.args = args
|
@@ -431,13 +434,13 @@ class AssertingPassthroughEventsClient(PrefectEventsClient):
|
|
431
434
|
during tests AND sends them to a Prefect server."""
|
432
435
|
|
433
436
|
last: ClassVar["Optional[AssertingPassthroughEventsClient]"] = None
|
434
|
-
all: ClassVar[
|
437
|
+
all: ClassVar[list["AssertingPassthroughEventsClient"]] = []
|
435
438
|
|
436
|
-
args:
|
437
|
-
kwargs:
|
438
|
-
events:
|
439
|
+
args: tuple[Any, ...]
|
440
|
+
kwargs: dict[str, Any]
|
441
|
+
events: list[Event]
|
439
442
|
|
440
|
-
def __init__(self, *args, **kwargs):
|
443
|
+
def __init__(self, *args: Any, **kwargs: Any):
|
441
444
|
super().__init__(*args, **kwargs)
|
442
445
|
AssertingPassthroughEventsClient.last = self
|
443
446
|
AssertingPassthroughEventsClient.all.append(self)
|
@@ -449,7 +452,7 @@ class AssertingPassthroughEventsClient(PrefectEventsClient):
|
|
449
452
|
cls.last = None
|
450
453
|
cls.all = []
|
451
454
|
|
452
|
-
def pop_events(self) ->
|
455
|
+
def pop_events(self) -> list[Event]:
|
453
456
|
events = self.events
|
454
457
|
self.events = []
|
455
458
|
return events
|
@@ -52,7 +52,7 @@ class Trigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-a
|
|
52
52
|
|
53
53
|
_deployment_id: Optional[UUID] = PrivateAttr(default=None)
|
54
54
|
|
55
|
-
def set_deployment_id(self, deployment_id: UUID):
|
55
|
+
def set_deployment_id(self, deployment_id: UUID) -> None:
|
56
56
|
self._deployment_id = deployment_id
|
57
57
|
|
58
58
|
def owner_resource(self) -> Optional[str]:
|
@@ -277,7 +277,7 @@ class MetricTriggerQuery(PrefectBaseModel):
|
|
277
277
|
)
|
278
278
|
|
279
279
|
@field_validator("range", "firing_for")
|
280
|
-
def enforce_minimum_range(cls, value: timedelta):
|
280
|
+
def enforce_minimum_range(cls, value: timedelta) -> timedelta:
|
281
281
|
if value < timedelta(seconds=300):
|
282
282
|
raise ValueError("The minimum range is 300 seconds (5 minutes)")
|
283
283
|
return value
|
@@ -404,13 +404,17 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
|
|
404
404
|
"""Defines an action a user wants to take when a certain number of events
|
405
405
|
do or don't happen to the matching resources"""
|
406
406
|
|
407
|
-
name: str = Field(
|
408
|
-
description: str = Field(
|
407
|
+
name: str = Field(default=..., description="The name of this automation")
|
408
|
+
description: str = Field(
|
409
|
+
default="", description="A longer description of this automation"
|
410
|
+
)
|
409
411
|
|
410
|
-
enabled: bool = Field(
|
412
|
+
enabled: bool = Field(
|
413
|
+
default=True, description="Whether this automation will be evaluated"
|
414
|
+
)
|
411
415
|
|
412
416
|
trigger: TriggerTypes = Field(
|
413
|
-
|
417
|
+
default=...,
|
414
418
|
description=(
|
415
419
|
"The criteria for which events this Automation covers and how it will "
|
416
420
|
"respond to the presence or absence of those events"
|
@@ -418,7 +422,7 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
|
|
418
422
|
)
|
419
423
|
|
420
424
|
actions: List[ActionTypes] = Field(
|
421
|
-
|
425
|
+
default=...,
|
422
426
|
description="The actions to perform when this Automation triggers",
|
423
427
|
)
|
424
428
|
|
@@ -438,4 +442,4 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
|
|
438
442
|
|
439
443
|
|
440
444
|
class Automation(AutomationCore):
|
441
|
-
id: UUID = Field(
|
445
|
+
id: UUID = Field(default=..., description="The ID of this automation")
|
prefect/events/schemas/events.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import copy
|
2
2
|
from collections import defaultdict
|
3
3
|
from typing import (
|
4
|
+
TYPE_CHECKING,
|
4
5
|
Any,
|
5
6
|
ClassVar,
|
6
7
|
Dict,
|
@@ -32,7 +33,10 @@ from prefect.types import DateTime
|
|
32
33
|
|
33
34
|
from .labelling import Labelled
|
34
35
|
|
35
|
-
|
36
|
+
if TYPE_CHECKING:
|
37
|
+
import logging
|
38
|
+
|
39
|
+
logger: "logging.Logger" = get_logger(__name__)
|
36
40
|
|
37
41
|
|
38
42
|
class Resource(Labelled):
|
prefect/events/worker.py
CHANGED
@@ -82,7 +82,7 @@ class EventsWorker(QueueService[Event]):
|
|
82
82
|
|
83
83
|
await self._client.emit(event)
|
84
84
|
|
85
|
-
async def attach_related_resources_from_context(self, event: Event):
|
85
|
+
async def attach_related_resources_from_context(self, event: Event) -> None:
|
86
86
|
if "prefect.resource.lineage-group" in event.resource:
|
87
87
|
# We attach related resources to lineage events in `emit_lineage_event`,
|
88
88
|
# instead of the worker, because not all run-related resources are
|
prefect/filesystems.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import abc
|
2
4
|
import urllib.parse
|
3
5
|
from pathlib import Path
|
@@ -92,7 +94,9 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
|
|
92
94
|
)
|
93
95
|
|
94
96
|
@field_validator("basepath", mode="before")
|
95
|
-
def cast_pathlib(cls, value):
|
97
|
+
def cast_pathlib(cls, value: str | Path | None) -> str | None:
|
98
|
+
if value is None:
|
99
|
+
return value
|
96
100
|
return stringify_path(value)
|
97
101
|
|
98
102
|
def _resolve_path(self, path: str, validate: bool = False) -> Path:
|
@@ -132,7 +136,7 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
|
|
132
136
|
Defaults to copying the entire contents of the block's basepath to the current working directory.
|
133
137
|
"""
|
134
138
|
if not from_path:
|
135
|
-
from_path = Path(self.basepath).expanduser().resolve()
|
139
|
+
from_path = Path(self.basepath or ".").expanduser().resolve()
|
136
140
|
else:
|
137
141
|
from_path = self._resolve_path(from_path)
|
138
142
|
|
@@ -277,7 +281,7 @@ class RemoteFileSystem(WritableFileSystem, WritableDeploymentStorage):
|
|
277
281
|
_filesystem: fsspec.AbstractFileSystem = None
|
278
282
|
|
279
283
|
@field_validator("basepath")
|
280
|
-
def check_basepath(cls, value):
|
284
|
+
def check_basepath(cls, value: str) -> str:
|
281
285
|
return validate_basepath(value)
|
282
286
|
|
283
287
|
def _resolve_path(self, path: str) -> str:
|