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/task_runners.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
4
  import asyncio
3
5
  import sys
@@ -14,7 +16,6 @@ from typing import (
14
16
  Iterable,
15
17
  List,
16
18
  Optional,
17
- Set,
18
19
  overload,
19
20
  )
20
21
 
@@ -39,12 +40,14 @@ from prefect.utilities.callables import (
39
40
  from prefect.utilities.collections import isiterable
40
41
 
41
42
  if TYPE_CHECKING:
43
+ import logging
44
+
42
45
  from prefect.tasks import Task
43
46
 
44
47
  P = ParamSpec("P")
45
48
  T = TypeVar("T")
46
49
  R = TypeVar("R")
47
- F = TypeVar("F", bound=PrefectFuture, default=PrefectConcurrentFuture)
50
+ F = TypeVar("F", bound=PrefectFuture[Any], default=PrefectConcurrentFuture[Any])
48
51
 
49
52
 
50
53
  class TaskRunner(abc.ABC, Generic[F]):
@@ -60,11 +63,11 @@ class TaskRunner(abc.ABC, Generic[F]):
60
63
  """
61
64
 
62
65
  def __init__(self):
63
- self.logger = get_logger(f"task_runner.{self.name}")
66
+ self.logger: "logging.Logger" = get_logger(f"task_runner.{self.name}")
64
67
  self._started = False
65
68
 
66
69
  @property
67
- def name(self):
70
+ def name(self) -> str:
68
71
  """The name of this task runner"""
69
72
  return type(self).__name__.lower().replace("taskrunner", "")
70
73
 
@@ -73,32 +76,42 @@ class TaskRunner(abc.ABC, Generic[F]):
73
76
  """Return a new instance of this task runner with the same configuration."""
74
77
  ...
75
78
 
79
+ @overload
76
80
  @abc.abstractmethod
77
81
  def submit(
78
82
  self,
79
- task: "Task",
80
- parameters: Dict[str, Any],
81
- wait_for: Optional[Iterable[PrefectFuture]] = None,
82
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
83
+ task: "Task[P, Coroutine[Any, Any, R]]",
84
+ parameters: dict[str, Any],
85
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
86
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
83
87
  ) -> F:
84
- """
85
- Submit a task to the task run engine.
88
+ ...
86
89
 
87
- Args:
88
- task: The task to submit.
89
- parameters: The parameters to use when running the task.
90
- wait_for: A list of futures that the task depends on.
90
+ @overload
91
+ @abc.abstractmethod
92
+ def submit(
93
+ self,
94
+ task: "Task[Any, R]",
95
+ parameters: dict[str, Any],
96
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
97
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
98
+ ) -> F:
99
+ ...
91
100
 
92
- Returns:
93
- A future object that can be used to wait for the task to complete and
94
- retrieve the result.
95
- """
101
+ @abc.abstractmethod
102
+ def submit(
103
+ self,
104
+ task: "Task[P, R]",
105
+ parameters: dict[str, Any],
106
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
107
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
108
+ ) -> F:
96
109
  ...
97
110
 
98
111
  def map(
99
112
  self,
100
113
  task: "Task[P, R]",
101
- parameters: Dict[str, Any],
114
+ parameters: dict[str, Any | unmapped[Any] | allow_failure[Any]],
102
115
  wait_for: Optional[Iterable[PrefectFuture[R]]] = None,
103
116
  ) -> PrefectFutureList[F]:
104
117
  """
@@ -138,9 +151,9 @@ class TaskRunner(abc.ABC, Generic[F]):
138
151
  # Ensure that any parameters in kwargs are expanded before this check
139
152
  parameters = explode_variadic_parameter(task.fn, parameters)
140
153
 
141
- iterable_parameters = {}
142
- static_parameters = {}
143
- annotated_parameters = {}
154
+ iterable_parameters: dict[str, Any] = {}
155
+ static_parameters: dict[str, Any] = {}
156
+ annotated_parameters: dict[str, Any] = {}
144
157
  for key, val in parameters.items():
145
158
  if isinstance(val, (allow_failure, quote)):
146
159
  # Unwrap annotated parameters to determine if they are iterable
@@ -172,9 +185,9 @@ class TaskRunner(abc.ABC, Generic[F]):
172
185
 
173
186
  map_length = list(lengths)[0]
174
187
 
175
- futures: List[PrefectFuture] = []
188
+ futures: List[PrefectFuture[Any]] = []
176
189
  for i in range(map_length):
177
- call_parameters = {
190
+ call_parameters: dict[str, Any] = {
178
191
  key: value[i] for key, value in iterable_parameters.items()
179
192
  }
180
193
  call_parameters.update(
@@ -204,7 +217,7 @@ class TaskRunner(abc.ABC, Generic[F]):
204
217
 
205
218
  return PrefectFutureList(futures)
206
219
 
207
- def __enter__(self):
220
+ def __enter__(self) -> Self:
208
221
  if self._started:
209
222
  raise RuntimeError("This task runner is already started")
210
223
 
@@ -212,12 +225,12 @@ class TaskRunner(abc.ABC, Generic[F]):
212
225
  self._started = True
213
226
  return self
214
227
 
215
- def __exit__(self, exc_type, exc_value, traceback):
228
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
216
229
  self.logger.debug("Stopping task runner")
217
230
  self._started = False
218
231
 
219
232
 
220
- class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
233
+ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture[R]]):
221
234
  def __init__(self, max_workers: Optional[int] = None):
222
235
  super().__init__()
223
236
  self._executor: Optional[ThreadPoolExecutor] = None
@@ -228,16 +241,16 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
228
241
  )
229
242
  self._cancel_events: Dict[uuid.UUID, threading.Event] = {}
230
243
 
231
- def duplicate(self) -> "ThreadPoolTaskRunner":
244
+ def duplicate(self) -> "ThreadPoolTaskRunner[R]":
232
245
  return type(self)(max_workers=self._max_workers)
233
246
 
234
247
  @overload
235
248
  def submit(
236
249
  self,
237
250
  task: "Task[P, Coroutine[Any, Any, R]]",
238
- parameters: Dict[str, Any],
239
- wait_for: Optional[Iterable[PrefectFuture]] = None,
240
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
251
+ parameters: dict[str, Any],
252
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
253
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
241
254
  ) -> PrefectConcurrentFuture[R]:
242
255
  ...
243
256
 
@@ -245,19 +258,19 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
245
258
  def submit(
246
259
  self,
247
260
  task: "Task[Any, R]",
248
- parameters: Dict[str, Any],
249
- wait_for: Optional[Iterable[PrefectFuture]] = None,
250
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
261
+ parameters: dict[str, Any],
262
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
263
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
251
264
  ) -> PrefectConcurrentFuture[R]:
252
265
  ...
253
266
 
254
267
  def submit(
255
268
  self,
256
- task: "Task",
257
- parameters: Dict[str, Any],
258
- wait_for: Optional[Iterable[PrefectFuture]] = None,
259
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
260
- ):
269
+ task: "Task[P, R | Coroutine[Any, Any, R]]",
270
+ parameters: dict[str, Any],
271
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
272
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
273
+ ) -> PrefectConcurrentFuture[R]:
261
274
  """
262
275
  Submit a task to the task run engine running in a separate thread.
263
276
 
@@ -289,7 +302,7 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
289
302
  else:
290
303
  self.logger.debug(f"Submitting task {task.name} to thread pool executor...")
291
304
 
292
- submit_kwargs = dict(
305
+ submit_kwargs: dict[str, Any] = dict(
293
306
  task=task,
294
307
  task_run_id=task_run_id,
295
308
  parameters=parameters,
@@ -322,8 +335,8 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
322
335
  def map(
323
336
  self,
324
337
  task: "Task[P, Coroutine[Any, Any, R]]",
325
- parameters: Dict[str, Any],
326
- wait_for: Optional[Iterable[PrefectFuture]] = None,
338
+ parameters: dict[str, Any],
339
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
327
340
  ) -> PrefectFutureList[PrefectConcurrentFuture[R]]:
328
341
  ...
329
342
 
@@ -331,20 +344,20 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
331
344
  def map(
332
345
  self,
333
346
  task: "Task[Any, R]",
334
- parameters: Dict[str, Any],
335
- wait_for: Optional[Iterable[PrefectFuture]] = None,
347
+ parameters: dict[str, Any],
348
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
336
349
  ) -> PrefectFutureList[PrefectConcurrentFuture[R]]:
337
350
  ...
338
351
 
339
352
  def map(
340
353
  self,
341
- task: "Task",
342
- parameters: Dict[str, Any],
343
- wait_for: Optional[Iterable[PrefectFuture]] = None,
344
- ):
354
+ task: "Task[P, R]",
355
+ parameters: dict[str, Any],
356
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
357
+ ) -> PrefectFutureList[PrefectConcurrentFuture[R]]:
345
358
  return super().map(task, parameters, wait_for)
346
359
 
347
- def cancel_all(self):
360
+ def cancel_all(self) -> None:
348
361
  for event in self._cancel_events.values():
349
362
  event.set()
350
363
  self.logger.debug("Set cancel event")
@@ -353,12 +366,12 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
353
366
  self._executor.shutdown(cancel_futures=True)
354
367
  self._executor = None
355
368
 
356
- def __enter__(self):
369
+ def __enter__(self) -> Self:
357
370
  super().__enter__()
358
371
  self._executor = ThreadPoolExecutor(max_workers=self._max_workers)
359
372
  return self
360
373
 
361
- def __exit__(self, exc_type, exc_value, traceback):
374
+ def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
362
375
  self.cancel_all()
363
376
  if self._executor is not None:
364
377
  self._executor.shutdown(cancel_futures=True)
@@ -375,20 +388,20 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture]):
375
388
  ConcurrentTaskRunner = ThreadPoolTaskRunner
376
389
 
377
390
 
378
- class PrefectTaskRunner(TaskRunner[PrefectDistributedFuture]):
391
+ class PrefectTaskRunner(TaskRunner[PrefectDistributedFuture[R]]):
379
392
  def __init__(self):
380
393
  super().__init__()
381
394
 
382
- def duplicate(self) -> "PrefectTaskRunner":
395
+ def duplicate(self) -> "PrefectTaskRunner[R]":
383
396
  return type(self)()
384
397
 
385
398
  @overload
386
399
  def submit(
387
400
  self,
388
401
  task: "Task[P, Coroutine[Any, Any, R]]",
389
- parameters: Dict[str, Any],
390
- wait_for: Optional[Iterable[PrefectFuture]] = None,
391
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
402
+ parameters: dict[str, Any],
403
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
404
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
392
405
  ) -> PrefectDistributedFuture[R]:
393
406
  ...
394
407
 
@@ -396,19 +409,19 @@ class PrefectTaskRunner(TaskRunner[PrefectDistributedFuture]):
396
409
  def submit(
397
410
  self,
398
411
  task: "Task[Any, R]",
399
- parameters: Dict[str, Any],
400
- wait_for: Optional[Iterable[PrefectFuture]] = None,
401
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
412
+ parameters: dict[str, Any],
413
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
414
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
402
415
  ) -> PrefectDistributedFuture[R]:
403
416
  ...
404
417
 
405
418
  def submit(
406
419
  self,
407
- task: "Task",
408
- parameters: Dict[str, Any],
409
- wait_for: Optional[Iterable[PrefectFuture]] = None,
410
- dependencies: Optional[Dict[str, Set[TaskRunInput]]] = None,
411
- ):
420
+ task: "Task[P, R]",
421
+ parameters: dict[str, Any],
422
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
423
+ dependencies: dict[str, set[TaskRunInput]] | None = None,
424
+ ) -> PrefectDistributedFuture[R]:
412
425
  """
413
426
  Submit a task to the task run engine running in a separate thread.
414
427
 
@@ -443,8 +456,8 @@ class PrefectTaskRunner(TaskRunner[PrefectDistributedFuture]):
443
456
  def map(
444
457
  self,
445
458
  task: "Task[P, Coroutine[Any, Any, R]]",
446
- parameters: Dict[str, Any],
447
- wait_for: Optional[Iterable[PrefectFuture]] = None,
459
+ parameters: dict[str, Any],
460
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
448
461
  ) -> PrefectFutureList[PrefectDistributedFuture[R]]:
449
462
  ...
450
463
 
@@ -452,15 +465,15 @@ class PrefectTaskRunner(TaskRunner[PrefectDistributedFuture]):
452
465
  def map(
453
466
  self,
454
467
  task: "Task[Any, R]",
455
- parameters: Dict[str, Any],
456
- wait_for: Optional[Iterable[PrefectFuture]] = None,
468
+ parameters: dict[str, Any],
469
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
457
470
  ) -> PrefectFutureList[PrefectDistributedFuture[R]]:
458
471
  ...
459
472
 
460
473
  def map(
461
474
  self,
462
- task: "Task",
463
- parameters: Dict[str, Any],
464
- wait_for: Optional[Iterable[PrefectFuture]] = None,
465
- ):
475
+ task: "Task[P, R]",
476
+ parameters: dict[str, Any],
477
+ wait_for: Iterable[PrefectFuture[Any]] | None = None,
478
+ ) -> PrefectFutureList[PrefectDistributedFuture[R]]:
466
479
  return super().map(task, parameters, wait_for)
prefect/task_runs.py CHANGED
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import atexit
3
5
  import threading
4
6
  import uuid
5
- from typing import Callable, Dict, Optional
7
+ from typing import TYPE_CHECKING, Callable, Dict, Optional
6
8
 
7
9
  import anyio
8
10
  from cachetools import TTLCache
@@ -15,6 +17,9 @@ from prefect.events.clients import get_events_subscriber
15
17
  from prefect.events.filters import EventFilter, EventNameFilter
16
18
  from prefect.logging.loggers import get_logger
17
19
 
20
+ if TYPE_CHECKING:
21
+ import logging
22
+
18
23
 
19
24
  class TaskRunWaiter:
20
25
  """
@@ -68,19 +73,19 @@ class TaskRunWaiter:
68
73
  _instance_lock = threading.Lock()
69
74
 
70
75
  def __init__(self):
71
- self.logger = get_logger("TaskRunWaiter")
72
- self._consumer_task: Optional[asyncio.Task] = None
76
+ self.logger: "logging.Logger" = get_logger("TaskRunWaiter")
77
+ self._consumer_task: "asyncio.Task[None] | None" = None
73
78
  self._observed_completed_task_runs: TTLCache[uuid.UUID, bool] = TTLCache(
74
79
  maxsize=10000, ttl=600
75
80
  )
76
81
  self._completion_events: Dict[uuid.UUID, asyncio.Event] = {}
77
- self._completion_callbacks: Dict[uuid.UUID, Callable] = {}
82
+ self._completion_callbacks: Dict[uuid.UUID, Callable[[], None]] = {}
78
83
  self._loop: Optional[asyncio.AbstractEventLoop] = None
79
84
  self._observed_completed_task_runs_lock = threading.Lock()
80
85
  self._completion_events_lock = threading.Lock()
81
86
  self._started = False
82
87
 
83
- def start(self):
88
+ def start(self) -> None:
84
89
  """
85
90
  Start the TaskRunWaiter service.
86
91
  """
@@ -89,10 +94,12 @@ class TaskRunWaiter:
89
94
  self.logger.debug("Starting TaskRunWaiter")
90
95
  loop_thread = get_global_loop()
91
96
 
92
- if not asyncio.get_running_loop() == loop_thread._loop:
97
+ if not asyncio.get_running_loop() == loop_thread.loop:
93
98
  raise RuntimeError("TaskRunWaiter must run on the global loop thread.")
94
99
 
95
- self._loop = loop_thread._loop
100
+ self._loop = loop_thread.loop
101
+ if TYPE_CHECKING:
102
+ assert self._loop is not None
96
103
 
97
104
  consumer_started = asyncio.Event()
98
105
  self._consumer_task = self._loop.create_task(
@@ -141,7 +148,7 @@ class TaskRunWaiter:
141
148
  except Exception as exc:
142
149
  self.logger.error(f"Error processing event: {exc}")
143
150
 
144
- def stop(self):
151
+ def stop(self) -> None:
145
152
  """
146
153
  Stop the TaskRunWaiter service.
147
154
  """
@@ -155,7 +162,7 @@ class TaskRunWaiter:
155
162
  @classmethod
156
163
  async def wait_for_task_run(
157
164
  cls, task_run_id: uuid.UUID, timeout: Optional[float] = None
158
- ):
165
+ ) -> None:
159
166
  """
160
167
  Wait for a task run to finish.
161
168
 
@@ -199,7 +206,9 @@ class TaskRunWaiter:
199
206
  instance._completion_events.pop(task_run_id, None)
200
207
 
201
208
  @classmethod
202
- def add_done_callback(cls, task_run_id: uuid.UUID, callback):
209
+ def add_done_callback(
210
+ cls, task_run_id: uuid.UUID, callback: Callable[[], None]
211
+ ) -> None:
203
212
  """
204
213
  Add a callback to be called when a task run finishes.
205
214
 
@@ -219,7 +228,7 @@ class TaskRunWaiter:
219
228
  instance._completion_callbacks[task_run_id] = callback
220
229
 
221
230
  @classmethod
222
- def instance(cls):
231
+ def instance(cls) -> Self:
223
232
  """
224
233
  Get the singleton instance of TaskRunWaiter.
225
234
  """
prefect/task_worker.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import asyncio
2
4
  import inspect
3
5
  import os
@@ -16,12 +18,12 @@ import pendulum
16
18
  import uvicorn
17
19
  from exceptiongroup import BaseExceptionGroup # novermin
18
20
  from fastapi import FastAPI
19
- from typing_extensions import ParamSpec, TypeVar
21
+ from typing_extensions import ParamSpec, Self, TypeVar
20
22
  from websockets.exceptions import InvalidStatusCode
21
23
 
22
24
  from prefect import Task
23
25
  from prefect._internal.concurrency.api import create_call, from_sync
24
- from prefect.cache_policies import DEFAULT, NONE
26
+ from prefect.cache_policies import DEFAULT, NO_CACHE
25
27
  from prefect.client.orchestration import get_client
26
28
  from prefect.client.schemas.objects import TaskRun
27
29
  from prefect.client.subscriptions import Subscription
@@ -42,7 +44,10 @@ from prefect.utilities.processutils import (
42
44
  from prefect.utilities.services import start_client_metrics_server
43
45
  from prefect.utilities.urls import url_for
44
46
 
45
- logger = get_logger("task_worker")
47
+ if TYPE_CHECKING:
48
+ import logging
49
+
50
+ logger: "logging.Logger" = get_logger("task_worker")
46
51
 
47
52
  P = ParamSpec("P")
48
53
  R = TypeVar("R", infer_variance=True)
@@ -85,7 +90,7 @@ class TaskWorker:
85
90
  def __init__(
86
91
  self,
87
92
  *tasks: Task[P, R],
88
- limit: Optional[int] = 10,
93
+ limit: int | None = 10,
89
94
  ):
90
95
  self.tasks: list["Task[..., Any]"] = []
91
96
  for t in tasks:
@@ -93,14 +98,14 @@ class TaskWorker:
93
98
  if not isinstance(t, Task):
94
99
  continue
95
100
 
96
- if t.cache_policy in [None, NONE, NotSet]:
101
+ if t.cache_policy in [None, NO_CACHE, NotSet]:
97
102
  self.tasks.append(
98
103
  t.with_options(persist_result=True, cache_policy=DEFAULT)
99
104
  )
100
105
  else:
101
106
  self.tasks.append(t.with_options(persist_result=True))
102
107
 
103
- self.task_keys = set(t.task_key for t in tasks if isinstance(t, Task)) # pyright: ignore[reportUnnecessaryIsInstance]
108
+ self.task_keys: set[str] = set(t.task_key for t in tasks if isinstance(t, Task)) # pyright: ignore[reportUnnecessaryIsInstance]
104
109
 
105
110
  self._started_at: Optional[pendulum.DateTime] = None
106
111
  self.stopping: bool = False
@@ -154,7 +159,7 @@ class TaskWorker:
154
159
  def available_tasks(self) -> Optional[int]:
155
160
  return int(self._limiter.available_tokens) if self._limiter else None
156
161
 
157
- def handle_sigterm(self, signum: int, frame: object):
162
+ def handle_sigterm(self, signum: int, frame: object) -> None:
158
163
  """
159
164
  Shuts down the task worker when a SIGTERM is received.
160
165
  """
@@ -355,14 +360,14 @@ class TaskWorker:
355
360
  )
356
361
  await asyncio.wrap_future(future)
357
362
 
358
- async def execute_task_run(self, task_run: TaskRun):
363
+ async def execute_task_run(self, task_run: TaskRun) -> None:
359
364
  """Execute a task run in the task worker."""
360
365
  async with self if not self.started else asyncnullcontext():
361
366
  token_acquired = await self._acquire_token(task_run.id)
362
367
  if token_acquired:
363
368
  await self._safe_submit_scheduled_task_run(task_run)
364
369
 
365
- async def __aenter__(self):
370
+ async def __aenter__(self) -> Self:
366
371
  logger.debug("Starting task worker...")
367
372
 
368
373
  if self._client._closed: # pyright: ignore[reportPrivateUsage]