prefect-client 2.18.0__py3-none-any.whl → 2.18.2__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/_internal/schemas/fields.py +31 -12
- prefect/automations.py +162 -0
- prefect/blocks/core.py +1 -1
- prefect/blocks/notifications.py +2 -2
- prefect/blocks/system.py +2 -3
- prefect/client/orchestration.py +309 -30
- prefect/client/schemas/objects.py +11 -8
- prefect/client/schemas/sorting.py +9 -0
- prefect/client/utilities.py +25 -3
- prefect/concurrency/asyncio.py +11 -5
- prefect/concurrency/events.py +3 -3
- prefect/concurrency/services.py +1 -1
- prefect/concurrency/sync.py +9 -5
- prefect/deployments/deployments.py +27 -18
- prefect/deployments/runner.py +34 -26
- prefect/engine.py +3 -1
- prefect/events/actions.py +2 -1
- prefect/events/cli/automations.py +207 -46
- prefect/events/clients.py +53 -20
- prefect/events/filters.py +31 -4
- prefect/events/instrument.py +40 -40
- prefect/events/related.py +2 -1
- prefect/events/schemas/automations.py +52 -7
- prefect/events/schemas/deployment_triggers.py +16 -228
- prefect/events/schemas/events.py +18 -11
- prefect/events/schemas/labelling.py +1 -1
- prefect/events/utilities.py +1 -1
- prefect/events/worker.py +10 -7
- prefect/flows.py +42 -24
- prefect/input/actions.py +9 -9
- prefect/input/run_input.py +51 -37
- prefect/new_flow_engine.py +444 -0
- prefect/new_task_engine.py +488 -0
- prefect/results.py +3 -2
- prefect/runner/runner.py +3 -2
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +45 -4
- prefect/settings.py +47 -0
- prefect/states.py +25 -19
- prefect/tasks.py +146 -19
- prefect/utilities/asyncutils.py +41 -0
- prefect/utilities/engine.py +6 -4
- prefect/utilities/schema_tools/validation.py +1 -1
- prefect/workers/process.py +2 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/METADATA +1 -1
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/RECORD +48 -46
- prefect/concurrency/common.py +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/LICENSE +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/WHEEL +0 -0
- {prefect_client-2.18.0.dist-info → prefect_client-2.18.2.dist-info}/top_level.txt +0 -0
prefect/client/orchestration.py
CHANGED
@@ -12,6 +12,7 @@ from typing import (
|
|
12
12
|
Optional,
|
13
13
|
Set,
|
14
14
|
Tuple,
|
15
|
+
TypeVar,
|
15
16
|
Union,
|
16
17
|
)
|
17
18
|
from uuid import UUID, uuid4
|
@@ -20,15 +21,19 @@ import certifi
|
|
20
21
|
import httpcore
|
21
22
|
import httpx
|
22
23
|
import pendulum
|
24
|
+
from typing_extensions import ParamSpec
|
23
25
|
|
24
26
|
from prefect._internal.compatibility.deprecated import (
|
25
27
|
handle_deprecated_infra_overrides_parameter,
|
26
28
|
)
|
27
29
|
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
30
|
+
from prefect.client.schemas import sorting
|
31
|
+
from prefect.events import filters
|
28
32
|
from prefect.settings import (
|
29
33
|
PREFECT_API_SERVICES_TRIGGERS_ENABLED,
|
30
34
|
PREFECT_EXPERIMENTAL_EVENTS,
|
31
35
|
)
|
36
|
+
from prefect.utilities.asyncutils import run_sync
|
32
37
|
|
33
38
|
if HAS_PYDANTIC_V2:
|
34
39
|
import pydantic.v1 as pydantic
|
@@ -151,6 +156,9 @@ if TYPE_CHECKING:
|
|
151
156
|
|
152
157
|
from prefect.client.base import ASGIApp, PrefectHttpxClient, app_lifespan_context
|
153
158
|
|
159
|
+
P = ParamSpec("P")
|
160
|
+
R = TypeVar("R")
|
161
|
+
|
154
162
|
|
155
163
|
class ServerType(AutoEnum):
|
156
164
|
EPHEMERAL = AutoEnum.auto()
|
@@ -164,7 +172,9 @@ class ServerType(AutoEnum):
|
|
164
172
|
return PREFECT_EXPERIMENTAL_EVENTS and PREFECT_API_SERVICES_TRIGGERS_ENABLED
|
165
173
|
|
166
174
|
|
167
|
-
def get_client(
|
175
|
+
def get_client(
|
176
|
+
httpx_settings: Optional[Dict[str, Any]] = None, sync_client: bool = False
|
177
|
+
) -> "PrefectClient":
|
168
178
|
"""
|
169
179
|
Retrieve a HTTP client for communicating with the Prefect REST API.
|
170
180
|
|
@@ -174,6 +184,13 @@ def get_client(httpx_settings: Optional[dict] = None) -> "PrefectClient":
|
|
174
184
|
async with get_client() as client:
|
175
185
|
await client.hello()
|
176
186
|
```
|
187
|
+
|
188
|
+
To return a synchronous client, pass sync_client=True:
|
189
|
+
|
190
|
+
```python
|
191
|
+
with get_client(sync_client=True) as client:
|
192
|
+
client.hello()
|
193
|
+
```
|
177
194
|
"""
|
178
195
|
ctx = prefect.context.get_settings_context()
|
179
196
|
api = PREFECT_API_URL.value()
|
@@ -184,11 +201,18 @@ def get_client(httpx_settings: Optional[dict] = None) -> "PrefectClient":
|
|
184
201
|
|
185
202
|
api = create_app(ctx.settings, ephemeral=True)
|
186
203
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
204
|
+
if sync_client:
|
205
|
+
return SyncPrefectClient(
|
206
|
+
api,
|
207
|
+
api_key=PREFECT_API_KEY.value(),
|
208
|
+
httpx_settings=httpx_settings,
|
209
|
+
)
|
210
|
+
else:
|
211
|
+
return PrefectClient(
|
212
|
+
api,
|
213
|
+
api_key=PREFECT_API_KEY.value(),
|
214
|
+
httpx_settings=httpx_settings,
|
215
|
+
)
|
192
216
|
|
193
217
|
|
194
218
|
class PrefectClient:
|
@@ -599,12 +623,12 @@ class PrefectClient:
|
|
599
623
|
async def create_flow_run(
|
600
624
|
self,
|
601
625
|
flow: "FlowObject",
|
602
|
-
name: str = None,
|
626
|
+
name: Optional[str] = None,
|
603
627
|
parameters: Optional[Dict[str, Any]] = None,
|
604
628
|
context: Optional[Dict[str, Any]] = None,
|
605
|
-
tags: Iterable[str] = None,
|
606
|
-
parent_task_run_id: UUID = None,
|
607
|
-
state: "prefect.states.State" = None,
|
629
|
+
tags: Optional[Iterable[str]] = None,
|
630
|
+
parent_task_run_id: Optional[UUID] = None,
|
631
|
+
state: Optional["prefect.states.State"] = None,
|
608
632
|
) -> FlowRun:
|
609
633
|
"""
|
610
634
|
Create a flow run for a flow.
|
@@ -2136,21 +2160,23 @@ class PrefectClient:
|
|
2136
2160
|
|
2137
2161
|
async def create_task_run(
|
2138
2162
|
self,
|
2139
|
-
task: "TaskObject",
|
2163
|
+
task: "TaskObject[P, R]",
|
2140
2164
|
flow_run_id: Optional[UUID],
|
2141
2165
|
dynamic_key: str,
|
2142
|
-
name: str = None,
|
2143
|
-
extra_tags: Iterable[str] = None,
|
2144
|
-
state: prefect.states.State = None,
|
2145
|
-
task_inputs:
|
2146
|
-
|
2147
|
-
|
2148
|
-
|
2149
|
-
|
2150
|
-
|
2151
|
-
|
2152
|
-
|
2153
|
-
|
2166
|
+
name: Optional[str] = None,
|
2167
|
+
extra_tags: Optional[Iterable[str]] = None,
|
2168
|
+
state: Optional[prefect.states.State[R]] = None,
|
2169
|
+
task_inputs: Optional[
|
2170
|
+
Dict[
|
2171
|
+
str,
|
2172
|
+
List[
|
2173
|
+
Union[
|
2174
|
+
TaskRunResult,
|
2175
|
+
Parameter,
|
2176
|
+
Constant,
|
2177
|
+
]
|
2178
|
+
],
|
2179
|
+
]
|
2154
2180
|
] = None,
|
2155
2181
|
) -> TaskRun:
|
2156
2182
|
"""
|
@@ -3135,6 +3161,16 @@ class PrefectClient:
|
|
3135
3161
|
|
3136
3162
|
return UUID(response.json()["id"])
|
3137
3163
|
|
3164
|
+
async def update_automation(self, automation_id: UUID, automation: AutomationCore):
|
3165
|
+
"""Updates an automation in Prefect Cloud."""
|
3166
|
+
if not self.server_type.supports_automations():
|
3167
|
+
self._raise_for_unsupported_automations()
|
3168
|
+
response = await self._client.put(
|
3169
|
+
f"/automations/{automation_id}",
|
3170
|
+
json=automation.dict(json_compatible=True, exclude_unset=True),
|
3171
|
+
)
|
3172
|
+
response.raise_for_status
|
3173
|
+
|
3138
3174
|
async def read_automations(self) -> List[Automation]:
|
3139
3175
|
if not self.server_type.supports_automations():
|
3140
3176
|
self._raise_for_unsupported_automations()
|
@@ -3144,17 +3180,23 @@ class PrefectClient:
|
|
3144
3180
|
return pydantic.parse_obj_as(List[Automation], response.json())
|
3145
3181
|
|
3146
3182
|
async def find_automation(
|
3147
|
-
self, id_or_name: str, exit_if_not_found: bool = True
|
3183
|
+
self, id_or_name: Union[str, UUID], exit_if_not_found: bool = True
|
3148
3184
|
) -> Optional[Automation]:
|
3149
|
-
|
3150
|
-
|
3151
|
-
|
3152
|
-
|
3185
|
+
if isinstance(id_or_name, str):
|
3186
|
+
try:
|
3187
|
+
id = UUID(id_or_name)
|
3188
|
+
except ValueError:
|
3189
|
+
id = None
|
3190
|
+
elif isinstance(id_or_name, UUID):
|
3191
|
+
id = id_or_name
|
3153
3192
|
|
3154
3193
|
if id:
|
3155
|
-
|
3156
|
-
|
3194
|
+
try:
|
3195
|
+
automation = await self.read_automation(id)
|
3157
3196
|
return automation
|
3197
|
+
except prefect.exceptions.HTTPStatusError as e:
|
3198
|
+
if e.response.status_code == status.HTTP_404_NOT_FOUND:
|
3199
|
+
raise prefect.exceptions.ObjectNotFound(http_exc=e) from e
|
3158
3200
|
|
3159
3201
|
automations = await self.read_automations()
|
3160
3202
|
|
@@ -3180,6 +3222,34 @@ class PrefectClient:
|
|
3180
3222
|
response.raise_for_status()
|
3181
3223
|
return Automation.parse_obj(response.json())
|
3182
3224
|
|
3225
|
+
async def read_automations_by_name(self, name: str) -> List[Automation]:
|
3226
|
+
"""
|
3227
|
+
Query the Prefect API for an automation by name. Only automations matching the provided name will be returned.
|
3228
|
+
|
3229
|
+
Args:
|
3230
|
+
name: the name of the automation to query
|
3231
|
+
|
3232
|
+
Returns:
|
3233
|
+
a list of Automation model representations of the automations
|
3234
|
+
"""
|
3235
|
+
if not self.server_type.supports_automations():
|
3236
|
+
self._raise_for_unsupported_automations()
|
3237
|
+
automation_filter = filters.AutomationFilter(name=dict(any_=[name]))
|
3238
|
+
|
3239
|
+
response = await self._client.post(
|
3240
|
+
"/automations/filter",
|
3241
|
+
json={
|
3242
|
+
"sort": sorting.AutomationSort.UPDATED_DESC,
|
3243
|
+
"automations": automation_filter.dict(json_compatible=True)
|
3244
|
+
if automation_filter
|
3245
|
+
else None,
|
3246
|
+
},
|
3247
|
+
)
|
3248
|
+
|
3249
|
+
response.raise_for_status()
|
3250
|
+
|
3251
|
+
return pydantic.parse_obj_as(List[Automation], response.json())
|
3252
|
+
|
3183
3253
|
async def pause_automation(self, automation_id: UUID):
|
3184
3254
|
if not self.server_type.supports_automations():
|
3185
3255
|
self._raise_for_unsupported_automations()
|
@@ -3284,3 +3354,212 @@ class PrefectClient:
|
|
3284
3354
|
|
3285
3355
|
def __exit__(self, *_):
|
3286
3356
|
assert False, "This should never be called but must be defined for __enter__"
|
3357
|
+
|
3358
|
+
|
3359
|
+
class SyncPrefectClient:
|
3360
|
+
"""
|
3361
|
+
A synchronous client for interacting with the [Prefect REST API](/api-ref/rest-api/).
|
3362
|
+
|
3363
|
+
Args:
|
3364
|
+
api: the REST API URL or FastAPI application to connect to
|
3365
|
+
api_key: An optional API key for authentication.
|
3366
|
+
api_version: The API version this client is compatible with.
|
3367
|
+
httpx_settings: An optional dictionary of settings to pass to the underlying
|
3368
|
+
`httpx.AsyncClient`
|
3369
|
+
|
3370
|
+
Examples:
|
3371
|
+
|
3372
|
+
Say hello to a Prefect REST API
|
3373
|
+
|
3374
|
+
<div class="terminal">
|
3375
|
+
```
|
3376
|
+
>>> with get_client(sync_client=True) as client:
|
3377
|
+
>>> response = client.hello()
|
3378
|
+
>>>
|
3379
|
+
>>> print(response.json())
|
3380
|
+
👋
|
3381
|
+
```
|
3382
|
+
</div>
|
3383
|
+
"""
|
3384
|
+
|
3385
|
+
def __init__(
|
3386
|
+
self,
|
3387
|
+
api: Union[str, ASGIApp],
|
3388
|
+
*,
|
3389
|
+
api_key: Optional[str] = None,
|
3390
|
+
api_version: Optional[str] = None,
|
3391
|
+
httpx_settings: Optional[Dict[str, Any]] = None,
|
3392
|
+
) -> None:
|
3393
|
+
self._prefect_client = PrefectClient(
|
3394
|
+
api=api,
|
3395
|
+
api_key=api_key,
|
3396
|
+
api_version=api_version,
|
3397
|
+
httpx_settings=httpx_settings,
|
3398
|
+
)
|
3399
|
+
|
3400
|
+
def __enter__(self):
|
3401
|
+
run_sync(self._prefect_client.__aenter__())
|
3402
|
+
return self
|
3403
|
+
|
3404
|
+
def __exit__(self, *exc_info):
|
3405
|
+
return run_sync(self._prefect_client.__aexit__(*exc_info))
|
3406
|
+
|
3407
|
+
async def __aenter__(self):
|
3408
|
+
raise RuntimeError(
|
3409
|
+
"The `SyncPrefectClient` must be entered with a sync context. Use '"
|
3410
|
+
"with SyncPrefectClient(...)' not 'async with SyncPrefectClient(...)'"
|
3411
|
+
)
|
3412
|
+
|
3413
|
+
async def __aexit__(self, *_):
|
3414
|
+
assert False, "This should never be called but must be defined for __aenter__"
|
3415
|
+
|
3416
|
+
def hello(self) -> httpx.Response:
|
3417
|
+
"""
|
3418
|
+
Send a GET request to /hello for testing purposes.
|
3419
|
+
"""
|
3420
|
+
return run_sync(self._prefect_client.hello())
|
3421
|
+
|
3422
|
+
def create_task_run(
|
3423
|
+
self,
|
3424
|
+
task: "TaskObject[P, R]",
|
3425
|
+
flow_run_id: Optional[UUID],
|
3426
|
+
dynamic_key: str,
|
3427
|
+
name: Optional[str] = None,
|
3428
|
+
extra_tags: Optional[Iterable[str]] = None,
|
3429
|
+
state: Optional[prefect.states.State[R]] = None,
|
3430
|
+
task_inputs: Optional[
|
3431
|
+
Dict[
|
3432
|
+
str,
|
3433
|
+
List[
|
3434
|
+
Union[
|
3435
|
+
TaskRunResult,
|
3436
|
+
Parameter,
|
3437
|
+
Constant,
|
3438
|
+
]
|
3439
|
+
],
|
3440
|
+
]
|
3441
|
+
] = None,
|
3442
|
+
) -> TaskRun:
|
3443
|
+
"""
|
3444
|
+
Create a task run
|
3445
|
+
|
3446
|
+
Args:
|
3447
|
+
task: The Task to run
|
3448
|
+
flow_run_id: The flow run id with which to associate the task run
|
3449
|
+
dynamic_key: A key unique to this particular run of a Task within the flow
|
3450
|
+
name: An optional name for the task run
|
3451
|
+
extra_tags: an optional list of extra tags to apply to the task run in
|
3452
|
+
addition to `task.tags`
|
3453
|
+
state: The initial state for the run. If not provided, defaults to
|
3454
|
+
`Pending` for now. Should always be a `Scheduled` type.
|
3455
|
+
task_inputs: the set of inputs passed to the task
|
3456
|
+
|
3457
|
+
Returns:
|
3458
|
+
The created task run.
|
3459
|
+
"""
|
3460
|
+
return run_sync(
|
3461
|
+
self._prefect_client.create_task_run(
|
3462
|
+
task=task,
|
3463
|
+
flow_run_id=flow_run_id,
|
3464
|
+
dynamic_key=dynamic_key,
|
3465
|
+
name=name,
|
3466
|
+
extra_tags=extra_tags,
|
3467
|
+
state=state,
|
3468
|
+
task_inputs=task_inputs,
|
3469
|
+
)
|
3470
|
+
)
|
3471
|
+
|
3472
|
+
def set_task_run_state(
|
3473
|
+
self,
|
3474
|
+
task_run_id: UUID,
|
3475
|
+
state: prefect.states.State,
|
3476
|
+
force: bool = False,
|
3477
|
+
) -> OrchestrationResult:
|
3478
|
+
"""
|
3479
|
+
Set the state of a task run.
|
3480
|
+
|
3481
|
+
Args:
|
3482
|
+
task_run_id: the id of the task run
|
3483
|
+
state: the state to set
|
3484
|
+
force: if True, disregard orchestration logic when setting the state,
|
3485
|
+
forcing the Prefect API to accept the state
|
3486
|
+
|
3487
|
+
Returns:
|
3488
|
+
an OrchestrationResult model representation of state orchestration output
|
3489
|
+
"""
|
3490
|
+
return run_sync(
|
3491
|
+
self._prefect_client.set_task_run_state(
|
3492
|
+
task_run_id=task_run_id,
|
3493
|
+
state=state,
|
3494
|
+
force=force,
|
3495
|
+
)
|
3496
|
+
)
|
3497
|
+
|
3498
|
+
def create_flow_run(
|
3499
|
+
self,
|
3500
|
+
flow_id: UUID,
|
3501
|
+
parameters: Optional[Dict[str, Any]] = None,
|
3502
|
+
context: Optional[Dict[str, Any]] = None,
|
3503
|
+
scheduled_start_time: Optional[datetime.datetime] = None,
|
3504
|
+
run_name: Optional[str] = None,
|
3505
|
+
labels: Optional[List[str]] = None,
|
3506
|
+
parameters_json: Optional[str] = None,
|
3507
|
+
run_config: Optional[Dict[str, Any]] = None,
|
3508
|
+
idempotency_key: Optional[str] = None,
|
3509
|
+
) -> FlowRunResponse:
|
3510
|
+
"""
|
3511
|
+
Create a new flow run.
|
3512
|
+
|
3513
|
+
Args:
|
3514
|
+
- flow_id (UUID): the ID of the flow to create a run for
|
3515
|
+
- parameters (Optional[Dict[str, Any]]): a dictionary of parameter values to pass to the flow
|
3516
|
+
- context (Optional[Dict[str, Any]]): a dictionary of context values to pass to the flow
|
3517
|
+
- scheduled_start_time (Optional[datetime.datetime]): the scheduled start time for the flow run
|
3518
|
+
- run_name (Optional[str]): a name to assign to the flow run
|
3519
|
+
- labels (Optional[List[str]]): a list of labels to assign to the flow run
|
3520
|
+
- parameters_json (Optional[str]): a JSON string of parameter values to pass to the flow
|
3521
|
+
- run_config (Optional[Dict[str, Any]]): a dictionary of run configuration options
|
3522
|
+
- idempotency_key (Optional[str]): a key to ensure idempotency when creating the flow run
|
3523
|
+
|
3524
|
+
Returns:
|
3525
|
+
- FlowRunResponse: the created flow run
|
3526
|
+
"""
|
3527
|
+
return run_sync(
|
3528
|
+
self._prefect_client.create_flow_run(
|
3529
|
+
flow_id=flow_id,
|
3530
|
+
parameters=parameters,
|
3531
|
+
context=context,
|
3532
|
+
scheduled_start_time=scheduled_start_time,
|
3533
|
+
run_name=run_name,
|
3534
|
+
labels=labels,
|
3535
|
+
parameters_json=parameters_json,
|
3536
|
+
run_config=run_config,
|
3537
|
+
idempotency_key=idempotency_key,
|
3538
|
+
)
|
3539
|
+
)
|
3540
|
+
|
3541
|
+
async def set_flow_run_state(
|
3542
|
+
self,
|
3543
|
+
flow_run_id: UUID,
|
3544
|
+
state: "prefect.states.State",
|
3545
|
+
force: bool = False,
|
3546
|
+
) -> OrchestrationResult:
|
3547
|
+
"""
|
3548
|
+
Set the state of a flow run.
|
3549
|
+
|
3550
|
+
Args:
|
3551
|
+
flow_run_id: the id of the flow run
|
3552
|
+
state: the state to set
|
3553
|
+
force: if True, disregard orchestration logic when setting the state,
|
3554
|
+
forcing the Prefect API to accept the state
|
3555
|
+
|
3556
|
+
Returns:
|
3557
|
+
an OrchestrationResult model representation of state orchestration output
|
3558
|
+
"""
|
3559
|
+
return run_sync(
|
3560
|
+
self._prefect_client.set_flow_run_state(
|
3561
|
+
flow_run_id=flow_run_id,
|
3562
|
+
state=state,
|
3563
|
+
force=force,
|
3564
|
+
)
|
3565
|
+
)
|
@@ -53,6 +53,7 @@ if TYPE_CHECKING:
|
|
53
53
|
from prefect.deprecated.data_documents import DataDocument
|
54
54
|
from prefect.results import BaseResult
|
55
55
|
|
56
|
+
|
56
57
|
R = TypeVar("R")
|
57
58
|
|
58
59
|
|
@@ -120,20 +121,20 @@ class WorkQueueStatus(AutoEnum):
|
|
120
121
|
|
121
122
|
|
122
123
|
class StateDetails(PrefectBaseModel):
|
123
|
-
flow_run_id: UUID = None
|
124
|
-
task_run_id: UUID = None
|
124
|
+
flow_run_id: Optional[UUID] = None
|
125
|
+
task_run_id: Optional[UUID] = None
|
125
126
|
# for task runs that represent subflows, the subflow's run ID
|
126
|
-
child_flow_run_id: UUID = None
|
127
|
+
child_flow_run_id: Optional[UUID] = None
|
127
128
|
scheduled_time: DateTimeTZ = None
|
128
|
-
cache_key: str = None
|
129
|
+
cache_key: Optional[str] = None
|
129
130
|
cache_expiration: DateTimeTZ = None
|
130
131
|
untrackable_result: bool = False
|
131
132
|
pause_timeout: DateTimeTZ = None
|
132
133
|
pause_reschedule: bool = False
|
133
|
-
pause_key: str = None
|
134
|
+
pause_key: Optional[str] = None
|
134
135
|
run_input_keyset: Optional[Dict[str, str]] = None
|
135
|
-
refresh_cache: bool = None
|
136
|
-
retriable: bool = None
|
136
|
+
refresh_cache: Optional[bool] = None
|
137
|
+
retriable: Optional[bool] = None
|
137
138
|
transition_id: Optional[UUID] = None
|
138
139
|
task_parameters_id: Optional[UUID] = None
|
139
140
|
|
@@ -160,7 +161,9 @@ class State(ObjectBaseModel, Generic[R]):
|
|
160
161
|
def result(self: "State[R]", raise_on_failure: bool = False) -> Union[R, Exception]:
|
161
162
|
...
|
162
163
|
|
163
|
-
def result(
|
164
|
+
def result(
|
165
|
+
self, raise_on_failure: bool = True, fetch: Optional[bool] = None
|
166
|
+
) -> Union[R, Exception]:
|
164
167
|
"""
|
165
168
|
Retrieve the result attached to this state.
|
166
169
|
|
@@ -27,6 +27,15 @@ class TaskRunSort(AutoEnum):
|
|
27
27
|
END_TIME_DESC = AutoEnum.auto()
|
28
28
|
|
29
29
|
|
30
|
+
class AutomationSort(AutoEnum):
|
31
|
+
"""Defines automation sorting options."""
|
32
|
+
|
33
|
+
CREATED_DESC = AutoEnum.auto()
|
34
|
+
UPDATED_DESC = AutoEnum.auto()
|
35
|
+
NAME_ASC = AutoEnum.auto()
|
36
|
+
NAME_DESC = AutoEnum.auto()
|
37
|
+
|
38
|
+
|
30
39
|
class LogSort(AutoEnum):
|
31
40
|
"""Defines log sorting options."""
|
32
41
|
|
prefect/client/utilities.py
CHANGED
@@ -6,14 +6,25 @@ Utilities for working with clients.
|
|
6
6
|
# circular imports for decorators such as `inject_client` which are widely used.
|
7
7
|
|
8
8
|
from functools import wraps
|
9
|
-
from typing import
|
10
|
-
|
11
|
-
|
9
|
+
from typing import (
|
10
|
+
TYPE_CHECKING,
|
11
|
+
Any,
|
12
|
+
Awaitable,
|
13
|
+
Callable,
|
14
|
+
Coroutine,
|
15
|
+
Optional,
|
16
|
+
Tuple,
|
17
|
+
TypeVar,
|
18
|
+
cast,
|
19
|
+
)
|
20
|
+
|
21
|
+
from typing_extensions import Concatenate, ParamSpec
|
12
22
|
|
13
23
|
if TYPE_CHECKING:
|
14
24
|
from prefect.client.orchestration import PrefectClient
|
15
25
|
|
16
26
|
P = ParamSpec("P")
|
27
|
+
R = TypeVar("R")
|
17
28
|
|
18
29
|
|
19
30
|
def get_or_create_client(
|
@@ -52,6 +63,17 @@ def get_or_create_client(
|
|
52
63
|
return get_httpx_client(), False
|
53
64
|
|
54
65
|
|
66
|
+
def client_injector(
|
67
|
+
func: Callable[Concatenate["PrefectClient", P], Awaitable[R]],
|
68
|
+
) -> Callable[P, Awaitable[R]]:
|
69
|
+
@wraps(func)
|
70
|
+
async def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
|
71
|
+
client, _ = get_or_create_client()
|
72
|
+
return await func(client, *args, **kwargs)
|
73
|
+
|
74
|
+
return wrapper
|
75
|
+
|
76
|
+
|
55
77
|
def inject_client(
|
56
78
|
fn: Callable[P, Coroutine[Any, Any, Any]],
|
57
79
|
) -> Callable[P, Coroutine[Any, Any, Any]]:
|
prefect/concurrency/asyncio.py
CHANGED
@@ -1,10 +1,16 @@
|
|
1
1
|
import asyncio
|
2
2
|
from contextlib import asynccontextmanager
|
3
|
-
from typing import List, Literal, Union
|
3
|
+
from typing import List, Literal, Union, cast
|
4
4
|
|
5
5
|
import httpx
|
6
6
|
import pendulum
|
7
7
|
|
8
|
+
try:
|
9
|
+
from pendulum import Interval
|
10
|
+
except ImportError:
|
11
|
+
# pendulum < 3
|
12
|
+
from pendulum.period import Period as Interval # type: ignore
|
13
|
+
|
8
14
|
from prefect import get_client
|
9
15
|
from prefect.client.schemas.responses import MinimalConcurrencyLimitResponse
|
10
16
|
|
@@ -30,10 +36,10 @@ async def concurrency(names: Union[str, List[str]], occupy: int = 1):
|
|
30
36
|
try:
|
31
37
|
yield
|
32
38
|
finally:
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
occupancy_period = cast(Interval, (pendulum.now("UTC") - acquisition_time))
|
40
|
+
await _release_concurrency_slots(
|
41
|
+
names, occupy, occupancy_period.total_seconds()
|
42
|
+
)
|
37
43
|
_emit_concurrency_release_events(limits, occupy, emitted_events)
|
38
44
|
|
39
45
|
|
prefect/concurrency/events.py
CHANGED
@@ -12,11 +12,11 @@ def _emit_concurrency_event(
|
|
12
12
|
slots: int,
|
13
13
|
follows: Union[Event, None] = None,
|
14
14
|
) -> Union[Event, None]:
|
15
|
-
resource = {
|
15
|
+
resource: Dict[str, str] = {
|
16
16
|
"prefect.resource.id": f"prefect.concurrency-limit.{primary_limit.id}",
|
17
17
|
"prefect.resource.name": primary_limit.name,
|
18
|
-
"slots-acquired": slots,
|
19
|
-
"limit": primary_limit.limit,
|
18
|
+
"slots-acquired": str(slots),
|
19
|
+
"limit": str(primary_limit.limit),
|
20
20
|
}
|
21
21
|
|
22
22
|
related = [
|
prefect/concurrency/services.py
CHANGED
@@ -64,7 +64,7 @@ class ConcurrencySlotAcquisitionService(QueueService):
|
|
64
64
|
raise RuntimeError("Cannot put items in a stopped service instance.")
|
65
65
|
|
66
66
|
logger.debug("Service %r enqueuing item %r", self, item)
|
67
|
-
future = concurrent.futures.Future()
|
67
|
+
future: concurrent.futures.Future = concurrent.futures.Future()
|
68
68
|
|
69
69
|
occupy, mode = item
|
70
70
|
self._queue.put_nowait((occupy, mode, future))
|
prefect/concurrency/sync.py
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
from contextlib import contextmanager
|
2
|
-
from typing import List, Union
|
2
|
+
from typing import List, Union, cast
|
3
3
|
|
4
4
|
import pendulum
|
5
5
|
|
6
|
+
try:
|
7
|
+
from pendulum import Interval
|
8
|
+
except ImportError:
|
9
|
+
# pendulum < 3
|
10
|
+
from pendulum.period import Period as Interval # type: ignore
|
11
|
+
|
6
12
|
from prefect._internal.concurrency.api import create_call, from_sync
|
7
13
|
from prefect._internal.concurrency.event_loop import get_running_loop
|
8
14
|
from prefect.client.schemas.responses import MinimalConcurrencyLimitResponse
|
@@ -30,11 +36,9 @@ def concurrency(names: Union[str, List[str]], occupy: int = 1):
|
|
30
36
|
try:
|
31
37
|
yield
|
32
38
|
finally:
|
33
|
-
|
34
|
-
pendulum.now("UTC") - acquisition_time
|
35
|
-
).total_seconds()
|
39
|
+
occupancy_period = cast(Interval, pendulum.now("UTC") - acquisition_time)
|
36
40
|
_call_async_function_from_sync(
|
37
|
-
_release_concurrency_slots, names, occupy,
|
41
|
+
_release_concurrency_slots, names, occupy, occupancy_period.total_seconds()
|
38
42
|
)
|
39
43
|
_emit_concurrency_release_events(limits, occupy, emitted_events)
|
40
44
|
|