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/runner/runner.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
"""
|
2
|
-
Runners are responsible for managing the execution of deployments
|
3
|
-
|
2
|
+
Runners are responsible for managing the execution of all deployments.
|
3
|
+
|
4
|
+
When creating a deployment using either `flow.serve` or the `serve` utility,
|
5
|
+
they also will poll for scheduled runs.
|
4
6
|
|
5
7
|
Example:
|
6
8
|
```python
|
@@ -30,6 +32,8 @@ Example:
|
|
30
32
|
|
31
33
|
"""
|
32
34
|
|
35
|
+
from __future__ import annotations
|
36
|
+
|
33
37
|
import asyncio
|
34
38
|
import datetime
|
35
39
|
import logging
|
@@ -61,13 +65,14 @@ import anyio
|
|
61
65
|
import anyio.abc
|
62
66
|
import pendulum
|
63
67
|
from cachetools import LRUCache
|
68
|
+
from typing_extensions import Self
|
64
69
|
|
65
70
|
from prefect._internal.concurrency.api import (
|
66
71
|
create_call,
|
67
72
|
from_async,
|
68
73
|
from_sync,
|
69
74
|
)
|
70
|
-
from prefect.client.orchestration import get_client
|
75
|
+
from prefect.client.orchestration import PrefectClient, get_client
|
71
76
|
from prefect.client.schemas.filters import (
|
72
77
|
FlowRunFilter,
|
73
78
|
FlowRunFilterId,
|
@@ -91,7 +96,7 @@ from prefect.events.related import tags_as_related_resources
|
|
91
96
|
from prefect.events.schemas.events import RelatedResource
|
92
97
|
from prefect.events.utilities import emit_event
|
93
98
|
from prefect.exceptions import Abort, ObjectNotFound
|
94
|
-
from prefect.flows import Flow, load_flow_from_flow_run
|
99
|
+
from prefect.flows import Flow, FlowStateHook, load_flow_from_flow_run
|
95
100
|
from prefect.logging.loggers import PrefectLogAdapter, flow_run_logger, get_logger
|
96
101
|
from prefect.runner.storage import RunnerStorage
|
97
102
|
from prefect.settings import (
|
@@ -123,10 +128,11 @@ from prefect.utilities.services import (
|
|
123
128
|
from prefect.utilities.slugify import slugify
|
124
129
|
|
125
130
|
if TYPE_CHECKING:
|
131
|
+
import concurrent.futures
|
132
|
+
|
126
133
|
from prefect.client.schemas.responses import DeploymentResponse
|
127
134
|
from prefect.client.types.flexible_schedule_list import FlexibleScheduleList
|
128
135
|
from prefect.deployments.runner import RunnerDeployment
|
129
|
-
|
130
136
|
__all__ = ["Runner"]
|
131
137
|
|
132
138
|
|
@@ -193,25 +199,25 @@ class Runner:
|
|
193
199
|
|
194
200
|
if name and ("/" in name or "%" in name):
|
195
201
|
raise ValueError("Runner name cannot contain '/' or '%'")
|
196
|
-
self.name = Path(name).stem if name is not None else f"runner-{uuid4()}"
|
197
|
-
self._logger = get_logger("runner")
|
198
|
-
|
199
|
-
self.started = False
|
200
|
-
self.stopping = False
|
201
|
-
self.pause_on_shutdown = pause_on_shutdown
|
202
|
-
self.limit = limit or settings.runner.process_limit
|
203
|
-
self.webserver = webserver
|
204
|
-
|
205
|
-
self.query_seconds = query_seconds or settings.runner.poll_frequency
|
206
|
-
self._prefetch_seconds = prefetch_seconds
|
207
|
-
self.heartbeat_seconds = (
|
202
|
+
self.name: str = Path(name).stem if name is not None else f"runner-{uuid4()}"
|
203
|
+
self._logger: "logging.Logger" = get_logger("runner")
|
204
|
+
|
205
|
+
self.started: bool = False
|
206
|
+
self.stopping: bool = False
|
207
|
+
self.pause_on_shutdown: bool = pause_on_shutdown
|
208
|
+
self.limit: int | None = limit or settings.runner.process_limit
|
209
|
+
self.webserver: bool = webserver
|
210
|
+
|
211
|
+
self.query_seconds: float = query_seconds or settings.runner.poll_frequency
|
212
|
+
self._prefetch_seconds: float = prefetch_seconds
|
213
|
+
self.heartbeat_seconds: float | None = (
|
208
214
|
heartbeat_seconds or settings.runner.heartbeat_frequency
|
209
215
|
)
|
210
216
|
if self.heartbeat_seconds is not None and self.heartbeat_seconds < 30:
|
211
217
|
raise ValueError("Heartbeat must be 30 seconds or greater.")
|
212
218
|
|
213
|
-
self._limiter:
|
214
|
-
self._client = get_client()
|
219
|
+
self._limiter: anyio.CapacityLimiter | None = None
|
220
|
+
self._client: PrefectClient = get_client()
|
215
221
|
self._submitting_flow_run_ids: set[UUID] = set()
|
216
222
|
self._cancelling_flow_run_ids: set[UUID] = set()
|
217
223
|
self._scheduled_task_scopes: set[UUID] = set()
|
@@ -221,8 +227,8 @@ class Runner:
|
|
221
227
|
self._tmp_dir: Path = (
|
222
228
|
Path(tempfile.gettempdir()) / "runner_storage" / str(uuid4())
|
223
229
|
)
|
224
|
-
self._storage_objs:
|
225
|
-
self._deployment_storage_map:
|
230
|
+
self._storage_objs: list[RunnerStorage] = []
|
231
|
+
self._deployment_storage_map: dict[UUID, RunnerStorage] = {}
|
226
232
|
|
227
233
|
self._loop: Optional[asyncio.AbstractEventLoop] = None
|
228
234
|
|
@@ -476,7 +482,7 @@ class Runner:
|
|
476
482
|
|
477
483
|
def execute_in_background(
|
478
484
|
self, func: Callable[..., Any], *args: Any, **kwargs: Any
|
479
|
-
):
|
485
|
+
) -> "concurrent.futures.Future[Any]":
|
480
486
|
"""
|
481
487
|
Executes a function in the background.
|
482
488
|
"""
|
@@ -485,7 +491,7 @@ class Runner:
|
|
485
491
|
|
486
492
|
return asyncio.run_coroutine_threadsafe(func(*args, **kwargs), self._loop)
|
487
493
|
|
488
|
-
async def cancel_all(self):
|
494
|
+
async def cancel_all(self) -> None:
|
489
495
|
runs_to_cancel = []
|
490
496
|
|
491
497
|
# done to avoid dictionary size changing during iteration
|
@@ -522,7 +528,7 @@ class Runner:
|
|
522
528
|
|
523
529
|
async def execute_flow_run(
|
524
530
|
self, flow_run_id: UUID, entrypoint: Optional[str] = None
|
525
|
-
):
|
531
|
+
) -> None:
|
526
532
|
"""
|
527
533
|
Executes a single flow run with the given ID.
|
528
534
|
|
@@ -774,7 +780,7 @@ class Runner:
|
|
774
780
|
if self.stopping:
|
775
781
|
return
|
776
782
|
runs_response = await self._get_scheduled_flow_runs()
|
777
|
-
self.last_polled = pendulum.now("UTC")
|
783
|
+
self.last_polled: pendulum.DateTime = pendulum.now("UTC")
|
778
784
|
return await self._submit_scheduled_flow_runs(flow_run_response=runs_response)
|
779
785
|
|
780
786
|
async def _check_for_cancelled_flow_runs(
|
@@ -1387,7 +1393,7 @@ class Runner:
|
|
1387
1393
|
|
1388
1394
|
await _run_hooks(hooks, flow_run, flow, state)
|
1389
1395
|
|
1390
|
-
async def __aenter__(self):
|
1396
|
+
async def __aenter__(self) -> Self:
|
1391
1397
|
self._logger.debug("Starting runner...")
|
1392
1398
|
self._client = get_client()
|
1393
1399
|
self._tmp_dir.mkdir(parents=True)
|
@@ -1409,7 +1415,7 @@ class Runner:
|
|
1409
1415
|
self.started = True
|
1410
1416
|
return self
|
1411
1417
|
|
1412
|
-
async def __aexit__(self, *exc_info):
|
1418
|
+
async def __aexit__(self, *exc_info: Any) -> None:
|
1413
1419
|
self._logger.debug("Stopping runner...")
|
1414
1420
|
if self.pause_on_shutdown:
|
1415
1421
|
await self._pause_schedules()
|
@@ -1427,7 +1433,7 @@ class Runner:
|
|
1427
1433
|
shutil.rmtree(str(self._tmp_dir))
|
1428
1434
|
del self._runs_task_group, self._loops_task_group
|
1429
1435
|
|
1430
|
-
def __repr__(self):
|
1436
|
+
def __repr__(self) -> str:
|
1431
1437
|
return f"Runner(name={self.name!r})"
|
1432
1438
|
|
1433
1439
|
|
@@ -1437,7 +1443,10 @@ if sys.platform == "win32":
|
|
1437
1443
|
|
1438
1444
|
|
1439
1445
|
async def _run_hooks(
|
1440
|
-
hooks:
|
1446
|
+
hooks: list[FlowStateHook[Any, Any]],
|
1447
|
+
flow_run: "FlowRun",
|
1448
|
+
flow: "Flow[..., Any]",
|
1449
|
+
state: State,
|
1441
1450
|
):
|
1442
1451
|
logger = flow_run_logger(flow_run, flow)
|
1443
1452
|
for hook in hooks:
|
prefect/runner/server.py
CHANGED
@@ -26,12 +26,14 @@ from prefect.utilities.asyncutils import run_coro_as_sync
|
|
26
26
|
from prefect.utilities.importtools import load_script_as_module
|
27
27
|
|
28
28
|
if TYPE_CHECKING:
|
29
|
+
import logging
|
30
|
+
|
29
31
|
from prefect.client.schemas.responses import DeploymentResponse
|
30
32
|
from prefect.runner import Runner
|
31
33
|
|
32
34
|
from pydantic import BaseModel
|
33
35
|
|
34
|
-
logger = get_logger("webserver")
|
36
|
+
logger: "logging.Logger" = get_logger("webserver")
|
35
37
|
|
36
38
|
RunnableEndpoint = Literal["deployment", "flow", "task"]
|
37
39
|
|
prefect/runner/storage.py
CHANGED
@@ -33,7 +33,7 @@ class RunnerStorage(Protocol):
|
|
33
33
|
remotely stored flow code.
|
34
34
|
"""
|
35
35
|
|
36
|
-
def set_base_path(self, path: Path):
|
36
|
+
def set_base_path(self, path: Path) -> None:
|
37
37
|
"""
|
38
38
|
Sets the base path to use when pulling contents from remote storage to
|
39
39
|
local storage.
|
@@ -55,7 +55,7 @@ class RunnerStorage(Protocol):
|
|
55
55
|
"""
|
56
56
|
...
|
57
57
|
|
58
|
-
async def pull_code(self):
|
58
|
+
async def pull_code(self) -> None:
|
59
59
|
"""
|
60
60
|
Pulls contents from remote storage to the local filesystem.
|
61
61
|
"""
|
@@ -150,7 +150,7 @@ class GitRepository:
|
|
150
150
|
def destination(self) -> Path:
|
151
151
|
return self._storage_base_path / self._name
|
152
152
|
|
153
|
-
def set_base_path(self, path: Path):
|
153
|
+
def set_base_path(self, path: Path) -> None:
|
154
154
|
self._storage_base_path = path
|
155
155
|
|
156
156
|
@property
|
@@ -221,7 +221,7 @@ class GitRepository:
|
|
221
221
|
except Exception:
|
222
222
|
return False
|
223
223
|
|
224
|
-
async def pull_code(self):
|
224
|
+
async def pull_code(self) -> None:
|
225
225
|
"""
|
226
226
|
Pulls the contents of the configured repository to the local filesystem.
|
227
227
|
"""
|
@@ -324,7 +324,7 @@ class GitRepository:
|
|
324
324
|
cwd=self.destination,
|
325
325
|
)
|
326
326
|
|
327
|
-
def __eq__(self, __value) -> bool:
|
327
|
+
def __eq__(self, __value: Any) -> bool:
|
328
328
|
if isinstance(__value, GitRepository):
|
329
329
|
return (
|
330
330
|
self._url == __value._url
|
@@ -339,7 +339,7 @@ class GitRepository:
|
|
339
339
|
f" branch={self._branch!r})"
|
340
340
|
)
|
341
341
|
|
342
|
-
def to_pull_step(self) ->
|
342
|
+
def to_pull_step(self) -> dict[str, Any]:
|
343
343
|
pull_step = {
|
344
344
|
"prefect.deployments.steps.git_clone": {
|
345
345
|
"repository": self._url,
|
@@ -466,7 +466,7 @@ class RemoteStorage:
|
|
466
466
|
|
467
467
|
return fsspec.filesystem(scheme, **settings_with_block_values)
|
468
468
|
|
469
|
-
def set_base_path(self, path: Path):
|
469
|
+
def set_base_path(self, path: Path) -> None:
|
470
470
|
self._storage_base_path = path
|
471
471
|
|
472
472
|
@property
|
@@ -492,7 +492,7 @@ class RemoteStorage:
|
|
492
492
|
_, netloc, urlpath, _, _ = urlsplit(self._url)
|
493
493
|
return Path(netloc) / Path(urlpath.lstrip("/"))
|
494
494
|
|
495
|
-
async def pull_code(self):
|
495
|
+
async def pull_code(self) -> None:
|
496
496
|
"""
|
497
497
|
Pulls contents from remote storage to the local filesystem.
|
498
498
|
"""
|
@@ -522,7 +522,7 @@ class RemoteStorage:
|
|
522
522
|
f" {self.destination!r}"
|
523
523
|
) from exc
|
524
524
|
|
525
|
-
def to_pull_step(self) -> dict:
|
525
|
+
def to_pull_step(self) -> dict[str, Any]:
|
526
526
|
"""
|
527
527
|
Returns a dictionary representation of the storage object that can be
|
528
528
|
used as a deployment pull step.
|
@@ -551,7 +551,7 @@ class RemoteStorage:
|
|
551
551
|
] = required_package
|
552
552
|
return step
|
553
553
|
|
554
|
-
def __eq__(self, __value) -> bool:
|
554
|
+
def __eq__(self, __value: Any) -> bool:
|
555
555
|
"""
|
556
556
|
Equality check for runner storage objects.
|
557
557
|
"""
|
@@ -590,7 +590,7 @@ class BlockStorageAdapter:
|
|
590
590
|
else str(uuid4())
|
591
591
|
)
|
592
592
|
|
593
|
-
def set_base_path(self, path: Path):
|
593
|
+
def set_base_path(self, path: Path) -> None:
|
594
594
|
self._storage_base_path = path
|
595
595
|
|
596
596
|
@property
|
@@ -601,12 +601,12 @@ class BlockStorageAdapter:
|
|
601
601
|
def destination(self) -> Path:
|
602
602
|
return self._storage_base_path / self._name
|
603
603
|
|
604
|
-
async def pull_code(self):
|
604
|
+
async def pull_code(self) -> None:
|
605
605
|
if not self.destination.exists():
|
606
606
|
self.destination.mkdir(parents=True, exist_ok=True)
|
607
607
|
await self._block.get_directory(local_path=str(self.destination))
|
608
608
|
|
609
|
-
def to_pull_step(self) -> dict:
|
609
|
+
def to_pull_step(self) -> dict[str, Any]:
|
610
610
|
# Give blocks the change to implement their own pull step
|
611
611
|
if hasattr(self._block, "get_pull_step"):
|
612
612
|
return self._block.get_pull_step()
|
@@ -623,7 +623,7 @@ class BlockStorageAdapter:
|
|
623
623
|
}
|
624
624
|
}
|
625
625
|
|
626
|
-
def __eq__(self, __value) -> bool:
|
626
|
+
def __eq__(self, __value: Any) -> bool:
|
627
627
|
if isinstance(__value, BlockStorageAdapter):
|
628
628
|
return self._block == __value._block
|
629
629
|
return False
|
@@ -658,19 +658,19 @@ class LocalStorage:
|
|
658
658
|
def destination(self) -> Path:
|
659
659
|
return self._path
|
660
660
|
|
661
|
-
def set_base_path(self, path: Path):
|
661
|
+
def set_base_path(self, path: Path) -> None:
|
662
662
|
self._storage_base_path = path
|
663
663
|
|
664
664
|
@property
|
665
665
|
def pull_interval(self) -> Optional[int]:
|
666
666
|
return self._pull_interval
|
667
667
|
|
668
|
-
async def pull_code(self):
|
668
|
+
async def pull_code(self) -> None:
|
669
669
|
# Local storage assumes the code already exists on the local filesystem
|
670
670
|
# and does not need to be pulled from a remote location
|
671
671
|
pass
|
672
672
|
|
673
|
-
def to_pull_step(self) -> dict:
|
673
|
+
def to_pull_step(self) -> dict[str, Any]:
|
674
674
|
"""
|
675
675
|
Returns a dictionary representation of the storage object that can be
|
676
676
|
used as a deployment pull step.
|
@@ -682,7 +682,7 @@ class LocalStorage:
|
|
682
682
|
}
|
683
683
|
return step
|
684
684
|
|
685
|
-
def __eq__(self, __value) -> bool:
|
685
|
+
def __eq__(self, __value: Any) -> bool:
|
686
686
|
if isinstance(__value, LocalStorage):
|
687
687
|
return self._path == __value._path
|
688
688
|
return False
|
prefect/runner/submit.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import asyncio
|
2
4
|
import inspect
|
3
5
|
import uuid
|
4
|
-
from typing import Any, Dict, List, Optional, Union, overload
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, overload
|
5
7
|
|
6
8
|
import anyio
|
7
9
|
import httpx
|
8
|
-
from typing_extensions import Literal
|
10
|
+
from typing_extensions import Literal, TypeAlias
|
9
11
|
|
10
12
|
from prefect.client.orchestration import get_client
|
11
13
|
from prefect.client.schemas.filters import FlowRunFilter, TaskRunFilter
|
@@ -22,12 +24,17 @@ from prefect.states import Pending
|
|
22
24
|
from prefect.tasks import Task
|
23
25
|
from prefect.utilities.asyncutils import sync_compatible
|
24
26
|
|
25
|
-
|
27
|
+
if TYPE_CHECKING:
|
28
|
+
import logging
|
29
|
+
|
30
|
+
logger: "logging.Logger" = get_logger("webserver")
|
31
|
+
|
32
|
+
FlowOrTask: TypeAlias = Union[Flow[Any, Any], Task[Any, Any]]
|
26
33
|
|
27
34
|
|
28
35
|
async def _submit_flow_to_runner(
|
29
|
-
flow: Flow,
|
30
|
-
parameters:
|
36
|
+
flow: Flow[Any, Any],
|
37
|
+
parameters: dict[str, Any],
|
31
38
|
retry_failed_submissions: bool = True,
|
32
39
|
) -> FlowRun:
|
33
40
|
"""
|
@@ -91,7 +98,7 @@ async def _submit_flow_to_runner(
|
|
91
98
|
|
92
99
|
@overload
|
93
100
|
def submit_to_runner(
|
94
|
-
prefect_callable: Union[Flow, Task],
|
101
|
+
prefect_callable: Union[Flow[Any, Any], Task[Any, Any]],
|
95
102
|
parameters: Dict[str, Any],
|
96
103
|
retry_failed_submissions: bool = True,
|
97
104
|
) -> FlowRun:
|
@@ -100,19 +107,19 @@ def submit_to_runner(
|
|
100
107
|
|
101
108
|
@overload
|
102
109
|
def submit_to_runner(
|
103
|
-
prefect_callable: Union[Flow, Task],
|
104
|
-
parameters:
|
110
|
+
prefect_callable: Union[Flow[Any, Any], Task[Any, Any]],
|
111
|
+
parameters: list[dict[str, Any]],
|
105
112
|
retry_failed_submissions: bool = True,
|
106
|
-
) ->
|
113
|
+
) -> list[FlowRun]:
|
107
114
|
...
|
108
115
|
|
109
116
|
|
110
117
|
@sync_compatible
|
111
118
|
async def submit_to_runner(
|
112
|
-
prefect_callable:
|
113
|
-
parameters: Optional[Union[
|
119
|
+
prefect_callable: FlowOrTask,
|
120
|
+
parameters: Optional[Union[dict[str, Any], list[dict[str, Any]]]] = None,
|
114
121
|
retry_failed_submissions: bool = True,
|
115
|
-
) -> Union[FlowRun,
|
122
|
+
) -> Union[FlowRun, list[FlowRun]]:
|
116
123
|
"""
|
117
124
|
Submit a callable in the background via the runner webserver one or more times.
|
118
125
|
|
prefect/runtime/deployment.py
CHANGED
@@ -25,8 +25,10 @@ Available attributes:
|
|
25
25
|
object or those directly provided via API for this run
|
26
26
|
"""
|
27
27
|
|
28
|
+
from __future__ import annotations
|
29
|
+
|
28
30
|
import os
|
29
|
-
from typing import Any,
|
31
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Optional
|
30
32
|
|
31
33
|
from prefect._internal.concurrency.api import create_call, from_sync
|
32
34
|
from prefect.client.orchestration import get_client
|
@@ -34,12 +36,17 @@ from prefect.context import FlowRunContext
|
|
34
36
|
|
35
37
|
from .flow_run import _get_flow_run
|
36
38
|
|
39
|
+
if TYPE_CHECKING:
|
40
|
+
from prefect.client.schemas.responses import DeploymentResponse
|
41
|
+
|
37
42
|
__all__ = ["id", "flow_run_id", "name", "parameters", "version"]
|
38
43
|
|
39
|
-
CACHED_DEPLOYMENT = {}
|
44
|
+
CACHED_DEPLOYMENT: dict[str, "DeploymentResponse"] = {}
|
40
45
|
|
41
46
|
|
42
|
-
type_cast
|
47
|
+
type_cast: dict[
|
48
|
+
type[bool] | type[int] | type[float] | type[str] | type[None], Callable[[Any], Any]
|
49
|
+
] = {
|
43
50
|
bool: lambda x: x.lower() == "true",
|
44
51
|
int: int,
|
45
52
|
float: float,
|
@@ -88,7 +95,7 @@ def __dir__() -> List[str]:
|
|
88
95
|
return sorted(__all__)
|
89
96
|
|
90
97
|
|
91
|
-
async def _get_deployment(deployment_id):
|
98
|
+
async def _get_deployment(deployment_id: str) -> "DeploymentResponse":
|
92
99
|
# deployments won't change between calls so let's avoid the lifecycle of a client
|
93
100
|
if CACHED_DEPLOYMENT.get(deployment_id):
|
94
101
|
return CACHED_DEPLOYMENT[deployment_id]
|
@@ -115,7 +122,7 @@ def get_id() -> Optional[str]:
|
|
115
122
|
return str(deployment_id)
|
116
123
|
|
117
124
|
|
118
|
-
def get_parameters() ->
|
125
|
+
def get_parameters() -> dict[str, Any]:
|
119
126
|
run_id = get_flow_run_id()
|
120
127
|
if run_id is None:
|
121
128
|
return {}
|
@@ -126,7 +133,7 @@ def get_parameters() -> Dict:
|
|
126
133
|
return flow_run.parameters or {}
|
127
134
|
|
128
135
|
|
129
|
-
def get_name() -> Optional[
|
136
|
+
def get_name() -> Optional[str]:
|
130
137
|
dep_id = get_id()
|
131
138
|
|
132
139
|
if dep_id is None:
|
@@ -138,7 +145,7 @@ def get_name() -> Optional[Dict]:
|
|
138
145
|
return deployment.name
|
139
146
|
|
140
147
|
|
141
|
-
def get_version() -> Optional[
|
148
|
+
def get_version() -> Optional[str]:
|
142
149
|
dep_id = get_id()
|
143
150
|
|
144
151
|
if dep_id is None:
|
@@ -154,7 +161,7 @@ def get_flow_run_id() -> Optional[str]:
|
|
154
161
|
return os.getenv("PREFECT__FLOW_RUN_ID")
|
155
162
|
|
156
163
|
|
157
|
-
FIELDS = {
|
164
|
+
FIELDS: dict[str, Callable[[], Any]] = {
|
158
165
|
"id": get_id,
|
159
166
|
"flow_run_id": get_flow_run_id,
|
160
167
|
"parameters": get_parameters,
|
prefect/runtime/flow_run.py
CHANGED
@@ -20,8 +20,10 @@ Available attributes:
|
|
20
20
|
- `run_count`: the number of times this flow run has been run
|
21
21
|
"""
|
22
22
|
|
23
|
+
from __future__ import annotations
|
24
|
+
|
23
25
|
import os
|
24
|
-
from typing import Any, Dict, List, Optional
|
26
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional
|
25
27
|
|
26
28
|
import pendulum
|
27
29
|
|
@@ -30,6 +32,9 @@ from prefect.client.orchestration import get_client
|
|
30
32
|
from prefect.context import FlowRunContext, TaskRunContext
|
31
33
|
from prefect.settings import PREFECT_API_URL, PREFECT_UI_URL
|
32
34
|
|
35
|
+
if TYPE_CHECKING:
|
36
|
+
from prefect.client.schemas.objects import Flow, FlowRun, TaskRun
|
37
|
+
|
33
38
|
__all__ = [
|
34
39
|
"id",
|
35
40
|
"tags",
|
@@ -56,7 +61,15 @@ def _pendulum_parse(dt: str) -> pendulum.DateTime:
|
|
56
61
|
return pendulum.parse(dt, tz=None, strict=False).set(tz="UTC")
|
57
62
|
|
58
63
|
|
59
|
-
type_cast
|
64
|
+
type_cast: dict[
|
65
|
+
type[bool]
|
66
|
+
| type[int]
|
67
|
+
| type[float]
|
68
|
+
| type[str]
|
69
|
+
| type[None]
|
70
|
+
| type[pendulum.DateTime],
|
71
|
+
Callable[[Any], Any],
|
72
|
+
] = {
|
60
73
|
bool: lambda x: x.lower() == "true",
|
61
74
|
int: int,
|
62
75
|
float: float,
|
@@ -106,17 +119,17 @@ def __dir__() -> List[str]:
|
|
106
119
|
return sorted(__all__)
|
107
120
|
|
108
121
|
|
109
|
-
async def _get_flow_run(flow_run_id):
|
122
|
+
async def _get_flow_run(flow_run_id: str) -> "FlowRun":
|
110
123
|
async with get_client() as client:
|
111
124
|
return await client.read_flow_run(flow_run_id)
|
112
125
|
|
113
126
|
|
114
|
-
async def _get_task_run(task_run_id):
|
127
|
+
async def _get_task_run(task_run_id: str) -> "TaskRun":
|
115
128
|
async with get_client() as client:
|
116
129
|
return await client.read_task_run(task_run_id)
|
117
130
|
|
118
131
|
|
119
|
-
async def _get_flow_from_run(flow_run_id):
|
132
|
+
async def _get_flow_from_run(flow_run_id: str) -> "Flow":
|
120
133
|
async with get_client() as client:
|
121
134
|
flow_run = await client.read_flow_run(flow_run_id)
|
122
135
|
return await client.read_flow(flow_run.flow_id)
|
@@ -323,7 +336,7 @@ def get_job_variables() -> Optional[Dict[str, Any]]:
|
|
323
336
|
return flow_run_ctx.flow_run.job_variables if flow_run_ctx else None
|
324
337
|
|
325
338
|
|
326
|
-
FIELDS = {
|
339
|
+
FIELDS: dict[str, Callable[[], Any]] = {
|
327
340
|
"id": get_id,
|
328
341
|
"tags": get_tags,
|
329
342
|
"scheduled_start_time": get_scheduled_start_time,
|
prefect/runtime/task_run.py
CHANGED
@@ -15,15 +15,19 @@ Available attributes:
|
|
15
15
|
- `task_name`: the name of the task
|
16
16
|
"""
|
17
17
|
|
18
|
+
from __future__ import annotations
|
19
|
+
|
18
20
|
import os
|
19
|
-
from typing import Any, Dict, List, Optional
|
21
|
+
from typing import Any, Callable, Dict, List, Optional
|
20
22
|
|
21
23
|
from prefect.context import TaskRunContext
|
22
24
|
|
23
25
|
__all__ = ["id", "tags", "name", "parameters", "run_count", "task_name"]
|
24
26
|
|
25
27
|
|
26
|
-
type_cast
|
28
|
+
type_cast: dict[
|
29
|
+
type[bool] | type[int] | type[float] | type[str] | type[None], Callable[[Any], Any]
|
30
|
+
] = {
|
27
31
|
bool: lambda x: x.lower() == "true",
|
28
32
|
int: int,
|
29
33
|
float: float,
|
@@ -118,7 +122,7 @@ def get_parameters() -> Dict[str, Any]:
|
|
118
122
|
return {}
|
119
123
|
|
120
124
|
|
121
|
-
FIELDS = {
|
125
|
+
FIELDS: dict[str, Callable[[], Any]] = {
|
122
126
|
"id": get_id,
|
123
127
|
"tags": get_tags,
|
124
128
|
"name": get_name,
|
prefect/settings/base.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import inspect
|
2
4
|
from functools import partial
|
3
5
|
from typing import Any, Dict, Tuple, Type
|
@@ -43,13 +45,17 @@ class PrefectBaseSettings(BaseSettings):
|
|
43
45
|
|
44
46
|
See https://docs.pydantic.dev/latest/concepts/pydantic_settings/#customise-settings-sources
|
45
47
|
"""
|
46
|
-
env_filter = set()
|
48
|
+
env_filter: set[str] = set()
|
47
49
|
for field_name, field in settings_cls.model_fields.items():
|
48
50
|
if field.validation_alias is not None and isinstance(
|
49
51
|
field.validation_alias, AliasChoices
|
50
52
|
):
|
51
53
|
for alias in field.validation_alias.choices:
|
52
|
-
if
|
54
|
+
if (
|
55
|
+
isinstance(alias, AliasPath)
|
56
|
+
and len(alias.path) > 0
|
57
|
+
and isinstance(alias.path[0], str)
|
58
|
+
):
|
53
59
|
env_filter.add(alias.path[0])
|
54
60
|
env_filter.add(field_name)
|
55
61
|
return (
|
@@ -88,13 +94,12 @@ class PrefectBaseSettings(BaseSettings):
|
|
88
94
|
include_secrets: bool = True,
|
89
95
|
) -> Dict[str, str]:
|
90
96
|
"""Convert the settings object to a dictionary of environment variables."""
|
91
|
-
|
92
97
|
env: Dict[str, Any] = self.model_dump(
|
93
98
|
exclude_unset=exclude_unset,
|
94
99
|
mode="json",
|
95
100
|
context={"include_secrets": include_secrets},
|
96
101
|
)
|
97
|
-
env_variables = {}
|
102
|
+
env_variables: dict[str, str] = {}
|
98
103
|
for key in self.model_fields.keys():
|
99
104
|
if isinstance(child_settings := getattr(self, key), PrefectBaseSettings):
|
100
105
|
child_env = child_settings.to_environment_variables(
|
@@ -175,7 +180,7 @@ def _add_environment_variables(
|
|
175
180
|
schema: Dict[str, Any], model: Type[PrefectBaseSettings]
|
176
181
|
) -> None:
|
177
182
|
for property in schema["properties"]:
|
178
|
-
env_vars = []
|
183
|
+
env_vars: list[str] = []
|
179
184
|
schema["properties"][property]["supported_environment_variables"] = env_vars
|
180
185
|
field = model.model_fields[property]
|
181
186
|
if inspect.isclass(field.annotation) and issubclass(
|
@@ -191,7 +196,7 @@ def _add_environment_variables(
|
|
191
196
|
env_vars.append(f"{model.model_config.get('env_prefix')}{property.upper()}")
|
192
197
|
|
193
198
|
|
194
|
-
def
|
199
|
+
def build_settings_config(
|
195
200
|
path: Tuple[str, ...] = tuple(), frozen: bool = False
|
196
201
|
) -> PrefectSettingsConfigDict:
|
197
202
|
env_prefix = f"PREFECT_{'_'.join(path).upper()}_" if path else "PREFECT_"
|
@@ -207,7 +212,12 @@ def _build_settings_config(
|
|
207
212
|
)
|
208
213
|
|
209
214
|
|
210
|
-
|
215
|
+
_build_settings_config = build_settings_config # noqa # TODO: remove once all usage updated
|
216
|
+
|
217
|
+
|
218
|
+
def _to_environment_variable_value(
|
219
|
+
value: list[object] | set[object] | tuple[object] | Any,
|
220
|
+
) -> str:
|
211
221
|
if isinstance(value, (list, set, tuple)):
|
212
222
|
return ",".join(str(v) for v in value)
|
213
223
|
return str(value)
|