prefect-client 3.2.13__py3-none-any.whl → 3.2.15__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/_build_info.py +3 -3
- prefect/_experimental/bundles.py +7 -1
- prefect/_internal/concurrency/services.py +13 -3
- prefect/cache_policies.py +31 -2
- prefect/client/orchestration/_flow_runs/client.py +34 -4
- prefect/client/schemas/actions.py +14 -1
- prefect/client/schemas/objects.py +18 -0
- prefect/deployments/runner.py +1 -9
- prefect/docker/docker_image.py +2 -1
- prefect/flow_engine.py +11 -5
- prefect/flow_runs.py +1 -1
- prefect/flows.py +27 -9
- prefect/locking/memory.py +16 -8
- prefect/logging/__init__.py +1 -1
- prefect/logging/configuration.py +6 -4
- prefect/logging/formatters.py +3 -3
- prefect/logging/handlers.py +37 -26
- prefect/results.py +9 -3
- prefect/runner/__init__.py +2 -0
- prefect/runner/runner.py +1 -1
- prefect/runner/server.py +12 -7
- prefect/runner/storage.py +37 -37
- prefect/runner/submit.py +36 -25
- prefect/runner/utils.py +9 -5
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +4 -4
- prefect/server/api/flow_runs.py +21 -0
- prefect/server/api/task_runs.py +52 -1
- prefect/settings/models/tasks.py +5 -0
- prefect/task_engine.py +18 -24
- prefect/tasks.py +31 -8
- prefect/transactions.py +5 -0
- prefect/utilities/callables.py +2 -0
- prefect/utilities/engine.py +2 -2
- prefect/utilities/importtools.py +6 -9
- prefect/workers/base.py +9 -4
- {prefect_client-3.2.13.dist-info → prefect_client-3.2.15.dist-info}/METADATA +3 -2
- {prefect_client-3.2.13.dist-info → prefect_client-3.2.15.dist-info}/RECORD +39 -39
- {prefect_client-3.2.13.dist-info → prefect_client-3.2.15.dist-info}/WHEEL +0 -0
- {prefect_client-3.2.13.dist-info → prefect_client-3.2.15.dist-info}/licenses/LICENSE +0 -0
prefect/runner/utils.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
from copy import deepcopy
|
2
|
-
from typing import Any
|
4
|
+
from typing import Any, Hashable
|
3
5
|
|
4
6
|
from fastapi import FastAPI
|
5
7
|
from fastapi.openapi.utils import get_openapi
|
@@ -8,7 +10,7 @@ from prefect import __version__ as PREFECT_VERSION
|
|
8
10
|
|
9
11
|
|
10
12
|
def inject_schemas_into_openapi(
|
11
|
-
webserver: FastAPI, schemas_to_inject: dict[
|
13
|
+
webserver: FastAPI, schemas_to_inject: dict[Hashable, Any]
|
12
14
|
) -> dict[str, Any]:
|
13
15
|
"""
|
14
16
|
Augments the webserver's OpenAPI schema with additional schemas from deployments / flows / tasks.
|
@@ -29,7 +31,7 @@ def inject_schemas_into_openapi(
|
|
29
31
|
|
30
32
|
|
31
33
|
def merge_definitions(
|
32
|
-
injected_schemas: dict[
|
34
|
+
injected_schemas: dict[Hashable, Any], openapi_schema: dict[str, Any]
|
33
35
|
) -> dict[str, Any]:
|
34
36
|
"""
|
35
37
|
Integrates definitions from injected schemas into the OpenAPI components.
|
@@ -51,7 +53,9 @@ def merge_definitions(
|
|
51
53
|
return openapi_schema_copy
|
52
54
|
|
53
55
|
|
54
|
-
def update_refs_in_schema(
|
56
|
+
def update_refs_in_schema(
|
57
|
+
schema_item: dict[str, Any] | list[Any], new_ref: str
|
58
|
+
) -> None:
|
55
59
|
"""
|
56
60
|
Recursively replaces `$ref` with a new reference base in a schema item.
|
57
61
|
|
@@ -64,7 +68,7 @@ def update_refs_in_schema(schema_item: Any, new_ref: str) -> None:
|
|
64
68
|
schema_item["$ref"] = schema_item["$ref"].replace("#/definitions/", new_ref)
|
65
69
|
for value in schema_item.values():
|
66
70
|
update_refs_in_schema(value, new_ref)
|
67
|
-
elif isinstance(schema_item, list):
|
71
|
+
elif isinstance(schema_item, list): # pyright: ignore[reportUnnecessaryIsInstance]
|
68
72
|
for item in schema_item:
|
69
73
|
update_refs_in_schema(item, new_ref)
|
70
74
|
|
@@ -559,7 +559,7 @@
|
|
559
559
|
"description": "To use any private container registry with a username and password, choose DockerRegistry. To use a private Azure Container Registry with a managed identity, choose ACRManagedIdentity.",
|
560
560
|
"anyOf": [
|
561
561
|
{
|
562
|
-
"$ref": "#/definitions/
|
562
|
+
"$ref": "#/definitions/DockerRegistryCredentials"
|
563
563
|
},
|
564
564
|
{
|
565
565
|
"$ref": "#/definitions/ACRManagedIdentity"
|
@@ -637,8 +637,8 @@
|
|
637
637
|
"subscription_id"
|
638
638
|
],
|
639
639
|
"definitions": {
|
640
|
-
"
|
641
|
-
"title": "
|
640
|
+
"DockerRegistryCredentials": {
|
641
|
+
"title": "DockerRegistryCredentials",
|
642
642
|
"description": "Connects to a Docker registry.\n\nRequires a Docker Engine to be connectable.",
|
643
643
|
"type": "object",
|
644
644
|
"properties": {
|
@@ -671,7 +671,7 @@
|
|
671
671
|
"password",
|
672
672
|
"registry_url"
|
673
673
|
],
|
674
|
-
"block_type_slug": "docker-registry",
|
674
|
+
"block_type_slug": "docker-registry-credentials",
|
675
675
|
"secret_fields": [
|
676
676
|
"password"
|
677
677
|
],
|
prefect/server/api/flow_runs.py
CHANGED
@@ -29,6 +29,7 @@ import prefect.server.schemas as schemas
|
|
29
29
|
from prefect.logging import get_logger
|
30
30
|
from prefect.server.api.run_history import run_history
|
31
31
|
from prefect.server.api.validation import validate_job_variables_for_deployment_flow_run
|
32
|
+
from prefect.server.api.workers import WorkerLookups
|
32
33
|
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
33
34
|
from prefect.server.exceptions import FlowRunGraphTooLarge
|
34
35
|
from prefect.server.models.flow_runs import (
|
@@ -68,6 +69,7 @@ async def create_flow_run(
|
|
68
69
|
orchestration_dependencies.provide_flow_orchestration_parameters
|
69
70
|
),
|
70
71
|
api_version: str = Depends(dependencies.provide_request_api_version),
|
72
|
+
worker_lookups: WorkerLookups = Depends(WorkerLookups),
|
71
73
|
) -> schemas.responses.FlowRunResponse:
|
72
74
|
"""
|
73
75
|
Create a flow run. If a flow run with the same flow_id and
|
@@ -91,6 +93,25 @@ async def create_flow_run(
|
|
91
93
|
right_now = now("UTC")
|
92
94
|
|
93
95
|
async with db.session_context(begin_transaction=True) as session:
|
96
|
+
if flow_run.work_pool_name:
|
97
|
+
if flow_run.work_queue_name:
|
98
|
+
work_queue_id = await worker_lookups._get_work_queue_id_from_name(
|
99
|
+
session=session,
|
100
|
+
work_pool_name=flow_run.work_pool_name,
|
101
|
+
work_queue_name=flow_run.work_queue_name,
|
102
|
+
)
|
103
|
+
else:
|
104
|
+
work_queue_id = (
|
105
|
+
await worker_lookups._get_default_work_queue_id_from_work_pool_name(
|
106
|
+
session=session,
|
107
|
+
work_pool_name=flow_run.work_pool_name,
|
108
|
+
)
|
109
|
+
)
|
110
|
+
else:
|
111
|
+
work_queue_id = None
|
112
|
+
|
113
|
+
flow_run_object.work_queue_id = work_queue_id
|
114
|
+
|
94
115
|
model = await models.flow_runs.create_flow_run(
|
95
116
|
session=session,
|
96
117
|
flow_run=flow_run_object,
|
prefect/server/api/task_runs.py
CHANGED
@@ -16,6 +16,7 @@ from fastapi import (
|
|
16
16
|
WebSocket,
|
17
17
|
status,
|
18
18
|
)
|
19
|
+
from fastapi.responses import ORJSONResponse
|
19
20
|
from starlette.websockets import WebSocketDisconnect
|
20
21
|
|
21
22
|
import prefect.server.api.dependencies as dependencies
|
@@ -27,7 +28,10 @@ from prefect.server.database import PrefectDBInterface, provide_database_interfa
|
|
27
28
|
from prefect.server.orchestration import dependencies as orchestration_dependencies
|
28
29
|
from prefect.server.orchestration.core_policy import CoreTaskPolicy
|
29
30
|
from prefect.server.orchestration.policies import TaskRunOrchestrationPolicy
|
30
|
-
from prefect.server.schemas.responses import
|
31
|
+
from prefect.server.schemas.responses import (
|
32
|
+
OrchestrationResult,
|
33
|
+
TaskRunPaginationResponse,
|
34
|
+
)
|
31
35
|
from prefect.server.task_queue import MultiQueue, TaskQueue
|
32
36
|
from prefect.server.utilities import subscriptions
|
33
37
|
from prefect.server.utilities.server import PrefectRouter
|
@@ -214,6 +218,53 @@ async def read_task_runs(
|
|
214
218
|
)
|
215
219
|
|
216
220
|
|
221
|
+
@router.post("/paginate", response_class=ORJSONResponse)
|
222
|
+
async def paginate_task_runs(
|
223
|
+
sort: schemas.sorting.TaskRunSort = Body(schemas.sorting.TaskRunSort.ID_DESC),
|
224
|
+
limit: int = dependencies.LimitBody(),
|
225
|
+
page: int = Body(1, ge=1),
|
226
|
+
flows: Optional[schemas.filters.FlowFilter] = None,
|
227
|
+
flow_runs: Optional[schemas.filters.FlowRunFilter] = None,
|
228
|
+
task_runs: Optional[schemas.filters.TaskRunFilter] = None,
|
229
|
+
deployments: Optional[schemas.filters.DeploymentFilter] = None,
|
230
|
+
db: PrefectDBInterface = Depends(provide_database_interface),
|
231
|
+
) -> TaskRunPaginationResponse:
|
232
|
+
"""
|
233
|
+
Pagination query for task runs.
|
234
|
+
"""
|
235
|
+
offset = (page - 1) * limit
|
236
|
+
|
237
|
+
async with db.session_context() as session:
|
238
|
+
runs = await models.task_runs.read_task_runs(
|
239
|
+
session=session,
|
240
|
+
flow_filter=flows,
|
241
|
+
flow_run_filter=flow_runs,
|
242
|
+
task_run_filter=task_runs,
|
243
|
+
deployment_filter=deployments,
|
244
|
+
offset=offset,
|
245
|
+
limit=limit,
|
246
|
+
sort=sort,
|
247
|
+
)
|
248
|
+
|
249
|
+
total_count = await models.task_runs.count_task_runs(
|
250
|
+
session=session,
|
251
|
+
flow_filter=flows,
|
252
|
+
flow_run_filter=flow_runs,
|
253
|
+
task_run_filter=task_runs,
|
254
|
+
deployment_filter=deployments,
|
255
|
+
)
|
256
|
+
|
257
|
+
return TaskRunPaginationResponse.model_validate(
|
258
|
+
dict(
|
259
|
+
results=runs,
|
260
|
+
count=total_count,
|
261
|
+
limit=limit,
|
262
|
+
pages=(total_count + limit - 1) // limit,
|
263
|
+
page=page,
|
264
|
+
)
|
265
|
+
)
|
266
|
+
|
267
|
+
|
217
268
|
@router.delete("/{id}", status_code=status.HTTP_204_NO_CONTENT)
|
218
269
|
async def delete_task_run(
|
219
270
|
task_run_id: UUID = Path(..., description="The task run id", alias="id"),
|
prefect/settings/models/tasks.py
CHANGED
@@ -57,6 +57,11 @@ class TasksSettings(PrefectBaseSettings):
|
|
57
57
|
description="If `True`, enables a refresh of cached results: re-executing the task will refresh the cached results.",
|
58
58
|
)
|
59
59
|
|
60
|
+
default_no_cache: bool = Field(
|
61
|
+
default=False,
|
62
|
+
description="If `True`, sets the default cache policy on all tasks to `NO_CACHE`.",
|
63
|
+
)
|
64
|
+
|
60
65
|
default_retries: int = Field(
|
61
66
|
default=0,
|
62
67
|
ge=0,
|
prefect/task_engine.py
CHANGED
@@ -755,11 +755,14 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
755
755
|
if self._telemetry.span
|
756
756
|
else nullcontext()
|
757
757
|
):
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
758
|
+
# Acquire a concurrency slot for each tag, but only if a limit
|
759
|
+
# matching the tag already exists.
|
760
|
+
with concurrency(list(self.task_run.tags), self.task_run.id):
|
761
|
+
self.begin_run()
|
762
|
+
try:
|
763
|
+
yield
|
764
|
+
finally:
|
765
|
+
self.call_hooks()
|
763
766
|
|
764
767
|
@contextmanager
|
765
768
|
def transaction_context(self) -> Generator[Transaction, None, None]:
|
@@ -820,13 +823,7 @@ class SyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
820
823
|
if transaction.is_committed():
|
821
824
|
result = transaction.read()
|
822
825
|
else:
|
823
|
-
|
824
|
-
# Acquire a concurrency slot for each tag, but only if a limit
|
825
|
-
# matching the tag already exists.
|
826
|
-
with concurrency(list(self.task_run.tags), self.task_run.id):
|
827
|
-
result = call_with_parameters(self.task.fn, parameters)
|
828
|
-
else:
|
829
|
-
result = call_with_parameters(self.task.fn, parameters)
|
826
|
+
result = call_with_parameters(self.task.fn, parameters)
|
830
827
|
self.handle_success(result, transaction=transaction)
|
831
828
|
return result
|
832
829
|
|
@@ -1288,11 +1285,14 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1288
1285
|
if self._telemetry.span
|
1289
1286
|
else nullcontext()
|
1290
1287
|
):
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1295
|
-
|
1288
|
+
# Acquire a concurrency slot for each tag, but only if a limit
|
1289
|
+
# matching the tag already exists.
|
1290
|
+
async with aconcurrency(list(self.task_run.tags), self.task_run.id):
|
1291
|
+
await self.begin_run()
|
1292
|
+
try:
|
1293
|
+
yield
|
1294
|
+
finally:
|
1295
|
+
await self.call_hooks()
|
1296
1296
|
|
1297
1297
|
@asynccontextmanager
|
1298
1298
|
async def transaction_context(self) -> AsyncGenerator[Transaction, None]:
|
@@ -1352,13 +1352,7 @@ class AsyncTaskRunEngine(BaseTaskRunEngine[P, R]):
|
|
1352
1352
|
if transaction.is_committed():
|
1353
1353
|
result = transaction.read()
|
1354
1354
|
else:
|
1355
|
-
|
1356
|
-
# Acquire a concurrency slot for each tag, but only if a limit
|
1357
|
-
# matching the tag already exists.
|
1358
|
-
async with aconcurrency(list(self.task_run.tags), self.task_run.id):
|
1359
|
-
result = await call_with_parameters(self.task.fn, parameters)
|
1360
|
-
else:
|
1361
|
-
result = await call_with_parameters(self.task.fn, parameters)
|
1355
|
+
result = await call_with_parameters(self.task.fn, parameters)
|
1362
1356
|
await self.handle_success(result, transaction=transaction)
|
1363
1357
|
return result
|
1364
1358
|
|
prefect/tasks.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
"""
|
2
2
|
Module containing the base workflow task class and decorator - for most use cases, using the [`@task` decorator][prefect.tasks.task] is preferred.
|
3
3
|
"""
|
4
|
+
|
4
5
|
# This file requires type-checking with pyright because mypy does not yet support PEP612
|
5
6
|
# See https://github.com/python/mypy/issues/8645
|
7
|
+
from __future__ import annotations
|
6
8
|
|
7
9
|
import asyncio
|
8
10
|
import datetime
|
@@ -314,7 +316,7 @@ class Task(Generic[P, R]):
|
|
314
316
|
# exactly in the @task decorator
|
315
317
|
def __init__(
|
316
318
|
self,
|
317
|
-
fn: Callable[P, R],
|
319
|
+
fn: Callable[P, R] | "classmethod[Any, P, R]" | "staticmethod[P, R]",
|
318
320
|
name: Optional[str] = None,
|
319
321
|
description: Optional[str] = None,
|
320
322
|
tags: Optional[Iterable[str]] = None,
|
@@ -378,6 +380,13 @@ class Task(Generic[P, R]):
|
|
378
380
|
" my_task():\n\tpass"
|
379
381
|
)
|
380
382
|
|
383
|
+
if isinstance(fn, classmethod):
|
384
|
+
fn = cast(Callable[P, R], fn.__func__)
|
385
|
+
|
386
|
+
if isinstance(fn, staticmethod):
|
387
|
+
fn = cast(Callable[P, R], fn.__func__)
|
388
|
+
setattr(fn, "__prefect_static__", True)
|
389
|
+
|
381
390
|
if not callable(fn):
|
382
391
|
raise TypeError("'fn' must be callable")
|
383
392
|
|
@@ -422,6 +431,11 @@ class Task(Generic[P, R]):
|
|
422
431
|
|
423
432
|
self.task_key: str = _generate_task_key(self.fn)
|
424
433
|
|
434
|
+
# determine cache and result configuration
|
435
|
+
settings = get_current_settings()
|
436
|
+
if settings.tasks.default_no_cache and cache_policy is NotSet:
|
437
|
+
cache_policy = NO_CACHE
|
438
|
+
|
425
439
|
if cache_policy is not NotSet and cache_key_fn is not None:
|
426
440
|
logger.warning(
|
427
441
|
f"Both `cache_policy` and `cache_key_fn` are set on task {self}. `cache_key_fn` will be used."
|
@@ -458,7 +472,7 @@ class Task(Generic[P, R]):
|
|
458
472
|
)
|
459
473
|
elif cache_policy is NotSet and result_storage_key is None:
|
460
474
|
self.cache_policy = DEFAULT
|
461
|
-
elif result_storage_key:
|
475
|
+
elif cache_policy != NO_CACHE and result_storage_key:
|
462
476
|
# TODO: handle this situation with double storage
|
463
477
|
self.cache_policy = None
|
464
478
|
else:
|
@@ -468,7 +482,6 @@ class Task(Generic[P, R]):
|
|
468
482
|
# TODO: We can instantiate a `TaskRunPolicy` and add Pydantic bound checks to
|
469
483
|
# validate that the user passes positive numbers here
|
470
484
|
|
471
|
-
settings = get_current_settings()
|
472
485
|
self.retries: int = (
|
473
486
|
retries if retries is not None else settings.tasks.default_retries
|
474
487
|
)
|
@@ -532,6 +545,14 @@ class Task(Generic[P, R]):
|
|
532
545
|
def ismethod(self) -> bool:
|
533
546
|
return hasattr(self.fn, "__prefect_self__")
|
534
547
|
|
548
|
+
@property
|
549
|
+
def isclassmethod(self) -> bool:
|
550
|
+
return hasattr(self.fn, "__prefect_cls__")
|
551
|
+
|
552
|
+
@property
|
553
|
+
def isstaticmethod(self) -> bool:
|
554
|
+
return getattr(self.fn, "__prefect_static__", False)
|
555
|
+
|
535
556
|
def __get__(self, instance: Any, owner: Any) -> "Task[P, R]":
|
536
557
|
"""
|
537
558
|
Implement the descriptor protocol so that the task can be used as an instance method.
|
@@ -539,10 +560,15 @@ class Task(Generic[P, R]):
|
|
539
560
|
an argument. We return a copy of the task with that instance bound to the task's function.
|
540
561
|
"""
|
541
562
|
|
542
|
-
|
543
|
-
if instance is None:
|
563
|
+
if self.isstaticmethod:
|
544
564
|
return self
|
545
565
|
|
566
|
+
# wrapped function is a classmethod
|
567
|
+
if not instance:
|
568
|
+
bound_task = copy(self)
|
569
|
+
setattr(bound_task.fn, "__prefect_cls__", owner)
|
570
|
+
return bound_task
|
571
|
+
|
546
572
|
# if the task is being accessed on an instance, bind the instance to the __prefect_self__ attribute
|
547
573
|
# of the task's function. This will allow it to be automatically added to the task's parameters
|
548
574
|
else:
|
@@ -1847,9 +1873,6 @@ def task(
|
|
1847
1873
|
"""
|
1848
1874
|
|
1849
1875
|
if __fn:
|
1850
|
-
if isinstance(__fn, (classmethod, staticmethod)):
|
1851
|
-
method_decorator = type(__fn).__name__
|
1852
|
-
raise TypeError(f"@{method_decorator} should be applied on top of @task")
|
1853
1876
|
return Task(
|
1854
1877
|
fn=__fn,
|
1855
1878
|
name=name,
|
prefect/transactions.py
CHANGED
@@ -23,6 +23,7 @@ from prefect.exceptions import (
|
|
23
23
|
MissingContextError,
|
24
24
|
SerializationError,
|
25
25
|
)
|
26
|
+
from prefect.filesystems import NullFileSystem
|
26
27
|
from prefect.logging.loggers import LoggingAdapter, get_logger, get_run_logger
|
27
28
|
from prefect.results import (
|
28
29
|
ResultRecord,
|
@@ -453,6 +454,10 @@ def transaction(
|
|
453
454
|
if key and not store:
|
454
455
|
store = get_result_store()
|
455
456
|
|
457
|
+
# Avoid inheriting a NullFileSystem for metadata_storage from a flow's result store
|
458
|
+
if store and isinstance(store.metadata_storage, NullFileSystem):
|
459
|
+
store = store.model_copy(update={"metadata_storage": None})
|
460
|
+
|
456
461
|
try:
|
457
462
|
_logger: Union[logging.Logger, LoggingAdapter] = logger or get_run_logger()
|
458
463
|
except MissingContextError:
|
prefect/utilities/callables.py
CHANGED
@@ -63,6 +63,8 @@ def get_call_parameters(
|
|
63
63
|
"""
|
64
64
|
if hasattr(fn, "__prefect_self__"):
|
65
65
|
call_args = (getattr(fn, "__prefect_self__"), *call_args)
|
66
|
+
if hasattr(fn, "__prefect_cls__"):
|
67
|
+
call_args = (getattr(fn, "__prefect_cls__"), *call_args)
|
66
68
|
|
67
69
|
try:
|
68
70
|
bound_signature = inspect.signature(fn).bind(*call_args, **call_kwargs)
|
prefect/utilities/engine.py
CHANGED
@@ -63,7 +63,7 @@ engine_logger: Logger = get_logger("engine")
|
|
63
63
|
T = TypeVar("T")
|
64
64
|
|
65
65
|
|
66
|
-
async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> set[
|
66
|
+
async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> set[TaskRunResult]:
|
67
67
|
"""
|
68
68
|
This function recurses through an expression to generate a set of any discernible
|
69
69
|
task run inputs it finds in the data structure. It produces a set of all inputs
|
@@ -76,7 +76,7 @@ async def collect_task_run_inputs(expr: Any, max_depth: int = -1) -> set[TaskRun
|
|
76
76
|
"""
|
77
77
|
# TODO: This function needs to be updated to detect parameters and constants
|
78
78
|
|
79
|
-
inputs: set[
|
79
|
+
inputs: set[TaskRunResult] = set()
|
80
80
|
|
81
81
|
def add_futures_and_states_to_inputs(obj: Any) -> None:
|
82
82
|
if isinstance(obj, PrefectFuture):
|
prefect/utilities/importtools.py
CHANGED
@@ -93,8 +93,11 @@ def load_script_as_module(path: str) -> ModuleType:
|
|
93
93
|
parent_path = str(Path(path).resolve().parent)
|
94
94
|
working_directory = os.getcwd()
|
95
95
|
|
96
|
-
|
97
|
-
|
96
|
+
module_name = os.path.splitext(Path(path).name)[0]
|
97
|
+
|
98
|
+
# fall back in case of filenames with the same names as modules
|
99
|
+
if module_name in sys.modules:
|
100
|
+
module_name = f"__prefect_loader_{id(path)}__"
|
98
101
|
|
99
102
|
spec = importlib.util.spec_from_file_location(
|
100
103
|
module_name,
|
@@ -112,15 +115,9 @@ def load_script_as_module(path: str) -> ModuleType:
|
|
112
115
|
with _get_sys_path_lock():
|
113
116
|
sys.path.insert(0, working_directory)
|
114
117
|
sys.path.insert(0, parent_path)
|
115
|
-
|
116
|
-
spec.loader.exec_module(module)
|
117
|
-
finally:
|
118
|
-
sys.path.remove(parent_path)
|
119
|
-
sys.path.remove(working_directory)
|
118
|
+
spec.loader.exec_module(module)
|
120
119
|
except Exception as exc:
|
121
120
|
raise ScriptError(user_exc=exc, path=path) from exc
|
122
|
-
finally:
|
123
|
-
sys.modules.pop(module_name)
|
124
121
|
|
125
122
|
return module
|
126
123
|
|
prefect/workers/base.py
CHANGED
@@ -60,6 +60,7 @@ from prefect.settings import (
|
|
60
60
|
get_current_settings,
|
61
61
|
)
|
62
62
|
from prefect.states import (
|
63
|
+
Cancelled,
|
63
64
|
Crashed,
|
64
65
|
Pending,
|
65
66
|
exception_to_failed_state,
|
@@ -1249,10 +1250,14 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
1249
1250
|
) -> None:
|
1250
1251
|
state_updates = state_updates or {}
|
1251
1252
|
state_updates.setdefault("name", "Cancelled")
|
1252
|
-
|
1253
|
-
if
|
1254
|
-
|
1255
|
-
|
1253
|
+
|
1254
|
+
if flow_run.state:
|
1255
|
+
state_updates.setdefault("type", StateType.CANCELLED)
|
1256
|
+
state = flow_run.state.model_copy(update=state_updates)
|
1257
|
+
else:
|
1258
|
+
# Unexpectedly when flow run does not have a state, create a new one
|
1259
|
+
# does not need to explicitly set the type
|
1260
|
+
state = Cancelled(**state_updates)
|
1256
1261
|
|
1257
1262
|
await self.client.set_flow_run_state(flow_run.id, state, force=True)
|
1258
1263
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: prefect-client
|
3
|
-
Version: 3.2.
|
3
|
+
Version: 3.2.15
|
4
4
|
Summary: Workflow orchestration and management.
|
5
5
|
Project-URL: Changelog, https://github.com/PrefectHQ/prefect/releases
|
6
6
|
Project-URL: Documentation, https://docs.prefect.io
|
@@ -48,13 +48,14 @@ Requires-Dist: pydantic-settings>2.2.1
|
|
48
48
|
Requires-Dist: python-dateutil<3.0.0,>=2.8.2
|
49
49
|
Requires-Dist: python-slugify<9.0,>=5.0
|
50
50
|
Requires-Dist: python-socks[asyncio]<3.0,>=2.5.3
|
51
|
+
Requires-Dist: pytz<2026,>=2021.1
|
51
52
|
Requires-Dist: pyyaml<7.0.0,>=5.4.1
|
52
53
|
Requires-Dist: rfc3339-validator<0.2.0,>=0.1.4
|
53
54
|
Requires-Dist: rich<14.0,>=11.0
|
54
55
|
Requires-Dist: ruamel-yaml>=0.17.0
|
55
56
|
Requires-Dist: sniffio<2.0.0,>=1.3.0
|
56
57
|
Requires-Dist: toml>=0.10.0
|
57
|
-
Requires-Dist: typing-extensions<5.0.0,>=4.
|
58
|
+
Requires-Dist: typing-extensions<5.0.0,>=4.10.0
|
58
59
|
Requires-Dist: ujson<6.0.0,>=5.8.0
|
59
60
|
Requires-Dist: uvicorn!=0.29.0,>=0.14.0
|
60
61
|
Requires-Dist: websockets<16.0,>=13.0
|