prefect-client 2.19.4__py3-none-any.whl → 3.0.0rc2__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 (242) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/redis.py +168 -0
  26. prefect/blocks/system.py +22 -11
  27. prefect/blocks/webhook.py +2 -9
  28. prefect/client/base.py +4 -4
  29. prefect/client/cloud.py +8 -13
  30. prefect/client/orchestration.py +362 -340
  31. prefect/client/schemas/actions.py +92 -86
  32. prefect/client/schemas/filters.py +20 -40
  33. prefect/client/schemas/objects.py +158 -152
  34. prefect/client/schemas/responses.py +16 -24
  35. prefect/client/schemas/schedules.py +47 -35
  36. prefect/client/subscriptions.py +2 -2
  37. prefect/client/utilities.py +5 -2
  38. prefect/concurrency/asyncio.py +4 -2
  39. prefect/concurrency/events.py +1 -1
  40. prefect/concurrency/services.py +7 -4
  41. prefect/context.py +195 -27
  42. prefect/deployments/__init__.py +5 -6
  43. prefect/deployments/base.py +7 -5
  44. prefect/deployments/flow_runs.py +185 -0
  45. prefect/deployments/runner.py +50 -45
  46. prefect/deployments/schedules.py +28 -23
  47. prefect/deployments/steps/__init__.py +0 -1
  48. prefect/deployments/steps/core.py +1 -0
  49. prefect/deployments/steps/pull.py +7 -21
  50. prefect/engine.py +12 -2422
  51. prefect/events/actions.py +17 -23
  52. prefect/events/cli/automations.py +19 -6
  53. prefect/events/clients.py +14 -37
  54. prefect/events/filters.py +14 -18
  55. prefect/events/related.py +2 -2
  56. prefect/events/schemas/__init__.py +0 -5
  57. prefect/events/schemas/automations.py +55 -46
  58. prefect/events/schemas/deployment_triggers.py +7 -197
  59. prefect/events/schemas/events.py +36 -65
  60. prefect/events/schemas/labelling.py +10 -14
  61. prefect/events/utilities.py +2 -3
  62. prefect/events/worker.py +2 -3
  63. prefect/filesystems.py +6 -517
  64. prefect/{new_flow_engine.py → flow_engine.py} +315 -74
  65. prefect/flow_runs.py +379 -7
  66. prefect/flows.py +248 -165
  67. prefect/futures.py +187 -345
  68. prefect/infrastructure/__init__.py +0 -27
  69. prefect/infrastructure/provisioners/__init__.py +5 -3
  70. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  71. prefect/infrastructure/provisioners/container_instance.py +11 -7
  72. prefect/infrastructure/provisioners/ecs.py +6 -4
  73. prefect/infrastructure/provisioners/modal.py +8 -5
  74. prefect/input/actions.py +2 -4
  75. prefect/input/run_input.py +9 -9
  76. prefect/logging/formatters.py +0 -2
  77. prefect/logging/handlers.py +3 -11
  78. prefect/logging/loggers.py +2 -2
  79. prefect/manifests.py +2 -1
  80. prefect/records/__init__.py +1 -0
  81. prefect/records/cache_policies.py +179 -0
  82. prefect/records/result_store.py +42 -0
  83. prefect/records/store.py +9 -0
  84. prefect/results.py +43 -39
  85. prefect/runner/runner.py +9 -9
  86. prefect/runner/server.py +6 -10
  87. prefect/runner/storage.py +3 -8
  88. prefect/runner/submit.py +2 -2
  89. prefect/runner/utils.py +2 -2
  90. prefect/serializers.py +24 -35
  91. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  92. prefect/settings.py +76 -136
  93. prefect/states.py +22 -50
  94. prefect/task_engine.py +666 -56
  95. prefect/task_runners.py +272 -300
  96. prefect/task_runs.py +203 -0
  97. prefect/{task_server.py → task_worker.py} +89 -60
  98. prefect/tasks.py +358 -341
  99. prefect/transactions.py +224 -0
  100. prefect/types/__init__.py +61 -82
  101. prefect/utilities/asyncutils.py +195 -136
  102. prefect/utilities/callables.py +121 -41
  103. prefect/utilities/collections.py +23 -38
  104. prefect/utilities/dispatch.py +11 -3
  105. prefect/utilities/dockerutils.py +4 -0
  106. prefect/utilities/engine.py +140 -20
  107. prefect/utilities/importtools.py +26 -27
  108. prefect/utilities/pydantic.py +128 -38
  109. prefect/utilities/schema_tools/hydration.py +5 -1
  110. prefect/utilities/templating.py +12 -2
  111. prefect/variables.py +84 -62
  112. prefect/workers/__init__.py +0 -1
  113. prefect/workers/base.py +26 -18
  114. prefect/workers/process.py +3 -8
  115. prefect/workers/server.py +2 -2
  116. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/METADATA +23 -21
  117. prefect_client-3.0.0rc2.dist-info/RECORD +179 -0
  118. prefect/_internal/pydantic/_base_model.py +0 -51
  119. prefect/_internal/pydantic/_compat.py +0 -82
  120. prefect/_internal/pydantic/_flags.py +0 -20
  121. prefect/_internal/pydantic/_types.py +0 -8
  122. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  123. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  124. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  125. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  126. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  127. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  128. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  129. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  130. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  131. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  132. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  133. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  134. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  135. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  136. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  137. prefect/_vendor/__init__.py +0 -0
  138. prefect/_vendor/fastapi/__init__.py +0 -25
  139. prefect/_vendor/fastapi/applications.py +0 -946
  140. prefect/_vendor/fastapi/background.py +0 -3
  141. prefect/_vendor/fastapi/concurrency.py +0 -44
  142. prefect/_vendor/fastapi/datastructures.py +0 -58
  143. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  144. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  145. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  146. prefect/_vendor/fastapi/encoders.py +0 -177
  147. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  148. prefect/_vendor/fastapi/exceptions.py +0 -46
  149. prefect/_vendor/fastapi/logger.py +0 -3
  150. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  151. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  152. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  153. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  154. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  155. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  156. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  157. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  158. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  159. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  160. prefect/_vendor/fastapi/openapi/models.py +0 -480
  161. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  162. prefect/_vendor/fastapi/param_functions.py +0 -340
  163. prefect/_vendor/fastapi/params.py +0 -453
  164. prefect/_vendor/fastapi/requests.py +0 -4
  165. prefect/_vendor/fastapi/responses.py +0 -40
  166. prefect/_vendor/fastapi/routing.py +0 -1331
  167. prefect/_vendor/fastapi/security/__init__.py +0 -15
  168. prefect/_vendor/fastapi/security/api_key.py +0 -98
  169. prefect/_vendor/fastapi/security/base.py +0 -6
  170. prefect/_vendor/fastapi/security/http.py +0 -172
  171. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  172. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  173. prefect/_vendor/fastapi/security/utils.py +0 -10
  174. prefect/_vendor/fastapi/staticfiles.py +0 -1
  175. prefect/_vendor/fastapi/templating.py +0 -3
  176. prefect/_vendor/fastapi/testclient.py +0 -1
  177. prefect/_vendor/fastapi/types.py +0 -3
  178. prefect/_vendor/fastapi/utils.py +0 -235
  179. prefect/_vendor/fastapi/websockets.py +0 -7
  180. prefect/_vendor/starlette/__init__.py +0 -1
  181. prefect/_vendor/starlette/_compat.py +0 -28
  182. prefect/_vendor/starlette/_exception_handler.py +0 -80
  183. prefect/_vendor/starlette/_utils.py +0 -88
  184. prefect/_vendor/starlette/applications.py +0 -261
  185. prefect/_vendor/starlette/authentication.py +0 -159
  186. prefect/_vendor/starlette/background.py +0 -43
  187. prefect/_vendor/starlette/concurrency.py +0 -59
  188. prefect/_vendor/starlette/config.py +0 -151
  189. prefect/_vendor/starlette/convertors.py +0 -87
  190. prefect/_vendor/starlette/datastructures.py +0 -707
  191. prefect/_vendor/starlette/endpoints.py +0 -130
  192. prefect/_vendor/starlette/exceptions.py +0 -60
  193. prefect/_vendor/starlette/formparsers.py +0 -276
  194. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  195. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  196. prefect/_vendor/starlette/middleware/base.py +0 -220
  197. prefect/_vendor/starlette/middleware/cors.py +0 -176
  198. prefect/_vendor/starlette/middleware/errors.py +0 -265
  199. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  200. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  201. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  202. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  203. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  204. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  205. prefect/_vendor/starlette/requests.py +0 -328
  206. prefect/_vendor/starlette/responses.py +0 -347
  207. prefect/_vendor/starlette/routing.py +0 -933
  208. prefect/_vendor/starlette/schemas.py +0 -154
  209. prefect/_vendor/starlette/staticfiles.py +0 -248
  210. prefect/_vendor/starlette/status.py +0 -199
  211. prefect/_vendor/starlette/templating.py +0 -231
  212. prefect/_vendor/starlette/testclient.py +0 -804
  213. prefect/_vendor/starlette/types.py +0 -30
  214. prefect/_vendor/starlette/websockets.py +0 -193
  215. prefect/agent.py +0 -698
  216. prefect/deployments/deployments.py +0 -1042
  217. prefect/deprecated/__init__.py +0 -0
  218. prefect/deprecated/data_documents.py +0 -350
  219. prefect/deprecated/packaging/__init__.py +0 -12
  220. prefect/deprecated/packaging/base.py +0 -96
  221. prefect/deprecated/packaging/docker.py +0 -146
  222. prefect/deprecated/packaging/file.py +0 -92
  223. prefect/deprecated/packaging/orion.py +0 -80
  224. prefect/deprecated/packaging/serializers.py +0 -171
  225. prefect/events/instrument.py +0 -135
  226. prefect/infrastructure/base.py +0 -323
  227. prefect/infrastructure/container.py +0 -818
  228. prefect/infrastructure/kubernetes.py +0 -920
  229. prefect/infrastructure/process.py +0 -289
  230. prefect/new_task_engine.py +0 -423
  231. prefect/pydantic/__init__.py +0 -76
  232. prefect/pydantic/main.py +0 -39
  233. prefect/software/__init__.py +0 -2
  234. prefect/software/base.py +0 -50
  235. prefect/software/conda.py +0 -199
  236. prefect/software/pip.py +0 -122
  237. prefect/software/python.py +0 -52
  238. prefect/workers/block.py +0 -218
  239. prefect_client-2.19.4.dist-info/RECORD +0 -292
  240. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/LICENSE +0 -0
  241. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/WHEEL +0 -0
  242. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/top_level.txt +0 -0
@@ -8,12 +8,13 @@ from typing import (
8
8
  Dict,
9
9
  Iterable,
10
10
  List,
11
- NoReturn,
11
+ Literal,
12
12
  Optional,
13
13
  Set,
14
14
  Tuple,
15
15
  TypeVar,
16
16
  Union,
17
+ overload,
17
18
  )
18
19
  from uuid import UUID, uuid4
19
20
 
@@ -21,35 +22,20 @@ import certifi
21
22
  import httpcore
22
23
  import httpx
23
24
  import pendulum
24
- from typing_extensions import ParamSpec
25
-
26
- from prefect._internal.compatibility.deprecated import (
27
- handle_deprecated_infra_overrides_parameter,
28
- )
29
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
30
- from prefect.client.schemas import sorting
31
- from prefect.events import filters
32
- from prefect.settings import (
33
- PREFECT_API_SERVICES_TRIGGERS_ENABLED,
34
- PREFECT_EXPERIMENTAL_EVENTS,
35
- )
36
-
37
- if HAS_PYDANTIC_V2:
38
- import pydantic.v1 as pydantic
39
- else:
40
- import pydantic
41
-
25
+ import pydantic
42
26
  from asgi_lifespan import LifespanManager
43
- from prefect._vendor.starlette import status
27
+ from starlette import status
28
+ from typing_extensions import ParamSpec
44
29
 
45
30
  import prefect
46
31
  import prefect.exceptions
47
32
  import prefect.settings
48
33
  import prefect.states
49
34
  from prefect.client.constants import SERVER_API_VERSION
50
- from prefect.client.schemas import FlowRun, OrchestrationResult, TaskRun
35
+ from prefect.client.schemas import FlowRun, OrchestrationResult, TaskRun, sorting
51
36
  from prefect.client.schemas.actions import (
52
37
  ArtifactCreate,
38
+ ArtifactUpdate,
53
39
  BlockDocumentCreate,
54
40
  BlockDocumentUpdate,
55
41
  BlockSchemaCreate,
@@ -133,7 +119,7 @@ from prefect.client.schemas.sorting import (
133
119
  LogSort,
134
120
  TaskRunSort,
135
121
  )
136
- from prefect.deprecated.data_documents import DataDocument
122
+ from prefect.events import filters
137
123
  from prefect.events.schemas.automations import Automation, AutomationCore
138
124
  from prefect.logging import get_logger
139
125
  from prefect.settings import (
@@ -171,16 +157,24 @@ class ServerType(AutoEnum):
171
157
  SERVER = AutoEnum.auto()
172
158
  CLOUD = AutoEnum.auto()
173
159
 
174
- def supports_automations(self) -> bool:
175
- if self == ServerType.CLOUD:
176
- return True
177
160
 
178
- return PREFECT_EXPERIMENTAL_EVENTS and PREFECT_API_SERVICES_TRIGGERS_ENABLED
161
+ @overload
162
+ def get_client(
163
+ httpx_settings: Optional[Dict[str, Any]] = None, sync_client: Literal[False] = False
164
+ ) -> "PrefectClient":
165
+ ...
166
+
167
+
168
+ @overload
169
+ def get_client(
170
+ httpx_settings: Optional[Dict[str, Any]] = None, sync_client: Literal[True] = True
171
+ ) -> "SyncPrefectClient":
172
+ ...
179
173
 
180
174
 
181
175
  def get_client(
182
176
  httpx_settings: Optional[Dict[str, Any]] = None, sync_client: bool = False
183
- ) -> Union["PrefectClient", "SyncPrefectClient"]:
177
+ ):
184
178
  """
185
179
  Retrieve a HTTP client for communicating with the Prefect REST API.
186
180
 
@@ -198,14 +192,39 @@ def get_client(
198
192
  client.hello()
199
193
  ```
200
194
  """
201
- ctx = prefect.context.get_settings_context()
195
+ import prefect.context
196
+
197
+ settings_ctx = prefect.context.get_settings_context()
198
+
199
+ # try to load clients from a client context, if possible
200
+ # only load clients that match the provided config / loop
201
+ try:
202
+ loop = asyncio.get_running_loop()
203
+ except RuntimeError:
204
+ loop = None
205
+
206
+ if client_ctx := prefect.context.ClientContext.get():
207
+ if (
208
+ sync_client
209
+ and client_ctx.sync_client
210
+ and client_ctx._httpx_settings == httpx_settings
211
+ ):
212
+ return client_ctx.sync_client
213
+ elif (
214
+ not sync_client
215
+ and client_ctx.async_client
216
+ and client_ctx._httpx_settings == httpx_settings
217
+ and loop in (client_ctx.async_client._loop, None)
218
+ ):
219
+ return client_ctx.async_client
220
+
202
221
  api = PREFECT_API_URL.value()
203
222
 
204
223
  if not api:
205
224
  # create an ephemeral API if none was provided
206
225
  from prefect.server.api.server import create_app
207
226
 
208
- api = create_app(ctx.settings, ephemeral=True)
227
+ api = create_app(settings_ctx.settings, ephemeral=True)
209
228
 
210
229
  if sync_client:
211
230
  return SyncPrefectClient(
@@ -273,6 +292,7 @@ class PrefectClient:
273
292
  httpx_settings["headers"].setdefault("Authorization", f"Bearer {api_key}")
274
293
 
275
294
  # Context management
295
+ self._context_stack: int = 0
276
296
  self._exit_stack = AsyncExitStack()
277
297
  self._ephemeral_app: Optional[ASGIApp] = None
278
298
  self.manage_lifespan = True
@@ -453,7 +473,7 @@ class PrefectClient:
453
473
  """
454
474
  flow_data = FlowCreate(name=flow_name)
455
475
  response = await self._client.post(
456
- "/flows/", json=flow_data.dict(json_compatible=True)
476
+ "/flows/", json=flow_data.model_dump(mode="json")
457
477
  )
458
478
 
459
479
  flow_id = response.json().get("id")
@@ -474,7 +494,7 @@ class PrefectClient:
474
494
  a [Flow model][prefect.client.schemas.objects.Flow] representation of the flow
475
495
  """
476
496
  response = await self._client.get(f"/flows/{flow_id}")
477
- return Flow.parse_obj(response.json())
497
+ return Flow.model_validate(response.json())
478
498
 
479
499
  async def read_flows(
480
500
  self,
@@ -508,29 +528,23 @@ class PrefectClient:
508
528
  a list of Flow model representations of the flows
509
529
  """
510
530
  body = {
511
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
531
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
512
532
  "flow_runs": (
513
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
533
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
514
534
  if flow_run_filter
515
535
  else None
516
536
  ),
517
537
  "task_runs": (
518
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
538
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
519
539
  ),
520
540
  "deployments": (
521
- deployment_filter.dict(json_compatible=True)
522
- if deployment_filter
523
- else None
541
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
524
542
  ),
525
543
  "work_pools": (
526
- work_pool_filter.dict(json_compatible=True)
527
- if work_pool_filter
528
- else None
544
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
529
545
  ),
530
546
  "work_queues": (
531
- work_queue_filter.dict(json_compatible=True)
532
- if work_queue_filter
533
- else None
547
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
534
548
  ),
535
549
  "sort": sort,
536
550
  "limit": limit,
@@ -538,7 +552,7 @@ class PrefectClient:
538
552
  }
539
553
 
540
554
  response = await self._client.post("/flows/filter", json=body)
541
- return pydantic.parse_obj_as(List[Flow], response.json())
555
+ return pydantic.TypeAdapter(List[Flow]).validate_python(response.json())
542
556
 
543
557
  async def read_flow_by_name(
544
558
  self,
@@ -554,7 +568,7 @@ class PrefectClient:
554
568
  a fully hydrated Flow model
555
569
  """
556
570
  response = await self._client.get(f"/flows/name/{flow_name}")
557
- return Flow.parse_obj(response.json())
571
+ return Flow.model_validate(response.json())
558
572
 
559
573
  async def create_flow_run_from_deployment(
560
574
  self,
@@ -622,9 +636,9 @@ class PrefectClient:
622
636
 
623
637
  response = await self._client.post(
624
638
  f"/deployments/{deployment_id}/create_flow_run",
625
- json=flow_run_create.dict(json_compatible=True, exclude_unset=True),
639
+ json=flow_run_create.model_dump(mode="json", exclude_unset=True),
626
640
  )
627
- return FlowRun.parse_obj(response.json())
641
+ return FlowRun.model_validate(response.json())
628
642
 
629
643
  async def create_flow_run(
630
644
  self,
@@ -680,9 +694,9 @@ class PrefectClient:
680
694
  ),
681
695
  )
682
696
 
683
- flow_run_create_json = flow_run_create.dict(json_compatible=True)
697
+ flow_run_create_json = flow_run_create.model_dump(mode="json")
684
698
  response = await self._client.post("/flow_runs/", json=flow_run_create_json)
685
- flow_run = FlowRun.parse_obj(response.json())
699
+ flow_run = FlowRun.model_validate(response.json())
686
700
 
687
701
  # Restore the parameters to the local objects to retain expectations about
688
702
  # Python objects
@@ -740,7 +754,7 @@ class PrefectClient:
740
754
 
741
755
  return await self._client.patch(
742
756
  f"/flow_runs/{flow_run_id}",
743
- json=flow_run_data.dict(json_compatible=True, exclude_unset=True),
757
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
744
758
  )
745
759
 
746
760
  async def delete_flow_run(
@@ -790,7 +804,7 @@ class PrefectClient:
790
804
  )
791
805
  response = await self._client.post(
792
806
  "/concurrency_limits/",
793
- json=concurrency_limit_create.dict(json_compatible=True),
807
+ json=concurrency_limit_create.model_dump(mode="json"),
794
808
  )
795
809
 
796
810
  concurrency_limit_id = response.json().get("id")
@@ -832,7 +846,7 @@ class PrefectClient:
832
846
  if not concurrency_limit_id:
833
847
  raise httpx.RequestError(f"Malformed response: {response}")
834
848
 
835
- concurrency_limit = ConcurrencyLimit.parse_obj(response.json())
849
+ concurrency_limit = ConcurrencyLimit.model_validate(response.json())
836
850
  return concurrency_limit
837
851
 
838
852
  async def read_concurrency_limits(
@@ -857,7 +871,9 @@ class PrefectClient:
857
871
  }
858
872
 
859
873
  response = await self._client.post("/concurrency_limits/filter", json=body)
860
- return pydantic.parse_obj_as(List[ConcurrencyLimit], response.json())
874
+ return pydantic.TypeAdapter(List[ConcurrencyLimit]).validate_python(
875
+ response.json()
876
+ )
861
877
 
862
878
  async def reset_concurrency_limit_by_tag(
863
879
  self,
@@ -969,7 +985,7 @@ class PrefectClient:
969
985
  if priority is not None:
970
986
  create_model.priority = priority
971
987
 
972
- data = create_model.dict(json_compatible=True)
988
+ data = create_model.model_dump(mode="json")
973
989
  try:
974
990
  if work_pool_name is not None:
975
991
  response = await self._client.post(
@@ -984,7 +1000,7 @@ class PrefectClient:
984
1000
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
985
1001
  else:
986
1002
  raise
987
- return WorkQueue.parse_obj(response.json())
1003
+ return WorkQueue.model_validate(response.json())
988
1004
 
989
1005
  async def read_work_queue_by_name(
990
1006
  self,
@@ -1019,7 +1035,7 @@ class PrefectClient:
1019
1035
  else:
1020
1036
  raise
1021
1037
 
1022
- return WorkQueue.parse_obj(response.json())
1038
+ return WorkQueue.model_validate(response.json())
1023
1039
 
1024
1040
  async def update_work_queue(self, id: UUID, **kwargs):
1025
1041
  """
@@ -1038,7 +1054,7 @@ class PrefectClient:
1038
1054
  if not kwargs:
1039
1055
  raise ValueError("No fields provided to update.")
1040
1056
 
1041
- data = WorkQueueUpdate(**kwargs).dict(json_compatible=True, exclude_unset=True)
1057
+ data = WorkQueueUpdate(**kwargs).model_dump(mode="json", exclude_unset=True)
1042
1058
  try:
1043
1059
  await self._client.patch(f"/work_queues/{id}", json=data)
1044
1060
  except httpx.HTTPStatusError as e:
@@ -1085,7 +1101,7 @@ class PrefectClient:
1085
1101
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1086
1102
  else:
1087
1103
  raise
1088
- return pydantic.parse_obj_as(List[FlowRun], response.json())
1104
+ return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
1089
1105
 
1090
1106
  async def read_work_queue(
1091
1107
  self,
@@ -1111,7 +1127,7 @@ class PrefectClient:
1111
1127
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1112
1128
  else:
1113
1129
  raise
1114
- return WorkQueue.parse_obj(response.json())
1130
+ return WorkQueue.model_validate(response.json())
1115
1131
 
1116
1132
  async def read_work_queue_status(
1117
1133
  self,
@@ -1137,7 +1153,7 @@ class PrefectClient:
1137
1153
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1138
1154
  else:
1139
1155
  raise
1140
- return WorkQueueStatusDetail.parse_obj(response.json())
1156
+ return WorkQueueStatusDetail.model_validate(response.json())
1141
1157
 
1142
1158
  async def match_work_queues(
1143
1159
  self,
@@ -1206,8 +1222,8 @@ class PrefectClient:
1206
1222
  try:
1207
1223
  response = await self._client.post(
1208
1224
  "/block_types/",
1209
- json=block_type.dict(
1210
- json_compatible=True, exclude_unset=True, exclude={"id"}
1225
+ json=block_type.model_dump(
1226
+ mode="json", exclude_unset=True, exclude={"id"}
1211
1227
  ),
1212
1228
  )
1213
1229
  except httpx.HTTPStatusError as e:
@@ -1215,7 +1231,7 @@ class PrefectClient:
1215
1231
  raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
1216
1232
  else:
1217
1233
  raise
1218
- return BlockType.parse_obj(response.json())
1234
+ return BlockType.model_validate(response.json())
1219
1235
 
1220
1236
  async def create_block_schema(self, block_schema: BlockSchemaCreate) -> BlockSchema:
1221
1237
  """
@@ -1224,8 +1240,8 @@ class PrefectClient:
1224
1240
  try:
1225
1241
  response = await self._client.post(
1226
1242
  "/block_schemas/",
1227
- json=block_schema.dict(
1228
- json_compatible=True,
1243
+ json=block_schema.model_dump(
1244
+ mode="json",
1229
1245
  exclude_unset=True,
1230
1246
  exclude={"id", "block_type", "checksum"},
1231
1247
  ),
@@ -1235,7 +1251,7 @@ class PrefectClient:
1235
1251
  raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
1236
1252
  else:
1237
1253
  raise
1238
- return BlockSchema.parse_obj(response.json())
1254
+ return BlockSchema.model_validate(response.json())
1239
1255
 
1240
1256
  async def create_block_document(
1241
1257
  self,
@@ -1252,24 +1268,14 @@ class PrefectClient:
1252
1268
  `SecretBytes` fields. Note Blocks may not work as expected if
1253
1269
  this is set to `False`.
1254
1270
  """
1255
- if isinstance(block_document, BlockDocument):
1256
- block_document = BlockDocumentCreate.parse_obj(
1257
- block_document.dict(
1258
- json_compatible=True,
1259
- include_secrets=include_secrets,
1260
- exclude_unset=True,
1261
- exclude={"id", "block_schema", "block_type"},
1262
- ),
1263
- )
1264
-
1265
1271
  try:
1266
1272
  response = await self._client.post(
1267
1273
  "/block_documents/",
1268
- json=block_document.dict(
1269
- json_compatible=True,
1270
- include_secrets=include_secrets,
1274
+ json=block_document.model_dump(
1275
+ mode="json",
1271
1276
  exclude_unset=True,
1272
1277
  exclude={"id", "block_schema", "block_type"},
1278
+ context={"include_secrets": include_secrets},
1273
1279
  ),
1274
1280
  )
1275
1281
  except httpx.HTTPStatusError as e:
@@ -1277,7 +1283,7 @@ class PrefectClient:
1277
1283
  raise prefect.exceptions.ObjectAlreadyExists(http_exc=e) from e
1278
1284
  else:
1279
1285
  raise
1280
- return BlockDocument.parse_obj(response.json())
1286
+ return BlockDocument.model_validate(response.json())
1281
1287
 
1282
1288
  async def update_block_document(
1283
1289
  self,
@@ -1290,11 +1296,10 @@ class PrefectClient:
1290
1296
  try:
1291
1297
  await self._client.patch(
1292
1298
  f"/block_documents/{block_document_id}",
1293
- json=block_document.dict(
1294
- json_compatible=True,
1299
+ json=block_document.model_dump(
1300
+ mode="json",
1295
1301
  exclude_unset=True,
1296
1302
  include={"data", "merge_existing_data", "block_schema_id"},
1297
- include_secrets=True,
1298
1303
  ),
1299
1304
  )
1300
1305
  except httpx.HTTPStatusError as e:
@@ -1326,7 +1331,7 @@ class PrefectClient:
1326
1331
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1327
1332
  else:
1328
1333
  raise
1329
- return BlockType.parse_obj(response.json())
1334
+ return BlockType.model_validate(response.json())
1330
1335
 
1331
1336
  async def read_block_schema_by_checksum(
1332
1337
  self, checksum: str, version: Optional[str] = None
@@ -1344,7 +1349,7 @@ class PrefectClient:
1344
1349
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1345
1350
  else:
1346
1351
  raise
1347
- return BlockSchema.parse_obj(response.json())
1352
+ return BlockSchema.model_validate(response.json())
1348
1353
 
1349
1354
  async def update_block_type(self, block_type_id: UUID, block_type: BlockTypeUpdate):
1350
1355
  """
@@ -1353,11 +1358,10 @@ class PrefectClient:
1353
1358
  try:
1354
1359
  await self._client.patch(
1355
1360
  f"/block_types/{block_type_id}",
1356
- json=block_type.dict(
1357
- json_compatible=True,
1361
+ json=block_type.model_dump(
1362
+ mode="json",
1358
1363
  exclude_unset=True,
1359
1364
  include=BlockTypeUpdate.updatable_fields(),
1360
- include_secrets=True,
1361
1365
  ),
1362
1366
  )
1363
1367
  except httpx.HTTPStatusError as e:
@@ -1396,7 +1400,7 @@ class PrefectClient:
1396
1400
  List of BlockTypes.
1397
1401
  """
1398
1402
  response = await self._client.post("/block_types/filter", json={})
1399
- return pydantic.parse_obj_as(List[BlockType], response.json())
1403
+ return pydantic.TypeAdapter(List[BlockType]).validate_python(response.json())
1400
1404
 
1401
1405
  async def read_block_schemas(self) -> List[BlockSchema]:
1402
1406
  """
@@ -1408,7 +1412,7 @@ class PrefectClient:
1408
1412
  A BlockSchema.
1409
1413
  """
1410
1414
  response = await self._client.post("/block_schemas/filter", json={})
1411
- return pydantic.parse_obj_as(List[BlockSchema], response.json())
1415
+ return pydantic.TypeAdapter(List[BlockSchema]).validate_python(response.json())
1412
1416
 
1413
1417
  async def get_most_recent_block_schema_for_block_type(
1414
1418
  self,
@@ -1436,7 +1440,9 @@ class PrefectClient:
1436
1440
  )
1437
1441
  except httpx.HTTPStatusError:
1438
1442
  raise
1439
- return BlockSchema.parse_obj(response.json()[0]) if response.json() else None
1443
+ return (
1444
+ BlockSchema.model_validate(response.json()[0]) if response.json() else None
1445
+ )
1440
1446
 
1441
1447
  async def read_block_document(
1442
1448
  self,
@@ -1474,7 +1480,7 @@ class PrefectClient:
1474
1480
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1475
1481
  else:
1476
1482
  raise
1477
- return BlockDocument.parse_obj(response.json())
1483
+ return BlockDocument.model_validate(response.json())
1478
1484
 
1479
1485
  async def read_block_document_by_name(
1480
1486
  self,
@@ -1512,7 +1518,7 @@ class PrefectClient:
1512
1518
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1513
1519
  else:
1514
1520
  raise
1515
- return BlockDocument.parse_obj(response.json())
1521
+ return BlockDocument.model_validate(response.json())
1516
1522
 
1517
1523
  async def read_block_documents(
1518
1524
  self,
@@ -1547,7 +1553,9 @@ class PrefectClient:
1547
1553
  include_secrets=include_secrets,
1548
1554
  ),
1549
1555
  )
1550
- return pydantic.parse_obj_as(List[BlockDocument], response.json())
1556
+ return pydantic.TypeAdapter(List[BlockDocument]).validate_python(
1557
+ response.json()
1558
+ )
1551
1559
 
1552
1560
  async def read_block_documents_by_type(
1553
1561
  self,
@@ -1576,26 +1584,27 @@ class PrefectClient:
1576
1584
  ),
1577
1585
  )
1578
1586
 
1579
- return pydantic.parse_obj_as(List[BlockDocument], response.json())
1587
+ return pydantic.TypeAdapter(List[BlockDocument]).validate_python(
1588
+ response.json()
1589
+ )
1580
1590
 
1581
1591
  async def create_deployment(
1582
1592
  self,
1583
1593
  flow_id: UUID,
1584
1594
  name: str,
1585
- version: str = None,
1586
- schedule: SCHEDULE_TYPES = None,
1587
- schedules: List[DeploymentScheduleCreate] = None,
1595
+ version: Optional[str] = None,
1596
+ schedule: Optional[SCHEDULE_TYPES] = None,
1597
+ schedules: Optional[List[DeploymentScheduleCreate]] = None,
1588
1598
  parameters: Optional[Dict[str, Any]] = None,
1589
- description: str = None,
1590
- work_queue_name: str = None,
1591
- work_pool_name: str = None,
1592
- tags: List[str] = None,
1593
- storage_document_id: UUID = None,
1594
- manifest_path: str = None,
1595
- path: str = None,
1596
- entrypoint: str = None,
1597
- infrastructure_document_id: UUID = None,
1598
- infra_overrides: Optional[Dict[str, Any]] = None, # for backwards compat
1599
+ description: Optional[str] = None,
1600
+ work_queue_name: Optional[str] = None,
1601
+ work_pool_name: Optional[str] = None,
1602
+ tags: Optional[List[str]] = None,
1603
+ storage_document_id: Optional[UUID] = None,
1604
+ manifest_path: Optional[str] = None,
1605
+ path: Optional[str] = None,
1606
+ entrypoint: Optional[str] = None,
1607
+ infrastructure_document_id: Optional[UUID] = None,
1599
1608
  parameter_openapi_schema: Optional[Dict[str, Any]] = None,
1600
1609
  is_schedule_active: Optional[bool] = None,
1601
1610
  paused: Optional[bool] = None,
@@ -1627,8 +1636,9 @@ class PrefectClient:
1627
1636
  Returns:
1628
1637
  the ID of the deployment in the backend
1629
1638
  """
1630
- jv = handle_deprecated_infra_overrides_parameter(job_variables, infra_overrides)
1631
1639
 
1640
+ if parameter_openapi_schema is None:
1641
+ parameter_openapi_schema = {}
1632
1642
  deployment_create = DeploymentCreate(
1633
1643
  flow_id=flow_id,
1634
1644
  name=name,
@@ -1642,7 +1652,7 @@ class PrefectClient:
1642
1652
  entrypoint=entrypoint,
1643
1653
  manifest_path=manifest_path, # for backwards compat
1644
1654
  infrastructure_document_id=infrastructure_document_id,
1645
- job_variables=jv,
1655
+ job_variables=dict(job_variables or {}),
1646
1656
  parameter_openapi_schema=parameter_openapi_schema,
1647
1657
  is_schedule_active=is_schedule_active,
1648
1658
  paused=paused,
@@ -1659,7 +1669,7 @@ class PrefectClient:
1659
1669
  exclude = {
1660
1670
  field
1661
1671
  for field in ["work_pool_name", "work_queue_name"]
1662
- if field not in deployment_create.__fields_set__
1672
+ if field not in deployment_create.model_fields_set
1663
1673
  }
1664
1674
 
1665
1675
  if deployment_create.is_schedule_active is None:
@@ -1674,7 +1684,7 @@ class PrefectClient:
1674
1684
  if deployment_create.enforce_parameter_schema is None:
1675
1685
  exclude.add("enforce_parameter_schema")
1676
1686
 
1677
- json = deployment_create.dict(json_compatible=True, exclude=exclude)
1687
+ json = deployment_create.model_dump(mode="json", exclude=exclude)
1678
1688
  response = await self._client.post(
1679
1689
  "/deployments/",
1680
1690
  json=json,
@@ -1732,7 +1742,7 @@ class PrefectClient:
1732
1742
 
1733
1743
  await self._client.patch(
1734
1744
  f"/deployments/{deployment.id}",
1735
- json=deployment_update.dict(json_compatible=True, exclude=exclude),
1745
+ json=deployment_update.model_dump(mode="json", exclude=exclude),
1736
1746
  )
1737
1747
 
1738
1748
  async def _create_deployment_from_schema(self, schema: DeploymentCreate) -> UUID:
@@ -1742,7 +1752,7 @@ class PrefectClient:
1742
1752
  # TODO: We are likely to remove this method once we have considered the
1743
1753
  # packaging interface for deployments further.
1744
1754
  response = await self._client.post(
1745
- "/deployments/", json=schema.dict(json_compatible=True)
1755
+ "/deployments/", json=schema.model_dump(mode="json")
1746
1756
  )
1747
1757
  deployment_id = response.json().get("id")
1748
1758
  if not deployment_id:
@@ -1770,7 +1780,7 @@ class PrefectClient:
1770
1780
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1771
1781
  else:
1772
1782
  raise
1773
- return DeploymentResponse.parse_obj(response.json())
1783
+ return DeploymentResponse.model_validate(response.json())
1774
1784
 
1775
1785
  async def read_deployment_by_name(
1776
1786
  self,
@@ -1797,19 +1807,19 @@ class PrefectClient:
1797
1807
  else:
1798
1808
  raise
1799
1809
 
1800
- return DeploymentResponse.parse_obj(response.json())
1810
+ return DeploymentResponse.model_validate(response.json())
1801
1811
 
1802
1812
  async def read_deployments(
1803
1813
  self,
1804
1814
  *,
1805
- flow_filter: FlowFilter = None,
1806
- flow_run_filter: FlowRunFilter = None,
1807
- task_run_filter: TaskRunFilter = None,
1808
- deployment_filter: DeploymentFilter = None,
1809
- work_pool_filter: WorkPoolFilter = None,
1810
- work_queue_filter: WorkQueueFilter = None,
1811
- limit: int = None,
1812
- sort: DeploymentSort = None,
1815
+ flow_filter: Optional[FlowFilter] = None,
1816
+ flow_run_filter: Optional[FlowRunFilter] = None,
1817
+ task_run_filter: Optional[TaskRunFilter] = None,
1818
+ deployment_filter: Optional[DeploymentFilter] = None,
1819
+ work_pool_filter: Optional[WorkPoolFilter] = None,
1820
+ work_queue_filter: Optional[WorkQueueFilter] = None,
1821
+ limit: Optional[int] = None,
1822
+ sort: Optional[DeploymentSort] = None,
1813
1823
  offset: int = 0,
1814
1824
  ) -> List[DeploymentResponse]:
1815
1825
  """
@@ -1831,29 +1841,23 @@ class PrefectClient:
1831
1841
  of the deployments
1832
1842
  """
1833
1843
  body = {
1834
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
1844
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
1835
1845
  "flow_runs": (
1836
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
1846
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
1837
1847
  if flow_run_filter
1838
1848
  else None
1839
1849
  ),
1840
1850
  "task_runs": (
1841
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
1851
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
1842
1852
  ),
1843
1853
  "deployments": (
1844
- deployment_filter.dict(json_compatible=True)
1845
- if deployment_filter
1846
- else None
1854
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
1847
1855
  ),
1848
1856
  "work_pools": (
1849
- work_pool_filter.dict(json_compatible=True)
1850
- if work_pool_filter
1851
- else None
1857
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
1852
1858
  ),
1853
1859
  "work_pool_queues": (
1854
- work_queue_filter.dict(json_compatible=True)
1855
- if work_queue_filter
1856
- else None
1860
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
1857
1861
  ),
1858
1862
  "limit": limit,
1859
1863
  "offset": offset,
@@ -1861,7 +1865,9 @@ class PrefectClient:
1861
1865
  }
1862
1866
 
1863
1867
  response = await self._client.post("/deployments/filter", json=body)
1864
- return pydantic.parse_obj_as(List[DeploymentResponse], response.json())
1868
+ return pydantic.TypeAdapter(List[DeploymentResponse]).validate_python(
1869
+ response.json()
1870
+ )
1865
1871
 
1866
1872
  async def delete_deployment(
1867
1873
  self,
@@ -1909,13 +1915,15 @@ class PrefectClient:
1909
1915
  ]
1910
1916
 
1911
1917
  json = [
1912
- deployment_schedule_create.dict(json_compatible=True)
1918
+ deployment_schedule_create.model_dump(mode="json")
1913
1919
  for deployment_schedule_create in deployment_schedule_create
1914
1920
  ]
1915
1921
  response = await self._client.post(
1916
1922
  f"/deployments/{deployment_id}/schedules", json=json
1917
1923
  )
1918
- return pydantic.parse_obj_as(List[DeploymentSchedule], response.json())
1924
+ return pydantic.TypeAdapter(List[DeploymentSchedule]).validate_python(
1925
+ response.json()
1926
+ )
1919
1927
 
1920
1928
  async def read_deployment_schedules(
1921
1929
  self,
@@ -1937,7 +1945,9 @@ class PrefectClient:
1937
1945
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
1938
1946
  else:
1939
1947
  raise
1940
- return pydantic.parse_obj_as(List[DeploymentSchedule], response.json())
1948
+ return pydantic.TypeAdapter(List[DeploymentSchedule]).validate_python(
1949
+ response.json()
1950
+ )
1941
1951
 
1942
1952
  async def update_deployment_schedule(
1943
1953
  self,
@@ -1962,7 +1972,7 @@ class PrefectClient:
1962
1972
  kwargs["schedule"] = schedule
1963
1973
 
1964
1974
  deployment_schedule_update = DeploymentScheduleUpdate(**kwargs)
1965
- json = deployment_schedule_update.dict(json_compatible=True, exclude_unset=True)
1975
+ json = deployment_schedule_update.model_dump(mode="json", exclude_unset=True)
1966
1976
 
1967
1977
  try:
1968
1978
  await self._client.patch(
@@ -2016,7 +2026,7 @@ class PrefectClient:
2016
2026
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
2017
2027
  else:
2018
2028
  raise
2019
- return FlowRun.parse_obj(response.json())
2029
+ return FlowRun.model_validate(response.json())
2020
2030
 
2021
2031
  async def resume_flow_run(
2022
2032
  self, flow_run_id: UUID, run_input: Optional[Dict] = None
@@ -2038,7 +2048,7 @@ class PrefectClient:
2038
2048
  except httpx.HTTPStatusError:
2039
2049
  raise
2040
2050
 
2041
- return OrchestrationResult.parse_obj(response.json())
2051
+ return OrchestrationResult.model_validate(response.json())
2042
2052
 
2043
2053
  async def read_flow_runs(
2044
2054
  self,
@@ -2073,29 +2083,23 @@ class PrefectClient:
2073
2083
  of the flow runs
2074
2084
  """
2075
2085
  body = {
2076
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
2086
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
2077
2087
  "flow_runs": (
2078
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
2088
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
2079
2089
  if flow_run_filter
2080
2090
  else None
2081
2091
  ),
2082
2092
  "task_runs": (
2083
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
2093
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
2084
2094
  ),
2085
2095
  "deployments": (
2086
- deployment_filter.dict(json_compatible=True)
2087
- if deployment_filter
2088
- else None
2096
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
2089
2097
  ),
2090
2098
  "work_pools": (
2091
- work_pool_filter.dict(json_compatible=True)
2092
- if work_pool_filter
2093
- else None
2099
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
2094
2100
  ),
2095
2101
  "work_pool_queues": (
2096
- work_queue_filter.dict(json_compatible=True)
2097
- if work_queue_filter
2098
- else None
2102
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
2099
2103
  ),
2100
2104
  "sort": sort,
2101
2105
  "limit": limit,
@@ -2103,7 +2107,7 @@ class PrefectClient:
2103
2107
  }
2104
2108
 
2105
2109
  response = await self._client.post("/flow_runs/filter", json=body)
2106
- return pydantic.parse_obj_as(List[FlowRun], response.json())
2110
+ return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
2107
2111
 
2108
2112
  async def set_flow_run_state(
2109
2113
  self,
@@ -2123,13 +2127,17 @@ class PrefectClient:
2123
2127
  Returns:
2124
2128
  an OrchestrationResult model representation of state orchestration output
2125
2129
  """
2130
+ flow_run_id = (
2131
+ flow_run_id if isinstance(flow_run_id, UUID) else UUID(flow_run_id)
2132
+ )
2126
2133
  state_create = state.to_state_create()
2127
2134
  state_create.state_details.flow_run_id = flow_run_id
2128
2135
  state_create.state_details.transition_id = uuid4()
2136
+ print(repr(state_create))
2129
2137
  try:
2130
2138
  response = await self._client.post(
2131
2139
  f"/flow_runs/{flow_run_id}/set_state",
2132
- json=dict(state=state_create.dict(json_compatible=True), force=force),
2140
+ json=dict(state=state_create.model_dump(mode="json"), force=force),
2133
2141
  )
2134
2142
  except httpx.HTTPStatusError as e:
2135
2143
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
@@ -2137,7 +2145,7 @@ class PrefectClient:
2137
2145
  else:
2138
2146
  raise
2139
2147
 
2140
- return OrchestrationResult.parse_obj(response.json())
2148
+ return OrchestrationResult.model_validate(response.json())
2141
2149
 
2142
2150
  async def read_flow_run_states(
2143
2151
  self, flow_run_id: UUID
@@ -2155,13 +2163,15 @@ class PrefectClient:
2155
2163
  response = await self._client.get(
2156
2164
  "/flow_run_states/", params=dict(flow_run_id=str(flow_run_id))
2157
2165
  )
2158
- return pydantic.parse_obj_as(List[prefect.states.State], response.json())
2166
+ return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
2167
+ response.json()
2168
+ )
2159
2169
 
2160
2170
  async def set_task_run_name(self, task_run_id: UUID, name: str):
2161
2171
  task_run_data = TaskRunUpdate(name=name)
2162
2172
  return await self._client.patch(
2163
2173
  f"/task_runs/{task_run_id}",
2164
- json=task_run_data.dict(json_compatible=True, exclude_unset=True),
2174
+ json=task_run_data.model_dump(mode="json", exclude_unset=True),
2165
2175
  )
2166
2176
 
2167
2177
  async def create_task_run(
@@ -2169,6 +2179,7 @@ class PrefectClient:
2169
2179
  task: "TaskObject[P, R]",
2170
2180
  flow_run_id: Optional[UUID],
2171
2181
  dynamic_key: str,
2182
+ id: Optional[UUID] = None,
2172
2183
  name: Optional[str] = None,
2173
2184
  extra_tags: Optional[Iterable[str]] = None,
2174
2185
  state: Optional[prefect.states.State[R]] = None,
@@ -2192,6 +2203,8 @@ class PrefectClient:
2192
2203
  task: The Task to run
2193
2204
  flow_run_id: The flow run id with which to associate the task run
2194
2205
  dynamic_key: A key unique to this particular run of a Task within the flow
2206
+ id: An optional ID for the task run. If not provided, one will be generated
2207
+ server-side.
2195
2208
  name: An optional name for the task run
2196
2209
  extra_tags: an optional list of extra tags to apply to the task run in
2197
2210
  addition to `task.tags`
@@ -2208,10 +2221,11 @@ class PrefectClient:
2208
2221
  state = prefect.states.Pending()
2209
2222
 
2210
2223
  task_run_data = TaskRunCreate(
2224
+ id=id,
2211
2225
  name=name,
2212
2226
  flow_run_id=flow_run_id,
2213
2227
  task_key=task.task_key,
2214
- dynamic_key=dynamic_key,
2228
+ dynamic_key=str(dynamic_key),
2215
2229
  tags=list(tags),
2216
2230
  task_version=task.version,
2217
2231
  empirical_policy=TaskRunPolicy(
@@ -2222,11 +2236,10 @@ class PrefectClient:
2222
2236
  state=state.to_state_create(),
2223
2237
  task_inputs=task_inputs or {},
2224
2238
  )
2239
+ content = task_run_data.model_dump_json(exclude={"id"} if id is None else None)
2225
2240
 
2226
- response = await self._client.post(
2227
- "/task_runs/", json=task_run_data.dict(json_compatible=True)
2228
- )
2229
- return TaskRun.parse_obj(response.json())
2241
+ response = await self._client.post("/task_runs/", content=content)
2242
+ return TaskRun.model_validate(response.json())
2230
2243
 
2231
2244
  async def read_task_run(self, task_run_id: UUID) -> TaskRun:
2232
2245
  """
@@ -2238,8 +2251,14 @@ class PrefectClient:
2238
2251
  Returns:
2239
2252
  a Task Run model representation of the task run
2240
2253
  """
2241
- response = await self._client.get(f"/task_runs/{task_run_id}")
2242
- return TaskRun.parse_obj(response.json())
2254
+ try:
2255
+ response = await self._client.get(f"/task_runs/{task_run_id}")
2256
+ return TaskRun.model_validate(response.json())
2257
+ except httpx.HTTPStatusError as e:
2258
+ if e.response.status_code == status.HTTP_404_NOT_FOUND:
2259
+ raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
2260
+ else:
2261
+ raise
2243
2262
 
2244
2263
  async def read_task_runs(
2245
2264
  self,
@@ -2270,26 +2289,24 @@ class PrefectClient:
2270
2289
  of the task runs
2271
2290
  """
2272
2291
  body = {
2273
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
2292
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
2274
2293
  "flow_runs": (
2275
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
2294
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
2276
2295
  if flow_run_filter
2277
2296
  else None
2278
2297
  ),
2279
2298
  "task_runs": (
2280
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
2299
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
2281
2300
  ),
2282
2301
  "deployments": (
2283
- deployment_filter.dict(json_compatible=True)
2284
- if deployment_filter
2285
- else None
2302
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
2286
2303
  ),
2287
2304
  "sort": sort,
2288
2305
  "limit": limit,
2289
2306
  "offset": offset,
2290
2307
  }
2291
2308
  response = await self._client.post("/task_runs/filter", json=body)
2292
- return pydantic.parse_obj_as(List[TaskRun], response.json())
2309
+ return pydantic.TypeAdapter(List[TaskRun]).validate_python(response.json())
2293
2310
 
2294
2311
  async def delete_task_run(self, task_run_id: UUID) -> None:
2295
2312
  """
@@ -2331,9 +2348,9 @@ class PrefectClient:
2331
2348
  state_create.state_details.task_run_id = task_run_id
2332
2349
  response = await self._client.post(
2333
2350
  f"/task_runs/{task_run_id}/set_state",
2334
- json=dict(state=state_create.dict(json_compatible=True), force=force),
2351
+ json=dict(state=state_create.model_dump(mode="json"), force=force),
2335
2352
  )
2336
- return OrchestrationResult.parse_obj(response.json())
2353
+ return OrchestrationResult.model_validate(response.json())
2337
2354
 
2338
2355
  async def read_task_run_states(
2339
2356
  self, task_run_id: UUID
@@ -2350,7 +2367,9 @@ class PrefectClient:
2350
2367
  response = await self._client.get(
2351
2368
  "/task_run_states/", params=dict(task_run_id=str(task_run_id))
2352
2369
  )
2353
- return pydantic.parse_obj_as(List[prefect.states.State], response.json())
2370
+ return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
2371
+ response.json()
2372
+ )
2354
2373
 
2355
2374
  async def create_logs(self, logs: Iterable[Union[LogCreate, dict]]) -> None:
2356
2375
  """
@@ -2360,7 +2379,7 @@ class PrefectClient:
2360
2379
  logs: An iterable of `LogCreate` objects or already json-compatible dicts
2361
2380
  """
2362
2381
  serialized_logs = [
2363
- log.dict(json_compatible=True) if isinstance(log, LogCreate) else log
2382
+ log.model_dump(mode="json") if isinstance(log, LogCreate) else log
2364
2383
  for log in logs
2365
2384
  ]
2366
2385
  await self._client.post("/logs/", json=serialized_logs)
@@ -2397,7 +2416,7 @@ class PrefectClient:
2397
2416
  )
2398
2417
  response = await self._client.post(
2399
2418
  "/flow_run_notification_policies/",
2400
- json=policy.dict(json_compatible=True),
2419
+ json=policy.model_dump(mode="json"),
2401
2420
  )
2402
2421
 
2403
2422
  policy_id = response.json().get("id")
@@ -2467,7 +2486,7 @@ class PrefectClient:
2467
2486
  try:
2468
2487
  await self._client.patch(
2469
2488
  f"/flow_run_notification_policies/{id}",
2470
- json=policy.dict(json_compatible=True, exclude_unset=True),
2489
+ json=policy.model_dump(mode="json", exclude_unset=True),
2471
2490
  )
2472
2491
  except httpx.HTTPStatusError as e:
2473
2492
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
@@ -2496,7 +2515,7 @@ class PrefectClient:
2496
2515
  """
2497
2516
  body = {
2498
2517
  "flow_run_notification_policy_filter": (
2499
- flow_run_notification_policy_filter.dict(json_compatible=True)
2518
+ flow_run_notification_policy_filter.model_dump(mode="json")
2500
2519
  if flow_run_notification_policy_filter
2501
2520
  else None
2502
2521
  ),
@@ -2506,7 +2525,9 @@ class PrefectClient:
2506
2525
  response = await self._client.post(
2507
2526
  "/flow_run_notification_policies/filter", json=body
2508
2527
  )
2509
- return pydantic.parse_obj_as(List[FlowRunNotificationPolicy], response.json())
2528
+ return pydantic.TypeAdapter(List[FlowRunNotificationPolicy]).validate_python(
2529
+ response.json()
2530
+ )
2510
2531
 
2511
2532
  async def read_logs(
2512
2533
  self,
@@ -2519,45 +2540,14 @@ class PrefectClient:
2519
2540
  Read flow and task run logs.
2520
2541
  """
2521
2542
  body = {
2522
- "logs": log_filter.dict(json_compatible=True) if log_filter else None,
2543
+ "logs": log_filter.model_dump(mode="json") if log_filter else None,
2523
2544
  "limit": limit,
2524
2545
  "offset": offset,
2525
2546
  "sort": sort,
2526
2547
  }
2527
2548
 
2528
2549
  response = await self._client.post("/logs/filter", json=body)
2529
- return pydantic.parse_obj_as(List[Log], response.json())
2530
-
2531
- async def resolve_datadoc(self, datadoc: DataDocument) -> Any:
2532
- """
2533
- Recursively decode possibly nested data documents.
2534
-
2535
- "server" encoded documents will be retrieved from the server.
2536
-
2537
- Args:
2538
- datadoc: The data document to resolve
2539
-
2540
- Returns:
2541
- a decoded object, the innermost data
2542
- """
2543
- if not isinstance(datadoc, DataDocument):
2544
- raise TypeError(
2545
- f"`resolve_datadoc` received invalid type {type(datadoc).__name__}"
2546
- )
2547
-
2548
- async def resolve_inner(data):
2549
- if isinstance(data, bytes):
2550
- try:
2551
- data = DataDocument.parse_raw(data)
2552
- except pydantic.ValidationError:
2553
- return data
2554
-
2555
- if isinstance(data, DataDocument):
2556
- return await resolve_inner(data.decode())
2557
-
2558
- return data
2559
-
2560
- return await resolve_inner(datadoc)
2550
+ return pydantic.TypeAdapter(List[Log]).validate_python(response.json())
2561
2551
 
2562
2552
  async def send_worker_heartbeat(
2563
2553
  self,
@@ -2601,7 +2591,7 @@ class PrefectClient:
2601
2591
  f"/work_pools/{work_pool_name}/workers/filter",
2602
2592
  json={
2603
2593
  "worker_filter": (
2604
- worker_filter.dict(json_compatible=True, exclude_unset=True)
2594
+ worker_filter.model_dump(mode="json", exclude_unset=True)
2605
2595
  if worker_filter
2606
2596
  else None
2607
2597
  ),
@@ -2610,7 +2600,7 @@ class PrefectClient:
2610
2600
  },
2611
2601
  )
2612
2602
 
2613
- return pydantic.parse_obj_as(List[Worker], response.json())
2603
+ return pydantic.TypeAdapter(List[Worker]).validate_python(response.json())
2614
2604
 
2615
2605
  async def read_work_pool(self, work_pool_name: str) -> WorkPool:
2616
2606
  """
@@ -2625,7 +2615,7 @@ class PrefectClient:
2625
2615
  """
2626
2616
  try:
2627
2617
  response = await self._client.get(f"/work_pools/{work_pool_name}")
2628
- return pydantic.parse_obj_as(WorkPool, response.json())
2618
+ return WorkPool.model_validate(response.json())
2629
2619
  except httpx.HTTPStatusError as e:
2630
2620
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
2631
2621
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
@@ -2654,13 +2644,11 @@ class PrefectClient:
2654
2644
  "limit": limit,
2655
2645
  "offset": offset,
2656
2646
  "work_pools": (
2657
- work_pool_filter.dict(json_compatible=True)
2658
- if work_pool_filter
2659
- else None
2647
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
2660
2648
  ),
2661
2649
  }
2662
2650
  response = await self._client.post("/work_pools/filter", json=body)
2663
- return pydantic.parse_obj_as(List[WorkPool], response.json())
2651
+ return pydantic.TypeAdapter(List[WorkPool]).validate_python(response.json())
2664
2652
 
2665
2653
  async def create_work_pool(
2666
2654
  self,
@@ -2678,7 +2666,7 @@ class PrefectClient:
2678
2666
  try:
2679
2667
  response = await self._client.post(
2680
2668
  "/work_pools/",
2681
- json=work_pool.dict(json_compatible=True, exclude_unset=True),
2669
+ json=work_pool.model_dump(mode="json", exclude_unset=True),
2682
2670
  )
2683
2671
  except httpx.HTTPStatusError as e:
2684
2672
  if e.response.status_code == status.HTTP_409_CONFLICT:
@@ -2686,7 +2674,7 @@ class PrefectClient:
2686
2674
  else:
2687
2675
  raise
2688
2676
 
2689
- return pydantic.parse_obj_as(WorkPool, response.json())
2677
+ return WorkPool.model_validate(response.json())
2690
2678
 
2691
2679
  async def update_work_pool(
2692
2680
  self,
@@ -2703,7 +2691,7 @@ class PrefectClient:
2703
2691
  try:
2704
2692
  await self._client.patch(
2705
2693
  f"/work_pools/{work_pool_name}",
2706
- json=work_pool.dict(json_compatible=True, exclude_unset=True),
2694
+ json=work_pool.model_dump(mode="json", exclude_unset=True),
2707
2695
  )
2708
2696
  except httpx.HTTPStatusError as e:
2709
2697
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
@@ -2750,7 +2738,7 @@ class PrefectClient:
2750
2738
  """
2751
2739
  json = {
2752
2740
  "work_queues": (
2753
- work_queue_filter.dict(json_compatible=True, exclude_unset=True)
2741
+ work_queue_filter.model_dump(mode="json", exclude_unset=True)
2754
2742
  if work_queue_filter
2755
2743
  else None
2756
2744
  ),
@@ -2772,14 +2760,14 @@ class PrefectClient:
2772
2760
  else:
2773
2761
  response = await self._client.post("/work_queues/filter", json=json)
2774
2762
 
2775
- return pydantic.parse_obj_as(List[WorkQueue], response.json())
2763
+ return pydantic.TypeAdapter(List[WorkQueue]).validate_python(response.json())
2776
2764
 
2777
2765
  async def get_scheduled_flow_runs_for_deployments(
2778
2766
  self,
2779
2767
  deployment_ids: List[UUID],
2780
2768
  scheduled_before: Optional[datetime.datetime] = None,
2781
2769
  limit: Optional[int] = None,
2782
- ):
2770
+ ) -> List[FlowRunResponse]:
2783
2771
  body: Dict[str, Any] = dict(deployment_ids=[str(id) for id in deployment_ids])
2784
2772
  if scheduled_before:
2785
2773
  body["scheduled_before"] = str(scheduled_before)
@@ -2791,7 +2779,9 @@ class PrefectClient:
2791
2779
  json=body,
2792
2780
  )
2793
2781
 
2794
- return pydantic.parse_obj_as(List[FlowRunResponse], response.json())
2782
+ return pydantic.TypeAdapter(List[FlowRunResponse]).validate_python(
2783
+ response.json()
2784
+ )
2795
2785
 
2796
2786
  async def get_scheduled_flow_runs_for_work_pool(
2797
2787
  self,
@@ -2824,7 +2814,9 @@ class PrefectClient:
2824
2814
  f"/work_pools/{work_pool_name}/get_scheduled_flow_runs",
2825
2815
  json=body,
2826
2816
  )
2827
- return pydantic.parse_obj_as(List[WorkerFlowRunResponse], response.json())
2817
+ return pydantic.TypeAdapter(List[WorkerFlowRunResponse]).validate_python(
2818
+ response.json()
2819
+ )
2828
2820
 
2829
2821
  async def create_artifact(
2830
2822
  self,
@@ -2841,10 +2833,29 @@ class PrefectClient:
2841
2833
 
2842
2834
  response = await self._client.post(
2843
2835
  "/artifacts/",
2844
- json=artifact.dict(json_compatible=True, exclude_unset=True),
2836
+ json=artifact.model_dump(mode="json", exclude_unset=True),
2845
2837
  )
2846
2838
 
2847
- return pydantic.parse_obj_as(Artifact, response.json())
2839
+ return Artifact.model_validate(response.json())
2840
+
2841
+ async def update_artifact(
2842
+ self,
2843
+ artifact_id: UUID,
2844
+ artifact: ArtifactUpdate,
2845
+ ) -> None:
2846
+ """
2847
+ Updates an artifact
2848
+
2849
+ Args:
2850
+ artifact: Desired values for the updated artifact.
2851
+ Returns:
2852
+ Information about the updated artifact.
2853
+ """
2854
+
2855
+ await self._client.patch(
2856
+ f"/artifacts/{artifact_id}",
2857
+ json=artifact.model_dump(mode="json", exclude_unset=True),
2858
+ )
2848
2859
 
2849
2860
  async def read_artifacts(
2850
2861
  self,
@@ -2871,20 +2882,20 @@ class PrefectClient:
2871
2882
  """
2872
2883
  body = {
2873
2884
  "artifacts": (
2874
- artifact_filter.dict(json_compatible=True) if artifact_filter else None
2885
+ artifact_filter.model_dump(mode="json") if artifact_filter else None
2875
2886
  ),
2876
2887
  "flow_runs": (
2877
- flow_run_filter.dict(json_compatible=True) if flow_run_filter else None
2888
+ flow_run_filter.model_dump(mode="json") if flow_run_filter else None
2878
2889
  ),
2879
2890
  "task_runs": (
2880
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
2891
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
2881
2892
  ),
2882
2893
  "sort": sort,
2883
2894
  "limit": limit,
2884
2895
  "offset": offset,
2885
2896
  }
2886
2897
  response = await self._client.post("/artifacts/filter", json=body)
2887
- return pydantic.parse_obj_as(List[Artifact], response.json())
2898
+ return pydantic.TypeAdapter(List[Artifact]).validate_python(response.json())
2888
2899
 
2889
2900
  async def read_latest_artifacts(
2890
2901
  self,
@@ -2911,20 +2922,22 @@ class PrefectClient:
2911
2922
  """
2912
2923
  body = {
2913
2924
  "artifacts": (
2914
- artifact_filter.dict(json_compatible=True) if artifact_filter else None
2925
+ artifact_filter.model_dump(mode="json") if artifact_filter else None
2915
2926
  ),
2916
2927
  "flow_runs": (
2917
- flow_run_filter.dict(json_compatible=True) if flow_run_filter else None
2928
+ flow_run_filter.model_dump(mode="json") if flow_run_filter else None
2918
2929
  ),
2919
2930
  "task_runs": (
2920
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
2931
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
2921
2932
  ),
2922
2933
  "sort": sort,
2923
2934
  "limit": limit,
2924
2935
  "offset": offset,
2925
2936
  }
2926
2937
  response = await self._client.post("/artifacts/latest/filter", json=body)
2927
- return pydantic.parse_obj_as(List[ArtifactCollection], response.json())
2938
+ return pydantic.TypeAdapter(List[ArtifactCollection]).validate_python(
2939
+ response.json()
2940
+ )
2928
2941
 
2929
2942
  async def delete_artifact(self, artifact_id: UUID) -> None:
2930
2943
  """
@@ -2952,7 +2965,7 @@ class PrefectClient:
2952
2965
  """
2953
2966
  response = await self._client.post(
2954
2967
  "/variables/",
2955
- json=variable.dict(json_compatible=True, exclude_unset=True),
2968
+ json=variable.model_dump(mode="json", exclude_unset=True),
2956
2969
  )
2957
2970
  return Variable(**response.json())
2958
2971
 
@@ -2967,7 +2980,7 @@ class PrefectClient:
2967
2980
  """
2968
2981
  await self._client.patch(
2969
2982
  f"/variables/name/{variable.name}",
2970
- json=variable.dict(json_compatible=True, exclude_unset=True),
2983
+ json=variable.model_dump(mode="json", exclude_unset=True),
2971
2984
  )
2972
2985
 
2973
2986
  async def read_variable_by_name(self, name: str) -> Optional[Variable]:
@@ -2994,7 +3007,7 @@ class PrefectClient:
2994
3007
  async def read_variables(self, limit: int = None) -> List[Variable]:
2995
3008
  """Reads all variables."""
2996
3009
  response = await self._client.post("/variables/filter", json={"limit": limit})
2997
- return pydantic.parse_obj_as(List[Variable], response.json())
3010
+ return pydantic.TypeAdapter(List[Variable]).validate_python(response.json())
2998
3011
 
2999
3012
  async def read_worker_metadata(self) -> Dict[str, Any]:
3000
3013
  """Reads worker metadata stored in Prefect collection registry."""
@@ -3027,7 +3040,7 @@ class PrefectClient:
3027
3040
  ) -> UUID:
3028
3041
  response = await self._client.post(
3029
3042
  "/v2/concurrency_limits/",
3030
- json=concurrency_limit.dict(json_compatible=True, exclude_unset=True),
3043
+ json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
3031
3044
  )
3032
3045
  return UUID(response.json()["id"])
3033
3046
 
@@ -3037,7 +3050,7 @@ class PrefectClient:
3037
3050
  try:
3038
3051
  response = await self._client.patch(
3039
3052
  f"/v2/concurrency_limits/{name}",
3040
- json=concurrency_limit.dict(json_compatible=True, exclude_unset=True),
3053
+ json=concurrency_limit.model_dump(mode="json", exclude_unset=True),
3041
3054
  )
3042
3055
  return response
3043
3056
  except httpx.HTTPStatusError as e:
@@ -3063,7 +3076,7 @@ class PrefectClient:
3063
3076
  ) -> GlobalConcurrencyLimitResponse:
3064
3077
  try:
3065
3078
  response = await self._client.get(f"/v2/concurrency_limits/{name}")
3066
- return GlobalConcurrencyLimitResponse.parse_obj(response.json())
3079
+ return GlobalConcurrencyLimitResponse.model_validate(response.json())
3067
3080
  except httpx.HTTPStatusError as e:
3068
3081
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
3069
3082
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
@@ -3080,9 +3093,9 @@ class PrefectClient:
3080
3093
  "offset": offset,
3081
3094
  },
3082
3095
  )
3083
- return pydantic.parse_obj_as(
3084
- List[GlobalConcurrencyLimitResponse], response.json()
3085
- )
3096
+ return pydantic.TypeAdapter(
3097
+ List[GlobalConcurrencyLimitResponse]
3098
+ ).validate_python(response.json())
3086
3099
 
3087
3100
  async def create_flow_run_input(
3088
3101
  self, flow_run_id: UUID, key: str, value: str, sender: Optional[str] = None
@@ -3118,7 +3131,7 @@ class PrefectClient:
3118
3131
  },
3119
3132
  )
3120
3133
  response.raise_for_status()
3121
- return pydantic.parse_obj_as(List[FlowRunInput], response.json())
3134
+ return pydantic.TypeAdapter(List[FlowRunInput]).validate_python(response.json())
3122
3135
 
3123
3136
  async def read_flow_run_input(self, flow_run_id: UUID, key: str) -> str:
3124
3137
  """
@@ -3143,49 +3156,27 @@ class PrefectClient:
3143
3156
  response = await self._client.delete(f"/flow_runs/{flow_run_id}/input/{key}")
3144
3157
  response.raise_for_status()
3145
3158
 
3146
- def _raise_for_unsupported_automations(self) -> NoReturn:
3147
- if not PREFECT_EXPERIMENTAL_EVENTS:
3148
- raise RuntimeError(
3149
- "The current server and client configuration does not support "
3150
- "events. Enable experimental events support with the "
3151
- "PREFECT_EXPERIMENTAL_EVENTS setting."
3152
- )
3153
- else:
3154
- raise RuntimeError(
3155
- "The current server and client configuration does not support "
3156
- "automations. Enable experimental automations with the "
3157
- "PREFECT_API_SERVICES_TRIGGERS_ENABLED setting."
3158
- )
3159
-
3160
3159
  async def create_automation(self, automation: AutomationCore) -> UUID:
3161
3160
  """Creates an automation in Prefect Cloud."""
3162
- if not self.server_type.supports_automations():
3163
- self._raise_for_unsupported_automations()
3164
-
3165
3161
  response = await self._client.post(
3166
3162
  "/automations/",
3167
- json=automation.dict(json_compatible=True),
3163
+ json=automation.model_dump(mode="json"),
3168
3164
  )
3169
3165
 
3170
3166
  return UUID(response.json()["id"])
3171
3167
 
3172
3168
  async def update_automation(self, automation_id: UUID, automation: AutomationCore):
3173
3169
  """Updates an automation in Prefect Cloud."""
3174
- if not self.server_type.supports_automations():
3175
- self._raise_for_unsupported_automations()
3176
3170
  response = await self._client.put(
3177
3171
  f"/automations/{automation_id}",
3178
- json=automation.dict(json_compatible=True, exclude_unset=True),
3172
+ json=automation.model_dump(mode="json", exclude_unset=True),
3179
3173
  )
3180
3174
  response.raise_for_status
3181
3175
 
3182
3176
  async def read_automations(self) -> List[Automation]:
3183
- if not self.server_type.supports_automations():
3184
- self._raise_for_unsupported_automations()
3185
-
3186
3177
  response = await self._client.post("/automations/filter")
3187
3178
  response.raise_for_status()
3188
- return pydantic.parse_obj_as(List[Automation], response.json())
3179
+ return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
3189
3180
 
3190
3181
  async def find_automation(
3191
3182
  self, id_or_name: Union[str, UUID], exit_if_not_found: bool = True
@@ -3221,14 +3212,11 @@ class PrefectClient:
3221
3212
  return None
3222
3213
 
3223
3214
  async def read_automation(self, automation_id: UUID) -> Optional[Automation]:
3224
- if not self.server_type.supports_automations():
3225
- self._raise_for_unsupported_automations()
3226
-
3227
3215
  response = await self._client.get(f"/automations/{automation_id}")
3228
3216
  if response.status_code == 404:
3229
3217
  return None
3230
3218
  response.raise_for_status()
3231
- return Automation.parse_obj(response.json())
3219
+ return Automation.model_validate(response.json())
3232
3220
 
3233
3221
  async def read_automations_by_name(self, name: str) -> List[Automation]:
3234
3222
  """
@@ -3240,15 +3228,13 @@ class PrefectClient:
3240
3228
  Returns:
3241
3229
  a list of Automation model representations of the automations
3242
3230
  """
3243
- if not self.server_type.supports_automations():
3244
- self._raise_for_unsupported_automations()
3245
3231
  automation_filter = filters.AutomationFilter(name=dict(any_=[name]))
3246
3232
 
3247
3233
  response = await self._client.post(
3248
3234
  "/automations/filter",
3249
3235
  json={
3250
3236
  "sort": sorting.AutomationSort.UPDATED_DESC,
3251
- "automations": automation_filter.dict(json_compatible=True)
3237
+ "automations": automation_filter.model_dump(mode="json")
3252
3238
  if automation_filter
3253
3239
  else None,
3254
3240
  },
@@ -3256,30 +3242,21 @@ class PrefectClient:
3256
3242
 
3257
3243
  response.raise_for_status()
3258
3244
 
3259
- return pydantic.parse_obj_as(List[Automation], response.json())
3245
+ return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
3260
3246
 
3261
3247
  async def pause_automation(self, automation_id: UUID):
3262
- if not self.server_type.supports_automations():
3263
- self._raise_for_unsupported_automations()
3264
-
3265
3248
  response = await self._client.patch(
3266
3249
  f"/automations/{automation_id}", json={"enabled": False}
3267
3250
  )
3268
3251
  response.raise_for_status()
3269
3252
 
3270
3253
  async def resume_automation(self, automation_id: UUID):
3271
- if not self.server_type.supports_automations():
3272
- self._raise_for_unsupported_automations()
3273
-
3274
3254
  response = await self._client.patch(
3275
3255
  f"/automations/{automation_id}", json={"enabled": True}
3276
3256
  )
3277
3257
  response.raise_for_status()
3278
3258
 
3279
3259
  async def delete_automation(self, automation_id: UUID):
3280
- if not self.server_type.supports_automations():
3281
- self._raise_for_unsupported_automations()
3282
-
3283
3260
  response = await self._client.delete(f"/automations/{automation_id}")
3284
3261
  if response.status_code == 404:
3285
3262
  return
@@ -3289,17 +3266,11 @@ class PrefectClient:
3289
3266
  async def read_resource_related_automations(
3290
3267
  self, resource_id: str
3291
3268
  ) -> List[Automation]:
3292
- if not self.server_type.supports_automations():
3293
- self._raise_for_unsupported_automations()
3294
-
3295
3269
  response = await self._client.get(f"/automations/related-to/{resource_id}")
3296
3270
  response.raise_for_status()
3297
- return pydantic.parse_obj_as(List[Automation], response.json())
3271
+ return pydantic.TypeAdapter(List[Automation]).validate_python(response.json())
3298
3272
 
3299
3273
  async def delete_resource_owned_automations(self, resource_id: str):
3300
- if not self.server_type.supports_automations():
3301
- self._raise_for_unsupported_automations()
3302
-
3303
3274
  await self._client.delete(f"/automations/owned-by/{resource_id}")
3304
3275
 
3305
3276
  async def __aenter__(self):
@@ -3318,9 +3289,11 @@ class PrefectClient:
3318
3289
  "Retrieve a new client with `get_client()` instead."
3319
3290
  )
3320
3291
 
3292
+ self._context_stack += 1
3293
+
3321
3294
  if self._started:
3322
- # httpx.AsyncClient does not allow reentrancy so we will not either.
3323
- raise RuntimeError("The client cannot be started more than once.")
3295
+ # allow reentrancy
3296
+ return self
3324
3297
 
3325
3298
  self._loop = asyncio.get_running_loop()
3326
3299
  await self._exit_stack.__aenter__()
@@ -3351,6 +3324,10 @@ class PrefectClient:
3351
3324
  """
3352
3325
  Shutdown the client.
3353
3326
  """
3327
+
3328
+ self._context_stack -= 1
3329
+ if self._context_stack > 0:
3330
+ return
3354
3331
  self._closed = True
3355
3332
  return await self._exit_stack.__aexit__(*exc_info)
3356
3333
 
@@ -3416,6 +3393,7 @@ class SyncPrefectClient:
3416
3393
  httpx_settings["headers"].setdefault("Authorization", f"Bearer {api_key}")
3417
3394
 
3418
3395
  # Context management
3396
+ self._context_stack: int = 0
3419
3397
  self._ephemeral_app: Optional[ASGIApp] = None
3420
3398
  self.manage_lifespan = True
3421
3399
  self.server_type: ServerType
@@ -3547,9 +3525,12 @@ class SyncPrefectClient:
3547
3525
  "Retrieve a new client with `get_client()` instead."
3548
3526
  )
3549
3527
 
3528
+ self._context_stack += 1
3529
+
3550
3530
  if self._started:
3551
- # httpx.Client does not allow reentrancy so we will not either.
3552
- raise RuntimeError("The client cannot be started more than once.")
3531
+ # allow reentrancy
3532
+ return self
3533
+
3553
3534
  self._client.__enter__()
3554
3535
  self._started = True
3555
3536
 
@@ -3559,6 +3540,9 @@ class SyncPrefectClient:
3559
3540
  """
3560
3541
  Shutdown the client.
3561
3542
  """
3543
+ self._context_stack -= 1
3544
+ if self._context_stack > 0:
3545
+ return
3562
3546
  self._closed = True
3563
3547
  self._client.__exit__(*exc_info)
3564
3548
 
@@ -3612,9 +3596,7 @@ class SyncPrefectClient:
3612
3596
  the ID of the flow in the backend
3613
3597
  """
3614
3598
  flow_data = FlowCreate(name=flow_name)
3615
- response = self._client.post(
3616
- "/flows/", json=flow_data.dict(json_compatible=True)
3617
- )
3599
+ response = self._client.post("/flows/", json=flow_data.model_dump(mode="json"))
3618
3600
 
3619
3601
  flow_id = response.json().get("id")
3620
3602
  if not flow_id:
@@ -3677,9 +3659,9 @@ class SyncPrefectClient:
3677
3659
  ),
3678
3660
  )
3679
3661
 
3680
- flow_run_create_json = flow_run_create.dict(json_compatible=True)
3662
+ flow_run_create_json = flow_run_create.model_dump(mode="json")
3681
3663
  response = self._client.post("/flow_runs/", json=flow_run_create_json)
3682
- flow_run = FlowRun.parse_obj(response.json())
3664
+ flow_run = FlowRun.model_validate(response.json())
3683
3665
 
3684
3666
  # Restore the parameters to the local objects to retain expectations about
3685
3667
  # Python objects
@@ -3704,7 +3686,7 @@ class SyncPrefectClient:
3704
3686
  raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3705
3687
  else:
3706
3688
  raise
3707
- return FlowRun.parse_obj(response.json())
3689
+ return FlowRun.model_validate(response.json())
3708
3690
 
3709
3691
  def read_flow_runs(
3710
3692
  self,
@@ -3739,29 +3721,23 @@ class SyncPrefectClient:
3739
3721
  of the flow runs
3740
3722
  """
3741
3723
  body = {
3742
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
3724
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
3743
3725
  "flow_runs": (
3744
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
3726
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
3745
3727
  if flow_run_filter
3746
3728
  else None
3747
3729
  ),
3748
3730
  "task_runs": (
3749
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
3731
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
3750
3732
  ),
3751
3733
  "deployments": (
3752
- deployment_filter.dict(json_compatible=True)
3753
- if deployment_filter
3754
- else None
3734
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
3755
3735
  ),
3756
3736
  "work_pools": (
3757
- work_pool_filter.dict(json_compatible=True)
3758
- if work_pool_filter
3759
- else None
3737
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
3760
3738
  ),
3761
3739
  "work_pool_queues": (
3762
- work_queue_filter.dict(json_compatible=True)
3763
- if work_queue_filter
3764
- else None
3740
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
3765
3741
  ),
3766
3742
  "sort": sort,
3767
3743
  "limit": limit,
@@ -3769,7 +3745,7 @@ class SyncPrefectClient:
3769
3745
  }
3770
3746
 
3771
3747
  response = self._client.post("/flow_runs/filter", json=body)
3772
- return pydantic.parse_obj_as(List[FlowRun], response.json())
3748
+ return pydantic.TypeAdapter(List[FlowRun]).validate_python(response.json())
3773
3749
 
3774
3750
  def set_flow_run_state(
3775
3751
  self,
@@ -3795,7 +3771,7 @@ class SyncPrefectClient:
3795
3771
  try:
3796
3772
  response = self._client.post(
3797
3773
  f"/flow_runs/{flow_run_id}/set_state",
3798
- json=dict(state=state_create.dict(json_compatible=True), force=force),
3774
+ json=dict(state=state_create.model_dump(mode="json"), force=force),
3799
3775
  )
3800
3776
  except httpx.HTTPStatusError as e:
3801
3777
  if e.response.status_code == status.HTTP_404_NOT_FOUND:
@@ -3803,13 +3779,28 @@ class SyncPrefectClient:
3803
3779
  else:
3804
3780
  raise
3805
3781
 
3806
- return OrchestrationResult.parse_obj(response.json())
3782
+ return OrchestrationResult.model_validate(response.json())
3783
+
3784
+ def set_flow_run_name(self, flow_run_id: UUID, name: str):
3785
+ flow_run_data = TaskRunUpdate(name=name)
3786
+ return self._client.patch(
3787
+ f"/flow_runs/{flow_run_id}",
3788
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
3789
+ )
3790
+
3791
+ def set_task_run_name(self, task_run_id: UUID, name: str):
3792
+ task_run_data = TaskRunUpdate(name=name)
3793
+ return self._client.patch(
3794
+ f"/task_runs/{task_run_id}",
3795
+ json=task_run_data.model_dump(mode="json", exclude_unset=True),
3796
+ )
3807
3797
 
3808
3798
  def create_task_run(
3809
3799
  self,
3810
3800
  task: "TaskObject[P, R]",
3811
3801
  flow_run_id: Optional[UUID],
3812
3802
  dynamic_key: str,
3803
+ id: Optional[UUID] = None,
3813
3804
  name: Optional[str] = None,
3814
3805
  extra_tags: Optional[Iterable[str]] = None,
3815
3806
  state: Optional[prefect.states.State[R]] = None,
@@ -3833,6 +3824,8 @@ class SyncPrefectClient:
3833
3824
  task: The Task to run
3834
3825
  flow_run_id: The flow run id with which to associate the task run
3835
3826
  dynamic_key: A key unique to this particular run of a Task within the flow
3827
+ id: An optional ID for the task run. If not provided, one will be generated
3828
+ server-side.
3836
3829
  name: An optional name for the task run
3837
3830
  extra_tags: an optional list of extra tags to apply to the task run in
3838
3831
  addition to `task.tags`
@@ -3849,6 +3842,7 @@ class SyncPrefectClient:
3849
3842
  state = prefect.states.Pending()
3850
3843
 
3851
3844
  task_run_data = TaskRunCreate(
3845
+ id=id,
3852
3846
  name=name,
3853
3847
  flow_run_id=flow_run_id,
3854
3848
  task_key=task.task_key,
@@ -3864,10 +3858,10 @@ class SyncPrefectClient:
3864
3858
  task_inputs=task_inputs or {},
3865
3859
  )
3866
3860
 
3867
- response = self._client.post(
3868
- "/task_runs/", json=task_run_data.dict(json_compatible=True)
3869
- )
3870
- return TaskRun.parse_obj(response.json())
3861
+ content = task_run_data.model_dump_json(exclude={"id"} if id is None else None)
3862
+
3863
+ response = self._client.post("/task_runs/", content=content)
3864
+ return TaskRun.model_validate(response.json())
3871
3865
 
3872
3866
  def read_task_run(self, task_run_id: UUID) -> TaskRun:
3873
3867
  """
@@ -3879,8 +3873,14 @@ class SyncPrefectClient:
3879
3873
  Returns:
3880
3874
  a Task Run model representation of the task run
3881
3875
  """
3882
- response = self._client.get(f"/task_runs/{task_run_id}")
3883
- return TaskRun.parse_obj(response.json())
3876
+ try:
3877
+ response = self._client.get(f"/task_runs/{task_run_id}")
3878
+ return TaskRun.model_validate(response.json())
3879
+ except httpx.HTTPStatusError as e:
3880
+ if e.response.status_code == status.HTTP_404_NOT_FOUND:
3881
+ raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3882
+ else:
3883
+ raise
3884
3884
 
3885
3885
  def read_task_runs(
3886
3886
  self,
@@ -3911,26 +3911,24 @@ class SyncPrefectClient:
3911
3911
  of the task runs
3912
3912
  """
3913
3913
  body = {
3914
- "flows": flow_filter.dict(json_compatible=True) if flow_filter else None,
3914
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
3915
3915
  "flow_runs": (
3916
- flow_run_filter.dict(json_compatible=True, exclude_unset=True)
3916
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
3917
3917
  if flow_run_filter
3918
3918
  else None
3919
3919
  ),
3920
3920
  "task_runs": (
3921
- task_run_filter.dict(json_compatible=True) if task_run_filter else None
3921
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
3922
3922
  ),
3923
3923
  "deployments": (
3924
- deployment_filter.dict(json_compatible=True)
3925
- if deployment_filter
3926
- else None
3924
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
3927
3925
  ),
3928
3926
  "sort": sort,
3929
3927
  "limit": limit,
3930
3928
  "offset": offset,
3931
3929
  }
3932
3930
  response = self._client.post("/task_runs/filter", json=body)
3933
- return pydantic.parse_obj_as(List[TaskRun], response.json())
3931
+ return pydantic.TypeAdapter(List[TaskRun]).validate_python(response.json())
3934
3932
 
3935
3933
  def set_task_run_state(
3936
3934
  self,
@@ -3954,9 +3952,9 @@ class SyncPrefectClient:
3954
3952
  state_create.state_details.task_run_id = task_run_id
3955
3953
  response = self._client.post(
3956
3954
  f"/task_runs/{task_run_id}/set_state",
3957
- json=dict(state=state_create.dict(json_compatible=True), force=force),
3955
+ json=dict(state=state_create.model_dump(mode="json"), force=force),
3958
3956
  )
3959
- return OrchestrationResult.parse_obj(response.json())
3957
+ return OrchestrationResult.model_validate(response.json())
3960
3958
 
3961
3959
  def read_task_run_states(self, task_run_id: UUID) -> List[prefect.states.State]:
3962
3960
  """
@@ -3971,4 +3969,28 @@ class SyncPrefectClient:
3971
3969
  response = self._client.get(
3972
3970
  "/task_run_states/", params=dict(task_run_id=str(task_run_id))
3973
3971
  )
3974
- return pydantic.parse_obj_as(List[prefect.states.State], response.json())
3972
+ return pydantic.TypeAdapter(List[prefect.states.State]).validate_python(
3973
+ response.json()
3974
+ )
3975
+
3976
+ def read_deployment(
3977
+ self,
3978
+ deployment_id: UUID,
3979
+ ) -> DeploymentResponse:
3980
+ """
3981
+ Query the Prefect API for a deployment by id.
3982
+
3983
+ Args:
3984
+ deployment_id: the deployment ID of interest
3985
+
3986
+ Returns:
3987
+ a [Deployment model][prefect.client.schemas.objects.Deployment] representation of the deployment
3988
+ """
3989
+ try:
3990
+ response = self._client.get(f"/deployments/{deployment_id}")
3991
+ except httpx.HTTPStatusError as e:
3992
+ if e.response.status_code == status.HTTP_404_NOT_FOUND:
3993
+ raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
3994
+ else:
3995
+ raise
3996
+ return DeploymentResponse.model_validate(response.json())