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.
Files changed (133) hide show
  1. prefect/_experimental/sla/__init__.py +0 -0
  2. prefect/_experimental/sla/client.py +92 -0
  3. prefect/_experimental/sla/objects.py +61 -0
  4. prefect/_internal/concurrency/services.py +2 -2
  5. prefect/_internal/concurrency/threads.py +6 -0
  6. prefect/_internal/retries.py +6 -3
  7. prefect/_internal/schemas/validators.py +6 -4
  8. prefect/_version.py +3 -3
  9. prefect/artifacts.py +4 -1
  10. prefect/automations.py +236 -30
  11. prefect/blocks/__init__.py +3 -3
  12. prefect/blocks/abstract.py +57 -31
  13. prefect/blocks/core.py +181 -82
  14. prefect/blocks/notifications.py +134 -73
  15. prefect/blocks/redis.py +13 -9
  16. prefect/blocks/system.py +24 -11
  17. prefect/blocks/webhook.py +7 -5
  18. prefect/cache_policies.py +23 -22
  19. prefect/client/orchestration/__init__.py +103 -2006
  20. prefect/client/orchestration/_automations/__init__.py +0 -0
  21. prefect/client/orchestration/_automations/client.py +329 -0
  22. prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
  23. prefect/client/orchestration/_blocks_documents/client.py +334 -0
  24. prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
  25. prefect/client/orchestration/_blocks_schemas/client.py +200 -0
  26. prefect/client/orchestration/_blocks_types/__init__.py +0 -0
  27. prefect/client/orchestration/_blocks_types/client.py +380 -0
  28. prefect/client/orchestration/_deployments/__init__.py +0 -0
  29. prefect/client/orchestration/_deployments/client.py +1128 -0
  30. prefect/client/orchestration/_flow_runs/__init__.py +0 -0
  31. prefect/client/orchestration/_flow_runs/client.py +903 -0
  32. prefect/client/orchestration/_flows/__init__.py +0 -0
  33. prefect/client/orchestration/_flows/client.py +343 -0
  34. prefect/client/orchestration/_logs/client.py +16 -14
  35. prefect/client/schemas/__init__.py +68 -28
  36. prefect/client/schemas/objects.py +5 -5
  37. prefect/client/utilities.py +3 -3
  38. prefect/context.py +15 -1
  39. prefect/deployments/base.py +13 -4
  40. prefect/deployments/flow_runs.py +5 -1
  41. prefect/deployments/runner.py +37 -1
  42. prefect/deployments/steps/core.py +1 -1
  43. prefect/deployments/steps/pull.py +8 -3
  44. prefect/deployments/steps/utility.py +2 -2
  45. prefect/docker/docker_image.py +13 -9
  46. prefect/engine.py +33 -11
  47. prefect/events/cli/automations.py +4 -4
  48. prefect/events/clients.py +17 -14
  49. prefect/events/schemas/automations.py +12 -8
  50. prefect/events/schemas/events.py +5 -1
  51. prefect/events/worker.py +1 -1
  52. prefect/filesystems.py +7 -3
  53. prefect/flow_engine.py +64 -47
  54. prefect/flows.py +128 -74
  55. prefect/futures.py +14 -7
  56. prefect/infrastructure/provisioners/__init__.py +2 -0
  57. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  58. prefect/infrastructure/provisioners/coiled.py +249 -0
  59. prefect/infrastructure/provisioners/container_instance.py +4 -3
  60. prefect/infrastructure/provisioners/ecs.py +55 -43
  61. prefect/infrastructure/provisioners/modal.py +5 -4
  62. prefect/input/actions.py +5 -1
  63. prefect/input/run_input.py +157 -43
  64. prefect/logging/configuration.py +3 -3
  65. prefect/logging/filters.py +2 -2
  66. prefect/logging/formatters.py +15 -11
  67. prefect/logging/handlers.py +24 -14
  68. prefect/logging/highlighters.py +5 -5
  69. prefect/logging/loggers.py +28 -18
  70. prefect/logging/logging.yml +1 -1
  71. prefect/main.py +3 -1
  72. prefect/results.py +166 -86
  73. prefect/runner/runner.py +38 -29
  74. prefect/runner/server.py +3 -1
  75. prefect/runner/storage.py +18 -18
  76. prefect/runner/submit.py +19 -12
  77. prefect/runtime/deployment.py +15 -8
  78. prefect/runtime/flow_run.py +19 -6
  79. prefect/runtime/task_run.py +7 -3
  80. prefect/settings/base.py +17 -7
  81. prefect/settings/legacy.py +4 -4
  82. prefect/settings/models/api.py +4 -3
  83. prefect/settings/models/cli.py +4 -3
  84. prefect/settings/models/client.py +7 -4
  85. prefect/settings/models/cloud.py +9 -3
  86. prefect/settings/models/deployments.py +4 -3
  87. prefect/settings/models/experiments.py +4 -8
  88. prefect/settings/models/flows.py +4 -3
  89. prefect/settings/models/internal.py +4 -3
  90. prefect/settings/models/logging.py +8 -6
  91. prefect/settings/models/results.py +4 -3
  92. prefect/settings/models/root.py +11 -16
  93. prefect/settings/models/runner.py +8 -5
  94. prefect/settings/models/server/api.py +6 -3
  95. prefect/settings/models/server/database.py +120 -25
  96. prefect/settings/models/server/deployments.py +4 -3
  97. prefect/settings/models/server/ephemeral.py +7 -4
  98. prefect/settings/models/server/events.py +6 -3
  99. prefect/settings/models/server/flow_run_graph.py +4 -3
  100. prefect/settings/models/server/root.py +4 -3
  101. prefect/settings/models/server/services.py +15 -12
  102. prefect/settings/models/server/tasks.py +7 -4
  103. prefect/settings/models/server/ui.py +4 -3
  104. prefect/settings/models/tasks.py +10 -5
  105. prefect/settings/models/testing.py +4 -3
  106. prefect/settings/models/worker.py +7 -4
  107. prefect/settings/profiles.py +13 -12
  108. prefect/settings/sources.py +20 -19
  109. prefect/states.py +74 -51
  110. prefect/task_engine.py +43 -33
  111. prefect/task_runners.py +85 -72
  112. prefect/task_runs.py +20 -11
  113. prefect/task_worker.py +14 -9
  114. prefect/tasks.py +36 -28
  115. prefect/telemetry/bootstrap.py +13 -9
  116. prefect/telemetry/run_telemetry.py +15 -13
  117. prefect/telemetry/services.py +4 -0
  118. prefect/transactions.py +3 -3
  119. prefect/types/__init__.py +3 -1
  120. prefect/utilities/_deprecated.py +38 -0
  121. prefect/utilities/engine.py +11 -4
  122. prefect/utilities/filesystem.py +2 -2
  123. prefect/utilities/generics.py +1 -1
  124. prefect/utilities/pydantic.py +21 -36
  125. prefect/utilities/templating.py +25 -1
  126. prefect/workers/base.py +58 -33
  127. prefect/workers/process.py +20 -15
  128. prefect/workers/server.py +4 -5
  129. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
  130. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
  131. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
  132. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
  133. {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 created and managed by
3
- either `flow.serve` or the `serve` utility.
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: Optional[anyio.CapacityLimiter] = None
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: List[RunnerStorage] = []
225
- self._deployment_storage_map: Dict[UUID, RunnerStorage] = {}
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: List[Callable[[Flow, "FlowRun", State], None]], flow_run, flow, state
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) -> Dict:
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
- logger = get_logger("webserver")
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: Dict[str, Any],
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: List[Dict[str, Any]],
110
+ prefect_callable: Union[Flow[Any, Any], Task[Any, Any]],
111
+ parameters: list[dict[str, Any]],
105
112
  retry_failed_submissions: bool = True,
106
- ) -> List[FlowRun]:
113
+ ) -> list[FlowRun]:
107
114
  ...
108
115
 
109
116
 
110
117
  @sync_compatible
111
118
  async def submit_to_runner(
112
- prefect_callable: Union[Flow, Task],
113
- parameters: Optional[Union[Dict[str, Any], List[Dict[str, Any]]]] = None,
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, List[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
 
@@ -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, Dict, List, Optional
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() -> Dict:
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[Dict]:
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[Dict]:
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,
@@ -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,
@@ -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 isinstance(alias, AliasPath) and len(alias.path) > 0:
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 _build_settings_config(
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
- def _to_environment_variable_value(value: Any) -> str:
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)