prefect-client 2.16.8__py3-none-any.whl → 2.16.9__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 (62) hide show
  1. prefect/_internal/compatibility/experimental.py +9 -8
  2. prefect/_internal/concurrency/api.py +23 -42
  3. prefect/_internal/concurrency/waiters.py +25 -22
  4. prefect/_internal/pydantic/__init__.py +12 -3
  5. prefect/_internal/pydantic/_base_model.py +7 -4
  6. prefect/_internal/pydantic/_compat.py +39 -453
  7. prefect/_internal/pydantic/_flags.py +2 -0
  8. prefect/_internal/pydantic/_types.py +8 -0
  9. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  10. prefect/_internal/pydantic/utilities/model_construct.py +56 -0
  11. prefect/_internal/pydantic/utilities/model_copy.py +55 -0
  12. prefect/_internal/pydantic/utilities/model_dump.py +136 -0
  13. prefect/_internal/pydantic/utilities/model_dump_json.py +112 -0
  14. prefect/_internal/pydantic/utilities/model_fields.py +50 -0
  15. prefect/_internal/pydantic/utilities/model_json_schema.py +82 -0
  16. prefect/_internal/pydantic/utilities/model_rebuild.py +80 -0
  17. prefect/_internal/pydantic/utilities/model_validate.py +75 -0
  18. prefect/_internal/pydantic/utilities/model_validate_json.py +68 -0
  19. prefect/_internal/pydantic/utilities/type_adapter.py +71 -0
  20. prefect/_internal/schemas/bases.py +1 -17
  21. prefect/_internal/schemas/validators.py +425 -4
  22. prefect/blocks/kubernetes.py +7 -3
  23. prefect/client/cloud.py +1 -1
  24. prefect/client/orchestration.py +8 -8
  25. prefect/client/schemas/actions.py +348 -285
  26. prefect/client/schemas/objects.py +47 -126
  27. prefect/client/schemas/responses.py +231 -57
  28. prefect/concurrency/events.py +2 -2
  29. prefect/context.py +2 -1
  30. prefect/deployments/base.py +4 -3
  31. prefect/deployments/runner.py +7 -25
  32. prefect/deprecated/packaging/base.py +5 -6
  33. prefect/deprecated/packaging/docker.py +19 -25
  34. prefect/deprecated/packaging/file.py +10 -5
  35. prefect/deprecated/packaging/orion.py +9 -4
  36. prefect/deprecated/packaging/serializers.py +8 -58
  37. prefect/engine.py +23 -22
  38. prefect/events/actions.py +16 -1
  39. prefect/events/related.py +4 -4
  40. prefect/events/schemas/automations.py +13 -2
  41. prefect/events/schemas/deployment_triggers.py +73 -5
  42. prefect/events/schemas/events.py +1 -1
  43. prefect/flows.py +3 -0
  44. prefect/infrastructure/provisioners/ecs.py +1 -0
  45. prefect/logging/configuration.py +2 -2
  46. prefect/pydantic/__init__.py +48 -2
  47. prefect/pydantic/main.py +2 -2
  48. prefect/serializers.py +6 -31
  49. prefect/settings.py +39 -16
  50. prefect/software/python.py +3 -5
  51. prefect/utilities/callables.py +1 -1
  52. prefect/utilities/collections.py +2 -1
  53. prefect/utilities/schema_tools/validation.py +2 -2
  54. prefect/workers/base.py +19 -10
  55. prefect/workers/block.py +3 -7
  56. prefect/workers/process.py +2 -5
  57. {prefect_client-2.16.8.dist-info → prefect_client-2.16.9.dist-info}/METADATA +3 -2
  58. {prefect_client-2.16.8.dist-info → prefect_client-2.16.9.dist-info}/RECORD +61 -50
  59. prefect/_internal/schemas/transformations.py +0 -106
  60. {prefect_client-2.16.8.dist-info → prefect_client-2.16.9.dist-info}/LICENSE +0 -0
  61. {prefect_client-2.16.8.dist-info → prefect_client-2.16.9.dist-info}/WHEEL +0 -0
  62. {prefect_client-2.16.8.dist-info → prefect_client-2.16.9.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ Warnings may also be disabled globally with the setting `PREFECT_EXPERIMENTAL_WA
10
10
  Some experimental features require opt-in to enable any usage. These require the setting
11
11
  `PREFECT_EXPERIMENTAL_ENABLE_<GROUP>` to be set or an error will be thrown on use.
12
12
  """
13
+
13
14
  import functools
14
15
  import warnings
15
16
  from typing import Any, Callable, Optional, Set, Type, TypeVar
@@ -24,7 +25,7 @@ else:
24
25
  from prefect.settings import PREFECT_EXPERIMENTAL_WARN, SETTING_VARIABLES, Setting
25
26
  from prefect.utilities.callables import get_call_parameters
26
27
 
27
- T = TypeVar("T", bound=Callable)
28
+ T = TypeVar("T", bound=Callable[..., Any])
28
29
  M = TypeVar("M", bound=pydantic.BaseModel)
29
30
 
30
31
 
@@ -47,12 +48,6 @@ class ExperimentalWarning(Warning):
47
48
  """
48
49
 
49
50
 
50
- class ExperimentalError(Exception):
51
- """
52
- An exception related to experimental code.
53
- """
54
-
55
-
56
51
  class ExperimentalFeature(ExperimentalWarning):
57
52
  """
58
53
  A warning displayed on use of an experimental feature.
@@ -61,6 +56,12 @@ class ExperimentalFeature(ExperimentalWarning):
61
56
  """
62
57
 
63
58
 
59
+ class ExperimentalError(Exception):
60
+ """
61
+ An exception related to experimental code.
62
+ """
63
+
64
+
64
65
  class ExperimentalFeatureDisabled(ExperimentalError):
65
66
  """
66
67
  An error displayed on use of a disabled experimental feature that requires opt-in.
@@ -112,7 +113,7 @@ def experimental(
112
113
 
113
114
  group_warn = _warn_setting_for_group(group)
114
115
 
115
- def decorator(fn: T):
116
+ def decorator(fn: T) -> T:
116
117
  @functools.wraps(fn)
117
118
  def wrapper(*args, **kwargs):
118
119
  if opt_in and not group_opt_in:
@@ -1,6 +1,7 @@
1
1
  """
2
2
  Primary developer-facing API for concurrency management.
3
3
  """
4
+
4
5
  import abc
5
6
  import asyncio
6
7
  import concurrent.futures
@@ -18,6 +19,7 @@ from typing import (
18
19
 
19
20
  from typing_extensions import ParamSpec
20
21
 
22
+ from prefect._internal.concurrency.calls import get_current_call
21
23
  from prefect._internal.concurrency.threads import (
22
24
  WorkerThread,
23
25
  get_global_loop,
@@ -104,40 +106,6 @@ class _base(abc.ABC):
104
106
  runner.submit(call)
105
107
  return call
106
108
 
107
- @staticmethod
108
- def call_soon_in_waiting_thread(
109
- __call: Union[Callable[[], T], Call[T]],
110
- thread: threading.Thread,
111
- timeout: Optional[float] = None,
112
- ) -> Call[T]:
113
- """
114
- Schedule a call for execution in the thread that is waiting for the current
115
- call.
116
-
117
- Returns the submitted call.
118
- """
119
- call = _cast_to_call(__call)
120
- waiter = get_waiter_for_thread(thread)
121
- if waiter is None:
122
- raise RuntimeError(f"No waiter found for thread {thread}.")
123
-
124
- call.set_timeout(timeout)
125
- waiter.submit(call)
126
- return call
127
-
128
- @staticmethod
129
- def call_in_waiting_thread(
130
- __call: Union[Callable[[], T], Call[T]],
131
- thread: threading.Thread,
132
- timeout: Optional[float] = None,
133
- ) -> T:
134
- """
135
- Run a call in the thread that is waiting for the current call.
136
-
137
- Returns the result of the call.
138
- """
139
- raise NotImplementedError()
140
-
141
109
  @staticmethod
142
110
  def call_in_new_thread(
143
111
  __call: Union[Callable[[], T], Call[T]], timeout: Optional[float] = None
@@ -196,13 +164,20 @@ class from_async(_base):
196
164
  return call.result()
197
165
 
198
166
  @staticmethod
199
- def call_in_waiting_thread(
167
+ def call_soon_in_waiting_thread(
200
168
  __call: Union[Callable[[], T], Call[T]],
201
169
  thread: threading.Thread,
202
170
  timeout: Optional[float] = None,
203
- ) -> Awaitable[T]:
204
- call = _base.call_soon_in_waiting_thread(__call, thread, timeout=timeout)
205
- return call.aresult()
171
+ ) -> Call[T]:
172
+ call = _cast_to_call(__call)
173
+ parent_call = get_current_call()
174
+ waiter = get_waiter_for_thread(thread, parent_call)
175
+ if waiter is None:
176
+ raise RuntimeError(f"No waiter found for thread {thread}.")
177
+
178
+ call.set_timeout(timeout)
179
+ waiter.submit(call)
180
+ return call
206
181
 
207
182
  @staticmethod
208
183
  def call_in_new_thread(
@@ -257,13 +232,19 @@ class from_sync(_base):
257
232
  return call.result()
258
233
 
259
234
  @staticmethod
260
- def call_in_waiting_thread(
235
+ def call_soon_in_waiting_thread(
261
236
  __call: Union[Callable[[], T], Call[T]],
262
237
  thread: threading.Thread,
263
238
  timeout: Optional[float] = None,
264
- ) -> T:
265
- call = _base.call_soon_in_waiting_thread(__call, thread, timeout=timeout)
266
- return call.result()
239
+ ) -> Call[T]:
240
+ call = _cast_to_call(__call)
241
+ waiter = get_waiter_for_thread(thread)
242
+ if waiter is None:
243
+ raise RuntimeError(f"No waiter found for thread {thread}.")
244
+
245
+ call.set_timeout(timeout)
246
+ waiter.submit(call)
247
+ return call
267
248
 
268
249
  @staticmethod
269
250
  def call_in_new_thread(
@@ -9,9 +9,9 @@ import contextlib
9
9
  import inspect
10
10
  import queue
11
11
  import threading
12
- import weakref
13
12
  from collections import deque
14
13
  from typing import Awaitable, Generic, List, Optional, TypeVar, Union
14
+ from weakref import WeakKeyDictionary
15
15
 
16
16
  import anyio
17
17
 
@@ -24,34 +24,37 @@ T = TypeVar("T")
24
24
 
25
25
 
26
26
  # Waiters are stored in a stack for each thread
27
- _WAITERS_BY_THREAD: "weakref.WeakKeyDictionary[threading.Thread, deque[Waiter]]" = (
28
- weakref.WeakKeyDictionary()
27
+ _WAITERS_BY_THREAD: "WeakKeyDictionary[threading.Thread, deque[Waiter]]" = (
28
+ WeakKeyDictionary()
29
29
  )
30
30
 
31
31
 
32
- def get_waiter_for_thread(thread: threading.Thread) -> Optional["Waiter"]:
32
+ def get_waiter_for_thread(
33
+ thread: threading.Thread, parent_call: Optional[Call] = None
34
+ ) -> Optional["Waiter"]:
33
35
  """
34
- Get the current waiter for a thread.
36
+ Get the current waiter for a thread and an optional parent call.
35
37
 
36
- Returns `None` if one does not exist.
38
+ To avoid assigning outer callbacks to inner waiters in the case of nested calls,
39
+ the parent call is used to determine which waiter to return. If a parent call is
40
+ not provided, we return the most recently created waiter (last in the stack).
41
+
42
+ see https://github.com/PrefectHQ/prefect/issues/12036
43
+
44
+ Returns `None` if no active waiter is found for the thread.
37
45
  """
38
- waiters = _WAITERS_BY_THREAD.get(thread)
39
-
40
- if waiters:
41
- idx = -1
42
- while abs(idx) <= len(waiters):
43
- try:
44
- waiter = waiters[idx]
45
- if not waiter.call_is_done():
46
- return waiter
47
- idx = idx - 1
48
- # It is possible that items are being added or removed
49
- # from the deque, so the index we're using may not always
50
- # be valid.
51
- except IndexError:
52
- break
53
46
 
54
- return None
47
+ waiters: "Optional[deque[Waiter]]" = _WAITERS_BY_THREAD.get(thread)
48
+
49
+ if waiters and (active_waiters := [w for w in waiters if not w.call_is_done()]):
50
+ if parent_call and (
51
+ matching_waiter := next(
52
+ (w for w in active_waiters if w._call == parent_call), None
53
+ )
54
+ ): # if exists an active waiter responsible for the parent call, return it
55
+ return matching_waiter
56
+ else: # otherwise, return the most recently created waiter
57
+ return active_waiters[-1]
55
58
 
56
59
 
57
60
  def add_waiter_for_thread(waiter: "Waiter", thread: threading.Thread):
@@ -6,21 +6,25 @@
6
6
  ### This is a tradeoff we're willing to make for now until pydantic v1 is
7
7
  ### no longer supported.
8
8
 
9
- from pydantic.version import VERSION as PYDANTIC_VERSION
10
9
 
11
- HAS_PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.")
10
+ from ._flags import HAS_PYDANTIC_V2
12
11
 
13
12
  from ._compat import (
14
13
  model_dump,
15
14
  model_json_schema,
16
15
  model_validate,
17
- IncEx,
18
16
  model_dump_json,
19
17
  model_copy,
20
18
  model_validate_json,
19
+ TypeAdapter,
21
20
  validate_python,
21
+ BaseModel,
22
+ Field,
23
+ FieldInfo,
22
24
  )
23
25
 
26
+ from ._types import IncEx
27
+
24
28
  __all__ = [
25
29
  "model_dump",
26
30
  "model_json_schema",
@@ -29,5 +33,10 @@ __all__ = [
29
33
  "model_dump_json",
30
34
  "model_copy",
31
35
  "model_validate_json",
36
+ "TypeAdapter",
32
37
  "validate_python",
38
+ "BaseModel",
39
+ "HAS_PYDANTIC_V2",
40
+ "Field",
41
+ "FieldInfo",
33
42
  ]
@@ -6,11 +6,14 @@ from prefect._internal.pydantic._flags import (
6
6
  )
7
7
 
8
8
  if typing.TYPE_CHECKING:
9
- from pydantic import BaseModel
9
+ from pydantic import BaseModel, Field
10
+ from pydantic.fields import FieldInfo
10
11
 
11
12
  if HAS_PYDANTIC_V2 and not USE_PYDANTIC_V2:
12
- from pydantic.v1 import BaseModel
13
+ from pydantic.v1 import BaseModel, Field
14
+ from pydantic.v1.fields import FieldInfo
13
15
  else:
14
- from pydantic import BaseModel
16
+ from pydantic import BaseModel, Field
17
+ from pydantic.fields import FieldInfo
15
18
 
16
- __all__ = ["BaseModel"]
19
+ __all__ = ["BaseModel", "Field", "FieldInfo"]