prefect-client 3.1.15__py3-none-any.whl → 3.2.0__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 (89) hide show
  1. prefect/_experimental/sla/objects.py +29 -1
  2. prefect/_internal/compatibility/deprecated.py +4 -4
  3. prefect/_internal/compatibility/migration.py +1 -1
  4. prefect/_internal/concurrency/calls.py +1 -2
  5. prefect/_internal/concurrency/cancellation.py +2 -4
  6. prefect/_internal/concurrency/threads.py +3 -3
  7. prefect/_internal/schemas/bases.py +3 -11
  8. prefect/_internal/schemas/validators.py +36 -60
  9. prefect/_result_records.py +235 -0
  10. prefect/_version.py +3 -3
  11. prefect/agent.py +1 -0
  12. prefect/automations.py +4 -8
  13. prefect/blocks/notifications.py +8 -8
  14. prefect/cache_policies.py +2 -0
  15. prefect/client/base.py +7 -8
  16. prefect/client/collections.py +3 -6
  17. prefect/client/orchestration/__init__.py +15 -263
  18. prefect/client/orchestration/_deployments/client.py +14 -6
  19. prefect/client/orchestration/_flow_runs/client.py +10 -6
  20. prefect/client/orchestration/_work_pools/__init__.py +0 -0
  21. prefect/client/orchestration/_work_pools/client.py +598 -0
  22. prefect/client/orchestration/base.py +9 -2
  23. prefect/client/schemas/actions.py +66 -2
  24. prefect/client/schemas/objects.py +22 -50
  25. prefect/client/schemas/schedules.py +7 -18
  26. prefect/client/types/flexible_schedule_list.py +2 -1
  27. prefect/context.py +2 -3
  28. prefect/deployments/flow_runs.py +1 -1
  29. prefect/deployments/runner.py +119 -43
  30. prefect/deployments/schedules.py +7 -1
  31. prefect/engine.py +4 -9
  32. prefect/events/schemas/automations.py +4 -2
  33. prefect/events/utilities.py +15 -13
  34. prefect/exceptions.py +1 -1
  35. prefect/flow_engine.py +8 -8
  36. prefect/flow_runs.py +4 -8
  37. prefect/flows.py +30 -22
  38. prefect/infrastructure/__init__.py +1 -0
  39. prefect/infrastructure/base.py +1 -0
  40. prefect/infrastructure/provisioners/__init__.py +3 -6
  41. prefect/infrastructure/provisioners/coiled.py +3 -3
  42. prefect/infrastructure/provisioners/container_instance.py +1 -0
  43. prefect/infrastructure/provisioners/ecs.py +6 -6
  44. prefect/infrastructure/provisioners/modal.py +3 -3
  45. prefect/input/run_input.py +5 -7
  46. prefect/locking/filesystem.py +4 -3
  47. prefect/main.py +1 -1
  48. prefect/results.py +42 -249
  49. prefect/runner/runner.py +9 -4
  50. prefect/runner/server.py +5 -5
  51. prefect/runner/storage.py +12 -10
  52. prefect/runner/submit.py +2 -4
  53. prefect/schedules.py +231 -0
  54. prefect/serializers.py +5 -5
  55. prefect/settings/__init__.py +2 -1
  56. prefect/settings/base.py +3 -3
  57. prefect/settings/models/root.py +4 -0
  58. prefect/settings/models/server/services.py +50 -9
  59. prefect/settings/sources.py +4 -4
  60. prefect/states.py +42 -11
  61. prefect/task_engine.py +10 -10
  62. prefect/task_runners.py +11 -22
  63. prefect/task_worker.py +9 -9
  64. prefect/tasks.py +22 -41
  65. prefect/telemetry/bootstrap.py +4 -6
  66. prefect/telemetry/services.py +2 -4
  67. prefect/types/__init__.py +2 -1
  68. prefect/types/_datetime.py +28 -1
  69. prefect/utilities/_engine.py +0 -1
  70. prefect/utilities/asyncutils.py +4 -8
  71. prefect/utilities/collections.py +13 -22
  72. prefect/utilities/dispatch.py +2 -4
  73. prefect/utilities/dockerutils.py +6 -6
  74. prefect/utilities/importtools.py +1 -68
  75. prefect/utilities/names.py +1 -1
  76. prefect/utilities/processutils.py +3 -6
  77. prefect/utilities/pydantic.py +4 -6
  78. prefect/utilities/schema_tools/hydration.py +6 -5
  79. prefect/utilities/templating.py +16 -10
  80. prefect/utilities/visualization.py +2 -4
  81. prefect/workers/base.py +3 -3
  82. prefect/workers/block.py +1 -0
  83. prefect/workers/cloud.py +1 -0
  84. prefect/workers/process.py +1 -0
  85. {prefect_client-3.1.15.dist-info → prefect_client-3.2.0.dist-info}/METADATA +1 -1
  86. {prefect_client-3.1.15.dist-info → prefect_client-3.2.0.dist-info}/RECORD +89 -85
  87. {prefect_client-3.1.15.dist-info → prefect_client-3.2.0.dist-info}/LICENSE +0 -0
  88. {prefect_client-3.1.15.dist-info → prefect_client-3.2.0.dist-info}/WHEEL +0 -0
  89. {prefect_client-3.1.15.dist-info → prefect_client-3.2.0.dist-info}/top_level.txt +0 -0
@@ -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, Union
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
 
@@ -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[:-len(suffix)]}{suffix}"
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(
@@ -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 != str and field_type_annotation is not None:
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__) != model_cls
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)
@@ -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, client: Optional["PrefectClient"] = None
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 data of the block document.
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(item, client=client)
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
- self._client and self._client._started
944
- ), "Client must be started to check flow run deployment."
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
@@ -1,6 +1,7 @@
1
1
  """
2
2
  2024-06-27: This surfaces an actionable error message for moved or removed objects in Prefect 3.0 upgrade.
3
3
  """
4
+
4
5
  from typing import Any, Callable
5
6
 
6
7
  from prefect._internal.compatibility.migration import getattr_migration
prefect/workers/cloud.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """
2
2
  2024-06-27: This surfaces an actionable error message for moved or removed objects in Prefect 3.0 upgrade.
3
3
  """
4
+
4
5
  from typing import Any, Callable
5
6
 
6
7
  from prefect._internal.compatibility.migration import getattr_migration
@@ -13,6 +13,7 @@ to poll for flow runs.
13
13
  For more information about work pools and workers,
14
14
  checkout out the [Prefect docs](/concepts/work-pools/).
15
15
  """
16
+
16
17
  from __future__ import annotations
17
18
 
18
19
  import contextlib
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prefect-client
3
- Version: 3.1.15
3
+ Version: 3.2.0
4
4
  Summary: Workflow orchestration and management.
5
5
  Home-page: https://www.prefect.io
6
6
  Author: Prefect Technologies, Inc.