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/flow_engine.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import logging
3
5
  import os
@@ -14,7 +16,6 @@ from typing import (
14
16
  Iterable,
15
17
  Literal,
16
18
  Optional,
17
- Tuple,
18
19
  Type,
19
20
  TypeVar,
20
21
  Union,
@@ -103,13 +104,15 @@ class FlowRunTimeoutError(TimeoutError):
103
104
  """Raised when a flow run exceeds its defined timeout."""
104
105
 
105
106
 
106
- def load_flow_and_flow_run(flow_run_id: UUID) -> Tuple[FlowRun, Flow]:
107
- ## TODO: add error handling to update state and log tracebacks
108
- entrypoint = os.environ.get("PREFECT__FLOW_ENTRYPOINT")
107
+ def load_flow_run(flow_run_id: UUID) -> FlowRun:
108
+ client = get_client(sync_client=True)
109
+ flow_run = client.read_flow_run(flow_run_id)
110
+ return flow_run
109
111
 
110
- client = cast(SyncPrefectClient, get_client(sync_client=True))
111
112
 
112
- flow_run = client.read_flow_run(flow_run_id)
113
+ def load_flow(flow_run: FlowRun) -> Flow[..., Any]:
114
+ entrypoint = os.environ.get("PREFECT__FLOW_ENTRYPOINT")
115
+
113
116
  if entrypoint:
114
117
  # we should not accept a placeholder flow at runtime
115
118
  flow = load_flow_from_entrypoint(entrypoint, use_placeholder_flow=False)
@@ -117,7 +120,12 @@ def load_flow_and_flow_run(flow_run_id: UUID) -> Tuple[FlowRun, Flow]:
117
120
  flow = run_coro_as_sync(
118
121
  load_flow_from_flow_run(flow_run, use_placeholder_flow=False)
119
122
  )
123
+ return flow
120
124
 
125
+
126
+ def load_flow_and_flow_run(flow_run_id: UUID) -> tuple[FlowRun, Flow[..., Any]]:
127
+ flow_run = load_flow_run(flow_run_id)
128
+ flow = load_flow(flow_run)
121
129
  return flow_run, flow
122
130
 
123
131
 
@@ -128,7 +136,7 @@ class BaseFlowRunEngine(Generic[P, R]):
128
136
  flow_run: Optional[FlowRun] = None
129
137
  flow_run_id: Optional[UUID] = None
130
138
  logger: logging.Logger = field(default_factory=lambda: get_logger("engine"))
131
- wait_for: Optional[Iterable[PrefectFuture]] = None
139
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None
132
140
  # holds the return value from the user code
133
141
  _return_value: Union[R, Type[NotSet]] = NotSet
134
142
  # holds the exception raised by the user code, if any
@@ -138,7 +146,7 @@ class BaseFlowRunEngine(Generic[P, R]):
138
146
  _flow_run_name_set: bool = False
139
147
  _telemetry: RunTelemetry = field(default_factory=RunTelemetry)
140
148
 
141
- def __post_init__(self):
149
+ def __post_init__(self) -> None:
142
150
  if self.flow is None and self.flow_run_id is None:
143
151
  raise ValueError("Either a flow or a flow_run_id must be provided.")
144
152
 
@@ -159,7 +167,7 @@ class BaseFlowRunEngine(Generic[P, R]):
159
167
  return False # TODO: handle this differently?
160
168
  return getattr(self, "flow_run").state.is_pending()
161
169
 
162
- def cancel_all_tasks(self):
170
+ def cancel_all_tasks(self) -> None:
163
171
  if hasattr(self.flow.task_runner, "cancel_all"):
164
172
  self.flow.task_runner.cancel_all() # type: ignore
165
173
 
@@ -200,6 +208,8 @@ class BaseFlowRunEngine(Generic[P, R]):
200
208
  @dataclass
201
209
  class FlowRunEngine(BaseFlowRunEngine[P, R]):
202
210
  _client: Optional[SyncPrefectClient] = None
211
+ flow_run: FlowRun | None = None
212
+ parameters: dict[str, Any] | None = None
203
213
 
204
214
  @property
205
215
  def client(self) -> SyncPrefectClient:
@@ -209,7 +219,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
209
219
 
210
220
  def _resolve_parameters(self):
211
221
  if not self.parameters:
212
- return {}
222
+ return
213
223
 
214
224
  resolved_parameters = {}
215
225
  for parameter, value in self.parameters.items():
@@ -277,7 +287,6 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
277
287
  ),
278
288
  )
279
289
  self.short_circuit = True
280
- self.call_hooks()
281
290
 
282
291
  new_state = Running()
283
292
  state = self.set_state(new_state)
@@ -300,6 +309,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
300
309
  self.flow_run.state_type = state.type # type: ignore
301
310
 
302
311
  self._telemetry.update_state(state)
312
+ self.call_hooks(state)
303
313
  return state
304
314
 
305
315
  def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
@@ -494,7 +504,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
494
504
  tags=TagsContext.get().current_tags,
495
505
  )
496
506
 
497
- def call_hooks(self, state: Optional[State] = None):
507
+ def call_hooks(self, state: Optional[State] = None) -> None:
498
508
  if state is None:
499
509
  state = self.state
500
510
  flow = self.flow
@@ -592,7 +602,9 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
592
602
 
593
603
  # set the logger to the flow run logger
594
604
 
595
- 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
596
608
 
597
609
  # update the flow run name if necessary
598
610
  if not self._flow_run_name_set and self.flow.flow_run_name:
@@ -711,8 +723,6 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
711
723
  ):
712
724
  self.begin_run()
713
725
 
714
- if self.state.is_running():
715
- self.call_hooks()
716
726
  yield
717
727
 
718
728
  @contextmanager
@@ -734,9 +744,6 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
734
744
  except Exception as exc:
735
745
  self.logger.exception("Encountered exception during execution: %r", exc)
736
746
  self.handle_exception(exc)
737
- finally:
738
- if self.state.is_final() or self.state.is_cancelling():
739
- self.call_hooks()
740
747
 
741
748
  def call_flow_fn(self) -> Union[R, Coroutine[Any, Any, R]]:
742
749
  """
@@ -765,6 +772,8 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
765
772
  """
766
773
 
767
774
  _client: Optional[PrefectClient] = None
775
+ parameters: dict[str, Any] | None = None
776
+ flow_run: FlowRun | None = None
768
777
 
769
778
  @property
770
779
  def client(self) -> PrefectClient:
@@ -774,7 +783,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
774
783
 
775
784
  def _resolve_parameters(self):
776
785
  if not self.parameters:
777
- return {}
786
+ return
778
787
 
779
788
  resolved_parameters = {}
780
789
  for parameter, value in self.parameters.items():
@@ -842,7 +851,6 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
842
851
  ),
843
852
  )
844
853
  self.short_circuit = True
845
- await self.call_hooks()
846
854
 
847
855
  new_state = Running()
848
856
  state = await self.set_state(new_state)
@@ -865,6 +873,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
865
873
  self.flow_run.state_type = state.type # type: ignore
866
874
 
867
875
  self._telemetry.update_state(state)
876
+ await self.call_hooks(state)
868
877
  return state
869
878
 
870
879
  async def result(self, raise_on_failure: bool = True) -> "Union[R, State, None]":
@@ -1058,7 +1067,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1058
1067
  tags=TagsContext.get().current_tags,
1059
1068
  )
1060
1069
 
1061
- async def call_hooks(self, state: Optional[State] = None):
1070
+ async def call_hooks(self, state: Optional[State] = None) -> None:
1062
1071
  if state is None:
1063
1072
  state = self.state
1064
1073
  flow = self.flow
@@ -1155,7 +1164,9 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1155
1164
  stack.enter_context(ConcurrencyContext())
1156
1165
 
1157
1166
  # set the logger to the flow run logger
1158
- 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
+ )
1159
1170
 
1160
1171
  # update the flow run name if necessary
1161
1172
 
@@ -1279,8 +1290,6 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1279
1290
  ):
1280
1291
  await self.begin_run()
1281
1292
 
1282
- if self.state.is_running():
1283
- await self.call_hooks()
1284
1293
  yield
1285
1294
 
1286
1295
  @asynccontextmanager
@@ -1302,9 +1311,6 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1302
1311
  except Exception as exc:
1303
1312
  self.logger.exception("Encountered exception during execution: %r", exc)
1304
1313
  await self.handle_exception(exc)
1305
- finally:
1306
- if self.state.is_final() or self.state.is_cancelling():
1307
- await self.call_hooks()
1308
1314
 
1309
1315
  async def call_flow_fn(self) -> Coroutine[Any, Any, R]:
1310
1316
  """
@@ -1322,7 +1328,7 @@ def run_flow_sync(
1322
1328
  flow: Flow[P, R],
1323
1329
  flow_run: Optional[FlowRun] = None,
1324
1330
  parameters: Optional[Dict[str, Any]] = None,
1325
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1331
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1326
1332
  return_type: Literal["state", "result"] = "result",
1327
1333
  ) -> Union[R, State, None]:
1328
1334
  engine = FlowRunEngine[P, R](
@@ -1344,7 +1350,7 @@ async def run_flow_async(
1344
1350
  flow: Flow[P, R],
1345
1351
  flow_run: Optional[FlowRun] = None,
1346
1352
  parameters: Optional[Dict[str, Any]] = None,
1347
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1353
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1348
1354
  return_type: Literal["state", "result"] = "result",
1349
1355
  ) -> Union[R, State, None]:
1350
1356
  engine = AsyncFlowRunEngine[P, R](
@@ -1363,7 +1369,7 @@ def run_generator_flow_sync(
1363
1369
  flow: Flow[P, R],
1364
1370
  flow_run: Optional[FlowRun] = None,
1365
1371
  parameters: Optional[Dict[str, Any]] = None,
1366
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1372
+ wait_for: Optional[Iterable[PrefectFuture[Any]]] = None,
1367
1373
  return_type: Literal["state", "result"] = "result",
1368
1374
  ) -> Generator[R, None, None]:
1369
1375
  if return_type != "result":
@@ -1439,25 +1445,36 @@ def run_flow(
1439
1445
  parameters: Optional[Dict[str, Any]] = None,
1440
1446
  wait_for: Optional[Iterable[PrefectFuture[R]]] = None,
1441
1447
  return_type: Literal["state", "result"] = "result",
1448
+ error_logger: Optional[logging.Logger] = None,
1442
1449
  ) -> Union[R, State, None]:
1443
- kwargs = dict(
1444
- flow=flow,
1445
- flow_run=flow_run,
1446
- parameters=_flow_parameters(
1447
- flow=flow, flow_run=flow_run, parameters=parameters
1448
- ),
1449
- wait_for=wait_for,
1450
- return_type=return_type,
1451
- )
1450
+ ret_val: Union[R, State, None] = None
1451
+
1452
+ try:
1453
+ kwargs: dict[str, Any] = dict(
1454
+ flow=flow,
1455
+ flow_run=flow_run,
1456
+ parameters=_flow_parameters(
1457
+ flow=flow, flow_run=flow_run, parameters=parameters
1458
+ ),
1459
+ wait_for=wait_for,
1460
+ return_type=return_type,
1461
+ )
1452
1462
 
1453
- if flow.isasync and flow.isgenerator:
1454
- return run_generator_flow_async(**kwargs)
1455
- elif flow.isgenerator:
1456
- return run_generator_flow_sync(**kwargs)
1457
- elif flow.isasync:
1458
- return run_flow_async(**kwargs)
1459
- else:
1460
- return run_flow_sync(**kwargs)
1463
+ if flow.isasync and flow.isgenerator:
1464
+ ret_val = run_generator_flow_async(**kwargs)
1465
+ elif flow.isgenerator:
1466
+ ret_val = run_generator_flow_sync(**kwargs)
1467
+ elif flow.isasync:
1468
+ ret_val = run_flow_async(**kwargs)
1469
+ else:
1470
+ ret_val = run_flow_sync(**kwargs)
1471
+ except:
1472
+ if error_logger:
1473
+ error_logger.error(
1474
+ "Engine execution exited with unexpected exception", exc_info=True
1475
+ )
1476
+ raise
1477
+ return ret_val
1461
1478
 
1462
1479
 
1463
1480
  def _flow_parameters(