prefect-client 3.1.10__py3-none-any.whl → 3.1.12__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 (141) hide show
  1. prefect/_experimental/lineage.py +7 -8
  2. prefect/_experimental/sla/__init__.py +0 -0
  3. prefect/_experimental/sla/client.py +66 -0
  4. prefect/_experimental/sla/objects.py +53 -0
  5. prefect/_internal/_logging.py +15 -3
  6. prefect/_internal/compatibility/async_dispatch.py +22 -16
  7. prefect/_internal/compatibility/deprecated.py +42 -18
  8. prefect/_internal/compatibility/migration.py +2 -2
  9. prefect/_internal/concurrency/inspection.py +12 -14
  10. prefect/_internal/concurrency/primitives.py +2 -2
  11. prefect/_internal/concurrency/services.py +154 -80
  12. prefect/_internal/concurrency/waiters.py +13 -9
  13. prefect/_internal/pydantic/annotations/pendulum.py +7 -7
  14. prefect/_internal/pytz.py +4 -3
  15. prefect/_internal/retries.py +10 -5
  16. prefect/_internal/schemas/bases.py +19 -10
  17. prefect/_internal/schemas/validators.py +227 -388
  18. prefect/_version.py +3 -3
  19. prefect/automations.py +236 -30
  20. prefect/blocks/__init__.py +3 -3
  21. prefect/blocks/abstract.py +53 -30
  22. prefect/blocks/core.py +183 -84
  23. prefect/blocks/notifications.py +133 -73
  24. prefect/blocks/redis.py +13 -9
  25. prefect/blocks/system.py +24 -11
  26. prefect/blocks/webhook.py +7 -5
  27. prefect/cache_policies.py +3 -2
  28. prefect/client/orchestration/__init__.py +1957 -0
  29. prefect/client/orchestration/_artifacts/__init__.py +0 -0
  30. prefect/client/orchestration/_artifacts/client.py +239 -0
  31. prefect/client/orchestration/_automations/__init__.py +0 -0
  32. prefect/client/orchestration/_automations/client.py +329 -0
  33. prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
  34. prefect/client/orchestration/_blocks_documents/client.py +334 -0
  35. prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
  36. prefect/client/orchestration/_blocks_schemas/client.py +200 -0
  37. prefect/client/orchestration/_blocks_types/__init__.py +0 -0
  38. prefect/client/orchestration/_blocks_types/client.py +380 -0
  39. prefect/client/orchestration/_concurrency_limits/__init__.py +0 -0
  40. prefect/client/orchestration/_concurrency_limits/client.py +762 -0
  41. prefect/client/orchestration/_deployments/__init__.py +0 -0
  42. prefect/client/orchestration/_deployments/client.py +1128 -0
  43. prefect/client/orchestration/_flow_runs/__init__.py +0 -0
  44. prefect/client/orchestration/_flow_runs/client.py +903 -0
  45. prefect/client/orchestration/_flows/__init__.py +0 -0
  46. prefect/client/orchestration/_flows/client.py +343 -0
  47. prefect/client/orchestration/_logs/__init__.py +0 -0
  48. prefect/client/orchestration/_logs/client.py +97 -0
  49. prefect/client/orchestration/_variables/__init__.py +0 -0
  50. prefect/client/orchestration/_variables/client.py +157 -0
  51. prefect/client/orchestration/base.py +46 -0
  52. prefect/client/orchestration/routes.py +145 -0
  53. prefect/client/schemas/__init__.py +68 -28
  54. prefect/client/schemas/actions.py +2 -2
  55. prefect/client/schemas/filters.py +5 -0
  56. prefect/client/schemas/objects.py +8 -15
  57. prefect/client/schemas/schedules.py +22 -10
  58. prefect/concurrency/_asyncio.py +87 -0
  59. prefect/concurrency/{events.py → _events.py} +10 -10
  60. prefect/concurrency/asyncio.py +20 -104
  61. prefect/concurrency/context.py +6 -4
  62. prefect/concurrency/services.py +26 -74
  63. prefect/concurrency/sync.py +23 -44
  64. prefect/concurrency/v1/_asyncio.py +63 -0
  65. prefect/concurrency/v1/{events.py → _events.py} +13 -15
  66. prefect/concurrency/v1/asyncio.py +27 -80
  67. prefect/concurrency/v1/context.py +6 -4
  68. prefect/concurrency/v1/services.py +33 -79
  69. prefect/concurrency/v1/sync.py +18 -37
  70. prefect/context.py +66 -45
  71. prefect/deployments/base.py +10 -144
  72. prefect/deployments/flow_runs.py +12 -2
  73. prefect/deployments/runner.py +53 -4
  74. prefect/deployments/steps/pull.py +13 -0
  75. prefect/engine.py +17 -4
  76. prefect/events/clients.py +7 -1
  77. prefect/events/schemas/events.py +3 -2
  78. prefect/filesystems.py +6 -2
  79. prefect/flow_engine.py +101 -85
  80. prefect/flows.py +10 -1
  81. prefect/input/run_input.py +2 -1
  82. prefect/logging/logging.yml +1 -1
  83. prefect/main.py +1 -3
  84. prefect/results.py +2 -307
  85. prefect/runner/runner.py +4 -2
  86. prefect/runner/storage.py +87 -21
  87. prefect/serializers.py +32 -25
  88. prefect/settings/legacy.py +4 -4
  89. prefect/settings/models/api.py +3 -3
  90. prefect/settings/models/cli.py +3 -3
  91. prefect/settings/models/client.py +5 -3
  92. prefect/settings/models/cloud.py +8 -3
  93. prefect/settings/models/deployments.py +3 -3
  94. prefect/settings/models/experiments.py +4 -7
  95. prefect/settings/models/flows.py +3 -3
  96. prefect/settings/models/internal.py +4 -2
  97. prefect/settings/models/logging.py +4 -3
  98. prefect/settings/models/results.py +3 -3
  99. prefect/settings/models/root.py +3 -2
  100. prefect/settings/models/runner.py +4 -4
  101. prefect/settings/models/server/api.py +3 -3
  102. prefect/settings/models/server/database.py +11 -4
  103. prefect/settings/models/server/deployments.py +6 -2
  104. prefect/settings/models/server/ephemeral.py +4 -2
  105. prefect/settings/models/server/events.py +3 -2
  106. prefect/settings/models/server/flow_run_graph.py +6 -2
  107. prefect/settings/models/server/root.py +3 -3
  108. prefect/settings/models/server/services.py +26 -11
  109. prefect/settings/models/server/tasks.py +6 -3
  110. prefect/settings/models/server/ui.py +3 -3
  111. prefect/settings/models/tasks.py +5 -5
  112. prefect/settings/models/testing.py +3 -3
  113. prefect/settings/models/worker.py +5 -3
  114. prefect/settings/profiles.py +15 -2
  115. prefect/states.py +61 -45
  116. prefect/task_engine.py +54 -75
  117. prefect/task_runners.py +56 -55
  118. prefect/task_worker.py +2 -2
  119. prefect/tasks.py +90 -36
  120. prefect/telemetry/bootstrap.py +10 -9
  121. prefect/telemetry/run_telemetry.py +13 -8
  122. prefect/telemetry/services.py +4 -0
  123. prefect/transactions.py +4 -15
  124. prefect/utilities/_git.py +34 -0
  125. prefect/utilities/asyncutils.py +1 -1
  126. prefect/utilities/engine.py +3 -19
  127. prefect/utilities/generics.py +18 -0
  128. prefect/utilities/templating.py +25 -1
  129. prefect/workers/base.py +6 -3
  130. prefect/workers/process.py +1 -1
  131. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/METADATA +2 -2
  132. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/RECORD +135 -109
  133. prefect/client/orchestration.py +0 -4523
  134. prefect/records/__init__.py +0 -1
  135. prefect/records/base.py +0 -235
  136. prefect/records/filesystem.py +0 -213
  137. prefect/records/memory.py +0 -184
  138. prefect/records/result_store.py +0 -70
  139. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/LICENSE +0 -0
  140. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/WHEEL +0 -0
  141. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/top_level.txt +0 -0
File without changes
@@ -0,0 +1,343 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from httpx import HTTPStatusError, RequestError
6
+
7
+ from prefect.client.orchestration.base import BaseAsyncClient, BaseClient
8
+ from prefect.exceptions import ObjectNotFound
9
+
10
+ if TYPE_CHECKING:
11
+ from uuid import UUID
12
+
13
+ from prefect.client.schemas.filters import (
14
+ DeploymentFilter,
15
+ FlowFilter,
16
+ FlowRunFilter,
17
+ TaskRunFilter,
18
+ WorkPoolFilter,
19
+ WorkQueueFilter,
20
+ )
21
+ from prefect.client.schemas.objects import (
22
+ Flow,
23
+ )
24
+ from prefect.client.schemas.sorting import FlowSort
25
+ from prefect.flows import Flow as FlowObject
26
+
27
+
28
+ class FlowClient(BaseClient):
29
+ def create_flow(self, flow: "FlowObject[Any, Any]") -> "UUID":
30
+ """
31
+ Create a flow in the Prefect API.
32
+
33
+ Args:
34
+ flow: a [Flow][prefect.flows.Flow] object
35
+
36
+ Raises:
37
+ httpx.RequestError: if a flow was not created for any reason
38
+
39
+ Returns:
40
+ the ID of the flow in the backend
41
+ """
42
+ return self.create_flow_from_name(flow.name)
43
+
44
+ def create_flow_from_name(self, flow_name: str) -> "UUID":
45
+ """
46
+ Create a flow in the Prefect API.
47
+
48
+ Args:
49
+ flow_name: the name of the new flow
50
+
51
+ Raises:
52
+ httpx.RequestError: if a flow was not created for any reason
53
+
54
+ Returns:
55
+ the ID of the flow in the backend
56
+ """
57
+ from prefect.client.schemas.actions import FlowCreate
58
+
59
+ flow_data = FlowCreate(name=flow_name)
60
+ response = self.request(
61
+ "POST", "/flows/", json=flow_data.model_dump(mode="json")
62
+ )
63
+
64
+ flow_id = response.json().get("id")
65
+ if not flow_id:
66
+ raise RequestError(f"Malformed response: {response}")
67
+
68
+ # Return the id of the created flow
69
+ from uuid import UUID
70
+
71
+ return UUID(flow_id)
72
+
73
+ def read_flow(self, flow_id: "UUID") -> "Flow":
74
+ """
75
+ Query the Prefect API for a flow by id.
76
+
77
+ Args:
78
+ flow_id: the flow ID of interest
79
+
80
+ Returns:
81
+ a [Flow model][prefect.client.schemas.objects.Flow] representation of the flow
82
+ """
83
+ response = self.request("GET", "/flows/{id}", path_params={"id": flow_id})
84
+ from prefect.client.schemas.objects import Flow
85
+
86
+ return Flow.model_validate(response.json())
87
+
88
+ def delete_flow(self, flow_id: "UUID") -> None:
89
+ """
90
+ Delete a flow by UUID.
91
+
92
+ Args:
93
+ flow_id: ID of the flow to be deleted
94
+ Raises:
95
+ prefect.exceptions.ObjectNotFound: If request returns 404
96
+ httpx.RequestError: If requests fail
97
+ """
98
+ try:
99
+ self.request("DELETE", "/flows/{id}", path_params={"id": flow_id})
100
+ except HTTPStatusError as e:
101
+ if e.response.status_code == 404:
102
+ raise ObjectNotFound(http_exc=e) from e
103
+ else:
104
+ raise
105
+
106
+ def read_flows(
107
+ self,
108
+ *,
109
+ flow_filter: "FlowFilter | None" = None,
110
+ flow_run_filter: "FlowRunFilter | None" = None,
111
+ task_run_filter: "TaskRunFilter | None" = None,
112
+ deployment_filter: "DeploymentFilter | None" = None,
113
+ work_pool_filter: "WorkPoolFilter | None" = None,
114
+ work_queue_filter: "WorkQueueFilter | None" = None,
115
+ sort: "FlowSort | None" = None,
116
+ limit: int | None = None,
117
+ offset: int = 0,
118
+ ) -> list["Flow"]:
119
+ """
120
+ Query the Prefect API for flows. Only flows matching all criteria will
121
+ be returned.
122
+
123
+ Args:
124
+ flow_filter: filter criteria for flows
125
+ flow_run_filter: filter criteria for flow runs
126
+ task_run_filter: filter criteria for task runs
127
+ deployment_filter: filter criteria for deployments
128
+ work_pool_filter: filter criteria for work pools
129
+ work_queue_filter: filter criteria for work pool queues
130
+ sort: sort criteria for the flows
131
+ limit: limit for the flow query
132
+ offset: offset for the flow query
133
+
134
+ Returns:
135
+ a list of Flow model representations of the flows
136
+ """
137
+ body: dict[str, Any] = {
138
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
139
+ "flow_runs": (
140
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
141
+ if flow_run_filter
142
+ else None
143
+ ),
144
+ "task_runs": (
145
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
146
+ ),
147
+ "deployments": (
148
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
149
+ ),
150
+ "work_pools": (
151
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
152
+ ),
153
+ "work_queues": (
154
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
155
+ ),
156
+ "sort": sort,
157
+ "limit": limit,
158
+ "offset": offset,
159
+ }
160
+
161
+ response = self.request("POST", "/flows/filter", json=body)
162
+ from prefect.client.schemas.objects import Flow
163
+
164
+ return Flow.model_validate_list(response.json())
165
+
166
+ def read_flow_by_name(
167
+ self,
168
+ flow_name: str,
169
+ ) -> "Flow":
170
+ """
171
+ Query the Prefect API for a flow by name.
172
+
173
+ Args:
174
+ flow_name: the name of a flow
175
+
176
+ Returns:
177
+ a fully hydrated Flow model
178
+ """
179
+ response = self.request(
180
+ "GET", "/flows/name/{name}", path_params={"name": flow_name}
181
+ )
182
+ from prefect.client.schemas.objects import Flow
183
+
184
+ return Flow.model_validate(response.json())
185
+
186
+
187
+ class FlowAsyncClient(BaseAsyncClient):
188
+ async def create_flow(self, flow: "FlowObject[Any, Any]") -> "UUID":
189
+ """
190
+ Create a flow in the Prefect API.
191
+
192
+ Args:
193
+ flow: a [Flow][prefect.flows.Flow] object
194
+
195
+ Raises:
196
+ httpx.RequestError: if a flow was not created for any reason
197
+
198
+ Returns:
199
+ the ID of the flow in the backend
200
+ """
201
+ return await self.create_flow_from_name(flow.name)
202
+
203
+ async def create_flow_from_name(self, flow_name: str) -> "UUID":
204
+ """
205
+ Create a flow in the Prefect API.
206
+
207
+ Args:
208
+ flow_name: the name of the new flow
209
+
210
+ Raises:
211
+ httpx.RequestError: if a flow was not created for any reason
212
+
213
+ Returns:
214
+ the ID of the flow in the backend
215
+ """
216
+ from prefect.client.schemas.actions import FlowCreate
217
+
218
+ flow_data = FlowCreate(name=flow_name)
219
+ response = await self.request(
220
+ "POST", "/flows/", json=flow_data.model_dump(mode="json")
221
+ )
222
+
223
+ flow_id = response.json().get("id")
224
+ if not flow_id:
225
+ raise RequestError(f"Malformed response: {response}")
226
+
227
+ # Return the id of the created flow
228
+ from uuid import UUID
229
+
230
+ return UUID(flow_id)
231
+
232
+ async def read_flow(self, flow_id: "UUID") -> "Flow":
233
+ """
234
+ Query the Prefect API for a flow by id.
235
+
236
+ Args:
237
+ flow_id: the flow ID of interest
238
+
239
+ Returns:
240
+ a [Flow model][prefect.client.schemas.objects.Flow] representation of the flow
241
+ """
242
+ response = await self.request("GET", "/flows/{id}", path_params={"id": flow_id})
243
+ from prefect.client.schemas.objects import Flow
244
+
245
+ return Flow.model_validate(response.json())
246
+
247
+ async def delete_flow(self, flow_id: "UUID") -> None:
248
+ """
249
+ Delete a flow by UUID.
250
+
251
+ Args:
252
+ flow_id: ID of the flow to be deleted
253
+ Raises:
254
+ prefect.exceptions.ObjectNotFound: If request returns 404
255
+ httpx.RequestError: If requests fail
256
+ """
257
+ try:
258
+ await self.request("DELETE", "/flows/{id}", path_params={"id": flow_id})
259
+ except HTTPStatusError as e:
260
+ if e.response.status_code == 404:
261
+ raise ObjectNotFound(http_exc=e) from e
262
+ else:
263
+ raise
264
+
265
+ async def read_flows(
266
+ self,
267
+ *,
268
+ flow_filter: "FlowFilter | None" = None,
269
+ flow_run_filter: "FlowRunFilter | None" = None,
270
+ task_run_filter: "TaskRunFilter | None" = None,
271
+ deployment_filter: "DeploymentFilter | None" = None,
272
+ work_pool_filter: "WorkPoolFilter | None" = None,
273
+ work_queue_filter: "WorkQueueFilter | None" = None,
274
+ sort: "FlowSort | None" = None,
275
+ limit: int | None = None,
276
+ offset: int = 0,
277
+ ) -> list["Flow"]:
278
+ """
279
+ Query the Prefect API for flows. Only flows matching all criteria will
280
+ be returned.
281
+
282
+ Args:
283
+ flow_filter: filter criteria for flows
284
+ flow_run_filter: filter criteria for flow runs
285
+ task_run_filter: filter criteria for task runs
286
+ deployment_filter: filter criteria for deployments
287
+ work_pool_filter: filter criteria for work pools
288
+ work_queue_filter: filter criteria for work pool queues
289
+ sort: sort criteria for the flows
290
+ limit: limit for the flow query
291
+ offset: offset for the flow query
292
+
293
+ Returns:
294
+ a list of Flow model representations of the flows
295
+ """
296
+ body: dict[str, Any] = {
297
+ "flows": flow_filter.model_dump(mode="json") if flow_filter else None,
298
+ "flow_runs": (
299
+ flow_run_filter.model_dump(mode="json", exclude_unset=True)
300
+ if flow_run_filter
301
+ else None
302
+ ),
303
+ "task_runs": (
304
+ task_run_filter.model_dump(mode="json") if task_run_filter else None
305
+ ),
306
+ "deployments": (
307
+ deployment_filter.model_dump(mode="json") if deployment_filter else None
308
+ ),
309
+ "work_pools": (
310
+ work_pool_filter.model_dump(mode="json") if work_pool_filter else None
311
+ ),
312
+ "work_queues": (
313
+ work_queue_filter.model_dump(mode="json") if work_queue_filter else None
314
+ ),
315
+ "sort": sort,
316
+ "limit": limit,
317
+ "offset": offset,
318
+ }
319
+
320
+ response = await self.request("POST", "/flows/filter", json=body)
321
+ from prefect.client.schemas.objects import Flow
322
+
323
+ return Flow.model_validate_list(response.json())
324
+
325
+ async def read_flow_by_name(
326
+ self,
327
+ flow_name: str,
328
+ ) -> "Flow":
329
+ """
330
+ Query the Prefect API for a flow by name.
331
+
332
+ Args:
333
+ flow_name: the name of a flow
334
+
335
+ Returns:
336
+ a fully hydrated Flow model
337
+ """
338
+ response = await self.request(
339
+ "GET", "/flows/name/{name}", path_params={"name": flow_name}
340
+ )
341
+ from prefect.client.schemas.objects import Flow
342
+
343
+ return Flow.model_validate(response.json())
File without changes
@@ -0,0 +1,97 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Iterable, Union
4
+
5
+ from prefect.client.orchestration.base import BaseAsyncClient, BaseClient
6
+
7
+ if TYPE_CHECKING:
8
+ from prefect.client.schemas.actions import (
9
+ LogCreate,
10
+ )
11
+ from prefect.client.schemas.filters import (
12
+ LogFilter,
13
+ )
14
+ from prefect.client.schemas.objects import (
15
+ Log,
16
+ )
17
+ from prefect.client.schemas.sorting import LogSort
18
+
19
+
20
+ class LogClient(BaseClient):
21
+ def create_logs(self, logs: Iterable[Union["LogCreate", dict[str, Any]]]) -> None:
22
+ """
23
+ Create logs for a flow or task run
24
+ """
25
+ from prefect.client.schemas.actions import LogCreate
26
+
27
+ serialized_logs = [
28
+ log.model_dump(mode="json") if isinstance(log, LogCreate) else log
29
+ for log in logs
30
+ ]
31
+ self.request("POST", "/logs/", json=serialized_logs)
32
+
33
+ def read_logs(
34
+ self,
35
+ log_filter: "LogFilter | None" = None,
36
+ limit: int | None = None,
37
+ offset: int | None = None,
38
+ sort: "LogSort | None" = None,
39
+ ) -> list["Log"]:
40
+ """
41
+ Read flow and task run logs.
42
+ """
43
+ from prefect.client.schemas.sorting import LogSort
44
+
45
+ body: dict[str, Any] = {
46
+ "logs": log_filter.model_dump(mode="json") if log_filter else None,
47
+ "limit": limit,
48
+ "offset": offset,
49
+ "sort": sort or LogSort.TIMESTAMP_ASC,
50
+ }
51
+ response = self.request("POST", "/logs/filter", json=body)
52
+ from prefect.client.schemas.objects import Log
53
+
54
+ return Log.model_validate_list(response.json())
55
+
56
+
57
+ class LogAsyncClient(BaseAsyncClient):
58
+ async def create_logs(
59
+ self, logs: Iterable[Union["LogCreate", dict[str, Any]]]
60
+ ) -> None:
61
+ """
62
+ Create logs for a flow or task run
63
+
64
+ Args:
65
+ logs: An iterable of `LogCreate` objects or already json-compatible dicts
66
+ """
67
+ from prefect.client.schemas.actions import LogCreate
68
+
69
+ serialized_logs = [
70
+ log.model_dump(mode="json") if isinstance(log, LogCreate) else log
71
+ for log in logs
72
+ ]
73
+ await self.request("POST", "/logs/", json=serialized_logs)
74
+
75
+ async def read_logs(
76
+ self,
77
+ log_filter: "LogFilter | None" = None,
78
+ limit: int | None = None,
79
+ offset: int | None = None,
80
+ sort: "LogSort | None" = None,
81
+ ) -> list[Log]:
82
+ """
83
+ Read flow and task run logs.
84
+ """
85
+ from prefect.client.schemas.sorting import LogSort
86
+
87
+ body: dict[str, Any] = {
88
+ "logs": log_filter.model_dump(mode="json") if log_filter else None,
89
+ "limit": limit,
90
+ "offset": offset,
91
+ "sort": sort or LogSort.TIMESTAMP_ASC,
92
+ }
93
+
94
+ response = await self.request("POST", "/logs/filter", json=body)
95
+ from prefect.client.schemas.objects import Log
96
+
97
+ return Log.model_validate_list(response.json())
File without changes
@@ -0,0 +1,157 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import httpx
6
+
7
+ from prefect.client.orchestration.base import BaseAsyncClient, BaseClient
8
+ from prefect.exceptions import ObjectNotFound
9
+
10
+ if TYPE_CHECKING:
11
+ from prefect.client.schemas.actions import (
12
+ VariableCreate,
13
+ VariableUpdate,
14
+ )
15
+ from prefect.client.schemas.objects import (
16
+ Variable,
17
+ )
18
+
19
+
20
+ class VariableClient(BaseClient):
21
+ def create_variable(self, variable: "VariableCreate") -> "Variable":
22
+ """
23
+ Creates an variable with the provided configuration.
24
+
25
+ Args:
26
+ variable: Desired configuration for the new variable.
27
+ Returns:
28
+ Information about the newly created variable.
29
+ """
30
+ response = self._client.post(
31
+ "/variables/",
32
+ json=variable.model_dump(mode="json", exclude_unset=True),
33
+ )
34
+ from prefect.client.schemas.objects import Variable
35
+
36
+ return Variable.model_validate(response.json())
37
+
38
+ def read_variable_by_name(self, name: str) -> "Variable | None":
39
+ """Reads a variable by name. Returns None if no variable is found."""
40
+ try:
41
+ response = self.request(
42
+ "GET", "/variables/name/{name}", path_params={"name": name}
43
+ )
44
+ from prefect.client.schemas.objects import Variable
45
+
46
+ return Variable(**response.json())
47
+ except httpx.HTTPStatusError as e:
48
+ if e.response.status_code == 404:
49
+ return None
50
+ else:
51
+ raise
52
+
53
+ def read_variables(self, limit: int | None = None) -> list["Variable"]:
54
+ """Reads all variables."""
55
+ response = self.request("POST", "/variables/filter", json={"limit": limit})
56
+ from prefect.client.schemas.objects import Variable
57
+
58
+ return Variable.model_validate_list(response.json())
59
+
60
+ def update_variable(self, variable: "VariableUpdate") -> None:
61
+ """
62
+ Updates a variable with the provided configuration.
63
+
64
+ Args:
65
+ variable: Desired configuration for the updated variable.
66
+ Returns:
67
+ Information about the updated variable.
68
+ """
69
+ self._client.patch(
70
+ f"/variables/name/{variable.name}",
71
+ json=variable.model_dump(mode="json", exclude_unset=True),
72
+ )
73
+ return None
74
+
75
+ def delete_variable_by_name(self, name: str) -> None:
76
+ """Deletes a variable by name."""
77
+ try:
78
+ self.request(
79
+ "DELETE",
80
+ "/variables/name/{name}",
81
+ path_params={"name": name},
82
+ )
83
+ return None
84
+ except httpx.HTTPStatusError as e:
85
+ if e.response.status_code == 404:
86
+ raise ObjectNotFound(http_exc=e) from e
87
+ else:
88
+ raise
89
+
90
+
91
+ class VariableAsyncClient(BaseAsyncClient):
92
+ async def create_variable(self, variable: "VariableCreate") -> "Variable":
93
+ """Creates a variable with the provided configuration."""
94
+ response = await self._client.post(
95
+ "/variables/",
96
+ json=variable.model_dump(mode="json", exclude_unset=True),
97
+ )
98
+ from prefect.client.schemas.objects import Variable
99
+
100
+ return Variable.model_validate(response.json())
101
+
102
+ async def read_variable_by_name(self, name: str) -> "Variable | None":
103
+ """Reads a variable by name. Returns None if no variable is found."""
104
+ try:
105
+ response = await self.request(
106
+ "GET",
107
+ "/variables/name/{name}",
108
+ path_params={"name": name},
109
+ )
110
+ from prefect.client.schemas.objects import Variable
111
+
112
+ return Variable.model_validate(response.json())
113
+ except httpx.HTTPStatusError as e:
114
+ if e.response.status_code == 404:
115
+ return None
116
+ else:
117
+ raise
118
+
119
+ async def read_variables(self, limit: int | None = None) -> list["Variable"]:
120
+ """Reads all variables."""
121
+ response = await self.request(
122
+ "POST", "/variables/filter", json={"limit": limit}
123
+ )
124
+ from prefect.client.schemas.objects import Variable
125
+
126
+ return Variable.model_validate_list(response.json())
127
+
128
+ async def update_variable(self, variable: "VariableUpdate") -> None:
129
+ """
130
+ Updates a variable with the provided configuration.
131
+
132
+ Args:
133
+ variable: Desired configuration for the updated variable.
134
+ Returns:
135
+ Information about the updated variable.
136
+ """
137
+ await self.request(
138
+ "PATCH",
139
+ "/variables/name/{name}",
140
+ path_params={"name": variable.name},
141
+ json=variable.model_dump(mode="json", exclude_unset=True),
142
+ )
143
+ return None
144
+
145
+ async def delete_variable_by_name(self, name: str) -> None:
146
+ """Deletes a variable by name."""
147
+ try:
148
+ await self.request(
149
+ "DELETE",
150
+ "/variables/name/{name}",
151
+ path_params={"name": name},
152
+ )
153
+ except httpx.HTTPStatusError as e:
154
+ if e.response.status_code == 404:
155
+ raise ObjectNotFound(http_exc=e) from e
156
+ else:
157
+ raise
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, Any, Literal
4
+
5
+ from typing_extensions import TypeAlias
6
+
7
+ if TYPE_CHECKING:
8
+ from httpx import AsyncClient, Client, Response
9
+
10
+ from prefect.client.orchestration.routes import ServerRoutes
11
+
12
+ HTTP_METHODS: TypeAlias = Literal["GET", "POST", "PUT", "DELETE", "PATCH"]
13
+
14
+
15
+ class BaseClient:
16
+ def __init__(self, client: "Client"):
17
+ self._client = client
18
+
19
+ def request(
20
+ self,
21
+ method: HTTP_METHODS,
22
+ path: "ServerRoutes",
23
+ params: dict[str, Any] | None = None,
24
+ path_params: dict[str, Any] | None = None,
25
+ **kwargs: Any,
26
+ ) -> "Response":
27
+ if path_params:
28
+ path = path.format(**path_params) # type: ignore
29
+ return self._client.request(method, path, params=params, **kwargs)
30
+
31
+
32
+ class BaseAsyncClient:
33
+ def __init__(self, client: "AsyncClient"):
34
+ self._client = client
35
+
36
+ async def request(
37
+ self,
38
+ method: HTTP_METHODS,
39
+ path: "ServerRoutes",
40
+ params: dict[str, Any] | None = None,
41
+ path_params: dict[str, Any] | None = None,
42
+ **kwargs: Any,
43
+ ) -> "Response":
44
+ if path_params:
45
+ path = path.format(**path_params) # type: ignore
46
+ return await self._client.request(method, path, params=params, **kwargs)