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.
- prefect/__init__.py +3 -0
- prefect/_internal/compatibility/migration.py +1 -1
- prefect/_internal/concurrency/api.py +52 -52
- prefect/_internal/concurrency/calls.py +59 -35
- prefect/_internal/concurrency/cancellation.py +34 -18
- prefect/_internal/concurrency/event_loop.py +7 -6
- prefect/_internal/concurrency/threads.py +41 -33
- prefect/_internal/concurrency/waiters.py +28 -21
- prefect/_internal/pydantic/v1_schema.py +2 -2
- prefect/_internal/pydantic/v2_schema.py +10 -9
- prefect/_internal/schemas/bases.py +9 -7
- prefect/_internal/schemas/validators.py +2 -1
- prefect/_version.py +3 -3
- prefect/automations.py +53 -47
- prefect/blocks/abstract.py +12 -10
- prefect/blocks/core.py +4 -2
- prefect/cache_policies.py +11 -11
- prefect/client/__init__.py +3 -1
- prefect/client/base.py +36 -37
- prefect/client/cloud.py +26 -19
- prefect/client/collections.py +2 -2
- prefect/client/orchestration.py +342 -273
- prefect/client/schemas/__init__.py +24 -0
- prefect/client/schemas/actions.py +123 -116
- prefect/client/schemas/objects.py +110 -81
- prefect/client/schemas/responses.py +18 -18
- prefect/client/schemas/schedules.py +136 -93
- prefect/client/subscriptions.py +28 -14
- prefect/client/utilities.py +32 -36
- prefect/concurrency/asyncio.py +6 -9
- prefect/concurrency/sync.py +35 -5
- prefect/context.py +39 -31
- prefect/deployments/flow_runs.py +3 -5
- prefect/docker/__init__.py +1 -1
- prefect/events/schemas/events.py +25 -20
- prefect/events/utilities.py +1 -2
- prefect/filesystems.py +3 -3
- prefect/flow_engine.py +61 -21
- prefect/flow_runs.py +3 -3
- prefect/flows.py +214 -170
- prefect/logging/configuration.py +1 -1
- prefect/logging/highlighters.py +1 -2
- prefect/logging/loggers.py +30 -20
- prefect/main.py +17 -24
- prefect/runner/runner.py +43 -21
- prefect/runner/server.py +30 -32
- prefect/runner/submit.py +3 -6
- prefect/runner/utils.py +6 -6
- prefect/runtime/flow_run.py +7 -0
- prefect/settings/constants.py +2 -2
- prefect/settings/legacy.py +1 -1
- prefect/settings/models/server/events.py +10 -0
- prefect/task_engine.py +72 -19
- prefect/task_runners.py +2 -2
- prefect/tasks.py +46 -33
- prefect/telemetry/bootstrap.py +15 -2
- prefect/telemetry/run_telemetry.py +107 -0
- prefect/transactions.py +14 -14
- prefect/types/__init__.py +1 -4
- prefect/utilities/_engine.py +96 -0
- prefect/utilities/annotations.py +25 -18
- prefect/utilities/asyncutils.py +126 -140
- prefect/utilities/callables.py +87 -78
- prefect/utilities/collections.py +278 -117
- prefect/utilities/compat.py +13 -21
- prefect/utilities/context.py +6 -5
- prefect/utilities/dispatch.py +23 -12
- prefect/utilities/dockerutils.py +33 -32
- prefect/utilities/engine.py +126 -239
- prefect/utilities/filesystem.py +18 -15
- prefect/utilities/hashing.py +10 -11
- prefect/utilities/importtools.py +40 -27
- prefect/utilities/math.py +9 -5
- prefect/utilities/names.py +3 -3
- prefect/utilities/processutils.py +121 -57
- prefect/utilities/pydantic.py +41 -36
- prefect/utilities/render_swagger.py +22 -12
- prefect/utilities/schema_tools/__init__.py +2 -1
- prefect/utilities/schema_tools/hydration.py +50 -43
- prefect/utilities/schema_tools/validation.py +52 -42
- prefect/utilities/services.py +13 -12
- prefect/utilities/templating.py +45 -45
- prefect/utilities/text.py +2 -1
- prefect/utilities/timeout.py +4 -4
- prefect/utilities/urls.py +9 -4
- prefect/utilities/visualization.py +46 -24
- prefect/variables.py +9 -8
- prefect/workers/base.py +15 -8
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/METADATA +4 -2
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/RECORD +93 -91
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.5.dist-info → prefect_client-3.1.6.dist-info}/top_level.txt +0 -0
prefect/transactions.py
CHANGED
@@ -23,7 +23,7 @@ from prefect.exceptions import (
|
|
23
23
|
MissingContextError,
|
24
24
|
SerializationError,
|
25
25
|
)
|
26
|
-
from prefect.logging.loggers import get_logger, get_run_logger
|
26
|
+
from prefect.logging.loggers import LoggingAdapter, get_logger, get_run_logger
|
27
27
|
from prefect.records import RecordStore
|
28
28
|
from prefect.records.base import TransactionRecord
|
29
29
|
from prefect.results import (
|
@@ -32,9 +32,9 @@ from prefect.results import (
|
|
32
32
|
ResultStore,
|
33
33
|
get_result_store,
|
34
34
|
)
|
35
|
+
from prefect.utilities._engine import get_hook_name
|
35
36
|
from prefect.utilities.annotations import NotSet
|
36
37
|
from prefect.utilities.collections import AutoEnum
|
37
|
-
from prefect.utilities.engine import _get_hook_name
|
38
38
|
|
39
39
|
|
40
40
|
class IsolationLevel(AutoEnum):
|
@@ -72,13 +72,13 @@ class Transaction(ContextModel):
|
|
72
72
|
default_factory=list
|
73
73
|
)
|
74
74
|
overwrite: bool = False
|
75
|
-
logger: Union[logging.Logger,
|
75
|
+
logger: Union[logging.Logger, LoggingAdapter] = Field(
|
76
76
|
default_factory=partial(get_logger, "transactions")
|
77
77
|
)
|
78
78
|
write_on_commit: bool = True
|
79
79
|
_stored_values: Dict[str, Any] = PrivateAttr(default_factory=dict)
|
80
80
|
_staged_value: Any = None
|
81
|
-
__var__: ContextVar = ContextVar("transaction")
|
81
|
+
__var__: ContextVar[Self] = ContextVar("transaction")
|
82
82
|
|
83
83
|
def set(self, name: str, value: Any) -> None:
|
84
84
|
"""
|
@@ -209,7 +209,7 @@ class Transaction(ContextModel):
|
|
209
209
|
self._token = self.__var__.set(self)
|
210
210
|
return self
|
211
211
|
|
212
|
-
def __exit__(self, *exc_info):
|
212
|
+
def __exit__(self, *exc_info: Any):
|
213
213
|
exc_type, exc_val, _ = exc_info
|
214
214
|
if not self._token:
|
215
215
|
raise RuntimeError(
|
@@ -254,7 +254,7 @@ class Transaction(ContextModel):
|
|
254
254
|
):
|
255
255
|
self.state = TransactionState.COMMITTED
|
256
256
|
|
257
|
-
def read(self) -> Union["BaseResult", ResultRecord, None]:
|
257
|
+
def read(self) -> Union["BaseResult[Any]", ResultRecord[Any], None]:
|
258
258
|
if self.store and self.key:
|
259
259
|
record = self.store.read(key=self.key)
|
260
260
|
if isinstance(record, ResultRecord):
|
@@ -354,8 +354,8 @@ class Transaction(ContextModel):
|
|
354
354
|
self.rollback()
|
355
355
|
return False
|
356
356
|
|
357
|
-
def run_hook(self, hook, hook_type: str) -> None:
|
358
|
-
hook_name =
|
357
|
+
def run_hook(self, hook: Callable[..., Any], hook_type: str) -> None:
|
358
|
+
hook_name = get_hook_name(hook)
|
359
359
|
# Undocumented way to disable logging for a hook. Subject to change.
|
360
360
|
should_log = getattr(hook, "log_on_run", True)
|
361
361
|
|
@@ -379,8 +379,8 @@ class Transaction(ContextModel):
|
|
379
379
|
def stage(
|
380
380
|
self,
|
381
381
|
value: Any,
|
382
|
-
on_rollback_hooks: Optional[
|
383
|
-
on_commit_hooks: Optional[
|
382
|
+
on_rollback_hooks: Optional[list[Callable[..., Any]]] = None,
|
383
|
+
on_commit_hooks: Optional[list[Callable[..., Any]]] = None,
|
384
384
|
) -> None:
|
385
385
|
"""
|
386
386
|
Stage a value to be committed later.
|
@@ -441,7 +441,7 @@ def transaction(
|
|
441
441
|
isolation_level: Optional[IsolationLevel] = None,
|
442
442
|
overwrite: bool = False,
|
443
443
|
write_on_commit: bool = True,
|
444
|
-
logger: Union[logging.Logger,
|
444
|
+
logger: Optional[Union[logging.Logger, LoggingAdapter]] = None,
|
445
445
|
) -> Generator[Transaction, None, None]:
|
446
446
|
"""
|
447
447
|
A context manager for opening and managing a transaction.
|
@@ -465,9 +465,9 @@ def transaction(
|
|
465
465
|
store = get_result_store()
|
466
466
|
|
467
467
|
try:
|
468
|
-
|
468
|
+
_logger: Union[logging.Logger, LoggingAdapter] = logger or get_run_logger()
|
469
469
|
except MissingContextError:
|
470
|
-
|
470
|
+
_logger = get_logger("transactions")
|
471
471
|
|
472
472
|
with Transaction(
|
473
473
|
key=key,
|
@@ -476,6 +476,6 @@ def transaction(
|
|
476
476
|
isolation_level=isolation_level,
|
477
477
|
overwrite=overwrite,
|
478
478
|
write_on_commit=write_on_commit,
|
479
|
-
logger=
|
479
|
+
logger=_logger,
|
480
480
|
) as txn:
|
481
481
|
yield txn
|
prefect/types/__init__.py
CHANGED
@@ -96,7 +96,7 @@ def cast_none_to_empty_dict(value: Any) -> dict[str, Any]:
|
|
96
96
|
|
97
97
|
|
98
98
|
KeyValueLabels = Annotated[
|
99
|
-
|
99
|
+
Dict[str, Union[StrictBool, StrictInt, StrictFloat, str]],
|
100
100
|
BeforeValidator(cast_none_to_empty_dict),
|
101
101
|
]
|
102
102
|
|
@@ -149,9 +149,6 @@ LogLevel = Annotated[
|
|
149
149
|
]
|
150
150
|
|
151
151
|
|
152
|
-
KeyValueLabels: TypeAlias = dict[str, Union[StrictBool, StrictInt, StrictFloat, str]]
|
153
|
-
|
154
|
-
|
155
152
|
def convert_none_to_empty_dict(v: Optional[KeyValueLabels]) -> KeyValueLabels:
|
156
153
|
return v or {}
|
157
154
|
|
@@ -0,0 +1,96 @@
|
|
1
|
+
"""Internal engine utilities"""
|
2
|
+
|
3
|
+
|
4
|
+
from collections.abc import Callable
|
5
|
+
from functools import partial
|
6
|
+
from typing import TYPE_CHECKING, Any, Union
|
7
|
+
from uuid import uuid4
|
8
|
+
|
9
|
+
from prefect.context import FlowRunContext
|
10
|
+
from prefect.flows import Flow
|
11
|
+
from prefect.tasks import Task, TaskRunNameCallbackWithParameters
|
12
|
+
|
13
|
+
|
14
|
+
def dynamic_key_for_task_run(
|
15
|
+
context: FlowRunContext, task: "Task[..., Any]", stable: bool = True
|
16
|
+
) -> Union[int, str]:
|
17
|
+
if (
|
18
|
+
stable is False or context.detached
|
19
|
+
): # this task is running on remote infrastructure
|
20
|
+
return str(uuid4())
|
21
|
+
elif context.flow_run is None: # this is an autonomous task run
|
22
|
+
context.task_run_dynamic_keys[task.task_key] = getattr(
|
23
|
+
task, "dynamic_key", str(uuid4())
|
24
|
+
)
|
25
|
+
|
26
|
+
elif task.task_key not in context.task_run_dynamic_keys:
|
27
|
+
context.task_run_dynamic_keys[task.task_key] = 0
|
28
|
+
else:
|
29
|
+
dynamic_key = context.task_run_dynamic_keys[task.task_key]
|
30
|
+
if TYPE_CHECKING:
|
31
|
+
assert isinstance(dynamic_key, int)
|
32
|
+
context.task_run_dynamic_keys[task.task_key] = dynamic_key + 1
|
33
|
+
|
34
|
+
return context.task_run_dynamic_keys[task.task_key]
|
35
|
+
|
36
|
+
|
37
|
+
def resolve_custom_flow_run_name(
|
38
|
+
flow: "Flow[..., Any]", parameters: dict[str, Any]
|
39
|
+
) -> str:
|
40
|
+
if callable(flow.flow_run_name):
|
41
|
+
flow_run_name = flow.flow_run_name()
|
42
|
+
if not TYPE_CHECKING:
|
43
|
+
if not isinstance(flow_run_name, str):
|
44
|
+
raise TypeError(
|
45
|
+
f"Callable {flow.flow_run_name} for 'flow_run_name' returned type"
|
46
|
+
f" {type(flow_run_name).__name__} but a string is required."
|
47
|
+
)
|
48
|
+
elif isinstance(flow.flow_run_name, str):
|
49
|
+
flow_run_name = flow.flow_run_name.format(**parameters)
|
50
|
+
else:
|
51
|
+
raise TypeError(
|
52
|
+
"Expected string or callable for 'flow_run_name'; got"
|
53
|
+
f" {type(flow.flow_run_name).__name__} instead."
|
54
|
+
)
|
55
|
+
|
56
|
+
return flow_run_name
|
57
|
+
|
58
|
+
|
59
|
+
def resolve_custom_task_run_name(
|
60
|
+
task: "Task[..., Any]", parameters: dict[str, Any]
|
61
|
+
) -> str:
|
62
|
+
if callable(task.task_run_name):
|
63
|
+
# If the callable accepts a 'parameters' kwarg, pass the entire parameters dict
|
64
|
+
if TaskRunNameCallbackWithParameters.is_callback_with_parameters(
|
65
|
+
task.task_run_name
|
66
|
+
):
|
67
|
+
task_run_name = task.task_run_name(parameters=parameters)
|
68
|
+
else:
|
69
|
+
# If it doesn't expect parameters, call it without arguments
|
70
|
+
task_run_name = task.task_run_name()
|
71
|
+
|
72
|
+
if not TYPE_CHECKING:
|
73
|
+
if not isinstance(task_run_name, str):
|
74
|
+
raise TypeError(
|
75
|
+
f"Callable {task.task_run_name} for 'task_run_name' returned type"
|
76
|
+
f" {type(task_run_name).__name__} but a string is required."
|
77
|
+
)
|
78
|
+
elif isinstance(task.task_run_name, str):
|
79
|
+
task_run_name = task.task_run_name.format(**parameters)
|
80
|
+
else:
|
81
|
+
raise TypeError(
|
82
|
+
"Expected string or callable for 'task_run_name'; got"
|
83
|
+
f" {type(task.task_run_name).__name__} instead."
|
84
|
+
)
|
85
|
+
|
86
|
+
return task_run_name
|
87
|
+
|
88
|
+
|
89
|
+
def get_hook_name(hook: Callable[..., Any]) -> str:
|
90
|
+
return (
|
91
|
+
hook.__name__
|
92
|
+
if hasattr(hook, "__name__")
|
93
|
+
else (
|
94
|
+
hook.func.__name__ if isinstance(hook, partial) else hook.__class__.__name__
|
95
|
+
)
|
96
|
+
)
|
prefect/utilities/annotations.py
CHANGED
@@ -1,33 +1,40 @@
|
|
1
1
|
import warnings
|
2
|
-
from
|
3
|
-
from
|
4
|
-
from typing import Generic, TypeVar
|
2
|
+
from operator import itemgetter
|
3
|
+
from typing import Any, cast
|
5
4
|
|
6
|
-
|
5
|
+
from typing_extensions import Self, TypeVar
|
7
6
|
|
7
|
+
T = TypeVar("T", infer_variance=True)
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
):
|
9
|
+
|
10
|
+
class BaseAnnotation(tuple[T]):
|
12
11
|
"""
|
13
12
|
Base class for Prefect annotation types.
|
14
13
|
|
15
|
-
Inherits from `
|
14
|
+
Inherits from `tuple` for unpacking support in other tools.
|
16
15
|
"""
|
17
16
|
|
17
|
+
__slots__ = ()
|
18
|
+
|
19
|
+
def __new__(cls, value: T) -> Self:
|
20
|
+
return super().__new__(cls, (value,))
|
21
|
+
|
22
|
+
# use itemgetter to minimise overhead, just like namedtuple generated code would
|
23
|
+
value: T = cast(T, property(itemgetter(0)))
|
24
|
+
|
18
25
|
def unwrap(self) -> T:
|
19
|
-
return self
|
26
|
+
return self[0]
|
20
27
|
|
21
|
-
def rewrap(self, value: T) ->
|
28
|
+
def rewrap(self, value: T) -> Self:
|
22
29
|
return type(self)(value)
|
23
30
|
|
24
|
-
def __eq__(self, other:
|
31
|
+
def __eq__(self, other: Any) -> bool:
|
25
32
|
if type(self) is not type(other):
|
26
33
|
return False
|
27
|
-
return
|
34
|
+
return super().__eq__(other)
|
28
35
|
|
29
36
|
def __repr__(self) -> str:
|
30
|
-
return f"{type(self).__name__}({self
|
37
|
+
return f"{type(self).__name__}({self[0]!r})"
|
31
38
|
|
32
39
|
|
33
40
|
class unmapped(BaseAnnotation[T]):
|
@@ -38,9 +45,9 @@ class unmapped(BaseAnnotation[T]):
|
|
38
45
|
operation instead of being split.
|
39
46
|
"""
|
40
47
|
|
41
|
-
def __getitem__(self, _) -> T:
|
48
|
+
def __getitem__(self, _: object) -> T: # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride]
|
42
49
|
# Internally, this acts as an infinite array where all items are the same value
|
43
|
-
return
|
50
|
+
return super().__getitem__(0)
|
44
51
|
|
45
52
|
|
46
53
|
class allow_failure(BaseAnnotation[T]):
|
@@ -87,14 +94,14 @@ class quote(BaseAnnotation[T]):
|
|
87
94
|
|
88
95
|
|
89
96
|
# Backwards compatibility stub for `Quote` class
|
90
|
-
class Quote(quote):
|
91
|
-
def
|
97
|
+
class Quote(quote[T]):
|
98
|
+
def __new__(cls, expr: T) -> Self:
|
92
99
|
warnings.warn(
|
93
100
|
"Use of `Quote` is deprecated. Use `quote` instead.",
|
94
101
|
DeprecationWarning,
|
95
102
|
stacklevel=2,
|
96
103
|
)
|
97
|
-
super().
|
104
|
+
return super().__new__(cls, expr)
|
98
105
|
|
99
106
|
|
100
107
|
class NotSet:
|