prefect-client 2.19.2__py3-none-any.whl → 3.0.0rc1__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/__init__.py +8 -56
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/concurrency/api.py +0 -34
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/schemas/bases.py +44 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +60 -158
- prefect/artifacts.py +161 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +268 -148
- prefect/blocks/fields.py +2 -57
- prefect/blocks/kubernetes.py +8 -12
- prefect/blocks/notifications.py +40 -20
- prefect/blocks/system.py +22 -11
- prefect/blocks/webhook.py +2 -9
- prefect/client/base.py +4 -4
- prefect/client/cloud.py +8 -13
- prefect/client/orchestration.py +347 -341
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +151 -145
- prefect/client/schemas/responses.py +16 -24
- prefect/client/schemas/schedules.py +47 -35
- prefect/client/subscriptions.py +2 -2
- prefect/client/utilities.py +5 -2
- prefect/concurrency/asyncio.py +3 -1
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +6 -3
- prefect/context.py +195 -27
- prefect/deployments/__init__.py +5 -6
- prefect/deployments/base.py +7 -5
- prefect/deployments/flow_runs.py +185 -0
- prefect/deployments/runner.py +50 -45
- prefect/deployments/schedules.py +28 -23
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +1 -0
- prefect/deployments/steps/pull.py +7 -21
- prefect/engine.py +12 -2422
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +19 -6
- prefect/events/clients.py +14 -37
- prefect/events/filters.py +14 -18
- prefect/events/related.py +2 -2
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +34 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +2 -3
- prefect/events/worker.py +2 -3
- prefect/filesystems.py +6 -517
- prefect/{new_flow_engine.py → flow_engine.py} +313 -72
- prefect/flow_runs.py +377 -5
- prefect/flows.py +307 -166
- prefect/futures.py +186 -345
- prefect/infrastructure/__init__.py +0 -27
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +11 -6
- prefect/infrastructure/provisioners/container_instance.py +11 -7
- prefect/infrastructure/provisioners/ecs.py +6 -4
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +5 -7
- prefect/logging/formatters.py +0 -2
- prefect/logging/handlers.py +3 -11
- prefect/logging/loggers.py +2 -2
- prefect/manifests.py +2 -1
- prefect/records/__init__.py +1 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +19 -15
- prefect/runner/server.py +6 -10
- prefect/runner/storage.py +3 -8
- prefect/runner/submit.py +2 -2
- prefect/runner/utils.py +2 -2
- prefect/serializers.py +24 -35
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +70 -133
- prefect/states.py +17 -47
- prefect/task_engine.py +697 -58
- prefect/task_runners.py +269 -301
- prefect/task_server.py +53 -34
- prefect/tasks.py +327 -337
- prefect/transactions.py +220 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +311 -43
- prefect/utilities/collections.py +23 -38
- prefect/utilities/dispatch.py +11 -3
- prefect/utilities/dockerutils.py +4 -0
- prefect/utilities/engine.py +140 -20
- prefect/utilities/importtools.py +97 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +78 -61
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +15 -17
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
- prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/__init__.py +0 -0
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/__init__.py +0 -0
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/agent.py +0 -698
- prefect/deployments/deployments.py +0 -1042
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/base.py +0 -323
- prefect/infrastructure/container.py +0 -818
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/workers/block.py +0 -218
- prefect_client-2.19.2.dist-info/RECORD +0 -292
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
prefect/results.py
CHANGED
@@ -16,13 +16,14 @@ from typing import (
|
|
16
16
|
)
|
17
17
|
from uuid import UUID
|
18
18
|
|
19
|
+
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, ValidationError
|
20
|
+
from pydantic_core import PydanticUndefinedType
|
19
21
|
from typing_extensions import ParamSpec, Self
|
20
22
|
|
21
23
|
import prefect
|
22
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
23
24
|
from prefect.blocks.core import Block
|
24
25
|
from prefect.client.utilities import inject_client
|
25
|
-
from prefect.exceptions import MissingResult
|
26
|
+
from prefect.exceptions import MissingResult, ObjectAlreadyExists
|
26
27
|
from prefect.filesystems import (
|
27
28
|
LocalFileSystem,
|
28
29
|
ReadableFileSystem,
|
@@ -41,13 +42,6 @@ from prefect.utilities.annotations import NotSet
|
|
41
42
|
from prefect.utilities.asyncutils import sync_compatible
|
42
43
|
from prefect.utilities.pydantic import get_dispatch_key, lookup_type, register_base_type
|
43
44
|
|
44
|
-
if HAS_PYDANTIC_V2:
|
45
|
-
import pydantic.v1 as pydantic
|
46
|
-
|
47
|
-
else:
|
48
|
-
import pydantic
|
49
|
-
|
50
|
-
|
51
45
|
if TYPE_CHECKING:
|
52
46
|
from prefect import Flow, Task
|
53
47
|
from prefect.client.orchestration import PrefectClient
|
@@ -84,14 +78,14 @@ _default_task_scheduling_storages: Dict[Tuple[str, str], WritableFileSystem] = {
|
|
84
78
|
|
85
79
|
async def get_or_create_default_task_scheduling_storage() -> ResultStorage:
|
86
80
|
"""
|
87
|
-
Generate a default file system for
|
81
|
+
Generate a default file system for background task parameter/result storage.
|
88
82
|
"""
|
89
83
|
default_storage_name, storage_path = cache_key = (
|
90
84
|
PREFECT_TASK_SCHEDULING_DEFAULT_STORAGE_BLOCK.value(),
|
91
85
|
PREFECT_LOCAL_STORAGE_PATH.value(),
|
92
86
|
)
|
93
87
|
|
94
|
-
async def get_storage():
|
88
|
+
async def get_storage() -> WritableFileSystem:
|
95
89
|
try:
|
96
90
|
return await Block.load(default_storage_name)
|
97
91
|
except ValueError as e:
|
@@ -110,12 +104,14 @@ async def get_or_create_default_task_scheduling_storage() -> ResultStorage:
|
|
110
104
|
|
111
105
|
try:
|
112
106
|
await block.save(name, overwrite=False)
|
113
|
-
return block
|
114
107
|
except ValueError as e:
|
115
108
|
if "already in use" not in str(e):
|
116
109
|
raise e
|
110
|
+
except ObjectAlreadyExists:
|
111
|
+
# Another client created the block before we reached this line
|
112
|
+
block = await Block.load(default_storage_name)
|
117
113
|
|
118
|
-
return
|
114
|
+
return block
|
119
115
|
|
120
116
|
try:
|
121
117
|
return _default_task_scheduling_storages[cache_key]
|
@@ -171,14 +167,14 @@ def task_features_require_result_persistence(task: "Task") -> bool:
|
|
171
167
|
return False
|
172
168
|
|
173
169
|
|
174
|
-
def _format_user_supplied_storage_key(key):
|
170
|
+
def _format_user_supplied_storage_key(key: str) -> str:
|
175
171
|
# Note here we are pinning to task runs since flow runs do not support storage keys
|
176
172
|
# yet; we'll need to split logic in the future or have two separate functions
|
177
173
|
runtime_vars = {key: getattr(prefect.runtime, key) for key in dir(prefect.runtime)}
|
178
174
|
return key.format(**runtime_vars, parameters=prefect.runtime.task_run.parameters)
|
179
175
|
|
180
176
|
|
181
|
-
class ResultFactory(
|
177
|
+
class ResultFactory(BaseModel):
|
182
178
|
"""
|
183
179
|
A utility to generate `Result` types.
|
184
180
|
"""
|
@@ -186,7 +182,7 @@ class ResultFactory(pydantic.BaseModel):
|
|
186
182
|
persist_result: bool
|
187
183
|
cache_result_in_memory: bool
|
188
184
|
serializer: Serializer
|
189
|
-
storage_block_id: Optional[uuid.UUID]
|
185
|
+
storage_block_id: Optional[uuid.UUID] = None
|
190
186
|
storage_block: WritableFileSystem
|
191
187
|
storage_key_fn: Callable[[], str]
|
192
188
|
|
@@ -430,7 +426,7 @@ class ResultFactory(pydantic.BaseModel):
|
|
430
426
|
)
|
431
427
|
|
432
428
|
@sync_compatible
|
433
|
-
async def create_result(self, obj: R) -> Union[R, "BaseResult[R]"]:
|
429
|
+
async def create_result(self, obj: R, key: str = None) -> Union[R, "BaseResult[R]"]:
|
434
430
|
"""
|
435
431
|
Create a result type for the given object.
|
436
432
|
|
@@ -450,11 +446,20 @@ class ResultFactory(pydantic.BaseModel):
|
|
450
446
|
if type(obj) in LITERAL_TYPES:
|
451
447
|
return await LiteralResult.create(obj)
|
452
448
|
|
449
|
+
if key:
|
450
|
+
|
451
|
+
def key_fn():
|
452
|
+
return key
|
453
|
+
|
454
|
+
storage_key_fn = key_fn
|
455
|
+
else:
|
456
|
+
storage_key_fn = self.storage_key_fn
|
457
|
+
|
453
458
|
return await PersistedResult.create(
|
454
459
|
obj,
|
455
460
|
storage_block=self.storage_block,
|
456
461
|
storage_block_id=self.storage_block_id,
|
457
|
-
storage_key_fn=
|
462
|
+
storage_key_fn=storage_key_fn,
|
458
463
|
serializer=self.serializer,
|
459
464
|
cache_object=should_cache_object,
|
460
465
|
)
|
@@ -475,17 +480,19 @@ class ResultFactory(pydantic.BaseModel):
|
|
475
480
|
assert (
|
476
481
|
self.storage_block_id is not None
|
477
482
|
), "Unexpected storage block ID. Was it persisted?"
|
478
|
-
blob = PersistedResultBlob.
|
483
|
+
blob = PersistedResultBlob.model_validate_json(
|
479
484
|
await self.storage_block.read_path(f"parameters/{identifier}")
|
480
485
|
)
|
481
486
|
return self.serializer.loads(blob.data)
|
482
487
|
|
483
488
|
|
484
489
|
@register_base_type
|
485
|
-
class BaseResult(
|
490
|
+
class BaseResult(BaseModel, abc.ABC, Generic[R]):
|
491
|
+
model_config = ConfigDict(extra="forbid")
|
492
|
+
|
486
493
|
type: str
|
487
|
-
artifact_type: Optional[str]
|
488
|
-
artifact_description: Optional[str]
|
494
|
+
artifact_type: Optional[str] = None
|
495
|
+
artifact_description: Optional[str] = None
|
489
496
|
|
490
497
|
def __init__(self, **data: Any) -> None:
|
491
498
|
type_string = get_dispatch_key(self) if type(self) != BaseResult else "__base__"
|
@@ -497,12 +504,12 @@ class BaseResult(pydantic.BaseModel, abc.ABC, Generic[R]):
|
|
497
504
|
try:
|
498
505
|
subcls = lookup_type(cls, dispatch_key=kwargs["type"])
|
499
506
|
except KeyError as exc:
|
500
|
-
raise
|
507
|
+
raise ValidationError(errors=[exc], model=cls)
|
501
508
|
return super().__new__(subcls)
|
502
509
|
else:
|
503
510
|
return super().__new__(cls)
|
504
511
|
|
505
|
-
_cache: Any =
|
512
|
+
_cache: Any = PrivateAttr(NotSet)
|
506
513
|
|
507
514
|
def _cache_object(self, obj: Any) -> None:
|
508
515
|
self._cache = obj
|
@@ -524,12 +531,10 @@ class BaseResult(pydantic.BaseModel, abc.ABC, Generic[R]):
|
|
524
531
|
) -> "BaseResult[R]":
|
525
532
|
...
|
526
533
|
|
527
|
-
class Config:
|
528
|
-
extra = "forbid"
|
529
|
-
|
530
534
|
@classmethod
|
531
535
|
def __dispatch_key__(cls, **kwargs):
|
532
|
-
|
536
|
+
default = cls.model_fields.get("type").get_default()
|
537
|
+
return cls.__name__ if isinstance(default, PydanticUndefinedType) else default
|
533
538
|
|
534
539
|
|
535
540
|
class UnpersistedResult(BaseResult):
|
@@ -537,7 +542,7 @@ class UnpersistedResult(BaseResult):
|
|
537
542
|
Result type for results that are not persisted outside of local memory.
|
538
543
|
"""
|
539
544
|
|
540
|
-
type = "unpersisted"
|
545
|
+
type: str = "unpersisted"
|
541
546
|
|
542
547
|
@sync_compatible
|
543
548
|
async def get(self) -> R:
|
@@ -572,8 +577,8 @@ class LiteralResult(BaseResult):
|
|
572
577
|
They are not persisted to external result storage.
|
573
578
|
"""
|
574
579
|
|
575
|
-
type = "literal"
|
576
|
-
value: Any
|
580
|
+
type: str = "literal"
|
581
|
+
value: Any = None
|
577
582
|
|
578
583
|
def has_cached_object(self) -> bool:
|
579
584
|
# This result type always has the object cached in memory
|
@@ -609,13 +614,13 @@ class PersistedResult(BaseResult):
|
|
609
614
|
content was written.
|
610
615
|
"""
|
611
616
|
|
612
|
-
type = "reference"
|
617
|
+
type: str = "reference"
|
613
618
|
|
614
619
|
serializer_type: str
|
615
620
|
storage_block_id: uuid.UUID
|
616
621
|
storage_key: str
|
617
622
|
|
618
|
-
_should_cache_object: bool =
|
623
|
+
_should_cache_object: bool = PrivateAttr(default=True)
|
619
624
|
|
620
625
|
@sync_compatible
|
621
626
|
@inject_client
|
@@ -643,7 +648,7 @@ class PersistedResult(BaseResult):
|
|
643
648
|
block_document = await client.read_block_document(self.storage_block_id)
|
644
649
|
storage_block: ReadableFileSystem = Block._from_block_document(block_document)
|
645
650
|
content = await storage_block.read_path(self.storage_key)
|
646
|
-
blob = PersistedResultBlob.
|
651
|
+
blob = PersistedResultBlob.model_validate_json(content)
|
647
652
|
return blob
|
648
653
|
|
649
654
|
@staticmethod
|
@@ -686,7 +691,6 @@ class PersistedResult(BaseResult):
|
|
686
691
|
raise TypeError(
|
687
692
|
f"Expected type 'str' for result storage key; got value {key!r}"
|
688
693
|
)
|
689
|
-
|
690
694
|
await storage_block.write_path(key, content=blob.to_bytes())
|
691
695
|
|
692
696
|
description = f"Result of type `{type(obj).__name__}`"
|
@@ -716,7 +720,7 @@ class PersistedResult(BaseResult):
|
|
716
720
|
return result
|
717
721
|
|
718
722
|
|
719
|
-
class PersistedResultBlob(
|
723
|
+
class PersistedResultBlob(BaseModel):
|
720
724
|
"""
|
721
725
|
The format of the content stored by a persisted result.
|
722
726
|
|
@@ -725,10 +729,10 @@ class PersistedResultBlob(pydantic.BaseModel):
|
|
725
729
|
|
726
730
|
serializer: Serializer
|
727
731
|
data: bytes
|
728
|
-
prefect_version: str =
|
732
|
+
prefect_version: str = Field(default=prefect.__version__)
|
729
733
|
|
730
734
|
def to_bytes(self) -> bytes:
|
731
|
-
return self.
|
735
|
+
return self.model_dump_json(serialize_as_any=True).encode()
|
732
736
|
|
733
737
|
|
734
738
|
class UnknownResult(BaseResult):
|
@@ -742,7 +746,7 @@ class UnknownResult(BaseResult):
|
|
742
746
|
completed task.
|
743
747
|
"""
|
744
748
|
|
745
|
-
type = "unknown"
|
749
|
+
type: str = "unknown"
|
746
750
|
value: None
|
747
751
|
|
748
752
|
def has_cached_object(self) -> bool:
|
prefect/runner/runner.py
CHANGED
@@ -71,19 +71,15 @@ from prefect.client.schemas.objects import (
|
|
71
71
|
StateType,
|
72
72
|
)
|
73
73
|
from prefect.client.schemas.schedules import SCHEDULE_TYPES
|
74
|
-
from prefect.deployments.deployments import load_flow_from_flow_run
|
75
74
|
from prefect.deployments.runner import (
|
76
75
|
EntrypointType,
|
77
76
|
RunnerDeployment,
|
78
77
|
)
|
79
78
|
from prefect.deployments.schedules import FlexibleScheduleList
|
80
79
|
from prefect.events import DeploymentTriggerTypes, TriggerTypes
|
81
|
-
from prefect.exceptions import
|
82
|
-
|
83
|
-
)
|
84
|
-
from prefect.flows import Flow
|
80
|
+
from prefect.exceptions import Abort, ObjectNotFound
|
81
|
+
from prefect.flows import Flow, load_flow_from_flow_run
|
85
82
|
from prefect.logging.loggers import PrefectLogAdapter, flow_run_logger, get_logger
|
86
|
-
from prefect.runner.server import start_webserver
|
87
83
|
from prefect.runner.storage import RunnerStorage
|
88
84
|
from prefect.settings import (
|
89
85
|
PREFECT_API_URL,
|
@@ -233,7 +229,7 @@ class Runner:
|
|
233
229
|
description: Optional[str] = None,
|
234
230
|
tags: Optional[List[str]] = None,
|
235
231
|
version: Optional[str] = None,
|
236
|
-
enforce_parameter_schema: bool =
|
232
|
+
enforce_parameter_schema: bool = True,
|
237
233
|
entrypoint_type: EntrypointType = EntrypointType.FILE_PATH,
|
238
234
|
) -> UUID:
|
239
235
|
"""
|
@@ -366,6 +362,8 @@ class Runner:
|
|
366
362
|
runner.start()
|
367
363
|
```
|
368
364
|
"""
|
365
|
+
from prefect.runner.server import start_webserver
|
366
|
+
|
369
367
|
_register_signal(signal.SIGTERM, self.handle_sigterm)
|
370
368
|
|
371
369
|
webserver = webserver if webserver is not None else self.webserver
|
@@ -1082,7 +1080,7 @@ class Runner:
|
|
1082
1080
|
state_updates = state_updates or {}
|
1083
1081
|
state_updates.setdefault("name", "Cancelled")
|
1084
1082
|
state_updates.setdefault("type", StateType.CANCELLED)
|
1085
|
-
state = flow_run.state.
|
1083
|
+
state = flow_run.state.model_copy(update=state_updates)
|
1086
1084
|
|
1087
1085
|
await self._client.set_flow_run_state(flow_run.id, state, force=True)
|
1088
1086
|
|
@@ -1130,12 +1128,18 @@ class Runner:
|
|
1130
1128
|
Run the hooks for a flow.
|
1131
1129
|
"""
|
1132
1130
|
if state.is_cancelling():
|
1133
|
-
|
1134
|
-
|
1135
|
-
|
1136
|
-
|
1131
|
+
try:
|
1132
|
+
flow = await load_flow_from_flow_run(
|
1133
|
+
flow_run, storage_base_path=str(self._tmp_dir)
|
1134
|
+
)
|
1135
|
+
hooks = flow.on_cancellation_hooks or []
|
1137
1136
|
|
1138
|
-
|
1137
|
+
await _run_hooks(hooks, flow_run, flow, state)
|
1138
|
+
except ObjectNotFound:
|
1139
|
+
run_logger = self._get_flow_run_logger(flow_run)
|
1140
|
+
run_logger.warning(
|
1141
|
+
f"Runner cannot retrieve flow to execute cancellation hooks for flow run {flow_run.id!r}."
|
1142
|
+
)
|
1139
1143
|
|
1140
1144
|
async def _run_on_crashed_hooks(
|
1141
1145
|
self,
|
@@ -1147,9 +1151,9 @@ class Runner:
|
|
1147
1151
|
"""
|
1148
1152
|
if state.is_crashed():
|
1149
1153
|
flow = await load_flow_from_flow_run(
|
1150
|
-
flow_run,
|
1154
|
+
flow_run, storage_base_path=str(self._tmp_dir)
|
1151
1155
|
)
|
1152
|
-
hooks = flow.
|
1156
|
+
hooks = flow.on_crashed_hooks or []
|
1153
1157
|
|
1154
1158
|
await _run_hooks(hooks, flow_run, flow, state)
|
1155
1159
|
|
prefect/runner/server.py
CHANGED
@@ -3,11 +3,10 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple
|
|
3
3
|
|
4
4
|
import pendulum
|
5
5
|
import uvicorn
|
6
|
-
from
|
7
|
-
from
|
6
|
+
from fastapi import APIRouter, FastAPI, HTTPException, status
|
7
|
+
from fastapi.responses import JSONResponse
|
8
8
|
from typing_extensions import Literal
|
9
9
|
|
10
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
11
10
|
from prefect._internal.schemas.validators import validate_values_conform_to_schema
|
12
11
|
from prefect.client.orchestration import get_client
|
13
12
|
from prefect.exceptions import MissingFlowError, ScriptError
|
@@ -30,10 +29,7 @@ if TYPE_CHECKING:
|
|
30
29
|
from prefect.client.schemas.responses import DeploymentResponse
|
31
30
|
from prefect.runner import Runner
|
32
31
|
|
33
|
-
|
34
|
-
from pydantic.v1 import BaseModel
|
35
|
-
else:
|
36
|
-
from pydantic import BaseModel
|
32
|
+
from pydantic import BaseModel
|
37
33
|
|
38
34
|
logger = get_logger("webserver")
|
39
35
|
|
@@ -161,7 +157,7 @@ async def get_subflow_schemas(runner: "Runner") -> Dict[str, Dict]:
|
|
161
157
|
script = deployment.entrypoint.split(":")[0]
|
162
158
|
subflows = load_flows_from_script(script)
|
163
159
|
for flow in subflows:
|
164
|
-
schemas[flow.name] = flow.parameters.
|
160
|
+
schemas[flow.name] = flow.parameters.model_dump()
|
165
161
|
|
166
162
|
return schemas
|
167
163
|
|
@@ -183,7 +179,7 @@ def _flow_schema_changed(flow: Flow, schemas: Dict[str, Dict]) -> bool:
|
|
183
179
|
flow_name_with_dashes = flow.name.replace("_", "-")
|
184
180
|
|
185
181
|
schema = schemas.get(flow.name, None) or schemas.get(flow_name_with_dashes, None)
|
186
|
-
if schema is not None and flow.parameters.
|
182
|
+
if schema is not None and flow.parameters.model_dump() != schema:
|
187
183
|
return True
|
188
184
|
return False
|
189
185
|
|
@@ -236,7 +232,7 @@ def _build_generic_endpoint_for_flows(
|
|
236
232
|
|
237
233
|
return JSONResponse(
|
238
234
|
status_code=status.HTTP_201_CREATED,
|
239
|
-
content=flow_run.
|
235
|
+
content=flow_run.model_dump(mode="json"),
|
240
236
|
)
|
241
237
|
|
242
238
|
return _create_flow_run_for_flow_from_fqn
|
prefect/runner/storage.py
CHANGED
@@ -8,20 +8,15 @@ from uuid import uuid4
|
|
8
8
|
|
9
9
|
import fsspec
|
10
10
|
from anyio import run_process
|
11
|
+
from pydantic import SecretStr
|
11
12
|
|
12
13
|
from prefect._internal.concurrency.api import create_call, from_async
|
13
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
14
14
|
from prefect.blocks.core import Block, BlockNotSavedError
|
15
15
|
from prefect.blocks.system import Secret
|
16
16
|
from prefect.filesystems import ReadableDeploymentStorage, WritableDeploymentStorage
|
17
17
|
from prefect.logging.loggers import get_logger
|
18
18
|
from prefect.utilities.collections import visit_collection
|
19
19
|
|
20
|
-
if HAS_PYDANTIC_V2:
|
21
|
-
from pydantic.v1 import SecretStr
|
22
|
-
else:
|
23
|
-
from pydantic import SecretStr
|
24
|
-
|
25
20
|
|
26
21
|
@runtime_checkable
|
27
22
|
class RunnerStorage(Protocol):
|
@@ -159,7 +154,7 @@ class GitRepository:
|
|
159
154
|
url_components = urlparse(self._url)
|
160
155
|
|
161
156
|
credentials = (
|
162
|
-
self._credentials.
|
157
|
+
self._credentials.model_dump()
|
163
158
|
if isinstance(self._credentials, Block)
|
164
159
|
else deepcopy(self._credentials)
|
165
160
|
)
|
@@ -392,7 +387,7 @@ class RemoteStorage:
|
|
392
387
|
if hasattr(obj, "value"):
|
393
388
|
return obj.value
|
394
389
|
else:
|
395
|
-
return obj.
|
390
|
+
return obj.model_dump()
|
396
391
|
return obj
|
397
392
|
|
398
393
|
settings_with_block_values = visit_collection(
|
prefect/runner/submit.py
CHANGED
@@ -43,7 +43,7 @@ async def _submit_flow_to_runner(
|
|
43
43
|
Returns:
|
44
44
|
A `FlowRun` object representing the flow run that was submitted.
|
45
45
|
"""
|
46
|
-
from prefect.engine import (
|
46
|
+
from prefect.utilities.engine import (
|
47
47
|
_dynamic_key_for_task_run,
|
48
48
|
collect_task_run_inputs,
|
49
49
|
resolve_inputs,
|
@@ -90,7 +90,7 @@ async def _submit_flow_to_runner(
|
|
90
90
|
)
|
91
91
|
response.raise_for_status()
|
92
92
|
|
93
|
-
return FlowRun.
|
93
|
+
return FlowRun.model_validate(response.json())
|
94
94
|
|
95
95
|
|
96
96
|
@overload
|
prefect/runner/utils.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
from copy import deepcopy
|
2
2
|
from typing import Any, Dict
|
3
3
|
|
4
|
-
from
|
5
|
-
from
|
4
|
+
from fastapi import FastAPI
|
5
|
+
from fastapi.openapi.utils import get_openapi
|
6
6
|
|
7
7
|
from prefect import __version__ as PREFECT_VERSION
|
8
8
|
|
prefect/serializers.py
CHANGED
@@ -15,6 +15,14 @@ import abc
|
|
15
15
|
import base64
|
16
16
|
from typing import Any, Dict, Generic, Optional, Type, TypeVar
|
17
17
|
|
18
|
+
from pydantic import (
|
19
|
+
BaseModel,
|
20
|
+
ConfigDict,
|
21
|
+
Field,
|
22
|
+
TypeAdapter,
|
23
|
+
ValidationError,
|
24
|
+
field_validator,
|
25
|
+
)
|
18
26
|
from typing_extensions import Literal, Self
|
19
27
|
|
20
28
|
from prefect._internal.schemas.validators import (
|
@@ -23,32 +31,11 @@ from prefect._internal.schemas.validators import (
|
|
23
31
|
validate_dump_kwargs,
|
24
32
|
validate_load_kwargs,
|
25
33
|
validate_picklelib,
|
26
|
-
validate_picklelib_version,
|
27
34
|
)
|
28
|
-
from prefect.pydantic import HAS_PYDANTIC_V2
|
29
35
|
from prefect.utilities.dispatch import get_dispatch_key, lookup_type, register_base_type
|
30
36
|
from prefect.utilities.importtools import from_qualified_name, to_qualified_name
|
31
37
|
from prefect.utilities.pydantic import custom_pydantic_encoder
|
32
38
|
|
33
|
-
if HAS_PYDANTIC_V2:
|
34
|
-
from pydantic.v1 import (
|
35
|
-
BaseModel,
|
36
|
-
Field,
|
37
|
-
ValidationError,
|
38
|
-
parse_obj_as,
|
39
|
-
root_validator,
|
40
|
-
validator,
|
41
|
-
)
|
42
|
-
else:
|
43
|
-
from pydantic import (
|
44
|
-
BaseModel,
|
45
|
-
Field,
|
46
|
-
ValidationError,
|
47
|
-
parse_obj_as,
|
48
|
-
root_validator,
|
49
|
-
validator,
|
50
|
-
)
|
51
|
-
|
52
39
|
D = TypeVar("D")
|
53
40
|
|
54
41
|
|
@@ -73,7 +60,9 @@ def prefect_json_object_decoder(result: dict):
|
|
73
60
|
with `prefect_json_object_encoder`
|
74
61
|
"""
|
75
62
|
if "__class__" in result:
|
76
|
-
return
|
63
|
+
return TypeAdapter(from_qualified_name(result["__class__"])).validate_python(
|
64
|
+
result["data"]
|
65
|
+
)
|
77
66
|
elif "__exc_type__" in result:
|
78
67
|
return from_qualified_name(result["__exc_type__"])(result["message"])
|
79
68
|
else:
|
@@ -112,12 +101,12 @@ class Serializer(BaseModel, Generic[D], abc.ABC):
|
|
112
101
|
def loads(self, blob: bytes) -> D:
|
113
102
|
"""Decode the blob of bytes into an object."""
|
114
103
|
|
115
|
-
|
116
|
-
extra = "forbid"
|
104
|
+
model_config = ConfigDict(extra="forbid")
|
117
105
|
|
118
106
|
@classmethod
|
119
|
-
def __dispatch_key__(cls):
|
120
|
-
|
107
|
+
def __dispatch_key__(cls) -> str:
|
108
|
+
type_str = cls.model_fields["type"].default
|
109
|
+
return type_str if isinstance(type_str, str) else None
|
121
110
|
|
122
111
|
|
123
112
|
class PickleSerializer(Serializer):
|
@@ -133,15 +122,15 @@ class PickleSerializer(Serializer):
|
|
133
122
|
type: Literal["pickle"] = "pickle"
|
134
123
|
|
135
124
|
picklelib: str = "cloudpickle"
|
136
|
-
picklelib_version: str = None
|
125
|
+
picklelib_version: Optional[str] = None
|
137
126
|
|
138
|
-
@
|
127
|
+
@field_validator("picklelib")
|
139
128
|
def check_picklelib(cls, value):
|
140
129
|
return validate_picklelib(value)
|
141
130
|
|
142
|
-
@
|
143
|
-
def check_picklelib_version(cls, values):
|
144
|
-
|
131
|
+
# @model_validator(mode="before")
|
132
|
+
# def check_picklelib_version(cls, values):
|
133
|
+
# return validate_picklelib_version(values)
|
145
134
|
|
146
135
|
def dumps(self, obj: Any) -> bytes:
|
147
136
|
pickler = from_qualified_name(self.picklelib)
|
@@ -185,11 +174,11 @@ class JSONSerializer(Serializer):
|
|
185
174
|
dumps_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
186
175
|
loads_kwargs: Dict[str, Any] = Field(default_factory=dict)
|
187
176
|
|
188
|
-
@
|
177
|
+
@field_validator("dumps_kwargs")
|
189
178
|
def dumps_kwargs_cannot_contain_default(cls, value):
|
190
179
|
return validate_dump_kwargs(value)
|
191
180
|
|
192
|
-
@
|
181
|
+
@field_validator("loads_kwargs")
|
193
182
|
def loads_kwargs_cannot_contain_object_hook(cls, value):
|
194
183
|
return validate_load_kwargs(value)
|
195
184
|
|
@@ -229,11 +218,11 @@ class CompressedSerializer(Serializer):
|
|
229
218
|
serializer: Serializer
|
230
219
|
compressionlib: str = "lzma"
|
231
220
|
|
232
|
-
@
|
221
|
+
@field_validator("serializer", mode="before")
|
233
222
|
def validate_serializer(cls, value):
|
234
223
|
return cast_type_names_to_serializers(value)
|
235
224
|
|
236
|
-
@
|
225
|
+
@field_validator("compressionlib")
|
237
226
|
def check_compressionlib(cls, value):
|
238
227
|
return validate_compressionlib(value)
|
239
228
|
|
@@ -1,14 +1,5 @@
|
|
1
1
|
{
|
2
2
|
"prefect": {
|
3
|
-
"prefect-agent": {
|
4
|
-
"type": "prefect-agent",
|
5
|
-
"documentation_url": "https://docs.prefect.io/latest/concepts/work-pools/#agent-overview",
|
6
|
-
"display_name": "Prefect Agent",
|
7
|
-
"logo_url": "https://cdn.sanity.io/images/3ugk85nk/production/c771bb53894c877e169c8db158c5598558b8f175-24x24.svg",
|
8
|
-
"install_command": "pip install prefect",
|
9
|
-
"default_base_job_configuration": {},
|
10
|
-
"description": "Execute flow runs on heterogeneous infrastructure using infrastructure blocks."
|
11
|
-
},
|
12
3
|
"process": {
|
13
4
|
"default_base_job_configuration": {
|
14
5
|
"job_configuration": {
|
@@ -806,7 +797,7 @@
|
|
806
797
|
"image": {
|
807
798
|
"title": "Image",
|
808
799
|
"description": "The image reference of a container image to use for created jobs. If not set, the latest Prefect image will be used.",
|
809
|
-
"example": "docker.io/prefecthq/prefect:
|
800
|
+
"example": "docker.io/prefecthq/prefect:3-latest",
|
810
801
|
"type": "string"
|
811
802
|
},
|
812
803
|
"registry_credentials": {
|
@@ -1040,7 +1031,7 @@
|
|
1040
1031
|
"image": {
|
1041
1032
|
"title": "Image Name",
|
1042
1033
|
"description": "The image to use for a new Cloud Run Job. If not set, the latest Prefect image will be used. See https://cloud.google.com/run/docs/deploying#images.",
|
1043
|
-
"example": "docker.io/prefecthq/prefect:
|
1034
|
+
"example": "docker.io/prefecthq/prefect:3-latest",
|
1044
1035
|
"type": "string"
|
1045
1036
|
},
|
1046
1037
|
"cpu": {
|
@@ -1211,7 +1202,7 @@
|
|
1211
1202
|
"image": {
|
1212
1203
|
"title": "Image Name",
|
1213
1204
|
"description": "The image to use for the Cloud Run job. If not provided the default Prefect image will be used.",
|
1214
|
-
"default": "prefecthq/prefect:
|
1205
|
+
"default": "prefecthq/prefect:3-latest",
|
1215
1206
|
"type": "string"
|
1216
1207
|
},
|
1217
1208
|
"args": {
|
@@ -1600,7 +1591,7 @@
|
|
1600
1591
|
"image": {
|
1601
1592
|
"title": "Image",
|
1602
1593
|
"description": "The image reference of a container image to use for created jobs. If not set, the latest Prefect image will be used.",
|
1603
|
-
"example": "docker.io/prefecthq/prefect:
|
1594
|
+
"example": "docker.io/prefecthq/prefect:3-latest",
|
1604
1595
|
"type": "string"
|
1605
1596
|
},
|
1606
1597
|
"service_account_name": {
|
@@ -1688,4 +1679,4 @@
|
|
1688
1679
|
"type": "kubernetes"
|
1689
1680
|
}
|
1690
1681
|
}
|
1691
|
-
}
|
1682
|
+
}
|