prefect-client 3.1.6__py3-none-any.whl → 3.1.7__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/__init__.py +0 -0
- prefect/_experimental/lineage.py +181 -0
- prefect/_internal/compatibility/async_dispatch.py +38 -9
- prefect/_internal/pydantic/v2_validated_func.py +15 -10
- prefect/_internal/retries.py +15 -6
- prefect/_internal/schemas/bases.py +2 -1
- prefect/_internal/schemas/validators.py +5 -4
- prefect/_version.py +3 -3
- prefect/blocks/core.py +144 -17
- prefect/blocks/system.py +2 -1
- prefect/client/orchestration.py +88 -0
- prefect/client/schemas/actions.py +5 -5
- prefect/client/schemas/filters.py +1 -1
- prefect/client/schemas/objects.py +5 -5
- prefect/client/schemas/responses.py +1 -2
- prefect/client/schemas/schedules.py +1 -1
- prefect/client/subscriptions.py +2 -1
- prefect/client/utilities.py +15 -1
- prefect/context.py +1 -1
- prefect/deployments/flow_runs.py +3 -3
- prefect/deployments/runner.py +14 -14
- prefect/deployments/steps/core.py +3 -1
- prefect/deployments/steps/pull.py +60 -12
- prefect/events/clients.py +55 -4
- prefect/events/filters.py +1 -1
- prefect/events/related.py +2 -1
- prefect/events/schemas/events.py +1 -1
- prefect/events/utilities.py +2 -0
- prefect/events/worker.py +8 -0
- prefect/flow_engine.py +41 -81
- prefect/flow_runs.py +4 -2
- prefect/flows.py +4 -6
- prefect/results.py +43 -22
- prefect/runner/storage.py +3 -3
- prefect/serializers.py +28 -24
- prefect/settings/models/experiments.py +5 -0
- prefect/task_engine.py +34 -26
- prefect/task_worker.py +43 -25
- prefect/tasks.py +118 -125
- prefect/telemetry/instrumentation.py +1 -1
- prefect/telemetry/processors.py +10 -7
- prefect/telemetry/run_telemetry.py +157 -33
- prefect/types/__init__.py +4 -1
- prefect/variables.py +127 -19
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/METADATA +2 -1
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/RECORD +49 -47
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.6.dist-info → prefect_client-3.1.7.dist-info}/top_level.txt +0 -0
prefect/task_worker.py
CHANGED
@@ -7,7 +7,7 @@ import sys
|
|
7
7
|
from concurrent.futures import ThreadPoolExecutor
|
8
8
|
from contextlib import AsyncExitStack
|
9
9
|
from contextvars import copy_context
|
10
|
-
from typing import Optional
|
10
|
+
from typing import TYPE_CHECKING, Any, Optional
|
11
11
|
from uuid import UUID
|
12
12
|
|
13
13
|
import anyio
|
@@ -16,6 +16,7 @@ import pendulum
|
|
16
16
|
import uvicorn
|
17
17
|
from exceptiongroup import BaseExceptionGroup # novermin
|
18
18
|
from fastapi import FastAPI
|
19
|
+
from typing_extensions import ParamSpec, TypeVar
|
19
20
|
from websockets.exceptions import InvalidStatusCode
|
20
21
|
|
21
22
|
from prefect import Task
|
@@ -35,12 +36,17 @@ from prefect.task_engine import run_task_async, run_task_sync
|
|
35
36
|
from prefect.utilities.annotations import NotSet
|
36
37
|
from prefect.utilities.asyncutils import asyncnullcontext, sync_compatible
|
37
38
|
from prefect.utilities.engine import emit_task_run_state_change_event
|
38
|
-
from prefect.utilities.processutils import
|
39
|
+
from prefect.utilities.processutils import (
|
40
|
+
_register_signal, # pyright: ignore[reportPrivateUsage]
|
41
|
+
)
|
39
42
|
from prefect.utilities.services import start_client_metrics_server
|
40
43
|
from prefect.utilities.urls import url_for
|
41
44
|
|
42
45
|
logger = get_logger("task_worker")
|
43
46
|
|
47
|
+
P = ParamSpec("P")
|
48
|
+
R = TypeVar("R", infer_variance=True)
|
49
|
+
|
44
50
|
|
45
51
|
class StopTaskWorker(Exception):
|
46
52
|
"""Raised when the task worker is stopped."""
|
@@ -48,8 +54,10 @@ class StopTaskWorker(Exception):
|
|
48
54
|
pass
|
49
55
|
|
50
56
|
|
51
|
-
def should_try_to_read_parameters(task: Task, task_run: TaskRun) -> bool:
|
57
|
+
def should_try_to_read_parameters(task: Task[P, R], task_run: TaskRun) -> bool:
|
52
58
|
"""Determines whether a task run should read parameters from the result store."""
|
59
|
+
if TYPE_CHECKING:
|
60
|
+
assert task_run.state is not None
|
53
61
|
new_enough_state_details = hasattr(
|
54
62
|
task_run.state.state_details, "task_parameters_id"
|
55
63
|
)
|
@@ -76,20 +84,23 @@ class TaskWorker:
|
|
76
84
|
|
77
85
|
def __init__(
|
78
86
|
self,
|
79
|
-
*tasks: Task,
|
87
|
+
*tasks: Task[P, R],
|
80
88
|
limit: Optional[int] = 10,
|
81
89
|
):
|
82
|
-
self.tasks = []
|
90
|
+
self.tasks: list["Task[..., Any]"] = []
|
83
91
|
for t in tasks:
|
84
|
-
if
|
85
|
-
if t
|
86
|
-
|
87
|
-
t.with_options(persist_result=True, cache_policy=DEFAULT)
|
88
|
-
)
|
89
|
-
else:
|
90
|
-
self.tasks.append(t.with_options(persist_result=True))
|
92
|
+
if not TYPE_CHECKING:
|
93
|
+
if not isinstance(t, Task):
|
94
|
+
continue
|
91
95
|
|
92
|
-
|
96
|
+
if t.cache_policy in [None, NONE, NotSet]:
|
97
|
+
self.tasks.append(
|
98
|
+
t.with_options(persist_result=True, cache_policy=DEFAULT)
|
99
|
+
)
|
100
|
+
else:
|
101
|
+
self.tasks.append(t.with_options(persist_result=True))
|
102
|
+
|
103
|
+
self.task_keys = set(t.task_key for t in tasks if isinstance(t, Task)) # pyright: ignore[reportUnnecessaryIsInstance]
|
93
104
|
|
94
105
|
self._started_at: Optional[pendulum.DateTime] = None
|
95
106
|
self.stopping: bool = False
|
@@ -97,7 +108,9 @@ class TaskWorker:
|
|
97
108
|
self._client = get_client()
|
98
109
|
self._exit_stack = AsyncExitStack()
|
99
110
|
|
100
|
-
|
111
|
+
try:
|
112
|
+
asyncio.get_running_loop()
|
113
|
+
except RuntimeError:
|
101
114
|
raise RuntimeError(
|
102
115
|
"TaskWorker must be initialized within an async context."
|
103
116
|
)
|
@@ -141,7 +154,7 @@ class TaskWorker:
|
|
141
154
|
def available_tasks(self) -> Optional[int]:
|
142
155
|
return int(self._limiter.available_tokens) if self._limiter else None
|
143
156
|
|
144
|
-
def handle_sigterm(self, signum, frame):
|
157
|
+
def handle_sigterm(self, signum: int, frame: object):
|
145
158
|
"""
|
146
159
|
Shuts down the task worker when a SIGTERM is received.
|
147
160
|
"""
|
@@ -252,6 +265,8 @@ class TaskWorker:
|
|
252
265
|
self._release_token(task_run.id)
|
253
266
|
|
254
267
|
async def _submit_scheduled_task_run(self, task_run: TaskRun):
|
268
|
+
if TYPE_CHECKING:
|
269
|
+
assert task_run.state is not None
|
255
270
|
logger.debug(
|
256
271
|
f"Found task run: {task_run.name!r} in state: {task_run.state.name!r}"
|
257
272
|
)
|
@@ -280,7 +295,7 @@ class TaskWorker:
|
|
280
295
|
result_storage=await get_or_create_default_task_scheduling_storage()
|
281
296
|
).update_for_task(task)
|
282
297
|
try:
|
283
|
-
run_data = await store.read_parameters(parameters_id)
|
298
|
+
run_data: dict[str, Any] = await store.read_parameters(parameters_id)
|
284
299
|
parameters = run_data.get("parameters", {})
|
285
300
|
wait_for = run_data.get("wait_for", [])
|
286
301
|
run_context = run_data.get("context", None)
|
@@ -350,7 +365,7 @@ class TaskWorker:
|
|
350
365
|
async def __aenter__(self):
|
351
366
|
logger.debug("Starting task worker...")
|
352
367
|
|
353
|
-
if self._client._closed:
|
368
|
+
if self._client._closed: # pyright: ignore[reportPrivateUsage]
|
354
369
|
self._client = get_client()
|
355
370
|
self._runs_task_group = anyio.create_task_group()
|
356
371
|
|
@@ -362,7 +377,7 @@ class TaskWorker:
|
|
362
377
|
self._started_at = pendulum.now()
|
363
378
|
return self
|
364
379
|
|
365
|
-
async def __aexit__(self, *exc_info):
|
380
|
+
async def __aexit__(self, *exc_info: Any) -> None:
|
366
381
|
logger.debug("Stopping task worker...")
|
367
382
|
self._started_at = None
|
368
383
|
await self._exit_stack.__aexit__(*exc_info)
|
@@ -372,7 +387,9 @@ def create_status_server(task_worker: TaskWorker) -> FastAPI:
|
|
372
387
|
status_app = FastAPI()
|
373
388
|
|
374
389
|
@status_app.get("/status")
|
375
|
-
def status():
|
390
|
+
def status(): # pyright: ignore[reportUnusedFunction]
|
391
|
+
if TYPE_CHECKING:
|
392
|
+
assert task_worker.started_at is not None
|
376
393
|
return {
|
377
394
|
"client_id": task_worker.client_id,
|
378
395
|
"started_at": task_worker.started_at.isoformat(),
|
@@ -393,11 +410,13 @@ def create_status_server(task_worker: TaskWorker) -> FastAPI:
|
|
393
410
|
|
394
411
|
@sync_compatible
|
395
412
|
async def serve(
|
396
|
-
*tasks: Task,
|
413
|
+
*tasks: Task[P, R],
|
414
|
+
limit: Optional[int] = 10,
|
415
|
+
status_server_port: Optional[int] = None,
|
397
416
|
):
|
398
|
-
"""Serve the provided tasks so that their runs may be submitted to
|
399
|
-
in the engine. Tasks do not need to be within a flow run context to be
|
400
|
-
You must `.submit` the same task object that you pass to `serve`.
|
417
|
+
"""Serve the provided tasks so that their runs may be submitted to
|
418
|
+
and executed in the engine. Tasks do not need to be within a flow run context to be
|
419
|
+
submitted. You must `.submit` the same task object that you pass to `serve`.
|
401
420
|
|
402
421
|
Args:
|
403
422
|
- tasks: A list of tasks to serve. When a scheduled task run is found for a
|
@@ -422,8 +441,7 @@ async def serve(
|
|
422
441
|
print(message.upper())
|
423
442
|
|
424
443
|
# starts a long-lived process that listens for scheduled runs of these tasks
|
425
|
-
|
426
|
-
serve(say, yell)
|
444
|
+
serve(say, yell)
|
427
445
|
```
|
428
446
|
"""
|
429
447
|
task_worker = TaskWorker(*tasks, limit=limit)
|