prefect-client 2.20.2__py3-none-any.whl → 3.0.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 (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +423 -164
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +667 -440
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2466
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +124 -51
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +138 -48
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.2.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/context.py CHANGED
@@ -1,4 +1,4 @@
1
- r"""
1
+ """
2
2
  Async and thread safe models for passing runtime context data.
3
3
 
4
4
  These contexts should never be directly mutated by the user.
@@ -9,50 +9,42 @@ For more user-accessible information about the current run, see [`prefect.runtim
9
9
  import os
10
10
  import sys
11
11
  import warnings
12
- from collections import defaultdict
13
- from contextlib import contextmanager
12
+ import weakref
13
+ from contextlib import ExitStack, asynccontextmanager, contextmanager
14
14
  from contextvars import ContextVar, Token
15
- from functools import update_wrapper
16
15
  from pathlib import Path
17
16
  from typing import (
18
17
  TYPE_CHECKING,
19
18
  Any,
20
- ContextManager,
19
+ AsyncGenerator,
21
20
  Dict,
22
21
  Generator,
23
- List,
22
+ Mapping,
24
23
  Optional,
25
24
  Set,
26
- Tuple,
27
25
  Type,
28
26
  TypeVar,
29
27
  Union,
30
28
  )
31
29
 
32
- import anyio.abc
33
30
  import pendulum
34
-
35
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
36
-
37
- if HAS_PYDANTIC_V2:
38
- from pydantic.v1 import BaseModel, Field, PrivateAttr
39
- else:
40
- from pydantic import BaseModel, Field, PrivateAttr
31
+ from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
32
+ from pydantic_extra_types.pendulum_dt import DateTime
33
+ from typing_extensions import Self
41
34
 
42
35
  import prefect.logging
43
36
  import prefect.logging.configuration
44
37
  import prefect.settings
45
- from prefect._internal.schemas.fields import DateTimeTZ
46
- from prefect.client.orchestration import PrefectClient, SyncPrefectClient
38
+ from prefect._internal.compatibility.migration import getattr_migration
39
+ from prefect.client.orchestration import PrefectClient, SyncPrefectClient, get_client
47
40
  from prefect.client.schemas import FlowRun, TaskRun
48
41
  from prefect.events.worker import EventsWorker
49
42
  from prefect.exceptions import MissingContextError
50
- from prefect.futures import PrefectFuture
51
- from prefect.results import ResultFactory
43
+ from prefect.results import ResultStore
52
44
  from prefect.settings import PREFECT_HOME, Profile, Settings
53
45
  from prefect.states import State
54
- from prefect.task_runners import BaseTaskRunner
55
- from prefect.utilities.importtools import load_script_as_module
46
+ from prefect.task_runners import TaskRunner
47
+ from prefect.utilities.services import start_client_metrics_server
56
48
 
57
49
  T = TypeVar("T")
58
50
 
@@ -63,7 +55,60 @@ if TYPE_CHECKING:
63
55
  # Define the global settings context variable
64
56
  # This will be populated downstream but must be null here to facilitate loading the
65
57
  # default settings.
66
- GLOBAL_SETTINGS_CONTEXT = None
58
+ GLOBAL_SETTINGS_CONTEXT = None # type: ignore
59
+
60
+
61
+ def serialize_context() -> Dict[str, Any]:
62
+ """
63
+ Serialize the current context for use in a remote execution environment.
64
+ """
65
+
66
+ flow_run_context = EngineContext.get()
67
+ task_run_context = TaskRunContext.get()
68
+ tags_context = TagsContext.get()
69
+ settings_context = SettingsContext.get()
70
+
71
+ return {
72
+ "flow_run_context": flow_run_context.serialize() if flow_run_context else {},
73
+ "task_run_context": task_run_context.serialize() if task_run_context else {},
74
+ "tags_context": tags_context.serialize() if tags_context else {},
75
+ "settings_context": settings_context.serialize() if settings_context else {},
76
+ }
77
+
78
+
79
+ @contextmanager
80
+ def hydrated_context(
81
+ serialized_context: Optional[Dict[str, Any]] = None,
82
+ client: Union[PrefectClient, SyncPrefectClient, None] = None,
83
+ ):
84
+ with ExitStack() as stack:
85
+ if serialized_context:
86
+ # Set up settings context
87
+ if settings_context := serialized_context.get("settings_context"):
88
+ stack.enter_context(SettingsContext(**settings_context))
89
+ # Set up parent flow run context
90
+ client = client or get_client(sync_client=True)
91
+ if flow_run_context := serialized_context.get("flow_run_context"):
92
+ flow = flow_run_context["flow"]
93
+ task_runner = stack.enter_context(flow.task_runner.duplicate())
94
+ flow_run_context = FlowRunContext(
95
+ **flow_run_context,
96
+ client=client,
97
+ task_runner=task_runner,
98
+ detached=True,
99
+ )
100
+ stack.enter_context(flow_run_context)
101
+ # Set up parent task run context
102
+ if parent_task_run_context := serialized_context.get("task_run_context"):
103
+ task_run_context = TaskRunContext(
104
+ **parent_task_run_context,
105
+ client=client,
106
+ )
107
+ stack.enter_context(task_run_context)
108
+ # Set up tags context
109
+ if tags_context := serialized_context.get("tags_context"):
110
+ stack.enter_context(tags(*tags_context["current_tags"]))
111
+ yield
67
112
 
68
113
 
69
114
  class ContextModel(BaseModel):
@@ -74,14 +119,13 @@ class ContextModel(BaseModel):
74
119
 
75
120
  # The context variable for storing data must be defined by the child class
76
121
  __var__: ContextVar
77
- _token: Token = PrivateAttr(None)
78
-
79
- class Config:
80
- # allow_mutation = False
81
- arbitrary_types_allowed = True
82
- extra = "forbid"
122
+ _token: Optional[Token] = PrivateAttr(None)
123
+ model_config = ConfigDict(
124
+ arbitrary_types_allowed=True,
125
+ extra="forbid",
126
+ )
83
127
 
84
- def __enter__(self):
128
+ def __enter__(self) -> Self:
85
129
  if self._token is not None:
86
130
  raise RuntimeError(
87
131
  "Context already entered. Context enter calls cannot be nested."
@@ -98,10 +142,13 @@ class ContextModel(BaseModel):
98
142
  self._token = None
99
143
 
100
144
  @classmethod
101
- def get(cls: Type[T]) -> Optional[T]:
145
+ def get(cls: Type[Self]) -> Optional[Self]:
146
+ """Get the current context instance"""
102
147
  return cls.__var__.get(None)
103
148
 
104
- def copy(self, **kwargs):
149
+ def model_copy(
150
+ self: Self, *, update: Optional[Dict[str, Any]] = None, deep: bool = False
151
+ ):
105
152
  """
106
153
  Duplicate the context model, optionally choosing which fields to include, exclude, or change.
107
154
 
@@ -115,90 +162,130 @@ class ContextModel(BaseModel):
115
162
  Returns:
116
163
  A new model instance.
117
164
  """
165
+ new = super().model_copy(update=update, deep=deep)
118
166
  # Remove the token on copy to avoid re-entrance errors
119
- new = super().copy(**kwargs)
120
167
  new._token = None
121
168
  return new
122
169
 
170
+ def serialize(self) -> Dict[str, Any]:
171
+ """
172
+ Serialize the context model to a dictionary that can be pickled with cloudpickle.
173
+ """
174
+ return self.model_dump(exclude_unset=True)
123
175
 
124
- class PrefectObjectRegistry(ContextModel):
125
- """
126
- A context that acts as a registry for all Prefect objects that are
127
- registered during load and execution.
128
176
 
129
- Attributes:
130
- start_time: The time the object registry was created.
131
- block_code_execution: If set, flow calls will be ignored.
132
- capture_failures: If set, failures during __init__ will be silenced and tracked.
177
+ class SyncClientContext(ContextModel):
178
+ """
179
+ A context for managing the sync Prefect client instances.
180
+
181
+ Clients were formerly tracked on the TaskRunContext and FlowRunContext, but
182
+ having two separate places and the addition of both sync and async clients
183
+ made it difficult to manage. This context is intended to be the single
184
+ source for sync clients.
185
+
186
+ The client creates a sync client, which can either be read directly from
187
+ the context object OR loaded with get_client, inject_client, or other
188
+ Prefect utilities.
189
+
190
+ with SyncClientContext.get_or_create() as ctx:
191
+ c1 = get_client(sync_client=True)
192
+ c2 = get_client(sync_client=True)
193
+ assert c1 is c2
194
+ assert c1 is ctx.client
133
195
  """
134
196
 
135
- start_time: DateTimeTZ = Field(default_factory=lambda: pendulum.now("UTC"))
136
-
137
- _instance_registry: Dict[Type[T], List[T]] = PrivateAttr(
138
- default_factory=lambda: defaultdict(list)
139
- )
197
+ __var__ = ContextVar("sync-client-context")
198
+ client: SyncPrefectClient
199
+ _httpx_settings: Optional[dict[str, Any]] = PrivateAttr(None)
200
+ _context_stack: int = PrivateAttr(0)
140
201
 
141
- # Failures will be a tuple of (exception, instance, args, kwargs)
142
- _instance_init_failures: Dict[
143
- Type[T], List[Tuple[Exception, T, Tuple, Dict]]
144
- ] = PrivateAttr(default_factory=lambda: defaultdict(list))
145
-
146
- block_code_execution: bool = False
147
- capture_failures: bool = False
148
-
149
- __var__ = ContextVar("object_registry")
150
-
151
- def get_instances(self, type_: Type[T]) -> List[T]:
152
- instances = []
153
- for registered_type, type_instances in self._instance_registry.items():
154
- if type_ in registered_type.mro():
155
- instances.extend(type_instances)
156
- return instances
157
-
158
- def get_instance_failures(
159
- self, type_: Type[T]
160
- ) -> List[Tuple[Exception, T, Tuple, Dict]]:
161
- failures = []
162
- for type__ in type_.mro():
163
- failures.extend(self._instance_init_failures[type__])
164
- return failures
165
-
166
- def register_instance(self, object):
167
- # TODO: Consider using a 'Set' to avoid duplicate entries
168
- self._instance_registry[type(object)].append(object)
169
-
170
- def register_init_failure(
171
- self, exc: Exception, object: Any, init_args: Tuple, init_kwargs: Dict
172
- ):
173
- self._instance_init_failures[type(object)].append(
174
- (exc, object, init_args, init_kwargs)
202
+ def __init__(self, httpx_settings: Optional[dict[str, Any]] = None):
203
+ super().__init__(
204
+ client=get_client(sync_client=True, httpx_settings=httpx_settings),
175
205
  )
206
+ self._httpx_settings = httpx_settings
207
+ self._context_stack = 0
208
+
209
+ def __enter__(self):
210
+ self._context_stack += 1
211
+ if self._context_stack == 1:
212
+ self.client.__enter__()
213
+ return super().__enter__()
214
+ else:
215
+ return self
216
+
217
+ def __exit__(self, *exc_info):
218
+ self._context_stack -= 1
219
+ if self._context_stack == 0:
220
+ self.client.__exit__(*exc_info)
221
+ return super().__exit__(*exc_info)
176
222
 
177
223
  @classmethod
178
- def register_instances(cls, type_: Type[T]) -> Type[T]:
179
- """
180
- Decorator for a class that adds registration to the `PrefectObjectRegistry`
181
- on initialization of instances.
182
- """
183
- original_init = type_.__init__
224
+ @contextmanager
225
+ def get_or_create(cls) -> Generator["SyncClientContext", None, None]:
226
+ ctx = SyncClientContext.get()
227
+ if ctx:
228
+ yield ctx
229
+ else:
230
+ with SyncClientContext() as ctx:
231
+ yield ctx
184
232
 
185
- def __register_init__(__self__: T, *args: Any, **kwargs: Any) -> None:
186
- registry = cls.get()
187
- try:
188
- original_init(__self__, *args, **kwargs)
189
- except Exception as exc:
190
- if not registry or not registry.capture_failures:
191
- raise
192
- else:
193
- registry.register_init_failure(exc, __self__, args, kwargs)
194
- else:
195
- if registry:
196
- registry.register_instance(__self__)
197
233
 
198
- update_wrapper(__register_init__, original_init)
234
+ class AsyncClientContext(ContextModel):
235
+ """
236
+ A context for managing the async Prefect client instances.
237
+
238
+ Clients were formerly tracked on the TaskRunContext and FlowRunContext, but
239
+ having two separate places and the addition of both sync and async clients
240
+ made it difficult to manage. This context is intended to be the single
241
+ source for async clients.
242
+
243
+ The client creates an async client, which can either be read directly from
244
+ the context object OR loaded with get_client, inject_client, or other
245
+ Prefect utilities.
246
+
247
+ with AsyncClientContext.get_or_create() as ctx:
248
+ c1 = get_client(sync_client=False)
249
+ c2 = get_client(sync_client=False)
250
+ assert c1 is c2
251
+ assert c1 is ctx.client
252
+ """
199
253
 
200
- type_.__init__ = __register_init__
201
- return type_
254
+ __var__ = ContextVar("async-client-context")
255
+ client: PrefectClient
256
+ _httpx_settings: Optional[dict[str, Any]] = PrivateAttr(None)
257
+ _context_stack: int = PrivateAttr(0)
258
+
259
+ def __init__(self, httpx_settings: Optional[dict[str, Any]] = None):
260
+ super().__init__(
261
+ client=get_client(sync_client=False, httpx_settings=httpx_settings),
262
+ )
263
+ self._httpx_settings = httpx_settings
264
+ self._context_stack = 0
265
+
266
+ async def __aenter__(self):
267
+ self._context_stack += 1
268
+ if self._context_stack == 1:
269
+ await self.client.__aenter__()
270
+ return super().__enter__()
271
+ else:
272
+ return self
273
+
274
+ async def __aexit__(self, *exc_info):
275
+ self._context_stack -= 1
276
+ if self._context_stack == 0:
277
+ await self.client.__aexit__(*exc_info)
278
+ return super().__exit__(*exc_info)
279
+
280
+ @classmethod
281
+ @asynccontextmanager
282
+ async def get_or_create(cls) -> AsyncGenerator[Self, None]:
283
+ ctx = cls.get()
284
+ if ctx:
285
+ yield ctx
286
+ else:
287
+ async with cls() as ctx:
288
+ yield ctx
202
289
 
203
290
 
204
291
  class RunContext(ContextModel):
@@ -211,10 +298,21 @@ class RunContext(ContextModel):
211
298
  client: The Prefect client instance being used for API communication
212
299
  """
213
300
 
214
- start_time: DateTimeTZ = Field(default_factory=lambda: pendulum.now("UTC"))
301
+ def __init__(self, *args, **kwargs):
302
+ super().__init__(*args, **kwargs)
303
+
304
+ start_client_metrics_server()
305
+
306
+ start_time: DateTime = Field(default_factory=lambda: pendulum.now("UTC"))
215
307
  input_keyset: Optional[Dict[str, Dict[str, str]]] = None
216
308
  client: Union[PrefectClient, SyncPrefectClient]
217
309
 
310
+ def serialize(self):
311
+ return self.model_dump(
312
+ include={"start_time", "input_keyset"},
313
+ exclude_unset=True,
314
+ )
315
+
218
316
 
219
317
  class EngineContext(RunContext):
220
318
  """
@@ -229,19 +327,20 @@ class EngineContext(RunContext):
229
327
  task_run_states: A list of states for task runs created within this flow run
230
328
  task_run_results: A mapping of result ids to task run states for this flow run
231
329
  flow_run_states: A list of states for flow runs created within this flow run
232
- sync_portal: A blocking portal for sync task/flow runs in an async flow
233
- timeout_scope: The cancellation scope for flow level timeouts
234
330
  """
235
331
 
236
332
  flow: Optional["Flow"] = None
237
333
  flow_run: Optional[FlowRun] = None
238
- autonomous_task_run: Optional[TaskRun] = None
239
- task_runner: BaseTaskRunner
334
+ task_runner: TaskRunner
240
335
  log_prints: bool = False
241
336
  parameters: Optional[Dict[str, Any]] = None
242
337
 
338
+ # Flag signaling if the flow run context has been serialized and sent
339
+ # to remote infrastructure.
340
+ detached: bool = False
341
+
243
342
  # Result handling
244
- result_factory: ResultFactory
343
+ result_store: ResultStore
245
344
 
246
345
  # Counter for task calls allowing unique
247
346
  task_run_dynamic_keys: Dict[str, int] = Field(default_factory=dict)
@@ -249,24 +348,31 @@ class EngineContext(RunContext):
249
348
  # Counter for flow pauses
250
349
  observed_flow_pauses: Dict[str, int] = Field(default_factory=dict)
251
350
 
252
- # Tracking for objects created by this flow run
253
- task_run_futures: List[PrefectFuture] = Field(default_factory=list)
254
- task_run_states: List[State] = Field(default_factory=list)
255
- task_run_results: Dict[int, State] = Field(default_factory=dict)
256
- flow_run_states: List[State] = Field(default_factory=list)
257
-
258
- # The synchronous portal is only created for async flows for creating engine calls
259
- # from synchronous task and subflow calls
260
- sync_portal: Optional[anyio.abc.BlockingPortal] = None
261
- timeout_scope: Optional[anyio.abc.CancelScope] = None
262
-
263
- # Task group that can be used for background tasks during the flow run
264
- background_tasks: anyio.abc.TaskGroup
351
+ # Tracking for result from task runs in this flow run for dependency tracking
352
+ # Holds the ID of the object returned by the task run and task run state
353
+ # This is a weakref dictionary to avoid undermining garbage collection
354
+ task_run_results: Mapping[int, State] = Field(
355
+ default_factory=weakref.WeakValueDictionary
356
+ )
265
357
 
266
- # Events worker to emit events to Prefect Cloud
358
+ # Events worker to emit events
267
359
  events: Optional[EventsWorker] = None
268
360
 
269
- __var__ = ContextVar("flow_run")
361
+ __var__: ContextVar = ContextVar("flow_run")
362
+
363
+ def serialize(self):
364
+ return self.model_dump(
365
+ include={
366
+ "flow_run",
367
+ "flow",
368
+ "parameters",
369
+ "log_prints",
370
+ "start_time",
371
+ "input_keyset",
372
+ "result_store",
373
+ },
374
+ exclude_unset=True,
375
+ )
270
376
 
271
377
 
272
378
  FlowRunContext = EngineContext # for backwards compatibility
@@ -288,10 +394,24 @@ class TaskRunContext(RunContext):
288
394
  parameters: Dict[str, Any]
289
395
 
290
396
  # Result handling
291
- result_factory: ResultFactory
397
+ result_store: ResultStore
292
398
 
293
399
  __var__ = ContextVar("task_run")
294
400
 
401
+ def serialize(self):
402
+ return self.model_dump(
403
+ include={
404
+ "task_run",
405
+ "task",
406
+ "parameters",
407
+ "log_prints",
408
+ "start_time",
409
+ "input_keyset",
410
+ "result_store",
411
+ },
412
+ exclude_unset=True,
413
+ )
414
+
295
415
 
296
416
  class TagsContext(ContextModel):
297
417
  """
@@ -452,23 +572,6 @@ def tags(*new_tags: str) -> Generator[Set[str], None, None]:
452
572
  yield new_tags
453
573
 
454
574
 
455
- def registry_from_script(
456
- path: str,
457
- block_code_execution: bool = True,
458
- capture_failures: bool = True,
459
- ) -> PrefectObjectRegistry:
460
- """
461
- Return a fresh registry with instances populated from execution of a script.
462
- """
463
- with PrefectObjectRegistry(
464
- block_code_execution=block_code_execution,
465
- capture_failures=capture_failures,
466
- ) as registry:
467
- load_script_as_module(path)
468
-
469
- return registry
470
-
471
-
472
575
  @contextmanager
473
576
  def use_profile(
474
577
  profile: Union[Profile, str],
@@ -555,7 +658,7 @@ def root_settings_context():
555
658
  ),
556
659
  file=sys.stderr,
557
660
  )
558
- active_name = "default"
661
+ active_name = "ephemeral"
559
662
 
560
663
  with use_profile(
561
664
  profiles[active_name],
@@ -569,14 +672,8 @@ def root_settings_context():
569
672
 
570
673
 
571
674
  GLOBAL_SETTINGS_CONTEXT: SettingsContext = root_settings_context()
572
- GLOBAL_OBJECT_REGISTRY: ContextManager[PrefectObjectRegistry] = None
573
-
574
-
575
- def initialize_object_registry():
576
- global GLOBAL_OBJECT_REGISTRY
577
675
 
578
- if GLOBAL_OBJECT_REGISTRY:
579
- return
580
676
 
581
- GLOBAL_OBJECT_REGISTRY = PrefectObjectRegistry()
582
- GLOBAL_OBJECT_REGISTRY.__enter__()
677
+ # 2024-07-02: This surfaces an actionable error message for removed objects
678
+ # in Prefect 3.0 upgrade.
679
+ __getattr__ = getattr_migration(__name__)
@@ -1,18 +1,33 @@
1
- import prefect.deployments.base
2
- import prefect.deployments.steps
3
- from prefect.deployments.base import (
4
- initialize_project,
5
- )
6
-
7
- from prefect.deployments.deployments import (
8
- run_deployment,
9
- load_flow_from_flow_run,
10
- load_deployments_from_yaml,
11
- Deployment,
12
- )
13
- from prefect.deployments.runner import (
14
- RunnerDeployment,
15
- deploy,
16
- DeploymentImage,
17
- EntrypointType,
18
- )
1
+ from typing import TYPE_CHECKING
2
+ from prefect._internal.compatibility.migration import getattr_migration
3
+
4
+
5
+ if TYPE_CHECKING:
6
+ from .flow_runs import run_deployment
7
+ from .base import initialize_project
8
+ from .runner import deploy
9
+
10
+ _public_api: dict[str, tuple[str, str]] = {
11
+ "initialize_project": (__spec__.parent, ".base"),
12
+ "run_deployment": (__spec__.parent, ".flow_runs"),
13
+ "deploy": (__spec__.parent, ".runner"),
14
+ }
15
+
16
+ # Declare API for type-checkers
17
+ __all__ = ["initialize_project", "deploy", "run_deployment"]
18
+
19
+
20
+ def __getattr__(attr_name: str) -> object:
21
+ dynamic_attr = _public_api.get(attr_name)
22
+ if dynamic_attr is None:
23
+ return getattr_migration(__name__)(attr_name)
24
+
25
+ package, module_name = dynamic_attr
26
+
27
+ from importlib import import_module
28
+
29
+ if module_name == "__module__":
30
+ return import_module(f".{attr_name}", package=package)
31
+ else:
32
+ module = import_module(module_name, package=package)
33
+ return getattr(module, attr_name)
@@ -19,7 +19,7 @@ import anyio
19
19
  import yaml
20
20
  from ruamel.yaml import YAML
21
21
 
22
- from prefect.client.schemas.objects import MinimalDeploymentSchedule
22
+ from prefect.client.schemas.actions import DeploymentScheduleCreate
23
23
  from prefect.client.schemas.schedules import IntervalSchedule
24
24
  from prefect.logging import get_logger
25
25
  from prefect.settings import PREFECT_DEBUG_MODE
@@ -29,7 +29,7 @@ from prefect.utilities.templating import apply_values
29
29
 
30
30
 
31
31
  def create_default_prefect_yaml(
32
- path: str, name: str = None, contents: Optional[Dict[str, Any]] = None
32
+ path: str, name: Optional[str] = None, contents: Optional[Dict[str, Any]] = None
33
33
  ) -> bool:
34
34
  """
35
35
  Creates default `prefect.yaml` file in the provided path if one does not already exist;
@@ -176,7 +176,9 @@ def _get_git_branch() -> Optional[str]:
176
176
 
177
177
 
178
178
  def initialize_project(
179
- name: str = None, recipe: str = None, inputs: Optional[Dict[str, Any]] = None
179
+ name: Optional[str] = None,
180
+ recipe: Optional[str] = None,
181
+ inputs: Optional[Dict[str, Any]] = None,
180
182
  ) -> List[str]:
181
183
  """
182
184
  Initializes a basic project structure with base files. If no name is provided, the name
@@ -256,28 +258,21 @@ def _format_deployment_for_saving_to_prefect_file(
256
258
  # Only want entrypoint to avoid errors
257
259
  deployment.pop("flow_name", None)
258
260
 
259
- if deployment.get("schedule"):
260
- if isinstance(deployment["schedule"], IntervalSchedule):
261
- deployment["schedule"] = _interval_schedule_to_dict(deployment["schedule"])
262
- else: # all valid SCHEDULE_TYPES are subclasses of BaseModel
263
- deployment["schedule"] = deployment["schedule"].dict()
264
-
265
- if "is_schedule_active" in deployment:
266
- deployment["schedule"]["active"] = deployment.pop("is_schedule_active")
267
-
268
261
  if deployment.get("schedules"):
269
262
  schedules = []
270
263
  for deployment_schedule in cast(
271
- List[MinimalDeploymentSchedule], deployment["schedules"]
264
+ List[DeploymentScheduleCreate], deployment["schedules"]
272
265
  ):
273
266
  if isinstance(deployment_schedule.schedule, IntervalSchedule):
274
267
  schedule_config = _interval_schedule_to_dict(
275
268
  deployment_schedule.schedule
276
269
  )
277
270
  else: # all valid SCHEDULE_TYPES are subclasses of BaseModel
278
- schedule_config = deployment_schedule.schedule.dict()
271
+ schedule_config = deployment_schedule.schedule.model_dump()
279
272
 
280
273
  schedule_config["active"] = deployment_schedule.active
274
+ schedule_config["max_active_runs"] = deployment_schedule.max_active_runs
275
+ schedule_config["catchup"] = deployment_schedule.catchup
281
276
  schedules.append(schedule_config)
282
277
 
283
278
  deployment["schedules"] = schedules
@@ -295,7 +290,7 @@ def _interval_schedule_to_dict(schedule: IntervalSchedule) -> Dict:
295
290
  Returns:
296
291
  - Dict: the schedule as a dictionary
297
292
  """
298
- schedule_config = schedule.dict()
293
+ schedule_config = schedule.model_dump()
299
294
  schedule_config["interval"] = schedule_config["interval"].total_seconds()
300
295
  schedule_config["anchor_date"] = schedule_config["anchor_date"].isoformat()
301
296