prefect-client 3.1.15__py3-none-any.whl → 3.2.1__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/objects.py +29 -1
- prefect/_internal/compatibility/deprecated.py +4 -4
- prefect/_internal/compatibility/migration.py +1 -1
- prefect/_internal/concurrency/calls.py +1 -2
- prefect/_internal/concurrency/cancellation.py +2 -4
- prefect/_internal/concurrency/threads.py +3 -3
- prefect/_internal/schemas/bases.py +3 -11
- prefect/_internal/schemas/validators.py +36 -60
- prefect/_result_records.py +235 -0
- prefect/_version.py +3 -3
- prefect/agent.py +1 -0
- prefect/automations.py +4 -8
- prefect/blocks/notifications.py +8 -8
- prefect/cache_policies.py +2 -0
- prefect/client/base.py +7 -8
- prefect/client/collections.py +3 -6
- prefect/client/orchestration/__init__.py +15 -263
- prefect/client/orchestration/_deployments/client.py +14 -6
- prefect/client/orchestration/_flow_runs/client.py +10 -6
- prefect/client/orchestration/_work_pools/__init__.py +0 -0
- prefect/client/orchestration/_work_pools/client.py +598 -0
- prefect/client/orchestration/base.py +9 -2
- prefect/client/schemas/actions.py +66 -2
- prefect/client/schemas/objects.py +22 -50
- prefect/client/schemas/schedules.py +7 -18
- prefect/client/types/flexible_schedule_list.py +2 -1
- prefect/context.py +2 -3
- prefect/deployments/flow_runs.py +1 -1
- prefect/deployments/runner.py +119 -43
- prefect/deployments/schedules.py +7 -1
- prefect/engine.py +4 -9
- prefect/events/schemas/automations.py +4 -2
- prefect/events/utilities.py +15 -13
- prefect/exceptions.py +1 -1
- prefect/flow_engine.py +19 -10
- prefect/flow_runs.py +4 -8
- prefect/flows.py +53 -22
- prefect/infrastructure/__init__.py +1 -0
- prefect/infrastructure/base.py +1 -0
- prefect/infrastructure/provisioners/__init__.py +3 -6
- prefect/infrastructure/provisioners/coiled.py +3 -3
- prefect/infrastructure/provisioners/container_instance.py +1 -0
- prefect/infrastructure/provisioners/ecs.py +6 -6
- prefect/infrastructure/provisioners/modal.py +3 -3
- prefect/input/run_input.py +5 -7
- prefect/locking/filesystem.py +4 -3
- prefect/main.py +1 -1
- prefect/results.py +42 -249
- prefect/runner/runner.py +9 -4
- prefect/runner/server.py +5 -5
- prefect/runner/storage.py +12 -10
- prefect/runner/submit.py +2 -4
- prefect/schedules.py +231 -0
- prefect/serializers.py +5 -5
- prefect/settings/__init__.py +2 -1
- prefect/settings/base.py +3 -3
- prefect/settings/models/root.py +4 -0
- prefect/settings/models/server/services.py +50 -9
- prefect/settings/sources.py +8 -4
- prefect/states.py +42 -11
- prefect/task_engine.py +10 -10
- prefect/task_runners.py +11 -22
- prefect/task_worker.py +9 -9
- prefect/tasks.py +22 -41
- prefect/telemetry/bootstrap.py +4 -6
- prefect/telemetry/services.py +2 -4
- prefect/types/__init__.py +2 -1
- prefect/types/_datetime.py +28 -1
- prefect/utilities/_engine.py +0 -1
- prefect/utilities/asyncutils.py +4 -8
- prefect/utilities/collections.py +13 -22
- prefect/utilities/dispatch.py +2 -4
- prefect/utilities/dockerutils.py +6 -6
- prefect/utilities/importtools.py +1 -68
- prefect/utilities/names.py +1 -1
- prefect/utilities/processutils.py +3 -6
- prefect/utilities/pydantic.py +4 -6
- prefect/utilities/schema_tools/hydration.py +6 -5
- prefect/utilities/templating.py +16 -10
- prefect/utilities/visualization.py +2 -4
- prefect/workers/base.py +3 -3
- prefect/workers/block.py +1 -0
- prefect/workers/cloud.py +1 -0
- prefect/workers/process.py +1 -0
- {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/METADATA +1 -1
- {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/RECORD +89 -85
- {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/top_level.txt +0 -0
prefect/utilities/importtools.py
CHANGED
@@ -2,25 +2,19 @@ import ast
|
|
2
2
|
import importlib
|
3
3
|
import importlib.util
|
4
4
|
import os
|
5
|
-
import runpy
|
6
5
|
import sys
|
7
6
|
import threading
|
8
7
|
import warnings
|
9
8
|
from collections.abc import Iterable, Sequence
|
10
9
|
from importlib.abc import Loader, MetaPathFinder
|
11
10
|
from importlib.machinery import ModuleSpec
|
12
|
-
from io import TextIOWrapper
|
13
11
|
from logging import Logger
|
14
12
|
from pathlib import Path
|
15
|
-
from tempfile import NamedTemporaryFile
|
16
13
|
from types import ModuleType
|
17
|
-
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Optional
|
18
|
-
|
19
|
-
import fsspec # type: ignore # no typing stubs available
|
14
|
+
from typing import TYPE_CHECKING, Any, Callable, NamedTuple, Optional
|
20
15
|
|
21
16
|
from prefect.exceptions import ScriptError
|
22
17
|
from prefect.logging.loggers import get_logger
|
23
|
-
from prefect.utilities.filesystem import filename, is_local_path, tmpchdir
|
24
18
|
|
25
19
|
logger: Logger = get_logger(__name__)
|
26
20
|
|
@@ -84,67 +78,6 @@ def from_qualified_name(name: str) -> Any:
|
|
84
78
|
return getattr(module, attr_name)
|
85
79
|
|
86
80
|
|
87
|
-
def objects_from_script(
|
88
|
-
path: str, text: Optional[Union[str, bytes]] = None
|
89
|
-
) -> dict[str, Any]:
|
90
|
-
"""
|
91
|
-
Run a python script and return all the global variables
|
92
|
-
|
93
|
-
Supports remote paths by copying to a local temporary file.
|
94
|
-
|
95
|
-
WARNING: The Python documentation does not recommend using runpy for this pattern.
|
96
|
-
|
97
|
-
> Furthermore, any functions and classes defined by the executed code are not
|
98
|
-
> guaranteed to work correctly after a runpy function has returned. If that
|
99
|
-
> limitation is not acceptable for a given use case, importlib is likely to be a
|
100
|
-
> more suitable choice than this module.
|
101
|
-
|
102
|
-
The function `load_script_as_module` uses importlib instead and should be used
|
103
|
-
instead for loading objects from scripts.
|
104
|
-
|
105
|
-
Args:
|
106
|
-
path: The path to the script to run
|
107
|
-
text: Optionally, the text of the script. Skips loading the contents if given.
|
108
|
-
|
109
|
-
Returns:
|
110
|
-
A dictionary mapping variable name to value
|
111
|
-
|
112
|
-
Raises:
|
113
|
-
ScriptError: if the script raises an exception during execution
|
114
|
-
"""
|
115
|
-
|
116
|
-
def run_script(run_path: str) -> dict[str, Any]:
|
117
|
-
# Cast to an absolute path before changing directories to ensure relative paths
|
118
|
-
# are not broken
|
119
|
-
abs_run_path = os.path.abspath(run_path)
|
120
|
-
with tmpchdir(run_path):
|
121
|
-
try:
|
122
|
-
return runpy.run_path(abs_run_path)
|
123
|
-
except Exception as exc:
|
124
|
-
raise ScriptError(user_exc=exc, path=path) from exc
|
125
|
-
|
126
|
-
if text:
|
127
|
-
with NamedTemporaryFile(
|
128
|
-
mode="wt" if isinstance(text, str) else "wb",
|
129
|
-
prefix=f"run-{filename(path)}",
|
130
|
-
suffix=".py",
|
131
|
-
) as tmpfile:
|
132
|
-
tmpfile.write(text)
|
133
|
-
tmpfile.flush()
|
134
|
-
return run_script(tmpfile.name)
|
135
|
-
|
136
|
-
else:
|
137
|
-
if not is_local_path(path):
|
138
|
-
# Remote paths need to be local to run
|
139
|
-
with fsspec.open(path) as f: # type: ignore # no typing stubs available
|
140
|
-
if TYPE_CHECKING:
|
141
|
-
assert isinstance(f, TextIOWrapper)
|
142
|
-
contents = f.read()
|
143
|
-
return objects_from_script(path, contents)
|
144
|
-
else:
|
145
|
-
return run_script(path)
|
146
|
-
|
147
|
-
|
148
81
|
def load_script_as_module(path: str) -> ModuleType:
|
149
82
|
"""Execute a script at the given path.
|
150
83
|
|
prefect/utilities/names.py
CHANGED
@@ -68,5 +68,5 @@ def obfuscate_string(s: str, show_tail: bool = False) -> str:
|
|
68
68
|
# take up to 4 characters, but only after the 10th character
|
69
69
|
suffix = s[10:][-4:]
|
70
70
|
if suffix and show_tail:
|
71
|
-
result = f"{result[
|
71
|
+
result = f"{result[: -len(suffix)]}{suffix}"
|
72
72
|
return result
|
@@ -256,8 +256,7 @@ async def run_process(
|
|
256
256
|
task_status: anyio.abc.TaskStatus[T] = ...,
|
257
257
|
task_status_handler: Callable[[anyio.abc.Process], T] = ...,
|
258
258
|
**kwargs: Any,
|
259
|
-
) -> anyio.abc.Process:
|
260
|
-
...
|
259
|
+
) -> anyio.abc.Process: ...
|
261
260
|
|
262
261
|
|
263
262
|
@overload
|
@@ -270,8 +269,7 @@ async def run_process(
|
|
270
269
|
task_status: Optional[anyio.abc.TaskStatus[int]] = ...,
|
271
270
|
task_status_handler: None = None,
|
272
271
|
**kwargs: Any,
|
273
|
-
) -> anyio.abc.Process:
|
274
|
-
...
|
272
|
+
) -> anyio.abc.Process: ...
|
275
273
|
|
276
274
|
|
277
275
|
@overload
|
@@ -284,8 +282,7 @@ async def run_process(
|
|
284
282
|
task_status: Optional[anyio.abc.TaskStatus[T]] = None,
|
285
283
|
task_status_handler: Optional[Callable[[anyio.abc.Process], T]] = None,
|
286
284
|
**kwargs: Any,
|
287
|
-
) -> anyio.abc.Process:
|
288
|
-
...
|
285
|
+
) -> anyio.abc.Process: ...
|
289
286
|
|
290
287
|
|
291
288
|
async def run_process(
|
prefect/utilities/pydantic.py
CHANGED
@@ -52,15 +52,13 @@ def _unreduce_model(model_name: str, json: str) -> Any:
|
|
52
52
|
|
53
53
|
|
54
54
|
@overload
|
55
|
-
def add_cloudpickle_reduction(__model_cls: type[M]) -> type[M]:
|
56
|
-
...
|
55
|
+
def add_cloudpickle_reduction(__model_cls: type[M]) -> type[M]: ...
|
57
56
|
|
58
57
|
|
59
58
|
@overload
|
60
59
|
def add_cloudpickle_reduction(
|
61
60
|
__model_cls: None = None, **kwargs: Any
|
62
|
-
) -> Callable[[type[M]], type[M]]:
|
63
|
-
...
|
61
|
+
) -> Callable[[type[M]], type[M]]: ...
|
64
62
|
|
65
63
|
|
66
64
|
def add_cloudpickle_reduction(
|
@@ -144,7 +142,7 @@ def add_type_dispatch(model_cls: type[M]) -> type[M]:
|
|
144
142
|
|
145
143
|
elif not defines_dispatch_key and defines_type_field:
|
146
144
|
field_type_annotation = model_cls.model_fields["type"].annotation
|
147
|
-
if field_type_annotation
|
145
|
+
if field_type_annotation is not str and field_type_annotation is not None:
|
148
146
|
raise TypeError(
|
149
147
|
f"Model class {model_cls.__name__!r} defines a 'type' field with "
|
150
148
|
f"type {field_type_annotation.__name__!r} but it must be 'str'."
|
@@ -169,7 +167,7 @@ def add_type_dispatch(model_cls: type[M]) -> type[M]:
|
|
169
167
|
def __init__(__pydantic_self__: M, **data: Any) -> None:
|
170
168
|
type_string = (
|
171
169
|
get_dispatch_key(__pydantic_self__)
|
172
|
-
if type(__pydantic_self__)
|
170
|
+
if type(__pydantic_self__) is not model_cls
|
173
171
|
else "__base__"
|
174
172
|
)
|
175
173
|
data.setdefault("type", type_string)
|
@@ -8,11 +8,6 @@ from pydantic import BaseModel, Field
|
|
8
8
|
from sqlalchemy.ext.asyncio import AsyncSession
|
9
9
|
from typing_extensions import Self, TypeAlias, TypeIs
|
10
10
|
|
11
|
-
from prefect.server.utilities.user_templates import (
|
12
|
-
TemplateSecurityError,
|
13
|
-
render_user_template_sync,
|
14
|
-
validate_user_template,
|
15
|
-
)
|
16
11
|
from prefect.types import StrictVariableValue
|
17
12
|
|
18
13
|
|
@@ -229,6 +224,12 @@ def json_handler(obj: dict[str, Any], ctx: HydrationContext):
|
|
229
224
|
|
230
225
|
@handler("jinja")
|
231
226
|
def jinja_handler(obj: dict[str, Any], ctx: HydrationContext) -> Any:
|
227
|
+
from prefect.server.utilities.user_templates import (
|
228
|
+
TemplateSecurityError,
|
229
|
+
render_user_template_sync,
|
230
|
+
validate_user_template,
|
231
|
+
)
|
232
|
+
|
232
233
|
if "template" in obj:
|
233
234
|
if isinstance(obj["template"], dict):
|
234
235
|
dehydrated_jinja = _hydrate(obj["template"], ctx)
|
prefect/utilities/templating.py
CHANGED
@@ -4,6 +4,7 @@ import re
|
|
4
4
|
from typing import (
|
5
5
|
TYPE_CHECKING,
|
6
6
|
Any,
|
7
|
+
Callable,
|
7
8
|
Literal,
|
8
9
|
NamedTuple,
|
9
10
|
Optional,
|
@@ -92,22 +93,19 @@ def find_placeholders(template: T) -> set[Placeholder]:
|
|
92
93
|
@overload
|
93
94
|
def apply_values(
|
94
95
|
template: T, values: dict[str, Any], remove_notset: Literal[True] = True
|
95
|
-
) -> T:
|
96
|
-
...
|
96
|
+
) -> T: ...
|
97
97
|
|
98
98
|
|
99
99
|
@overload
|
100
100
|
def apply_values(
|
101
101
|
template: T, values: dict[str, Any], remove_notset: Literal[False] = False
|
102
|
-
) -> Union[T, type[NotSet]]:
|
103
|
-
...
|
102
|
+
) -> Union[T, type[NotSet]]: ...
|
104
103
|
|
105
104
|
|
106
105
|
@overload
|
107
106
|
def apply_values(
|
108
107
|
template: T, values: dict[str, Any], remove_notset: bool = False
|
109
|
-
) -> Union[T, type[NotSet]]:
|
110
|
-
...
|
108
|
+
) -> Union[T, type[NotSet]]: ...
|
111
109
|
|
112
110
|
|
113
111
|
def apply_values(
|
@@ -197,11 +195,13 @@ def apply_values(
|
|
197
195
|
|
198
196
|
@inject_client
|
199
197
|
async def resolve_block_document_references(
|
200
|
-
template: T,
|
198
|
+
template: T,
|
199
|
+
client: Optional["PrefectClient"] = None,
|
200
|
+
value_transformer: Optional[Callable[[str, Any], Any]] = None,
|
201
201
|
) -> Union[T, dict[str, Any]]:
|
202
202
|
"""
|
203
203
|
Resolve block document references in a template by replacing each reference with
|
204
|
-
the
|
204
|
+
its value or the return value of the transformer function if provided.
|
205
205
|
|
206
206
|
Recursively searches for block document references in dictionaries and lists.
|
207
207
|
|
@@ -258,6 +258,7 @@ async def resolve_block_document_references(
|
|
258
258
|
|
259
259
|
Args:
|
260
260
|
template: The template to resolve block documents in
|
261
|
+
value_transformer: A function that takes the block placeholder and the block value and returns replacement text for the template
|
261
262
|
|
262
263
|
Returns:
|
263
264
|
The template with block documents resolved
|
@@ -275,13 +276,15 @@ async def resolve_block_document_references(
|
|
275
276
|
updated_template: dict[str, Any] = {}
|
276
277
|
for key, value in template.items():
|
277
278
|
updated_value = await resolve_block_document_references(
|
278
|
-
value, client=client
|
279
|
+
value, value_transformer=value_transformer, client=client
|
279
280
|
)
|
280
281
|
updated_template[key] = updated_value
|
281
282
|
return updated_template
|
282
283
|
elif isinstance(template, list):
|
283
284
|
return [
|
284
|
-
await resolve_block_document_references(
|
285
|
+
await resolve_block_document_references(
|
286
|
+
item, value_transformer=value_transformer, client=client
|
287
|
+
)
|
285
288
|
for item in template
|
286
289
|
]
|
287
290
|
elif isinstance(template, str):
|
@@ -326,6 +329,9 @@ async def resolve_block_document_references(
|
|
326
329
|
)
|
327
330
|
value = from_dict
|
328
331
|
|
332
|
+
if value_transformer:
|
333
|
+
value = value_transformer(placeholder.full_match, value)
|
334
|
+
|
329
335
|
return value
|
330
336
|
else:
|
331
337
|
raise ValueError(
|
@@ -42,8 +42,7 @@ def track_viz_task(
|
|
42
42
|
task_name: str,
|
43
43
|
parameters: dict[str, Any],
|
44
44
|
viz_return_value: Optional[Any] = None,
|
45
|
-
) -> Coroutine[Any, Any, Any]:
|
46
|
-
...
|
45
|
+
) -> Coroutine[Any, Any, Any]: ...
|
47
46
|
|
48
47
|
|
49
48
|
@overload
|
@@ -52,8 +51,7 @@ def track_viz_task(
|
|
52
51
|
task_name: str,
|
53
52
|
parameters: dict[str, Any],
|
54
53
|
viz_return_value: Optional[Any] = None,
|
55
|
-
) -> Any:
|
56
|
-
...
|
54
|
+
) -> Any: ...
|
57
55
|
|
58
56
|
|
59
57
|
def track_viz_task(
|
prefect/workers/base.py
CHANGED
@@ -939,9 +939,9 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
939
939
|
was created from a deployment with a storage block.
|
940
940
|
"""
|
941
941
|
if flow_run.deployment_id:
|
942
|
-
assert (
|
943
|
-
|
944
|
-
)
|
942
|
+
assert self._client and self._client._started, (
|
943
|
+
"Client must be started to check flow run deployment."
|
944
|
+
)
|
945
945
|
deployment = await self._client.read_deployment(flow_run.deployment_id)
|
946
946
|
if deployment.storage_document_id:
|
947
947
|
raise ValueError(
|
prefect/workers/block.py
CHANGED
prefect/workers/cloud.py
CHANGED
prefect/workers/process.py
CHANGED