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.
- prefect/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +92 -0
- prefect/_experimental/sla/objects.py +61 -0
- prefect/_internal/concurrency/services.py +2 -2
- prefect/_internal/concurrency/threads.py +6 -0
- prefect/_internal/retries.py +6 -3
- prefect/_internal/schemas/validators.py +6 -4
- prefect/_version.py +3 -3
- prefect/artifacts.py +4 -1
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +57 -31
- prefect/blocks/core.py +181 -82
- prefect/blocks/notifications.py +134 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +23 -22
- prefect/client/orchestration/__init__.py +103 -2006
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/client.py +16 -14
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/objects.py +5 -5
- prefect/client/utilities.py +3 -3
- prefect/context.py +15 -1
- prefect/deployments/base.py +13 -4
- prefect/deployments/flow_runs.py +5 -1
- prefect/deployments/runner.py +37 -1
- prefect/deployments/steps/core.py +1 -1
- prefect/deployments/steps/pull.py +8 -3
- prefect/deployments/steps/utility.py +2 -2
- prefect/docker/docker_image.py +13 -9
- prefect/engine.py +33 -11
- prefect/events/cli/automations.py +4 -4
- prefect/events/clients.py +17 -14
- prefect/events/schemas/automations.py +12 -8
- prefect/events/schemas/events.py +5 -1
- prefect/events/worker.py +1 -1
- prefect/filesystems.py +7 -3
- prefect/flow_engine.py +64 -47
- prefect/flows.py +128 -74
- prefect/futures.py +14 -7
- prefect/infrastructure/provisioners/__init__.py +2 -0
- prefect/infrastructure/provisioners/cloud_run.py +4 -4
- prefect/infrastructure/provisioners/coiled.py +249 -0
- prefect/infrastructure/provisioners/container_instance.py +4 -3
- prefect/infrastructure/provisioners/ecs.py +55 -43
- prefect/infrastructure/provisioners/modal.py +5 -4
- prefect/input/actions.py +5 -1
- prefect/input/run_input.py +157 -43
- prefect/logging/configuration.py +3 -3
- prefect/logging/filters.py +2 -2
- prefect/logging/formatters.py +15 -11
- prefect/logging/handlers.py +24 -14
- prefect/logging/highlighters.py +5 -5
- prefect/logging/loggers.py +28 -18
- prefect/logging/logging.yml +1 -1
- prefect/main.py +3 -1
- prefect/results.py +166 -86
- prefect/runner/runner.py +38 -29
- prefect/runner/server.py +3 -1
- prefect/runner/storage.py +18 -18
- prefect/runner/submit.py +19 -12
- prefect/runtime/deployment.py +15 -8
- prefect/runtime/flow_run.py +19 -6
- prefect/runtime/task_run.py +7 -3
- prefect/settings/base.py +17 -7
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +4 -3
- prefect/settings/models/cli.py +4 -3
- prefect/settings/models/client.py +7 -4
- prefect/settings/models/cloud.py +9 -3
- prefect/settings/models/deployments.py +4 -3
- prefect/settings/models/experiments.py +4 -8
- prefect/settings/models/flows.py +4 -3
- prefect/settings/models/internal.py +4 -3
- prefect/settings/models/logging.py +8 -6
- prefect/settings/models/results.py +4 -3
- prefect/settings/models/root.py +11 -16
- prefect/settings/models/runner.py +8 -5
- prefect/settings/models/server/api.py +6 -3
- prefect/settings/models/server/database.py +120 -25
- prefect/settings/models/server/deployments.py +4 -3
- prefect/settings/models/server/ephemeral.py +7 -4
- prefect/settings/models/server/events.py +6 -3
- prefect/settings/models/server/flow_run_graph.py +4 -3
- prefect/settings/models/server/root.py +4 -3
- prefect/settings/models/server/services.py +15 -12
- prefect/settings/models/server/tasks.py +7 -4
- prefect/settings/models/server/ui.py +4 -3
- prefect/settings/models/tasks.py +10 -5
- prefect/settings/models/testing.py +4 -3
- prefect/settings/models/worker.py +7 -4
- prefect/settings/profiles.py +13 -12
- prefect/settings/sources.py +20 -19
- prefect/states.py +74 -51
- prefect/task_engine.py +43 -33
- prefect/task_runners.py +85 -72
- prefect/task_runs.py +20 -11
- prefect/task_worker.py +14 -9
- prefect/tasks.py +36 -28
- prefect/telemetry/bootstrap.py +13 -9
- prefect/telemetry/run_telemetry.py +15 -13
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +3 -3
- prefect/types/__init__.py +3 -1
- prefect/utilities/_deprecated.py +38 -0
- prefect/utilities/engine.py +11 -4
- prefect/utilities/filesystem.py +2 -2
- prefect/utilities/generics.py +1 -1
- prefect/utilities/pydantic.py +21 -36
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +58 -33
- prefect/workers/process.py +20 -15
- prefect/workers/server.py +4 -5
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
prefect/states.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import asyncio
|
2
4
|
import datetime
|
3
5
|
import sys
|
@@ -6,7 +8,7 @@ import uuid
|
|
6
8
|
import warnings
|
7
9
|
from collections import Counter
|
8
10
|
from types import GeneratorType, TracebackType
|
9
|
-
from typing import Any, Dict, Iterable, Optional, Type
|
11
|
+
from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Type
|
10
12
|
|
11
13
|
import anyio
|
12
14
|
import httpx
|
@@ -28,17 +30,19 @@ from prefect.exceptions import (
|
|
28
30
|
UnfinishedRun,
|
29
31
|
)
|
30
32
|
from prefect.logging.loggers import get_logger, get_run_logger
|
31
|
-
from prefect.results import (
|
32
|
-
R,
|
33
|
-
ResultRecord,
|
34
|
-
ResultRecordMetadata,
|
35
|
-
ResultStore,
|
36
|
-
)
|
37
33
|
from prefect.utilities.annotations import BaseAnnotation
|
38
34
|
from prefect.utilities.asyncutils import in_async_main_thread, sync_compatible
|
39
35
|
from prefect.utilities.collections import ensure_iterable
|
40
36
|
|
41
|
-
|
37
|
+
if TYPE_CHECKING:
|
38
|
+
import logging
|
39
|
+
|
40
|
+
from prefect.results import (
|
41
|
+
R,
|
42
|
+
ResultStore,
|
43
|
+
)
|
44
|
+
|
45
|
+
logger: "logging.Logger" = get_logger("states")
|
42
46
|
|
43
47
|
|
44
48
|
@deprecated.deprecated_parameter(
|
@@ -49,11 +53,11 @@ logger = get_logger("states")
|
|
49
53
|
help="Please ensure you are awaiting the call to `result()` when calling in an async context.",
|
50
54
|
)
|
51
55
|
def get_state_result(
|
52
|
-
state: State[R],
|
56
|
+
state: "State[R]",
|
53
57
|
raise_on_failure: bool = True,
|
54
58
|
fetch: bool = True,
|
55
59
|
retry_result_failure: bool = True,
|
56
|
-
) -> R:
|
60
|
+
) -> "R":
|
57
61
|
"""
|
58
62
|
Get the result from a state.
|
59
63
|
|
@@ -86,13 +90,18 @@ RESULT_READ_RETRY_DELAY = 0.25
|
|
86
90
|
|
87
91
|
|
88
92
|
async def _get_state_result_data_with_retries(
|
89
|
-
state: State[R], retry_result_failure: bool = True
|
90
|
-
) -> R:
|
93
|
+
state: "State[R]", retry_result_failure: bool = True
|
94
|
+
) -> "R":
|
91
95
|
# Results may be written asynchronously, possibly after their corresponding
|
92
96
|
# state has been written and events have been emitted, so we should give some
|
93
97
|
# grace here about missing results. The exception below could come in the form
|
94
98
|
# of a missing file, a short read, or other types of errors depending on the
|
95
99
|
# result storage backend.
|
100
|
+
from prefect.results import (
|
101
|
+
ResultRecord,
|
102
|
+
ResultRecordMetadata,
|
103
|
+
)
|
104
|
+
|
96
105
|
if retry_result_failure is False:
|
97
106
|
max_attempts = 1
|
98
107
|
else:
|
@@ -120,11 +129,16 @@ async def _get_state_result_data_with_retries(
|
|
120
129
|
|
121
130
|
@sync_compatible
|
122
131
|
async def _get_state_result(
|
123
|
-
state: State[R], raise_on_failure: bool, retry_result_failure: bool = True
|
124
|
-
) -> R:
|
132
|
+
state: "State[R]", raise_on_failure: bool, retry_result_failure: bool = True
|
133
|
+
) -> "R":
|
125
134
|
"""
|
126
135
|
Internal implementation for `get_state_result` without async backwards compatibility
|
127
136
|
"""
|
137
|
+
from prefect.results import (
|
138
|
+
ResultRecord,
|
139
|
+
ResultRecordMetadata,
|
140
|
+
)
|
141
|
+
|
128
142
|
if state.is_paused():
|
129
143
|
# Paused states are not truly terminal and do not have results associated with them
|
130
144
|
raise PausedRun("Run is paused, its result is not available.", state=state)
|
@@ -181,7 +195,7 @@ def format_exception(exc: BaseException, tb: TracebackType = None) -> str:
|
|
181
195
|
|
182
196
|
async def exception_to_crashed_state(
|
183
197
|
exc: BaseException,
|
184
|
-
result_store: Optional[ResultStore] = None,
|
198
|
+
result_store: Optional["ResultStore"] = None,
|
185
199
|
) -> State:
|
186
200
|
"""
|
187
201
|
Takes an exception that occurs _outside_ of user code and converts it to a
|
@@ -233,10 +247,10 @@ async def exception_to_crashed_state(
|
|
233
247
|
|
234
248
|
async def exception_to_failed_state(
|
235
249
|
exc: Optional[BaseException] = None,
|
236
|
-
result_store: Optional[ResultStore] = None,
|
250
|
+
result_store: Optional["ResultStore"] = None,
|
237
251
|
write_result: bool = False,
|
238
|
-
**kwargs,
|
239
|
-
) -> State:
|
252
|
+
**kwargs: Any,
|
253
|
+
) -> State[BaseException]:
|
240
254
|
"""
|
241
255
|
Convenience function for creating `Failed` states from exceptions
|
242
256
|
"""
|
@@ -285,12 +299,12 @@ async def exception_to_failed_state(
|
|
285
299
|
|
286
300
|
|
287
301
|
async def return_value_to_state(
|
288
|
-
retval: R,
|
289
|
-
result_store: ResultStore,
|
302
|
+
retval: "R",
|
303
|
+
result_store: "ResultStore",
|
290
304
|
key: Optional[str] = None,
|
291
305
|
expiration: Optional[datetime.datetime] = None,
|
292
306
|
write_result: bool = False,
|
293
|
-
) -> State[R]:
|
307
|
+
) -> "State[R]":
|
294
308
|
"""
|
295
309
|
Given a return value from a user's function, create a `State` the run should
|
296
310
|
be placed in.
|
@@ -311,6 +325,11 @@ async def return_value_to_state(
|
|
311
325
|
Callers should resolve all futures into states before passing return values to this
|
312
326
|
function.
|
313
327
|
"""
|
328
|
+
from prefect.results import (
|
329
|
+
ResultRecord,
|
330
|
+
ResultRecordMetadata,
|
331
|
+
)
|
332
|
+
|
314
333
|
try:
|
315
334
|
local_logger = get_run_logger()
|
316
335
|
except MissingContextError:
|
@@ -443,6 +462,10 @@ async def get_state_exception(state: State) -> BaseException:
|
|
443
462
|
- `CrashedRun` if the state type is CRASHED.
|
444
463
|
- `CancelledRun` if the state type is CANCELLED.
|
445
464
|
"""
|
465
|
+
from prefect.results import (
|
466
|
+
ResultRecord,
|
467
|
+
ResultRecordMetadata,
|
468
|
+
)
|
446
469
|
|
447
470
|
if state.is_failed():
|
448
471
|
wrapper = FailedRun
|
@@ -534,17 +557,17 @@ def is_state_iterable(obj: Any) -> TypeGuard[Iterable[State]]:
|
|
534
557
|
|
535
558
|
|
536
559
|
class StateGroup:
|
537
|
-
def __init__(self, states:
|
538
|
-
self.states = states
|
539
|
-
self.type_counts = self._get_type_counts(states)
|
540
|
-
self.total_count = len(states)
|
541
|
-
self.cancelled_count = self.type_counts[StateType.CANCELLED]
|
542
|
-
self.final_count = sum(state.is_final() for state in states)
|
543
|
-
self.not_final_count = self.total_count - self.final_count
|
544
|
-
self.paused_count = self.type_counts[StateType.PAUSED]
|
560
|
+
def __init__(self, states: list[State]) -> None:
|
561
|
+
self.states: list[State] = states
|
562
|
+
self.type_counts: dict[StateType, int] = self._get_type_counts(states)
|
563
|
+
self.total_count: int = len(states)
|
564
|
+
self.cancelled_count: int = self.type_counts[StateType.CANCELLED]
|
565
|
+
self.final_count: int = sum(state.is_final() for state in states)
|
566
|
+
self.not_final_count: int = self.total_count - self.final_count
|
567
|
+
self.paused_count: int = self.type_counts[StateType.PAUSED]
|
545
568
|
|
546
569
|
@property
|
547
|
-
def fail_count(self):
|
570
|
+
def fail_count(self) -> int:
|
548
571
|
return self.type_counts[StateType.FAILED] + self.type_counts[StateType.CRASHED]
|
549
572
|
|
550
573
|
def all_completed(self) -> bool:
|
@@ -586,7 +609,7 @@ class StateGroup:
|
|
586
609
|
return f"StateGroup<{self.counts_message()}>"
|
587
610
|
|
588
611
|
|
589
|
-
def _traced(cls: Type[State[R]], **kwargs: Any) -> State[R]:
|
612
|
+
def _traced(cls: Type["State[R]"], **kwargs: Any) -> "State[R]":
|
590
613
|
state_details = StateDetails.model_validate(kwargs.pop("state_details", {}))
|
591
614
|
|
592
615
|
carrier = {}
|
@@ -597,10 +620,10 @@ def _traced(cls: Type[State[R]], **kwargs: Any) -> State[R]:
|
|
597
620
|
|
598
621
|
|
599
622
|
def Scheduled(
|
600
|
-
cls: Type[State[R]] = State,
|
623
|
+
cls: Type["State[R]"] = State,
|
601
624
|
scheduled_time: Optional[datetime.datetime] = None,
|
602
625
|
**kwargs: Any,
|
603
|
-
) -> State[R]:
|
626
|
+
) -> "State[R]":
|
604
627
|
"""Convenience function for creating `Scheduled` states.
|
605
628
|
|
606
629
|
Returns:
|
@@ -616,7 +639,7 @@ def Scheduled(
|
|
616
639
|
return _traced(cls, type=StateType.SCHEDULED, state_details=state_details, **kwargs)
|
617
640
|
|
618
641
|
|
619
|
-
def Completed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
642
|
+
def Completed(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
620
643
|
"""Convenience function for creating `Completed` states.
|
621
644
|
|
622
645
|
Returns:
|
@@ -626,7 +649,7 @@ def Completed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
626
649
|
return _traced(cls, type=StateType.COMPLETED, **kwargs)
|
627
650
|
|
628
651
|
|
629
|
-
def Running(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
652
|
+
def Running(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
630
653
|
"""Convenience function for creating `Running` states.
|
631
654
|
|
632
655
|
Returns:
|
@@ -635,7 +658,7 @@ def Running(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
635
658
|
return _traced(cls, type=StateType.RUNNING, **kwargs)
|
636
659
|
|
637
660
|
|
638
|
-
def Failed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
661
|
+
def Failed(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
639
662
|
"""Convenience function for creating `Failed` states.
|
640
663
|
|
641
664
|
Returns:
|
@@ -644,7 +667,7 @@ def Failed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
644
667
|
return _traced(cls, type=StateType.FAILED, **kwargs)
|
645
668
|
|
646
669
|
|
647
|
-
def Crashed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
670
|
+
def Crashed(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
648
671
|
"""Convenience function for creating `Crashed` states.
|
649
672
|
|
650
673
|
Returns:
|
@@ -653,7 +676,7 @@ def Crashed(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
653
676
|
return _traced(cls, type=StateType.CRASHED, **kwargs)
|
654
677
|
|
655
678
|
|
656
|
-
def Cancelling(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
679
|
+
def Cancelling(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
657
680
|
"""Convenience function for creating `Cancelling` states.
|
658
681
|
|
659
682
|
Returns:
|
@@ -662,7 +685,7 @@ def Cancelling(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
662
685
|
return _traced(cls, type=StateType.CANCELLING, **kwargs)
|
663
686
|
|
664
687
|
|
665
|
-
def Cancelled(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
688
|
+
def Cancelled(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
666
689
|
"""Convenience function for creating `Cancelled` states.
|
667
690
|
|
668
691
|
Returns:
|
@@ -671,7 +694,7 @@ def Cancelled(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
671
694
|
return _traced(cls, type=StateType.CANCELLED, **kwargs)
|
672
695
|
|
673
696
|
|
674
|
-
def Pending(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
697
|
+
def Pending(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
675
698
|
"""Convenience function for creating `Pending` states.
|
676
699
|
|
677
700
|
Returns:
|
@@ -681,13 +704,13 @@ def Pending(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
681
704
|
|
682
705
|
|
683
706
|
def Paused(
|
684
|
-
cls: Type[State[R]] = State,
|
707
|
+
cls: Type["State[R]"] = State,
|
685
708
|
timeout_seconds: Optional[int] = None,
|
686
709
|
pause_expiration_time: Optional[datetime.datetime] = None,
|
687
710
|
reschedule: bool = False,
|
688
711
|
pause_key: Optional[str] = None,
|
689
712
|
**kwargs: Any,
|
690
|
-
) -> State[R]:
|
713
|
+
) -> "State[R]":
|
691
714
|
"""Convenience function for creating `Paused` states.
|
692
715
|
|
693
716
|
Returns:
|
@@ -717,12 +740,12 @@ def Paused(
|
|
717
740
|
|
718
741
|
|
719
742
|
def Suspended(
|
720
|
-
cls: Type[State[R]] = State,
|
743
|
+
cls: Type["State[R]"] = State,
|
721
744
|
timeout_seconds: Optional[int] = None,
|
722
745
|
pause_expiration_time: Optional[datetime.datetime] = None,
|
723
746
|
pause_key: Optional[str] = None,
|
724
747
|
**kwargs: Any,
|
725
|
-
):
|
748
|
+
) -> "State[R]":
|
726
749
|
"""Convenience function for creating `Suspended` states.
|
727
750
|
|
728
751
|
Returns:
|
@@ -740,10 +763,10 @@ def Suspended(
|
|
740
763
|
|
741
764
|
|
742
765
|
def AwaitingRetry(
|
743
|
-
cls: Type[State[R]] = State,
|
766
|
+
cls: Type["State[R]"] = State,
|
744
767
|
scheduled_time: Optional[datetime.datetime] = None,
|
745
768
|
**kwargs: Any,
|
746
|
-
) -> State[R]:
|
769
|
+
) -> "State[R]":
|
747
770
|
"""Convenience function for creating `AwaitingRetry` states.
|
748
771
|
|
749
772
|
Returns:
|
@@ -755,10 +778,10 @@ def AwaitingRetry(
|
|
755
778
|
|
756
779
|
|
757
780
|
def AwaitingConcurrencySlot(
|
758
|
-
cls: Type[State[R]] = State,
|
781
|
+
cls: Type["State[R]"] = State,
|
759
782
|
scheduled_time: Optional[datetime.datetime] = None,
|
760
783
|
**kwargs: Any,
|
761
|
-
) -> State[R]:
|
784
|
+
) -> "State[R]":
|
762
785
|
"""Convenience function for creating `AwaitingConcurrencySlot` states.
|
763
786
|
|
764
787
|
Returns:
|
@@ -769,7 +792,7 @@ def AwaitingConcurrencySlot(
|
|
769
792
|
)
|
770
793
|
|
771
794
|
|
772
|
-
def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
795
|
+
def Retrying(cls: Type["State[R]"] = State, **kwargs: Any) -> "State[R]":
|
773
796
|
"""Convenience function for creating `Retrying` states.
|
774
797
|
|
775
798
|
Returns:
|
@@ -779,10 +802,10 @@ def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
|
|
779
802
|
|
780
803
|
|
781
804
|
def Late(
|
782
|
-
cls: Type[State[R]] = State,
|
805
|
+
cls: Type["State[R]"] = State,
|
783
806
|
scheduled_time: Optional[datetime.datetime] = None,
|
784
807
|
**kwargs: Any,
|
785
|
-
) -> State[R]:
|
808
|
+
) -> "State[R]":
|
786
809
|
"""Convenience function for creating `Late` states.
|
787
810
|
|
788
811
|
Returns:
|
prefect/task_engine.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import asyncio
|
2
4
|
import inspect
|
3
5
|
import logging
|
@@ -28,8 +30,9 @@ from uuid import UUID
|
|
28
30
|
import anyio
|
29
31
|
import pendulum
|
30
32
|
from opentelemetry import trace
|
31
|
-
from typing_extensions import ParamSpec
|
33
|
+
from typing_extensions import ParamSpec, Self
|
32
34
|
|
35
|
+
from prefect.cache_policies import CachePolicy
|
33
36
|
from prefect.client.orchestration import PrefectClient, SyncPrefectClient, get_client
|
34
37
|
from prefect.client.schemas import TaskRun
|
35
38
|
from prefect.client.schemas.objects import State, TaskRunInput
|
@@ -55,7 +58,7 @@ from prefect.exceptions import (
|
|
55
58
|
from prefect.logging.loggers import get_logger, patch_print, task_run_logger
|
56
59
|
from prefect.results import (
|
57
60
|
ResultRecord,
|
58
|
-
_format_user_supplied_storage_key,
|
61
|
+
_format_user_supplied_storage_key, # type: ignore[reportPrivateUsage]
|
59
62
|
get_result_store,
|
60
63
|
should_persist_result,
|
61
64
|
)
|
@@ -115,20 +118,20 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
115
118
|
# holds the return value from the user code
|
116
119
|
_return_value: Union[R, Type[NotSet]] = NotSet
|
117
120
|
# holds the exception raised by the user code, if any
|
118
|
-
_raised: Union[Exception, Type[NotSet]] = NotSet
|
121
|
+
_raised: Union[Exception, BaseException, Type[NotSet]] = NotSet
|
119
122
|
_initial_run_context: Optional[TaskRunContext] = None
|
120
123
|
_is_started: bool = False
|
121
124
|
_task_name_set: bool = False
|
122
125
|
_last_event: Optional[PrefectEvent] = None
|
123
126
|
_telemetry: RunTelemetry = field(default_factory=RunTelemetry)
|
124
127
|
|
125
|
-
def __post_init__(self):
|
128
|
+
def __post_init__(self) -> None:
|
126
129
|
if self.parameters is None:
|
127
130
|
self.parameters = {}
|
128
131
|
|
129
132
|
@property
|
130
133
|
def state(self) -> State:
|
131
|
-
if not self.task_run:
|
134
|
+
if not self.task_run or not self.task_run.state:
|
132
135
|
raise ValueError("Task run is not set")
|
133
136
|
return self.task_run.state
|
134
137
|
|
@@ -142,8 +145,8 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
142
145
|
return False
|
143
146
|
|
144
147
|
def compute_transaction_key(self) -> Optional[str]:
|
145
|
-
key = None
|
146
|
-
if self.task.cache_policy:
|
148
|
+
key: Optional[str] = None
|
149
|
+
if self.task.cache_policy and isinstance(self.task.cache_policy, CachePolicy):
|
147
150
|
flow_run_context = FlowRunContext.get()
|
148
151
|
task_run_context = TaskRunContext.get()
|
149
152
|
|
@@ -153,10 +156,12 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
153
156
|
parameters = None
|
154
157
|
|
155
158
|
try:
|
159
|
+
if not task_run_context:
|
160
|
+
raise ValueError("Task run context is not set")
|
156
161
|
key = self.task.cache_policy.compute_key(
|
157
162
|
task_ctx=task_run_context,
|
158
|
-
inputs=self.parameters,
|
159
|
-
flow_parameters=parameters,
|
163
|
+
inputs=self.parameters or {},
|
164
|
+
flow_parameters=parameters or {},
|
160
165
|
)
|
161
166
|
except Exception:
|
162
167
|
self.logger.exception(
|
@@ -169,7 +174,7 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
169
174
|
|
170
175
|
def _resolve_parameters(self):
|
171
176
|
if not self.parameters:
|
172
|
-
return
|
177
|
+
return None
|
173
178
|
|
174
179
|
resolved_parameters = {}
|
175
180
|
for parameter, value in self.parameters.items():
|
@@ -227,10 +232,8 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
227
232
|
if self.task_run and self.task_run.start_time and not self.task_run.end_time:
|
228
233
|
self.task_run.end_time = state.timestamp
|
229
234
|
|
230
|
-
if self.
|
231
|
-
self.task_run.total_run_time +=
|
232
|
-
state.timestamp - self.task_run.state.timestamp
|
233
|
-
)
|
235
|
+
if self.state.is_running():
|
236
|
+
self.task_run.total_run_time += state.timestamp - self.state.timestamp
|
234
237
|
|
235
238
|
def is_running(self) -> bool:
|
236
239
|
"""Whether or not the engine is currently running a task."""
|
@@ -238,7 +241,7 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
238
241
|
return False
|
239
242
|
return task_run.state.is_running() or task_run.state.is_scheduled()
|
240
243
|
|
241
|
-
def log_finished_message(self):
|
244
|
+
def log_finished_message(self) -> None:
|
242
245
|
if not self.task_run:
|
243
246
|
return
|
244
247
|
|
@@ -294,6 +297,7 @@ class BaseTaskRunEngine(Generic[P, R]):
|
|
294
297
|
|
295
298
|
@dataclass
|
296
299
|
class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
300
|
+
task_run: Optional[TaskRun] = None
|
297
301
|
_client: Optional[SyncPrefectClient] = None
|
298
302
|
|
299
303
|
@property
|
@@ -336,7 +340,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
336
340
|
)
|
337
341
|
return False
|
338
342
|
|
339
|
-
def call_hooks(self, state: Optional[State] = None):
|
343
|
+
def call_hooks(self, state: Optional[State] = None) -> None:
|
340
344
|
if state is None:
|
341
345
|
state = self.state
|
342
346
|
task = self.task
|
@@ -371,7 +375,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
371
375
|
else:
|
372
376
|
self.logger.info(f"Hook {hook_name!r} finished running successfully")
|
373
377
|
|
374
|
-
def begin_run(self):
|
378
|
+
def begin_run(self) -> None:
|
375
379
|
try:
|
376
380
|
self._resolve_parameters()
|
377
381
|
self._set_custom_task_run_name()
|
@@ -390,6 +394,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
390
394
|
|
391
395
|
new_state = Running()
|
392
396
|
|
397
|
+
assert self.task_run is not None, "Task run is not set"
|
393
398
|
self.task_run.start_time = new_state.timestamp
|
394
399
|
|
395
400
|
flow_run_context = FlowRunContext.get()
|
@@ -406,7 +411,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
406
411
|
# result reference that no longer exists
|
407
412
|
if state.is_completed():
|
408
413
|
try:
|
409
|
-
state.result(retry_result_failure=False, _sync=True)
|
414
|
+
state.result(retry_result_failure=False, _sync=True) # type: ignore[reportCallIssue]
|
410
415
|
except Exception:
|
411
416
|
state = self.set_state(new_state, force=True)
|
412
417
|
|
@@ -422,7 +427,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
422
427
|
time.sleep(interval)
|
423
428
|
state = self.set_state(new_state)
|
424
429
|
|
425
|
-
def set_state(self, state: State, force: bool = False) -> State:
|
430
|
+
def set_state(self, state: State[R], force: bool = False) -> State[R]:
|
426
431
|
last_state = self.state
|
427
432
|
if not self.task_run:
|
428
433
|
raise ValueError("Task run is not set")
|
@@ -537,7 +542,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
537
542
|
new_state = Retrying()
|
538
543
|
|
539
544
|
self.logger.info(
|
540
|
-
"Task run failed with exception: %r -
|
545
|
+
"Task run failed with exception: %r - Retry %s/%s will start %s",
|
541
546
|
exc,
|
542
547
|
self.retries + 1,
|
543
548
|
self.task.retries,
|
@@ -545,7 +550,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
545
550
|
)
|
546
551
|
|
547
552
|
self.set_state(new_state, force=True)
|
548
|
-
self.retries = self.retries + 1
|
553
|
+
self.retries: int = self.retries + 1
|
549
554
|
return True
|
550
555
|
elif self.retries >= self.task.retries:
|
551
556
|
self.logger.error(
|
@@ -639,7 +644,9 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
639
644
|
stack.enter_context(ConcurrencyContextV1())
|
640
645
|
stack.enter_context(ConcurrencyContext())
|
641
646
|
|
642
|
-
self.logger = task_run_logger(
|
647
|
+
self.logger: "logging.Logger" = task_run_logger(
|
648
|
+
task_run=self.task_run, task=self.task
|
649
|
+
) # type: ignore
|
643
650
|
|
644
651
|
yield
|
645
652
|
|
@@ -648,7 +655,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
648
655
|
self,
|
649
656
|
task_run_id: Optional[UUID] = None,
|
650
657
|
dependencies: Optional[dict[str, set[TaskRunInput]]] = None,
|
651
|
-
) -> Generator[
|
658
|
+
) -> Generator[Self, Any, Any]:
|
652
659
|
"""
|
653
660
|
Enters a client context and creates a task run if needed.
|
654
661
|
"""
|
@@ -718,7 +725,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
718
725
|
self._is_started = False
|
719
726
|
self._client = None
|
720
727
|
|
721
|
-
async def wait_until_ready(self):
|
728
|
+
async def wait_until_ready(self) -> None:
|
722
729
|
"""Waits until the scheduled time (if its the future), then enters Running."""
|
723
730
|
if scheduled_time := self.state.state_details.scheduled_time:
|
724
731
|
sleep_time = (scheduled_time - pendulum.now("utc")).total_seconds()
|
@@ -825,6 +832,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
825
832
|
|
826
833
|
@dataclass
|
827
834
|
class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
835
|
+
task_run: TaskRun | None = None
|
828
836
|
_client: Optional[PrefectClient] = None
|
829
837
|
|
830
838
|
@property
|
@@ -866,7 +874,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
866
874
|
)
|
867
875
|
return False
|
868
876
|
|
869
|
-
async def call_hooks(self, state: Optional[State] = None):
|
877
|
+
async def call_hooks(self, state: Optional[State] = None) -> None:
|
870
878
|
if state is None:
|
871
879
|
state = self.state
|
872
880
|
task = self.task
|
@@ -901,7 +909,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
901
909
|
else:
|
902
910
|
self.logger.info(f"Hook {hook_name!r} finished running successfully")
|
903
911
|
|
904
|
-
async def begin_run(self):
|
912
|
+
async def begin_run(self) -> None:
|
905
913
|
try:
|
906
914
|
self._resolve_parameters()
|
907
915
|
self._set_custom_task_run_name()
|
@@ -1067,7 +1075,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1067
1075
|
new_state = Retrying()
|
1068
1076
|
|
1069
1077
|
self.logger.info(
|
1070
|
-
"Task run failed with exception: %r -
|
1078
|
+
"Task run failed with exception: %r - Retry %s/%s will start %s",
|
1071
1079
|
exc,
|
1072
1080
|
self.retries + 1,
|
1073
1081
|
self.task.retries,
|
@@ -1075,7 +1083,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1075
1083
|
)
|
1076
1084
|
|
1077
1085
|
await self.set_state(new_state, force=True)
|
1078
|
-
self.retries = self.retries + 1
|
1086
|
+
self.retries: int = self.retries + 1
|
1079
1087
|
return True
|
1080
1088
|
elif self.retries >= self.task.retries:
|
1081
1089
|
self.logger.error(
|
@@ -1169,7 +1177,9 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1169
1177
|
)
|
1170
1178
|
stack.enter_context(ConcurrencyContext())
|
1171
1179
|
|
1172
|
-
self.logger = task_run_logger(
|
1180
|
+
self.logger: "logging.Logger" = task_run_logger(
|
1181
|
+
task_run=self.task_run, task=self.task
|
1182
|
+
) # type: ignore
|
1173
1183
|
|
1174
1184
|
yield
|
1175
1185
|
|
@@ -1178,7 +1188,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1178
1188
|
self,
|
1179
1189
|
task_run_id: Optional[UUID] = None,
|
1180
1190
|
dependencies: Optional[dict[str, set[TaskRunInput]]] = None,
|
1181
|
-
) -> AsyncGenerator[
|
1191
|
+
) -> AsyncGenerator[Self, Any]:
|
1182
1192
|
"""
|
1183
1193
|
Enters a client context and creates a task run if needed.
|
1184
1194
|
"""
|
@@ -1246,7 +1256,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1246
1256
|
self._is_started = False
|
1247
1257
|
self._client = None
|
1248
1258
|
|
1249
|
-
async def wait_until_ready(self):
|
1259
|
+
async def wait_until_ready(self) -> None:
|
1250
1260
|
"""Waits until the scheduled time (if its the future), then enters Running."""
|
1251
1261
|
if scheduled_time := self.state.state_details.scheduled_time:
|
1252
1262
|
sleep_time = (scheduled_time - pendulum.now("utc")).total_seconds()
|
@@ -1341,7 +1351,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1341
1351
|
if transaction.is_committed():
|
1342
1352
|
result = transaction.read()
|
1343
1353
|
else:
|
1344
|
-
if self.task_run.tags:
|
1354
|
+
if self.task_run and self.task_run.tags:
|
1345
1355
|
# Acquire a concurrency slot for each tag, but only if a limit
|
1346
1356
|
# matching the tag already exists.
|
1347
1357
|
async with aconcurrency(list(self.task_run.tags), self.task_run.id):
|
@@ -1546,7 +1556,7 @@ def run_task(
|
|
1546
1556
|
Returns:
|
1547
1557
|
The result of the task run
|
1548
1558
|
"""
|
1549
|
-
kwargs = dict(
|
1559
|
+
kwargs: dict[str, Any] = dict(
|
1550
1560
|
task=task,
|
1551
1561
|
task_run_id=task_run_id,
|
1552
1562
|
task_run=task_run,
|