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.
- prefect/_experimental/lineage.py +7 -8
- prefect/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +66 -0
- prefect/_experimental/sla/objects.py +53 -0
- prefect/_internal/_logging.py +15 -3
- prefect/_internal/compatibility/async_dispatch.py +22 -16
- prefect/_internal/compatibility/deprecated.py +42 -18
- prefect/_internal/compatibility/migration.py +2 -2
- prefect/_internal/concurrency/inspection.py +12 -14
- prefect/_internal/concurrency/primitives.py +2 -2
- prefect/_internal/concurrency/services.py +154 -80
- prefect/_internal/concurrency/waiters.py +13 -9
- prefect/_internal/pydantic/annotations/pendulum.py +7 -7
- prefect/_internal/pytz.py +4 -3
- prefect/_internal/retries.py +10 -5
- prefect/_internal/schemas/bases.py +19 -10
- prefect/_internal/schemas/validators.py +227 -388
- prefect/_version.py +3 -3
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +53 -30
- prefect/blocks/core.py +183 -84
- prefect/blocks/notifications.py +133 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +3 -2
- prefect/client/orchestration/__init__.py +1957 -0
- prefect/client/orchestration/_artifacts/__init__.py +0 -0
- prefect/client/orchestration/_artifacts/client.py +239 -0
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_concurrency_limits/__init__.py +0 -0
- prefect/client/orchestration/_concurrency_limits/client.py +762 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/__init__.py +0 -0
- prefect/client/orchestration/_logs/client.py +97 -0
- prefect/client/orchestration/_variables/__init__.py +0 -0
- prefect/client/orchestration/_variables/client.py +157 -0
- prefect/client/orchestration/base.py +46 -0
- prefect/client/orchestration/routes.py +145 -0
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/actions.py +2 -2
- prefect/client/schemas/filters.py +5 -0
- prefect/client/schemas/objects.py +8 -15
- prefect/client/schemas/schedules.py +22 -10
- prefect/concurrency/_asyncio.py +87 -0
- prefect/concurrency/{events.py → _events.py} +10 -10
- prefect/concurrency/asyncio.py +20 -104
- prefect/concurrency/context.py +6 -4
- prefect/concurrency/services.py +26 -74
- prefect/concurrency/sync.py +23 -44
- prefect/concurrency/v1/_asyncio.py +63 -0
- prefect/concurrency/v1/{events.py → _events.py} +13 -15
- prefect/concurrency/v1/asyncio.py +27 -80
- prefect/concurrency/v1/context.py +6 -4
- prefect/concurrency/v1/services.py +33 -79
- prefect/concurrency/v1/sync.py +18 -37
- prefect/context.py +66 -45
- prefect/deployments/base.py +10 -144
- prefect/deployments/flow_runs.py +12 -2
- prefect/deployments/runner.py +53 -4
- prefect/deployments/steps/pull.py +13 -0
- prefect/engine.py +17 -4
- prefect/events/clients.py +7 -1
- prefect/events/schemas/events.py +3 -2
- prefect/filesystems.py +6 -2
- prefect/flow_engine.py +101 -85
- prefect/flows.py +10 -1
- prefect/input/run_input.py +2 -1
- prefect/logging/logging.yml +1 -1
- prefect/main.py +1 -3
- prefect/results.py +2 -307
- prefect/runner/runner.py +4 -2
- prefect/runner/storage.py +87 -21
- prefect/serializers.py +32 -25
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +3 -3
- prefect/settings/models/cli.py +3 -3
- prefect/settings/models/client.py +5 -3
- prefect/settings/models/cloud.py +8 -3
- prefect/settings/models/deployments.py +3 -3
- prefect/settings/models/experiments.py +4 -7
- prefect/settings/models/flows.py +3 -3
- prefect/settings/models/internal.py +4 -2
- prefect/settings/models/logging.py +4 -3
- prefect/settings/models/results.py +3 -3
- prefect/settings/models/root.py +3 -2
- prefect/settings/models/runner.py +4 -4
- prefect/settings/models/server/api.py +3 -3
- prefect/settings/models/server/database.py +11 -4
- prefect/settings/models/server/deployments.py +6 -2
- prefect/settings/models/server/ephemeral.py +4 -2
- prefect/settings/models/server/events.py +3 -2
- prefect/settings/models/server/flow_run_graph.py +6 -2
- prefect/settings/models/server/root.py +3 -3
- prefect/settings/models/server/services.py +26 -11
- prefect/settings/models/server/tasks.py +6 -3
- prefect/settings/models/server/ui.py +3 -3
- prefect/settings/models/tasks.py +5 -5
- prefect/settings/models/testing.py +3 -3
- prefect/settings/models/worker.py +5 -3
- prefect/settings/profiles.py +15 -2
- prefect/states.py +61 -45
- prefect/task_engine.py +54 -75
- prefect/task_runners.py +56 -55
- prefect/task_worker.py +2 -2
- prefect/tasks.py +90 -36
- prefect/telemetry/bootstrap.py +10 -9
- prefect/telemetry/run_telemetry.py +13 -8
- prefect/telemetry/services.py +4 -0
- prefect/transactions.py +4 -15
- prefect/utilities/_git.py +34 -0
- prefect/utilities/asyncutils.py +1 -1
- prefect/utilities/engine.py +3 -19
- prefect/utilities/generics.py +18 -0
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +6 -3
- prefect/workers/process.py +1 -1
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/METADATA +2 -2
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/RECORD +135 -109
- prefect/client/orchestration.py +0 -4523
- prefect/records/__init__.py +0 -1
- prefect/records/base.py +0 -235
- prefect/records/filesystem.py +0 -213
- prefect/records/memory.py +0 -184
- prefect/records/result_store.py +0 -70
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/WHEEL +0 -0
- {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)
|