prefect-client 3.1.5__py3-none-any.whl → 3.1.6__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 (93) hide show
  1. prefect/__init__.py +3 -0
  2. prefect/_internal/compatibility/migration.py +1 -1
  3. prefect/_internal/concurrency/api.py +52 -52
  4. prefect/_internal/concurrency/calls.py +59 -35
  5. prefect/_internal/concurrency/cancellation.py +34 -18
  6. prefect/_internal/concurrency/event_loop.py +7 -6
  7. prefect/_internal/concurrency/threads.py +41 -33
  8. prefect/_internal/concurrency/waiters.py +28 -21
  9. prefect/_internal/pydantic/v1_schema.py +2 -2
  10. prefect/_internal/pydantic/v2_schema.py +10 -9
  11. prefect/_internal/schemas/bases.py +9 -7
  12. prefect/_internal/schemas/validators.py +2 -1
  13. prefect/_version.py +3 -3
  14. prefect/automations.py +53 -47
  15. prefect/blocks/abstract.py +12 -10
  16. prefect/blocks/core.py +4 -2
  17. prefect/cache_policies.py +11 -11
  18. prefect/client/__init__.py +3 -1
  19. prefect/client/base.py +36 -37
  20. prefect/client/cloud.py +26 -19
  21. prefect/client/collections.py +2 -2
  22. prefect/client/orchestration.py +342 -273
  23. prefect/client/schemas/__init__.py +24 -0
  24. prefect/client/schemas/actions.py +123 -116
  25. prefect/client/schemas/objects.py +110 -81
  26. prefect/client/schemas/responses.py +18 -18
  27. prefect/client/schemas/schedules.py +136 -93
  28. prefect/client/subscriptions.py +28 -14
  29. prefect/client/utilities.py +32 -36
  30. prefect/concurrency/asyncio.py +6 -9
  31. prefect/concurrency/sync.py +35 -5
  32. prefect/context.py +39 -31
  33. prefect/deployments/flow_runs.py +3 -5
  34. prefect/docker/__init__.py +1 -1
  35. prefect/events/schemas/events.py +25 -20
  36. prefect/events/utilities.py +1 -2
  37. prefect/filesystems.py +3 -3
  38. prefect/flow_engine.py +61 -21
  39. prefect/flow_runs.py +3 -3
  40. prefect/flows.py +214 -170
  41. prefect/logging/configuration.py +1 -1
  42. prefect/logging/highlighters.py +1 -2
  43. prefect/logging/loggers.py +30 -20
  44. prefect/main.py +17 -24
  45. prefect/runner/runner.py +43 -21
  46. prefect/runner/server.py +30 -32
  47. prefect/runner/submit.py +3 -6
  48. prefect/runner/utils.py +6 -6
  49. prefect/runtime/flow_run.py +7 -0
  50. prefect/settings/constants.py +2 -2
  51. prefect/settings/legacy.py +1 -1
  52. prefect/settings/models/server/events.py +10 -0
  53. prefect/task_engine.py +72 -19
  54. prefect/task_runners.py +2 -2
  55. prefect/tasks.py +46 -33
  56. prefect/telemetry/bootstrap.py +15 -2
  57. prefect/telemetry/run_telemetry.py +107 -0
  58. prefect/transactions.py +14 -14
  59. prefect/types/__init__.py +1 -4
  60. prefect/utilities/_engine.py +96 -0
  61. prefect/utilities/annotations.py +25 -18
  62. prefect/utilities/asyncutils.py +126 -140
  63. prefect/utilities/callables.py +87 -78
  64. prefect/utilities/collections.py +278 -117
  65. prefect/utilities/compat.py +13 -21
  66. prefect/utilities/context.py +6 -5
  67. prefect/utilities/dispatch.py +23 -12
  68. prefect/utilities/dockerutils.py +33 -32
  69. prefect/utilities/engine.py +126 -239
  70. prefect/utilities/filesystem.py +18 -15
  71. prefect/utilities/hashing.py +10 -11
  72. prefect/utilities/importtools.py +40 -27
  73. prefect/utilities/math.py +9 -5
  74. prefect/utilities/names.py +3 -3
  75. prefect/utilities/processutils.py +121 -57
  76. prefect/utilities/pydantic.py +41 -36
  77. prefect/utilities/render_swagger.py +22 -12
  78. prefect/utilities/schema_tools/__init__.py +2 -1
  79. prefect/utilities/schema_tools/hydration.py +50 -43
  80. prefect/utilities/schema_tools/validation.py +52 -42
  81. prefect/utilities/services.py +13 -12
  82. prefect/utilities/templating.py +45 -45
  83. prefect/utilities/text.py +2 -1
  84. prefect/utilities/timeout.py +4 -4
  85. prefect/utilities/urls.py +9 -4
  86. prefect/utilities/visualization.py +46 -24
  87. prefect/variables.py +9 -8
  88. prefect/workers/base.py +15 -8
  89. {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/METADATA +4 -2
  90. {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/RECORD +93 -91
  91. {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/LICENSE +0 -0
  92. {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/WHEEL +0 -0
  93. {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/top_level.txt +0 -0
prefect/flow_engine.py CHANGED
@@ -2,7 +2,7 @@ import asyncio
2
2
  import logging
3
3
  import os
4
4
  import time
5
- from contextlib import ExitStack, asynccontextmanager, contextmanager
5
+ from contextlib import ExitStack, asynccontextmanager, contextmanager, nullcontext
6
6
  from dataclasses import dataclass, field
7
7
  from typing import (
8
8
  Any,
@@ -23,7 +23,7 @@ from typing import (
23
23
  from uuid import UUID
24
24
 
25
25
  from anyio import CancelScope
26
- from opentelemetry import trace
26
+ from opentelemetry import propagate, trace
27
27
  from opentelemetry.trace import Tracer, get_tracer
28
28
  from typing_extensions import ParamSpec
29
29
 
@@ -72,6 +72,9 @@ from prefect.states import (
72
72
  exception_to_failed_state,
73
73
  return_value_to_state,
74
74
  )
75
+ from prefect.telemetry.run_telemetry import OTELSetter
76
+ from prefect.types import KeyValueLabels
77
+ from prefect.utilities._engine import get_hook_name, resolve_custom_flow_run_name
75
78
  from prefect.utilities.annotations import NotSet
76
79
  from prefect.utilities.asyncutils import run_coro_as_sync
77
80
  from prefect.utilities.callables import (
@@ -81,8 +84,6 @@ from prefect.utilities.callables import (
81
84
  )
82
85
  from prefect.utilities.collections import visit_collection
83
86
  from prefect.utilities.engine import (
84
- _get_hook_name,
85
- _resolve_custom_flow_run_name,
86
87
  capture_sigterm,
87
88
  link_state_to_result,
88
89
  propose_state,
@@ -94,6 +95,8 @@ from prefect.utilities.urls import url_for
94
95
 
95
96
  P = ParamSpec("P")
96
97
  R = TypeVar("R")
98
+ LABELS_TRACEPARENT_KEY = "__OTEL_TRACEPARENT"
99
+ TRACEPARENT_KEY = "traceparent"
97
100
 
98
101
 
99
102
  class FlowRunTimeoutError(TimeoutError):
@@ -178,6 +181,37 @@ class BaseFlowRunEngine(Generic[P, R]):
178
181
  if hasattr(self.flow.task_runner, "cancel_all"):
179
182
  self.flow.task_runner.cancel_all() # type: ignore
180
183
 
184
+ def _update_otel_labels(
185
+ self, span: trace.Span, client: Union[SyncPrefectClient, PrefectClient]
186
+ ):
187
+ parent_flow_run_ctx = FlowRunContext.get()
188
+ if parent_flow_run_ctx and parent_flow_run_ctx.flow_run:
189
+ if traceparent := parent_flow_run_ctx.flow_run.labels.get(
190
+ LABELS_TRACEPARENT_KEY
191
+ ):
192
+ carrier: KeyValueLabels = {TRACEPARENT_KEY: traceparent}
193
+ propagate.get_global_textmap().inject(
194
+ carrier={TRACEPARENT_KEY: traceparent},
195
+ setter=OTELSetter(),
196
+ )
197
+ else:
198
+ carrier: KeyValueLabels = {}
199
+ propagate.get_global_textmap().inject(
200
+ carrier,
201
+ context=trace.set_span_in_context(span),
202
+ setter=OTELSetter(),
203
+ )
204
+ if carrier.get(TRACEPARENT_KEY):
205
+ if self.flow_run:
206
+ client.update_flow_run_labels(
207
+ flow_run_id=self.flow_run.id,
208
+ labels={LABELS_TRACEPARENT_KEY: carrier[TRACEPARENT_KEY]},
209
+ )
210
+ else:
211
+ self.logger.info(
212
+ f"Tried to set traceparent {carrier[TRACEPARENT_KEY]} for flow run, but None was found"
213
+ )
214
+
181
215
 
182
216
  @dataclass
183
217
  class FlowRunEngine(BaseFlowRunEngine[P, R]):
@@ -283,7 +317,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
283
317
 
284
318
  if self._span:
285
319
  self._span.add_event(
286
- state.name,
320
+ state.name or state.type,
287
321
  {
288
322
  "prefect.state.message": state.message or "",
289
323
  "prefect.state.type": state.type,
@@ -402,7 +436,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
402
436
  self.set_state(state, force=True)
403
437
  self._raised = exc
404
438
 
405
- self._end_span_on_error(exc, state.message)
439
+ self._end_span_on_error(exc, state.message if state else "")
406
440
 
407
441
  def load_subflow_run(
408
442
  self,
@@ -537,7 +571,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
537
571
  hooks = None
538
572
 
539
573
  for hook in hooks or []:
540
- hook_name = _get_hook_name(hook)
574
+ hook_name = get_hook_name(hook)
541
575
 
542
576
  try:
543
577
  self.logger.info(
@@ -600,7 +634,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
600
634
 
601
635
  # update the flow run name if necessary
602
636
  if not self._flow_run_name_set and self.flow.flow_run_name:
603
- flow_run_name = _resolve_custom_flow_run_name(
637
+ flow_run_name = resolve_custom_flow_run_name(
604
638
  flow=self.flow, parameters=self.parameters
605
639
  )
606
640
  self.client.set_flow_run_name(
@@ -647,7 +681,7 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
647
681
  empirical_policy=self.flow_run.empirical_policy,
648
682
  )
649
683
 
650
- self._span = self._tracer.start_span(
684
+ span = self._tracer.start_span(
651
685
  name=self.flow_run.name,
652
686
  attributes={
653
687
  **self.flow_run.labels,
@@ -657,6 +691,9 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
657
691
  "prefect.flow.name": self.flow.name,
658
692
  },
659
693
  )
694
+ self._update_otel_labels(span, self.client)
695
+
696
+ self._span = span
660
697
 
661
698
  try:
662
699
  yield self
@@ -698,12 +735,13 @@ class FlowRunEngine(BaseFlowRunEngine[P, R]):
698
735
 
699
736
  @contextmanager
700
737
  def start(self) -> Generator[None, None, None]:
701
- with self.initialize_run(), trace.use_span(self._span):
702
- self.begin_run()
738
+ with self.initialize_run():
739
+ with trace.use_span(self._span) if self._span else nullcontext():
740
+ self.begin_run()
703
741
 
704
- if self.state.is_running():
705
- self.call_hooks()
706
- yield
742
+ if self.state.is_running():
743
+ self.call_hooks()
744
+ yield
707
745
 
708
746
  @contextmanager
709
747
  def run_context(self):
@@ -856,7 +894,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
856
894
 
857
895
  if self._span:
858
896
  self._span.add_event(
859
- state.name,
897
+ state.name or state.type,
860
898
  {
861
899
  "prefect.state.message": state.message or "",
862
900
  "prefect.state.type": state.type,
@@ -1107,7 +1145,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1107
1145
  hooks = None
1108
1146
 
1109
1147
  for hook in hooks or []:
1110
- hook_name = _get_hook_name(hook)
1148
+ hook_name = get_hook_name(hook)
1111
1149
 
1112
1150
  try:
1113
1151
  self.logger.info(
@@ -1170,7 +1208,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1170
1208
 
1171
1209
  # update the flow run name if necessary
1172
1210
  if not self._flow_run_name_set and self.flow.flow_run_name:
1173
- flow_run_name = _resolve_custom_flow_run_name(
1211
+ flow_run_name = resolve_custom_flow_run_name(
1174
1212
  flow=self.flow, parameters=self.parameters
1175
1213
  )
1176
1214
  await self.client.set_flow_run_name(
@@ -1217,7 +1255,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1217
1255
  empirical_policy=self.flow_run.empirical_policy,
1218
1256
  )
1219
1257
 
1220
- self._span = self._tracer.start_span(
1258
+ span = self._tracer.start_span(
1221
1259
  name=self.flow_run.name,
1222
1260
  attributes={
1223
1261
  **self.flow_run.labels,
@@ -1227,6 +1265,8 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1227
1265
  "prefect.flow.name": self.flow.name,
1228
1266
  },
1229
1267
  )
1268
+ self._update_otel_labels(span, self.client)
1269
+ self._span = span
1230
1270
 
1231
1271
  try:
1232
1272
  yield self
@@ -1269,7 +1309,7 @@ class AsyncFlowRunEngine(BaseFlowRunEngine[P, R]):
1269
1309
  @asynccontextmanager
1270
1310
  async def start(self) -> AsyncGenerator[None, None]:
1271
1311
  async with self.initialize_run():
1272
- with trace.use_span(self._span):
1312
+ with trace.use_span(self._span) if self._span else nullcontext():
1273
1313
  await self.begin_run()
1274
1314
 
1275
1315
  if self.state.is_running():
@@ -1392,7 +1432,7 @@ async def run_generator_flow_async(
1392
1432
  flow: Flow[P, R],
1393
1433
  flow_run: Optional[FlowRun] = None,
1394
1434
  parameters: Optional[Dict[str, Any]] = None,
1395
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1435
+ wait_for: Optional[Iterable[PrefectFuture[R]]] = None,
1396
1436
  return_type: Literal["state", "result"] = "result",
1397
1437
  ) -> AsyncGenerator[R, None]:
1398
1438
  if return_type != "result":
@@ -1430,7 +1470,7 @@ def run_flow(
1430
1470
  flow: Flow[P, R],
1431
1471
  flow_run: Optional[FlowRun] = None,
1432
1472
  parameters: Optional[Dict[str, Any]] = None,
1433
- wait_for: Optional[Iterable[PrefectFuture]] = None,
1473
+ wait_for: Optional[Iterable[PrefectFuture[R]]] = None,
1434
1474
  return_type: Literal["state", "result"] = "result",
1435
1475
  ) -> Union[R, State, None]:
1436
1476
  kwargs = dict(
prefect/flow_runs.py CHANGED
@@ -307,7 +307,7 @@ async def suspend_flow_run(
307
307
  flow_run_id: Optional[UUID] = None,
308
308
  timeout: Optional[int] = 3600,
309
309
  key: Optional[str] = None,
310
- client: PrefectClient = None,
310
+ client: Optional[PrefectClient] = None,
311
311
  ) -> None:
312
312
  ...
313
313
 
@@ -318,7 +318,7 @@ async def suspend_flow_run(
318
318
  flow_run_id: Optional[UUID] = None,
319
319
  timeout: Optional[int] = 3600,
320
320
  key: Optional[str] = None,
321
- client: PrefectClient = None,
321
+ client: Optional[PrefectClient] = None,
322
322
  ) -> T:
323
323
  ...
324
324
 
@@ -330,7 +330,7 @@ async def suspend_flow_run(
330
330
  flow_run_id: Optional[UUID] = None,
331
331
  timeout: Optional[int] = 3600,
332
332
  key: Optional[str] = None,
333
- client: PrefectClient = None,
333
+ client: Optional[PrefectClient] = None,
334
334
  ) -> Optional[T]:
335
335
  """
336
336
  Suspends a flow run by stopping code execution until resumed.