prefect-client 3.1.12__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 (105) hide show
  1. prefect/_experimental/sla/client.py +53 -27
  2. prefect/_experimental/sla/objects.py +10 -2
  3. prefect/_internal/concurrency/services.py +2 -2
  4. prefect/_internal/concurrency/threads.py +6 -0
  5. prefect/_internal/retries.py +6 -3
  6. prefect/_internal/schemas/validators.py +6 -4
  7. prefect/_version.py +3 -3
  8. prefect/artifacts.py +4 -1
  9. prefect/automations.py +1 -1
  10. prefect/blocks/abstract.py +5 -2
  11. prefect/blocks/notifications.py +1 -0
  12. prefect/cache_policies.py +20 -20
  13. prefect/client/utilities.py +3 -3
  14. prefect/deployments/base.py +7 -4
  15. prefect/deployments/flow_runs.py +5 -1
  16. prefect/deployments/runner.py +6 -11
  17. prefect/deployments/steps/core.py +1 -1
  18. prefect/deployments/steps/pull.py +8 -3
  19. prefect/deployments/steps/utility.py +2 -2
  20. prefect/docker/docker_image.py +13 -9
  21. prefect/engine.py +19 -10
  22. prefect/events/cli/automations.py +4 -4
  23. prefect/events/clients.py +17 -14
  24. prefect/events/schemas/automations.py +12 -8
  25. prefect/events/schemas/events.py +5 -1
  26. prefect/events/worker.py +1 -1
  27. prefect/filesystems.py +1 -1
  28. prefect/flow_engine.py +17 -9
  29. prefect/flows.py +118 -73
  30. prefect/futures.py +14 -7
  31. prefect/infrastructure/provisioners/__init__.py +2 -0
  32. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  33. prefect/infrastructure/provisioners/coiled.py +249 -0
  34. prefect/infrastructure/provisioners/container_instance.py +4 -3
  35. prefect/infrastructure/provisioners/ecs.py +55 -43
  36. prefect/infrastructure/provisioners/modal.py +5 -4
  37. prefect/input/actions.py +5 -1
  38. prefect/input/run_input.py +157 -43
  39. prefect/logging/configuration.py +3 -3
  40. prefect/logging/filters.py +2 -2
  41. prefect/logging/formatters.py +15 -11
  42. prefect/logging/handlers.py +24 -14
  43. prefect/logging/highlighters.py +5 -5
  44. prefect/logging/loggers.py +28 -18
  45. prefect/main.py +3 -1
  46. prefect/results.py +166 -86
  47. prefect/runner/runner.py +34 -27
  48. prefect/runner/server.py +3 -1
  49. prefect/runner/storage.py +18 -18
  50. prefect/runner/submit.py +19 -12
  51. prefect/runtime/deployment.py +15 -8
  52. prefect/runtime/flow_run.py +19 -6
  53. prefect/runtime/task_run.py +7 -3
  54. prefect/settings/base.py +17 -7
  55. prefect/settings/legacy.py +4 -4
  56. prefect/settings/models/api.py +4 -3
  57. prefect/settings/models/cli.py +4 -3
  58. prefect/settings/models/client.py +7 -4
  59. prefect/settings/models/cloud.py +4 -3
  60. prefect/settings/models/deployments.py +4 -3
  61. prefect/settings/models/experiments.py +4 -3
  62. prefect/settings/models/flows.py +4 -3
  63. prefect/settings/models/internal.py +4 -3
  64. prefect/settings/models/logging.py +8 -6
  65. prefect/settings/models/results.py +4 -3
  66. prefect/settings/models/root.py +11 -16
  67. prefect/settings/models/runner.py +8 -5
  68. prefect/settings/models/server/api.py +6 -3
  69. prefect/settings/models/server/database.py +120 -25
  70. prefect/settings/models/server/deployments.py +4 -3
  71. prefect/settings/models/server/ephemeral.py +7 -4
  72. prefect/settings/models/server/events.py +6 -3
  73. prefect/settings/models/server/flow_run_graph.py +4 -3
  74. prefect/settings/models/server/root.py +4 -3
  75. prefect/settings/models/server/services.py +15 -12
  76. prefect/settings/models/server/tasks.py +7 -4
  77. prefect/settings/models/server/ui.py +4 -3
  78. prefect/settings/models/tasks.py +10 -5
  79. prefect/settings/models/testing.py +4 -3
  80. prefect/settings/models/worker.py +7 -4
  81. prefect/settings/profiles.py +13 -12
  82. prefect/settings/sources.py +20 -19
  83. prefect/states.py +17 -13
  84. prefect/task_engine.py +43 -33
  85. prefect/task_runners.py +35 -23
  86. prefect/task_runs.py +20 -11
  87. prefect/task_worker.py +12 -7
  88. prefect/tasks.py +30 -24
  89. prefect/telemetry/bootstrap.py +4 -1
  90. prefect/telemetry/run_telemetry.py +15 -13
  91. prefect/transactions.py +3 -3
  92. prefect/types/__init__.py +3 -1
  93. prefect/utilities/_deprecated.py +38 -0
  94. prefect/utilities/engine.py +11 -4
  95. prefect/utilities/filesystem.py +2 -2
  96. prefect/utilities/generics.py +1 -1
  97. prefect/utilities/pydantic.py +21 -36
  98. prefect/workers/base.py +52 -30
  99. prefect/workers/process.py +20 -15
  100. prefect/workers/server.py +4 -5
  101. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/METADATA +2 -2
  102. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/RECORD +105 -103
  103. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
  104. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
  105. {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
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
- engine_logger = get_logger("engine")
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:
@@ -37,11 +44,11 @@ if __name__ == "__main__":
37
44
  run_flow,
38
45
  )
39
46
 
40
- flow_run = load_flow_run(flow_run_id=flow_run_id)
41
- run_logger = flow_run_logger(flow_run=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)
42
49
 
43
50
  try:
44
- flow = load_flow(flow_run)
51
+ flow: "Flow[..., Any]" = load_flow(flow_run)
45
52
  except Exception:
46
53
  run_logger.error(
47
54
  "Unexpected exception encountered when trying to load flow",
@@ -55,15 +62,17 @@ if __name__ == "__main__":
55
62
  else:
56
63
  run_flow(flow, flow_run=flow_run, error_logger=run_logger)
57
64
 
58
- except Abort as exc:
65
+ except Abort as abort_signal:
66
+ abort_signal: Abort
59
67
  engine_logger.info(
60
68
  f"Engine execution of flow run '{flow_run_id}' aborted by orchestrator:"
61
- f" {exc}"
69
+ f" {abort_signal}"
62
70
  )
63
71
  exit(0)
64
- except Pause as exc:
72
+ except Pause as pause_signal:
73
+ pause_signal: Pause
65
74
  engine_logger.info(
66
- f"Engine execution of flow run '{flow_run_id}' is paused: {exc}"
75
+ f"Engine execution of flow run '{flow_run_id}' is paused: {pause_signal}"
67
76
  )
68
77
  exit(0)
69
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
- logger = get_logger(__name__)
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: Tuple
254
- kwargs: Dict[str, Any]
255
- events: List[Event]
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[List["AssertingPassthroughEventsClient"]] = []
437
+ all: ClassVar[list["AssertingPassthroughEventsClient"]] = []
435
438
 
436
- args: Tuple
437
- kwargs: Dict[str, Any]
438
- events: List[Event]
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) -> List[Event]:
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(..., description="The name of this automation")
408
- description: str = Field("", description="A longer description of this automation")
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(True, description="Whether this automation will be evaluated")
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(..., description="The ID of this automation")
445
+ id: UUID = Field(default=..., description="The ID of this automation")
@@ -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
- logger = get_logger(__name__)
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
@@ -281,7 +281,7 @@ class RemoteFileSystem(WritableFileSystem, WritableDeploymentStorage):
281
281
  _filesystem: fsspec.AbstractFileSystem = None
282
282
 
283
283
  @field_validator("basepath")
284
- def check_basepath(cls, value):
284
+ def check_basepath(cls, value: str) -> str:
285
285
  return validate_basepath(value)
286
286
 
287
287
  def _resolve_path(self, path: str) -> str:
prefect/flow_engine.py CHANGED
@@ -146,7 +146,7 @@ class BaseFlowRunEngine(Generic[P, R]):
146
146
  _flow_run_name_set: bool = False
147
147
  _telemetry: RunTelemetry = field(default_factory=RunTelemetry)
148
148
 
149
- def __post_init__(self):
149
+ def __post_init__(self) -> None:
150
150
  if self.flow is None and self.flow_run_id is None:
151
151
  raise ValueError("Either a flow or a flow_run_id must be provided.")
152
152
 
@@ -167,7 +167,7 @@ class BaseFlowRunEngine(Generic[P, R]):
167
167
  return False # TODO: handle this differently?
168
168
  return getattr(self, "flow_run").state.is_pending()
169
169
 
170
- def cancel_all_tasks(self):
170
+ def cancel_all_tasks(self) -> None:
171
171
  if hasattr(self.flow.task_runner, "cancel_all"):
172
172
  self.flow.task_runner.cancel_all() # type: ignore
173
173
 
@@ -208,6 +208,8 @@ class BaseFlowRunEngine(Generic[P, R]):
208
208
  @dataclass
209
209
  class FlowRunEngine(BaseFlowRunEngine[P, R]):
210
210
  _client: Optional[SyncPrefectClient] = None
211
+ flow_run: FlowRun | None = None
212
+ parameters: dict[str, Any] | None = None
211
213
 
212
214
  @property
213
215
  def client(self) -> SyncPrefectClient:
@@ -502,7 +504,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
502
504
  tags=TagsContext.get().current_tags,
503
505
  )
504
506
 
505
- def call_hooks(self, state: Optional[State] = None):
507
+ def call_hooks(self, state: Optional[State] = None) -> None:
506
508
  if state is None:
507
509
  state = self.state
508
510
  flow = self.flow
@@ -600,7 +602,9 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
600
602
 
601
603
  # set the logger to the flow run logger
602
604
 
603
- self.logger = flow_run_logger(flow_run=self.flow_run, flow=self.flow)
605
+ self.logger: "logging.Logger" = flow_run_logger(
606
+ flow_run=self.flow_run, flow=self.flow
607
+ ) # type: ignore
604
608
 
605
609
  # update the flow run name if necessary
606
610
  if not self._flow_run_name_set and self.flow.flow_run_name:
@@ -768,6 +772,8 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
768
772
  """
769
773
 
770
774
  _client: Optional[PrefectClient] = None
775
+ parameters: dict[str, Any] | None = None
776
+ flow_run: FlowRun | None = None
771
777
 
772
778
  @property
773
779
  def client(self) -> PrefectClient:
@@ -1061,7 +1067,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1061
1067
  tags=TagsContext.get().current_tags,
1062
1068
  )
1063
1069
 
1064
- async def call_hooks(self, state: Optional[State] = None):
1070
+ async def call_hooks(self, state: Optional[State] = None) -> None:
1065
1071
  if state is None:
1066
1072
  state = self.state
1067
1073
  flow = self.flow
@@ -1158,7 +1164,9 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1158
1164
  stack.enter_context(ConcurrencyContext())
1159
1165
 
1160
1166
  # set the logger to the flow run logger
1161
- self.logger = flow_run_logger(flow_run=self.flow_run, flow=self.flow)
1167
+ self.logger: "logging.Logger" = flow_run_logger(
1168
+ flow_run=self.flow_run, flow=self.flow
1169
+ )
1162
1170
 
1163
1171
  # update the flow run name if necessary
1164
1172
 
@@ -1320,7 +1328,7 @@ def run_flow_sync(
1320
1328
  flow: Flow[P, R],
1321
1329
  flow_run: Optional[FlowRun] = None,
1322
1330
  parameters: Optional[Dict[str, Any]] = None,
1323
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1331
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1324
1332
  return_type: Literal["state", "result"] = "result",
1325
1333
  ) -> Union[R, State, None]:
1326
1334
  engine = FlowRunEngine[P, R](
@@ -1342,7 +1350,7 @@ async def run_flow_async(
1342
1350
  flow: Flow[P, R],
1343
1351
  flow_run: Optional[FlowRun] = None,
1344
1352
  parameters: Optional[Dict[str, Any]] = None,
1345
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1353
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1346
1354
  return_type: Literal["state", "result"] = "result",
1347
1355
  ) -> Union[R, State, None]:
1348
1356
  engine = AsyncFlowRunEngine[P, R](
@@ -1361,7 +1369,7 @@ def run_generator_flow_sync(
1361
1369
  flow: Flow[P, R],
1362
1370
  flow_run: Optional[FlowRun] = None,
1363
1371
  parameters: Optional[Dict[str, Any]] = None,
1364
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1372
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1365
1373
  return_type: Literal["state", "result"] = "result",
1366
1374
  ) -> Generator[R, None, None]:
1367
1375
  if return_type != "result":