prefect-client 3.1.11__py3-none-any.whl → 3.1.13__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 (133) hide show
  1. prefect/_experimental/sla/__init__.py +0 -0
  2. prefect/_experimental/sla/client.py +92 -0
  3. prefect/_experimental/sla/objects.py +61 -0
  4. prefect/_internal/concurrency/services.py +2 -2
  5. prefect/_internal/concurrency/threads.py +6 -0
  6. prefect/_internal/retries.py +6 -3
  7. prefect/_internal/schemas/validators.py +6 -4
  8. prefect/_version.py +3 -3
  9. prefect/artifacts.py +4 -1
  10. prefect/automations.py +236 -30
  11. prefect/blocks/__init__.py +3 -3
  12. prefect/blocks/abstract.py +57 -31
  13. prefect/blocks/core.py +181 -82
  14. prefect/blocks/notifications.py +134 -73
  15. prefect/blocks/redis.py +13 -9
  16. prefect/blocks/system.py +24 -11
  17. prefect/blocks/webhook.py +7 -5
  18. prefect/cache_policies.py +23 -22
  19. prefect/client/orchestration/__init__.py +103 -2006
  20. prefect/client/orchestration/_automations/__init__.py +0 -0
  21. prefect/client/orchestration/_automations/client.py +329 -0
  22. prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
  23. prefect/client/orchestration/_blocks_documents/client.py +334 -0
  24. prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
  25. prefect/client/orchestration/_blocks_schemas/client.py +200 -0
  26. prefect/client/orchestration/_blocks_types/__init__.py +0 -0
  27. prefect/client/orchestration/_blocks_types/client.py +380 -0
  28. prefect/client/orchestration/_deployments/__init__.py +0 -0
  29. prefect/client/orchestration/_deployments/client.py +1128 -0
  30. prefect/client/orchestration/_flow_runs/__init__.py +0 -0
  31. prefect/client/orchestration/_flow_runs/client.py +903 -0
  32. prefect/client/orchestration/_flows/__init__.py +0 -0
  33. prefect/client/orchestration/_flows/client.py +343 -0
  34. prefect/client/orchestration/_logs/client.py +16 -14
  35. prefect/client/schemas/__init__.py +68 -28
  36. prefect/client/schemas/objects.py +5 -5
  37. prefect/client/utilities.py +3 -3
  38. prefect/context.py +15 -1
  39. prefect/deployments/base.py +13 -4
  40. prefect/deployments/flow_runs.py +5 -1
  41. prefect/deployments/runner.py +37 -1
  42. prefect/deployments/steps/core.py +1 -1
  43. prefect/deployments/steps/pull.py +8 -3
  44. prefect/deployments/steps/utility.py +2 -2
  45. prefect/docker/docker_image.py +13 -9
  46. prefect/engine.py +33 -11
  47. prefect/events/cli/automations.py +4 -4
  48. prefect/events/clients.py +17 -14
  49. prefect/events/schemas/automations.py +12 -8
  50. prefect/events/schemas/events.py +5 -1
  51. prefect/events/worker.py +1 -1
  52. prefect/filesystems.py +7 -3
  53. prefect/flow_engine.py +64 -47
  54. prefect/flows.py +128 -74
  55. prefect/futures.py +14 -7
  56. prefect/infrastructure/provisioners/__init__.py +2 -0
  57. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  58. prefect/infrastructure/provisioners/coiled.py +249 -0
  59. prefect/infrastructure/provisioners/container_instance.py +4 -3
  60. prefect/infrastructure/provisioners/ecs.py +55 -43
  61. prefect/infrastructure/provisioners/modal.py +5 -4
  62. prefect/input/actions.py +5 -1
  63. prefect/input/run_input.py +157 -43
  64. prefect/logging/configuration.py +3 -3
  65. prefect/logging/filters.py +2 -2
  66. prefect/logging/formatters.py +15 -11
  67. prefect/logging/handlers.py +24 -14
  68. prefect/logging/highlighters.py +5 -5
  69. prefect/logging/loggers.py +28 -18
  70. prefect/logging/logging.yml +1 -1
  71. prefect/main.py +3 -1
  72. prefect/results.py +166 -86
  73. prefect/runner/runner.py +38 -29
  74. prefect/runner/server.py +3 -1
  75. prefect/runner/storage.py +18 -18
  76. prefect/runner/submit.py +19 -12
  77. prefect/runtime/deployment.py +15 -8
  78. prefect/runtime/flow_run.py +19 -6
  79. prefect/runtime/task_run.py +7 -3
  80. prefect/settings/base.py +17 -7
  81. prefect/settings/legacy.py +4 -4
  82. prefect/settings/models/api.py +4 -3
  83. prefect/settings/models/cli.py +4 -3
  84. prefect/settings/models/client.py +7 -4
  85. prefect/settings/models/cloud.py +9 -3
  86. prefect/settings/models/deployments.py +4 -3
  87. prefect/settings/models/experiments.py +4 -8
  88. prefect/settings/models/flows.py +4 -3
  89. prefect/settings/models/internal.py +4 -3
  90. prefect/settings/models/logging.py +8 -6
  91. prefect/settings/models/results.py +4 -3
  92. prefect/settings/models/root.py +11 -16
  93. prefect/settings/models/runner.py +8 -5
  94. prefect/settings/models/server/api.py +6 -3
  95. prefect/settings/models/server/database.py +120 -25
  96. prefect/settings/models/server/deployments.py +4 -3
  97. prefect/settings/models/server/ephemeral.py +7 -4
  98. prefect/settings/models/server/events.py +6 -3
  99. prefect/settings/models/server/flow_run_graph.py +4 -3
  100. prefect/settings/models/server/root.py +4 -3
  101. prefect/settings/models/server/services.py +15 -12
  102. prefect/settings/models/server/tasks.py +7 -4
  103. prefect/settings/models/server/ui.py +4 -3
  104. prefect/settings/models/tasks.py +10 -5
  105. prefect/settings/models/testing.py +4 -3
  106. prefect/settings/models/worker.py +7 -4
  107. prefect/settings/profiles.py +13 -12
  108. prefect/settings/sources.py +20 -19
  109. prefect/states.py +74 -51
  110. prefect/task_engine.py +43 -33
  111. prefect/task_runners.py +85 -72
  112. prefect/task_runs.py +20 -11
  113. prefect/task_worker.py +14 -9
  114. prefect/tasks.py +36 -28
  115. prefect/telemetry/bootstrap.py +13 -9
  116. prefect/telemetry/run_telemetry.py +15 -13
  117. prefect/telemetry/services.py +4 -0
  118. prefect/transactions.py +3 -3
  119. prefect/types/__init__.py +3 -1
  120. prefect/utilities/_deprecated.py +38 -0
  121. prefect/utilities/engine.py +11 -4
  122. prefect/utilities/filesystem.py +2 -2
  123. prefect/utilities/generics.py +1 -1
  124. prefect/utilities/pydantic.py +21 -36
  125. prefect/utilities/templating.py +25 -1
  126. prefect/workers/base.py +58 -33
  127. prefect/workers/process.py +20 -15
  128. prefect/workers/server.py +4 -5
  129. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
  130. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
  131. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
  132. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
  133. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,903 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from typing import TYPE_CHECKING, Any
5
+
6
+ import httpx
7
+ from typing_extensions import TypeVar
8
+
9
+ from prefect.client.orchestration.base import BaseAsyncClient, BaseClient
10
+ from prefect.exceptions import ObjectNotFound
11
+
12
+ T = TypeVar("T")
13
+ R = TypeVar("R", infer_variance=True)
14
+
15
+ if TYPE_CHECKING:
16
+ from uuid import UUID
17
+
18
+ from prefect.client.schemas import FlowRun, OrchestrationResult
19
+ from prefect.client.schemas.filters import (
20
+ DeploymentFilter,
21
+ FlowFilter,
22
+ FlowRunFilter,
23
+ TaskRunFilter,
24
+ WorkPoolFilter,
25
+ WorkQueueFilter,
26
+ )
27
+ from prefect.client.schemas.objects import (
28
+ FlowRunInput,
29
+ FlowRunPolicy,
30
+ )
31
+ from prefect.client.schemas.sorting import (
32
+ FlowRunSort,
33
+ )
34
+ from prefect.flows import Flow as FlowObject
35
+ from prefect.states import State
36
+ from prefect.types import KeyValueLabelsField
37
+
38
+
39
+ class FlowRunClient(BaseClient):
40
+ def create_flow_run(
41
+ self,
42
+ flow: "FlowObject[Any, R]",
43
+ name: str | None = None,
44
+ parameters: dict[str, Any] | None = None,
45
+ context: dict[str, Any] | None = None,
46
+ tags: "Iterable[str] | None" = None,
47
+ parent_task_run_id: "UUID | None" = None,
48
+ state: "State[R] | None" = None,
49
+ ) -> "FlowRun":
50
+ """
51
+ Create a flow run for a flow.
52
+
53
+ Args:
54
+ flow: The flow model to create the flow run for
55
+ name: An optional name for the flow run
56
+ parameters: Parameter overrides for this flow run.
57
+ context: Optional run context data
58
+ tags: a list of tags to apply to this flow run
59
+ parent_task_run_id: if a subflow run is being created, the placeholder task
60
+ run identifier in the parent flow
61
+ state: The initial state for the run. If not provided, defaults to
62
+ `Scheduled` for now. Should always be a `Scheduled` type.
63
+
64
+ Raises:
65
+ httpx.RequestError: if the Prefect API does not successfully create a run for any reason
66
+
67
+ Returns:
68
+ The flow run model
69
+ """
70
+ from prefect.client.schemas.actions import FlowCreate, FlowRunCreate
71
+ from prefect.client.schemas.objects import Flow, FlowRun, FlowRunPolicy
72
+ from prefect.states import Pending
73
+
74
+ parameters = parameters or {}
75
+ context = context or {}
76
+
77
+ if state is None:
78
+ state = Pending()
79
+
80
+ # Retrieve the flow id
81
+
82
+ flow_data = FlowCreate(name=flow.name)
83
+ response = self.request(
84
+ "POST", "/flows/", json=flow_data.model_dump(mode="json")
85
+ )
86
+ flow_id = Flow.model_validate(response.json()).id
87
+
88
+ flow_run_create = FlowRunCreate(
89
+ flow_id=flow_id,
90
+ flow_version=flow.version,
91
+ name=name,
92
+ parameters=parameters,
93
+ context=context,
94
+ tags=list(tags or []),
95
+ parent_task_run_id=parent_task_run_id,
96
+ state=state.to_state_create(),
97
+ empirical_policy=FlowRunPolicy(
98
+ retries=flow.retries,
99
+ retry_delay=int(flow.retry_delay_seconds or 0),
100
+ ),
101
+ )
102
+
103
+ flow_run_create_json = flow_run_create.model_dump(mode="json")
104
+ response = self.request("POST", "/flow_runs/", json=flow_run_create_json)
105
+
106
+ flow_run = FlowRun.model_validate(response.json())
107
+
108
+ # Restore the parameters to the local objects to retain expectations about
109
+ # Python objects
110
+ flow_run.parameters = parameters
111
+
112
+ return flow_run
113
+
114
+ def update_flow_run(
115
+ self,
116
+ flow_run_id: "UUID",
117
+ flow_version: str | None = None,
118
+ parameters: dict[str, Any] | None = None,
119
+ name: str | None = None,
120
+ tags: "Iterable[str] | None" = None,
121
+ empirical_policy: "FlowRunPolicy | None" = None,
122
+ infrastructure_pid: str | None = None,
123
+ job_variables: dict[str, Any] | None = None,
124
+ ) -> httpx.Response:
125
+ """
126
+ Update a flow run's details.
127
+
128
+ Args:
129
+ flow_run_id: The identifier for the flow run to update.
130
+ flow_version: A new version string for the flow run.
131
+ parameters: A dictionary of parameter values for the flow run. This will not
132
+ be merged with any existing parameters.
133
+ name: A new name for the flow run.
134
+ empirical_policy: A new flow run orchestration policy. This will not be
135
+ merged with any existing policy.
136
+ tags: An iterable of new tags for the flow run. These will not be merged with
137
+ any existing tags.
138
+ infrastructure_pid: The id of flow run as returned by an
139
+ infrastructure block.
140
+
141
+ Returns:
142
+ an `httpx.Response` object from the PATCH request
143
+ """
144
+ params: dict[str, Any] = {}
145
+ if flow_version is not None:
146
+ params["flow_version"] = flow_version
147
+ if parameters is not None:
148
+ params["parameters"] = parameters
149
+ if name is not None:
150
+ params["name"] = name
151
+ if tags is not None:
152
+ params["tags"] = tags
153
+ if empirical_policy is not None:
154
+ params["empirical_policy"] = empirical_policy
155
+ if infrastructure_pid:
156
+ params["infrastructure_pid"] = infrastructure_pid
157
+ if job_variables is not None:
158
+ params["job_variables"] = job_variables
159
+
160
+ from prefect.client.schemas.actions import FlowRunUpdate
161
+
162
+ flow_run_data = FlowRunUpdate(**params)
163
+
164
+ return self.request(
165
+ "PATCH",
166
+ "/flow_runs/{id}",
167
+ path_params={"id": flow_run_id},
168
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
169
+ )
170
+
171
+ def delete_flow_run(
172
+ self,
173
+ flow_run_id: "UUID",
174
+ ) -> None:
175
+ """
176
+ Delete a flow run by UUID.
177
+
178
+ Args:
179
+ flow_run_id: The flow run UUID of interest.
180
+ Raises:
181
+ ObjectNotFound: If request returns 404
182
+ httpx.RequestError: If requests fails
183
+ """
184
+ try:
185
+ self.request("DELETE", "/flow_runs/{id}", path_params={"id": flow_run_id})
186
+ except httpx.HTTPStatusError as e:
187
+ if e.response.status_code == 404:
188
+ raise ObjectNotFound(http_exc=e) from e
189
+ else:
190
+ raise
191
+
192
+ def read_flow_run(self, flow_run_id: "UUID") -> "FlowRun":
193
+ """
194
+ Query the Prefect API for a flow run by id.
195
+
196
+ Args:
197
+ flow_run_id: the flow run ID of interest
198
+
199
+ Returns:
200
+ a Flow Run model representation of the flow run
201
+ """
202
+ try:
203
+ response = self.request(
204
+ "GET", "/flow_runs/{id}", path_params={"id": flow_run_id}
205
+ )
206
+ except httpx.HTTPStatusError as e:
207
+ if e.response.status_code == 404:
208
+ raise ObjectNotFound(http_exc=e) from e
209
+ else:
210
+ raise
211
+ from prefect.client.schemas.objects import FlowRun
212
+
213
+ return FlowRun.model_validate(response.json())
214
+
215
+ def resume_flow_run(
216
+ self, flow_run_id: "UUID", run_input: dict[str, Any] | None = None
217
+ ) -> "OrchestrationResult[Any]":
218
+ """
219
+ Resumes a paused flow run.
220
+
221
+ Args:
222
+ flow_run_id: the flow run ID of interest
223
+ run_input: the input to resume the flow run with
224
+
225
+ Returns:
226
+ an OrchestrationResult model representation of state orchestration output
227
+ """
228
+ try:
229
+ response = self.request(
230
+ "POST",
231
+ "/flow_runs/{id}/resume",
232
+ path_params={"id": flow_run_id},
233
+ json={"run_input": run_input},
234
+ )
235
+ except httpx.HTTPStatusError:
236
+ raise
237
+ from prefect.client.schemas import OrchestrationResult
238
+
239
+ result: OrchestrationResult[Any] = OrchestrationResult.model_validate(
240
+ response.json()
241
+ )
242
+ return result
243
+
244
+ def read_flow_runs(
245
+ self,
246
+ *,
247
+ flow_filter: "FlowFilter | None" = None,
248
+ flow_run_filter: "FlowRunFilter | None" = None,
249
+ task_run_filter: "TaskRunFilter | None" = None,
250
+ deployment_filter: "DeploymentFilter | None" = None,
251
+ work_pool_filter: "WorkPoolFilter | None" = None,
252
+ work_queue_filter: "WorkQueueFilter | None" = None,
253
+ sort: "FlowRunSort | None" = None,
254
+ limit: int | None = None,
255
+ offset: int = 0,
256
+ ) -> "list[FlowRun]":
257
+ """
258
+ Query the Prefect API for flow runs. Only flow runs matching all criteria will
259
+ be returned.
260
+
261
+ Args:
262
+ flow_filter: filter criteria for flows
263
+ flow_run_filter: filter criteria for flow runs
264
+ task_run_filter: filter criteria for task runs
265
+ deployment_filter: filter criteria for deployments
266
+ work_pool_filter: filter criteria for work pools
267
+ work_queue_filter: filter criteria for work pool queues
268
+ sort: sort criteria for the flow runs
269
+ limit: limit for the flow run query
270
+ offset: offset for the flow run query
271
+
272
+ Returns:
273
+ a list of Flow Run model representations
274
+ of the flow runs
275
+ """
276
+ body: dict[str, Any] = {
277
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
278
+ "flow_runs": (
279
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
280
+ if flow_run_filter
281
+ else None
282
+ ),
283
+ "task_runs": (
284
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
285
+ ),
286
+ "deployments": (
287
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
288
+ ),
289
+ "work_pools": (
290
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
291
+ ),
292
+ "work_pool_queues": (
293
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
294
+ ),
295
+ "sort": sort,
296
+ "limit": limit,
297
+ "offset": offset,
298
+ }
299
+
300
+ response = self.request("POST", "/flow_runs/filter", json=body)
301
+ from prefect.client.schemas.objects import FlowRun
302
+
303
+ return FlowRun.model_validate_list(response.json())
304
+
305
+ def set_flow_run_state(
306
+ self,
307
+ flow_run_id: "UUID | str",
308
+ state: "State[T]",
309
+ force: bool = False,
310
+ ) -> "OrchestrationResult[T]":
311
+ """
312
+ Set the state of a flow run.
313
+
314
+ Args:
315
+ flow_run_id: the id of the flow run
316
+ state: the state to set
317
+ force: if True, disregard orchestration logic when setting the state,
318
+ forcing the Prefect API to accept the state
319
+
320
+ Returns:
321
+ an OrchestrationResult model representation of state orchestration output
322
+ """
323
+ from uuid import UUID, uuid4
324
+
325
+ flow_run_id = (
326
+ flow_run_id if isinstance(flow_run_id, UUID) else UUID(flow_run_id)
327
+ )
328
+ state_create = state.to_state_create()
329
+ state_create.state_details.flow_run_id = flow_run_id
330
+ state_create.state_details.transition_id = uuid4()
331
+ try:
332
+ response = self.request(
333
+ "POST",
334
+ "/flow_runs/{id}/set_state",
335
+ path_params={"id": flow_run_id},
336
+ json=dict(
337
+ state=state_create.model_dump(mode="json", serialize_as_any=True),
338
+ force=force,
339
+ ),
340
+ )
341
+ except httpx.HTTPStatusError as e:
342
+ if e.response.status_code == 404:
343
+ raise ObjectNotFound(http_exc=e) from e
344
+ else:
345
+ raise
346
+ from prefect.client.schemas import OrchestrationResult
347
+
348
+ result: OrchestrationResult[T] = OrchestrationResult.model_validate(
349
+ response.json()
350
+ )
351
+ return result
352
+
353
+ def read_flow_run_states(self, flow_run_id: "UUID") -> "list[State]":
354
+ """
355
+ Query for the states of a flow run
356
+
357
+ Args:
358
+ flow_run_id: the id of the flow run
359
+
360
+ Returns:
361
+ a list of State model representations
362
+ of the flow run states
363
+ """
364
+ response = self.request(
365
+ "GET", "/flow_run_states/", params=dict(flow_run_id=str(flow_run_id))
366
+ )
367
+ from prefect.states import State
368
+
369
+ return State.model_validate_list(response.json())
370
+
371
+ def set_flow_run_name(self, flow_run_id: "UUID", name: str) -> httpx.Response:
372
+ from prefect.client.schemas.actions import FlowRunUpdate
373
+
374
+ flow_run_data = FlowRunUpdate(name=name)
375
+ return self.request(
376
+ "PATCH",
377
+ "/flow_runs/{id}",
378
+ path_params={"id": flow_run_id},
379
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
380
+ )
381
+
382
+ def create_flow_run_input(
383
+ self, flow_run_id: "UUID", key: str, value: str, sender: str | None = None
384
+ ) -> None:
385
+ """
386
+ Creates a flow run input.
387
+
388
+ Args:
389
+ flow_run_id: The flow run id.
390
+ key: The input key.
391
+ value: The input value.
392
+ sender: The sender of the input.
393
+ """
394
+
395
+ # Initialize the input to ensure that the key is valid.
396
+ FlowRunInput(flow_run_id=flow_run_id, key=key, value=value)
397
+
398
+ response = self.request(
399
+ "POST",
400
+ "/flow_runs/{id}/input",
401
+ path_params={"id": flow_run_id},
402
+ json={"key": key, "value": value, "sender": sender},
403
+ )
404
+ response.raise_for_status()
405
+
406
+ def filter_flow_run_input(
407
+ self, flow_run_id: "UUID", key_prefix: str, limit: int, exclude_keys: "set[str]"
408
+ ) -> "list[FlowRunInput]":
409
+ response = self.request(
410
+ "POST",
411
+ "/flow_runs/{id}/input/filter",
412
+ path_params={"id": flow_run_id},
413
+ json={
414
+ "prefix": key_prefix,
415
+ "limit": limit,
416
+ "exclude_keys": list(exclude_keys),
417
+ },
418
+ )
419
+ response.raise_for_status()
420
+ from prefect.client.schemas.objects import FlowRunInput
421
+
422
+ return FlowRunInput.model_validate_list(response.json())
423
+
424
+ def read_flow_run_input(self, flow_run_id: "UUID", key: str) -> str:
425
+ """
426
+ Reads a flow run input.
427
+
428
+ Args:
429
+ flow_run_id: The flow run id.
430
+ key: The input key.
431
+ """
432
+ response = self.request(
433
+ "GET",
434
+ "/flow_runs/{id}/input/{key}",
435
+ path_params={"id": flow_run_id, "key": key},
436
+ )
437
+ response.raise_for_status()
438
+ return response.content.decode()
439
+
440
+ def delete_flow_run_input(self, flow_run_id: "UUID", key: str) -> None:
441
+ """
442
+ Deletes a flow run input.
443
+
444
+ Args:
445
+ flow_run_id: The flow run id.
446
+ key: The input key.
447
+ """
448
+ response = self.request(
449
+ "DELETE",
450
+ "/flow_runs/{id}/input/{key}",
451
+ path_params={"id": flow_run_id, "key": key},
452
+ )
453
+ response.raise_for_status()
454
+
455
+ def update_flow_run_labels(
456
+ self, flow_run_id: "UUID", labels: "KeyValueLabelsField"
457
+ ) -> None:
458
+ """
459
+ Updates the labels of a flow run.
460
+ """
461
+
462
+ response = self.request(
463
+ "PATCH",
464
+ "/flow_runs/{id}/labels",
465
+ path_params={"id": flow_run_id},
466
+ json=labels,
467
+ )
468
+ response.raise_for_status()
469
+
470
+
471
+ class FlowRunAsyncClient(BaseAsyncClient):
472
+ async def create_flow_run(
473
+ self,
474
+ flow: "FlowObject[Any, R]",
475
+ name: str | None = None,
476
+ parameters: dict[str, Any] | None = None,
477
+ context: dict[str, Any] | None = None,
478
+ tags: "Iterable[str] | None" = None,
479
+ parent_task_run_id: "UUID | None" = None,
480
+ state: "State[R] | None" = None,
481
+ ) -> "FlowRun":
482
+ """
483
+ Create a flow run for a flow.
484
+
485
+ Args:
486
+ flow: The flow model to create the flow run for
487
+ name: An optional name for the flow run
488
+ parameters: Parameter overrides for this flow run.
489
+ context: Optional run context data
490
+ tags: a list of tags to apply to this flow run
491
+ parent_task_run_id: if a subflow run is being created, the placeholder task
492
+ run identifier in the parent flow
493
+ state: The initial state for the run. If not provided, defaults to
494
+ `Scheduled` for now. Should always be a `Scheduled` type.
495
+
496
+ Raises:
497
+ httpx.RequestError: if the Prefect API does not successfully create a run for any reason
498
+
499
+ Returns:
500
+ The flow run model
501
+ """
502
+ from prefect.client.schemas.actions import FlowCreate, FlowRunCreate
503
+ from prefect.client.schemas.objects import Flow, FlowRun, FlowRunPolicy
504
+ from prefect.states import Pending
505
+
506
+ parameters = parameters or {}
507
+ context = context or {}
508
+
509
+ if state is None:
510
+ state = Pending()
511
+
512
+ # Retrieve the flow id
513
+
514
+ flow_data = FlowCreate(name=flow.name)
515
+ response = await self.request(
516
+ "POST", "/flows/", json=flow_data.model_dump(mode="json")
517
+ )
518
+ flow_id = Flow.model_validate(response.json()).id
519
+
520
+ flow_run_create = FlowRunCreate(
521
+ flow_id=flow_id,
522
+ flow_version=flow.version,
523
+ name=name,
524
+ parameters=parameters,
525
+ context=context,
526
+ tags=list(tags or []),
527
+ parent_task_run_id=parent_task_run_id,
528
+ state=state.to_state_create(),
529
+ empirical_policy=FlowRunPolicy(
530
+ retries=flow.retries,
531
+ retry_delay=int(flow.retry_delay_seconds or 0),
532
+ ),
533
+ )
534
+
535
+ flow_run_create_json = flow_run_create.model_dump(mode="json")
536
+ response = await self.request("POST", "/flow_runs/", json=flow_run_create_json)
537
+
538
+ flow_run = FlowRun.model_validate(response.json())
539
+
540
+ # Restore the parameters to the local objects to retain expectations about
541
+ # Python objects
542
+ flow_run.parameters = parameters
543
+
544
+ return flow_run
545
+
546
+ async def update_flow_run(
547
+ self,
548
+ flow_run_id: "UUID",
549
+ flow_version: str | None = None,
550
+ parameters: dict[str, Any] | None = None,
551
+ name: str | None = None,
552
+ tags: "Iterable[str] | None" = None,
553
+ empirical_policy: "FlowRunPolicy | None" = None,
554
+ infrastructure_pid: str | None = None,
555
+ job_variables: dict[str, Any] | None = None,
556
+ ) -> httpx.Response:
557
+ """
558
+ Update a flow run's details.
559
+
560
+ Args:
561
+ flow_run_id: The identifier for the flow run to update.
562
+ flow_version: A new version string for the flow run.
563
+ parameters: A dictionary of parameter values for the flow run. This will not
564
+ be merged with any existing parameters.
565
+ name: A new name for the flow run.
566
+ empirical_policy: A new flow run orchestration policy. This will not be
567
+ merged with any existing policy.
568
+ tags: An iterable of new tags for the flow run. These will not be merged with
569
+ any existing tags.
570
+ infrastructure_pid: The id of flow run as returned by an
571
+ infrastructure block.
572
+
573
+ Returns:
574
+ an `httpx.Response` object from the PATCH request
575
+ """
576
+ params: dict[str, Any] = {}
577
+ if flow_version is not None:
578
+ params["flow_version"] = flow_version
579
+ if parameters is not None:
580
+ params["parameters"] = parameters
581
+ if name is not None:
582
+ params["name"] = name
583
+ if tags is not None:
584
+ params["tags"] = tags
585
+ if empirical_policy is not None:
586
+ params["empirical_policy"] = empirical_policy
587
+ if infrastructure_pid:
588
+ params["infrastructure_pid"] = infrastructure_pid
589
+ if job_variables is not None:
590
+ params["job_variables"] = job_variables
591
+ from prefect.client.schemas.actions import FlowRunUpdate
592
+
593
+ flow_run_data = FlowRunUpdate(**params)
594
+
595
+ return await self.request(
596
+ "PATCH",
597
+ "/flow_runs/{id}",
598
+ path_params={"id": flow_run_id},
599
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
600
+ )
601
+
602
+ async def delete_flow_run(
603
+ self,
604
+ flow_run_id: "UUID",
605
+ ) -> None:
606
+ """
607
+ Delete a flow run by UUID.
608
+
609
+ Args:
610
+ flow_run_id: The flow run UUID of interest.
611
+ Raises:
612
+ ObjectNotFound: If request returns 404
613
+ httpx.RequestError: If requests fails
614
+ """
615
+ try:
616
+ await self.request(
617
+ "DELETE", "/flow_runs/{id}", path_params={"id": flow_run_id}
618
+ )
619
+ except httpx.HTTPStatusError as e:
620
+ if e.response.status_code == 404:
621
+ raise ObjectNotFound(http_exc=e) from e
622
+ else:
623
+ raise
624
+
625
+ async def read_flow_run(self, flow_run_id: "UUID") -> "FlowRun":
626
+ """
627
+ Query the Prefect API for a flow run by id.
628
+
629
+ Args:
630
+ flow_run_id: the flow run ID of interest
631
+
632
+ Returns:
633
+ a Flow Run model representation of the flow run
634
+ """
635
+ try:
636
+ response = await self.request(
637
+ "GET", "/flow_runs/{id}", path_params={"id": flow_run_id}
638
+ )
639
+ except httpx.HTTPStatusError as e:
640
+ if e.response.status_code == 404:
641
+ raise ObjectNotFound(http_exc=e) from e
642
+ else:
643
+ raise
644
+ from prefect.client.schemas.objects import FlowRun
645
+
646
+ return FlowRun.model_validate(response.json())
647
+
648
+ async def resume_flow_run(
649
+ self, flow_run_id: "UUID", run_input: dict[str, Any] | None = None
650
+ ) -> "OrchestrationResult[Any]":
651
+ """
652
+ Resumes a paused flow run.
653
+
654
+ Args:
655
+ flow_run_id: the flow run ID of interest
656
+ run_input: the input to resume the flow run with
657
+
658
+ Returns:
659
+ an OrchestrationResult model representation of state orchestration output
660
+ """
661
+ try:
662
+ response = await self.request(
663
+ "POST",
664
+ "/flow_runs/{id}/resume",
665
+ path_params={"id": flow_run_id},
666
+ json={"run_input": run_input},
667
+ )
668
+ except httpx.HTTPStatusError:
669
+ raise
670
+ from prefect.client.schemas import OrchestrationResult
671
+
672
+ result: OrchestrationResult[Any] = OrchestrationResult.model_validate(
673
+ response.json()
674
+ )
675
+ return result
676
+
677
+ async def read_flow_runs(
678
+ self,
679
+ *,
680
+ flow_filter: "FlowFilter | None" = None,
681
+ flow_run_filter: "FlowRunFilter | None" = None,
682
+ task_run_filter: "TaskRunFilter | None" = None,
683
+ deployment_filter: "DeploymentFilter | None" = None,
684
+ work_pool_filter: "WorkPoolFilter | None" = None,
685
+ work_queue_filter: "WorkQueueFilter | None" = None,
686
+ sort: "FlowRunSort | None" = None,
687
+ limit: int | None = None,
688
+ offset: int = 0,
689
+ ) -> "list[FlowRun]":
690
+ """
691
+ Query the Prefect API for flow runs. Only flow runs matching all criteria will
692
+ be returned.
693
+
694
+ Args:
695
+ flow_filter: filter criteria for flows
696
+ flow_run_filter: filter criteria for flow runs
697
+ task_run_filter: filter criteria for task runs
698
+ deployment_filter: filter criteria for deployments
699
+ work_pool_filter: filter criteria for work pools
700
+ work_queue_filter: filter criteria for work pool queues
701
+ sort: sort criteria for the flow runs
702
+ limit: limit for the flow run query
703
+ offset: offset for the flow run query
704
+
705
+ Returns:
706
+ a list of Flow Run model representations
707
+ of the flow runs
708
+ """
709
+ body: dict[str, Any] = {
710
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
711
+ "flow_runs": (
712
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
713
+ if flow_run_filter
714
+ else None
715
+ ),
716
+ "task_runs": (
717
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
718
+ ),
719
+ "deployments": (
720
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
721
+ ),
722
+ "work_pools": (
723
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
724
+ ),
725
+ "work_pool_queues": (
726
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
727
+ ),
728
+ "sort": sort,
729
+ "limit": limit,
730
+ "offset": offset,
731
+ }
732
+
733
+ response = await self.request("POST", "/flow_runs/filter", json=body)
734
+ from prefect.client.schemas.objects import FlowRun
735
+
736
+ return FlowRun.model_validate_list(response.json())
737
+
738
+ async def set_flow_run_state(
739
+ self,
740
+ flow_run_id: "UUID | str",
741
+ state: "State[T]",
742
+ force: bool = False,
743
+ ) -> "OrchestrationResult[T]":
744
+ """
745
+ Set the state of a flow run.
746
+
747
+ Args:
748
+ flow_run_id: the id of the flow run
749
+ state: the state to set
750
+ force: if True, disregard orchestration logic when setting the state,
751
+ forcing the Prefect API to accept the state
752
+
753
+ Returns:
754
+ an OrchestrationResult model representation of state orchestration output
755
+ """
756
+ from uuid import UUID, uuid4
757
+
758
+ flow_run_id = (
759
+ flow_run_id if isinstance(flow_run_id, UUID) else UUID(flow_run_id)
760
+ )
761
+ state_create = state.to_state_create()
762
+ state_create.state_details.flow_run_id = flow_run_id
763
+ state_create.state_details.transition_id = uuid4()
764
+ try:
765
+ response = await self.request(
766
+ "POST",
767
+ "/flow_runs/{id}/set_state",
768
+ path_params={"id": flow_run_id},
769
+ json=dict(
770
+ state=state_create.model_dump(mode="json", serialize_as_any=True),
771
+ force=force,
772
+ ),
773
+ )
774
+ except httpx.HTTPStatusError as e:
775
+ if e.response.status_code == 404:
776
+ raise ObjectNotFound(http_exc=e) from e
777
+ else:
778
+ raise
779
+ from prefect.client.schemas import OrchestrationResult
780
+
781
+ result: OrchestrationResult[T] = OrchestrationResult.model_validate(
782
+ response.json()
783
+ )
784
+ return result
785
+
786
+ async def read_flow_run_states(self, flow_run_id: "UUID") -> "list[State]":
787
+ """
788
+ Query for the states of a flow run
789
+
790
+ Args:
791
+ flow_run_id: the id of the flow run
792
+
793
+ Returns:
794
+ a list of State model representations
795
+ of the flow run states
796
+ """
797
+ response = await self.request(
798
+ "GET", "/flow_run_states/", params=dict(flow_run_id=str(flow_run_id))
799
+ )
800
+ from prefect.states import State
801
+
802
+ return State.model_validate_list(response.json())
803
+
804
+ async def set_flow_run_name(self, flow_run_id: "UUID", name: str) -> httpx.Response:
805
+ from prefect.client.schemas.actions import FlowRunUpdate
806
+
807
+ flow_run_data = FlowRunUpdate(name=name)
808
+ return await self.request(
809
+ "PATCH",
810
+ "/flow_runs/{id}",
811
+ path_params={"id": flow_run_id},
812
+ json=flow_run_data.model_dump(mode="json", exclude_unset=True),
813
+ )
814
+
815
+ async def create_flow_run_input(
816
+ self, flow_run_id: "UUID", key: str, value: str, sender: str | None = None
817
+ ) -> None:
818
+ """
819
+ Creates a flow run input.
820
+
821
+ Args:
822
+ flow_run_id: The flow run id.
823
+ key: The input key.
824
+ value: The input value.
825
+ sender: The sender of the input.
826
+ """
827
+
828
+ # Initialize the input to ensure that the key is valid.
829
+ from prefect.client.schemas.objects import FlowRunInput
830
+
831
+ FlowRunInput(flow_run_id=flow_run_id, key=key, value=value)
832
+
833
+ response = await self.request(
834
+ "POST",
835
+ "/flow_runs/{id}/input",
836
+ path_params={"id": flow_run_id},
837
+ json={"key": key, "value": value, "sender": sender},
838
+ )
839
+ response.raise_for_status()
840
+
841
+ async def filter_flow_run_input(
842
+ self, flow_run_id: "UUID", key_prefix: str, limit: int, exclude_keys: "set[str]"
843
+ ) -> "list[FlowRunInput]":
844
+ response = await self.request(
845
+ "POST",
846
+ "/flow_runs/{id}/input/filter",
847
+ path_params={"id": flow_run_id},
848
+ json={
849
+ "prefix": key_prefix,
850
+ "limit": limit,
851
+ "exclude_keys": list(exclude_keys),
852
+ },
853
+ )
854
+ response.raise_for_status()
855
+ from prefect.client.schemas.objects import FlowRunInput
856
+
857
+ return FlowRunInput.model_validate_list(response.json())
858
+
859
+ async def read_flow_run_input(self, flow_run_id: "UUID", key: str) -> str:
860
+ """
861
+ Reads a flow run input.
862
+
863
+ Args:
864
+ flow_run_id: The flow run id.
865
+ key: The input key.
866
+ """
867
+ response = await self.request(
868
+ "GET",
869
+ "/flow_runs/{id}/input/{key}",
870
+ path_params={"id": flow_run_id, "key": key},
871
+ )
872
+ response.raise_for_status()
873
+ return response.content.decode()
874
+
875
+ async def delete_flow_run_input(self, flow_run_id: "UUID", key: str) -> None:
876
+ """
877
+ Deletes a flow run input.
878
+
879
+ Args:
880
+ flow_run_id: The flow run id.
881
+ key: The input key.
882
+ """
883
+ response = await self.request(
884
+ "DELETE",
885
+ "/flow_runs/{id}/input/{key}",
886
+ path_params={"id": flow_run_id, "key": key},
887
+ )
888
+ response.raise_for_status()
889
+
890
+ async def update_flow_run_labels(
891
+ self, flow_run_id: "UUID", labels: "KeyValueLabelsField"
892
+ ) -> None:
893
+ """
894
+ Updates the labels of a flow run.
895
+ """
896
+
897
+ response = await self.request(
898
+ "PATCH",
899
+ "/flow_runs/{id}/labels",
900
+ path_params={"id": flow_run_id},
901
+ json=labels,
902
+ )
903
+ response.raise_for_status()