prefect-client 3.4.0__py3-none-any.whl → 3.4.1__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/__init__.py +1 -1
- prefect/_internal/schemas/bases.py +11 -1
- prefect/_internal/schemas/validators.py +0 -98
- prefect/_internal/uuid7.py +11 -0
- prefect/client/orchestration/__init__.py +16 -8
- prefect/client/schemas/actions.py +13 -35
- prefect/client/schemas/objects.py +26 -22
- prefect/client/subscriptions.py +18 -9
- prefect/events/clients.py +6 -6
- prefect/events/filters.py +25 -11
- prefect/events/schemas/automations.py +3 -1
- prefect/events/schemas/events.py +3 -2
- prefect/flows.py +79 -28
- prefect/runner/_observers.py +60 -0
- prefect/runner/runner.py +71 -213
- prefect/server/api/workers.py +3 -2
- prefect/task_runners.py +2 -1
- prefect/tasks.py +3 -2
- prefect/types/__init__.py +24 -36
- prefect/types/names.py +139 -0
- prefect/utilities/dockerutils.py +18 -8
- prefect/utilities/importtools.py +12 -4
- prefect/workers/base.py +32 -10
- {prefect_client-3.4.0.dist-info → prefect_client-3.4.1.dist-info}/METADATA +2 -1
- {prefect_client-3.4.0.dist-info → prefect_client-3.4.1.dist-info}/RECORD +28 -25
- {prefect_client-3.4.0.dist-info → prefect_client-3.4.1.dist-info}/WHEEL +0 -0
- {prefect_client-3.4.0.dist-info → prefect_client-3.4.1.dist-info}/licenses/LICENSE +0 -0
prefect/server/api/workers.py
CHANGED
@@ -3,7 +3,7 @@ Routes for interacting with work queue objects.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
from typing import TYPE_CHECKING, List, Optional
|
6
|
-
from uuid import UUID
|
6
|
+
from uuid import UUID
|
7
7
|
|
8
8
|
import sqlalchemy as sa
|
9
9
|
from fastapi import (
|
@@ -20,6 +20,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
20
20
|
import prefect.server.api.dependencies as dependencies
|
21
21
|
import prefect.server.models as models
|
22
22
|
import prefect.server.schemas as schemas
|
23
|
+
from prefect._internal.uuid7 import uuid7
|
23
24
|
from prefect.server.api.validation import validate_job_variable_defaults_for_work_pool
|
24
25
|
from prefect.server.database import PrefectDBInterface, provide_database_interface
|
25
26
|
from prefect.server.models.deployments import mark_deployments_ready
|
@@ -184,7 +185,7 @@ async def create_work_pool(
|
|
184
185
|
)
|
185
186
|
|
186
187
|
await emit_work_pool_status_event(
|
187
|
-
event_id=
|
188
|
+
event_id=uuid7(),
|
188
189
|
occurred=now("UTC"),
|
189
190
|
pre_update_work_pool=None,
|
190
191
|
work_pool=model,
|
prefect/task_runners.py
CHANGED
@@ -21,6 +21,7 @@ from typing import (
|
|
21
21
|
|
22
22
|
from typing_extensions import ParamSpec, Self, TypeVar
|
23
23
|
|
24
|
+
from prefect._internal.uuid7 import uuid7
|
24
25
|
from prefect.client.schemas.objects import TaskRunInput
|
25
26
|
from prefect.exceptions import MappingLengthMismatch, MappingMissingIterable
|
26
27
|
from prefect.futures import (
|
@@ -290,7 +291,7 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture[R]]):
|
|
290
291
|
from prefect.context import FlowRunContext
|
291
292
|
from prefect.task_engine import run_task_async, run_task_sync
|
292
293
|
|
293
|
-
task_run_id =
|
294
|
+
task_run_id = uuid7()
|
294
295
|
cancel_event = threading.Event()
|
295
296
|
self._cancel_events[task_run_id] = cancel_event
|
296
297
|
context = copy_context()
|
prefect/tasks.py
CHANGED
@@ -32,6 +32,7 @@ from uuid import UUID, uuid4
|
|
32
32
|
from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypeIs
|
33
33
|
|
34
34
|
import prefect.states
|
35
|
+
from prefect._internal.uuid7 import uuid7
|
35
36
|
from prefect.cache_policies import DEFAULT, NO_CACHE, CachePolicy
|
36
37
|
from prefect.client.orchestration import get_client
|
37
38
|
from prefect.client.schemas import TaskRun
|
@@ -953,7 +954,7 @@ class Task(Generic[P, R]):
|
|
953
954
|
if flow_run_context and flow_run_context.flow_run
|
954
955
|
else None
|
955
956
|
)
|
956
|
-
task_run_id = id or
|
957
|
+
task_run_id = id or uuid7()
|
957
958
|
state = prefect.states.Pending(
|
958
959
|
state_details=StateDetails(
|
959
960
|
task_run_id=task_run_id,
|
@@ -1551,7 +1552,7 @@ class Task(Generic[P, R]):
|
|
1551
1552
|
validated_state=task_run.state,
|
1552
1553
|
)
|
1553
1554
|
|
1554
|
-
if task_run_url := url_for(task_run):
|
1555
|
+
if get_current_settings().ui_url and (task_run_url := url_for(task_run)):
|
1555
1556
|
logger.info(
|
1556
1557
|
f"Created task run {task_run.name!r}. View it in the UI at {task_run_url!r}"
|
1557
1558
|
)
|
prefect/types/__init__.py
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from functools import partial
|
4
|
-
from typing import Annotated, Any,
|
4
|
+
from typing import Annotated, Any, Optional, TypeVar, Union, cast
|
5
|
+
from uuid import UUID
|
5
6
|
from typing_extensions import Literal
|
6
7
|
import orjson
|
7
8
|
import pydantic
|
8
9
|
|
9
|
-
|
10
10
|
from ._datetime import DateTime, Date
|
11
|
+
from .names import (
|
12
|
+
Name,
|
13
|
+
NameOrEmpty,
|
14
|
+
NonEmptyishName,
|
15
|
+
BANNED_CHARACTERS,
|
16
|
+
WITHOUT_BANNED_CHARACTERS,
|
17
|
+
MAX_VARIABLE_NAME_LENGTH,
|
18
|
+
)
|
11
19
|
from pydantic import (
|
12
20
|
BeforeValidator,
|
13
21
|
Field,
|
@@ -21,7 +29,6 @@ from zoneinfo import available_timezones
|
|
21
29
|
|
22
30
|
T = TypeVar("T")
|
23
31
|
|
24
|
-
MAX_VARIABLE_NAME_LENGTH = 255
|
25
32
|
MAX_VARIABLE_VALUE_LENGTH = 5000
|
26
33
|
|
27
34
|
NonNegativeInteger = Annotated[int, Field(ge=0)]
|
@@ -39,37 +46,14 @@ TimeZone = Annotated[
|
|
39
46
|
]
|
40
47
|
|
41
48
|
|
42
|
-
BANNED_CHARACTERS = ["/", "%", "&", ">", "<"]
|
43
|
-
|
44
|
-
WITHOUT_BANNED_CHARACTERS = r"^[^" + "".join(BANNED_CHARACTERS) + "]+$"
|
45
|
-
Name = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS)]
|
46
|
-
|
47
|
-
WITHOUT_BANNED_CHARACTERS_EMPTY_OK = r"^[^" + "".join(BANNED_CHARACTERS) + "]*$"
|
48
|
-
NameOrEmpty = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS_EMPTY_OK)]
|
49
|
-
|
50
|
-
|
51
|
-
def non_emptyish(value: str) -> str:
|
52
|
-
if not value.strip("' \""):
|
53
|
-
raise ValueError("name cannot be an empty string")
|
54
|
-
|
55
|
-
return value
|
56
|
-
|
57
|
-
|
58
|
-
NonEmptyishName = Annotated[
|
59
|
-
str,
|
60
|
-
Field(pattern=WITHOUT_BANNED_CHARACTERS),
|
61
|
-
BeforeValidator(non_emptyish),
|
62
|
-
]
|
63
|
-
|
64
|
-
|
65
49
|
VariableValue = Union[
|
66
50
|
StrictStr,
|
67
51
|
StrictInt,
|
68
52
|
StrictBool,
|
69
53
|
StrictFloat,
|
70
54
|
None,
|
71
|
-
|
72
|
-
|
55
|
+
dict[str, Any],
|
56
|
+
list[Any],
|
73
57
|
]
|
74
58
|
|
75
59
|
|
@@ -100,24 +84,24 @@ def cast_none_to_empty_dict(value: Any) -> dict[str, Any]:
|
|
100
84
|
|
101
85
|
|
102
86
|
KeyValueLabels = Annotated[
|
103
|
-
|
87
|
+
dict[str, Union[StrictBool, StrictInt, StrictFloat, str]],
|
104
88
|
BeforeValidator(cast_none_to_empty_dict),
|
105
89
|
]
|
106
90
|
|
107
91
|
|
108
92
|
ListOfNonEmptyStrings = Annotated[
|
109
|
-
|
93
|
+
list[str],
|
110
94
|
BeforeValidator(lambda x: [str(s) for s in x if str(s).strip()]),
|
111
95
|
]
|
112
96
|
|
113
97
|
|
114
|
-
class SecretDict(pydantic.Secret[
|
98
|
+
class SecretDict(pydantic.Secret[dict[str, Any]]):
|
115
99
|
pass
|
116
100
|
|
117
101
|
|
118
102
|
def validate_set_T_from_delim_string(
|
119
|
-
value: Union[str, T,
|
120
|
-
) ->
|
103
|
+
value: Union[str, T, set[T], None], type_: Any, delim: str | None = None
|
104
|
+
) -> set[T]:
|
121
105
|
"""
|
122
106
|
"no-info" before validator useful in scooping env vars
|
123
107
|
|
@@ -131,20 +115,20 @@ def validate_set_T_from_delim_string(
|
|
131
115
|
delim = delim or ","
|
132
116
|
if isinstance(value, str):
|
133
117
|
return {T_adapter.validate_strings(s.strip()) for s in value.split(delim)}
|
134
|
-
errors = []
|
118
|
+
errors: list[pydantic.ValidationError] = []
|
135
119
|
try:
|
136
120
|
return {T_adapter.validate_python(value)}
|
137
121
|
except pydantic.ValidationError as e:
|
138
122
|
errors.append(e)
|
139
123
|
try:
|
140
|
-
return TypeAdapter(
|
124
|
+
return TypeAdapter(set[type_]).validate_python(value)
|
141
125
|
except pydantic.ValidationError as e:
|
142
126
|
errors.append(e)
|
143
127
|
raise ValueError(f"Invalid set[{type_}]: {errors}")
|
144
128
|
|
145
129
|
|
146
130
|
ClientRetryExtraCodes = Annotated[
|
147
|
-
Union[str, StatusCode,
|
131
|
+
Union[str, StatusCode, set[StatusCode], None],
|
148
132
|
BeforeValidator(partial(validate_set_T_from_delim_string, type_=StatusCode)),
|
149
133
|
]
|
150
134
|
|
@@ -170,11 +154,15 @@ KeyValueLabelsField = Annotated[
|
|
170
154
|
|
171
155
|
|
172
156
|
__all__ = [
|
157
|
+
"BANNED_CHARACTERS",
|
158
|
+
"WITHOUT_BANNED_CHARACTERS",
|
173
159
|
"ClientRetryExtraCodes",
|
174
160
|
"Date",
|
175
161
|
"DateTime",
|
176
162
|
"LogLevel",
|
177
163
|
"KeyValueLabelsField",
|
164
|
+
"MAX_VARIABLE_NAME_LENGTH",
|
165
|
+
"MAX_VARIABLE_VALUE_LENGTH",
|
178
166
|
"NonNegativeInteger",
|
179
167
|
"PositiveInteger",
|
180
168
|
"ListOfNonEmptyStrings",
|
prefect/types/names.py
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import re
|
4
|
+
from functools import partial
|
5
|
+
from typing import Annotated, overload
|
6
|
+
|
7
|
+
from pydantic import AfterValidator, BeforeValidator, Field
|
8
|
+
|
9
|
+
LOWERCASE_LETTERS_NUMBERS_AND_DASHES_ONLY_REGEX = "^[a-z0-9-]*$"
|
10
|
+
LOWERCASE_LETTERS_NUMBERS_AND_UNDERSCORES_REGEX = "^[a-z0-9_]*$"
|
11
|
+
LOWERCASE_LETTERS_NUMBERS_AND_DASHES_OR_UNDERSCORES_REGEX = "^[a-z0-9-_]*$"
|
12
|
+
|
13
|
+
|
14
|
+
@overload
|
15
|
+
def raise_on_name_alphanumeric_dashes_only(
|
16
|
+
value: str, field_name: str = ...
|
17
|
+
) -> str: ...
|
18
|
+
|
19
|
+
|
20
|
+
@overload
|
21
|
+
def raise_on_name_alphanumeric_dashes_only(
|
22
|
+
value: None, field_name: str = ...
|
23
|
+
) -> None: ...
|
24
|
+
|
25
|
+
|
26
|
+
def raise_on_name_alphanumeric_dashes_only(
|
27
|
+
value: str | None, field_name: str = "value"
|
28
|
+
) -> str | None:
|
29
|
+
if value is not None and not bool(
|
30
|
+
re.match(LOWERCASE_LETTERS_NUMBERS_AND_DASHES_ONLY_REGEX, value)
|
31
|
+
):
|
32
|
+
raise ValueError(
|
33
|
+
f"{field_name} must only contain lowercase letters, numbers, and dashes."
|
34
|
+
)
|
35
|
+
return value
|
36
|
+
|
37
|
+
|
38
|
+
@overload
|
39
|
+
def raise_on_name_alphanumeric_underscores_only(
|
40
|
+
value: str, field_name: str = ...
|
41
|
+
) -> str: ...
|
42
|
+
|
43
|
+
|
44
|
+
@overload
|
45
|
+
def raise_on_name_alphanumeric_underscores_only(
|
46
|
+
value: None, field_name: str = ...
|
47
|
+
) -> None: ...
|
48
|
+
|
49
|
+
|
50
|
+
def raise_on_name_alphanumeric_underscores_only(
|
51
|
+
value: str | None, field_name: str = "value"
|
52
|
+
) -> str | None:
|
53
|
+
if value is not None and not re.match(
|
54
|
+
LOWERCASE_LETTERS_NUMBERS_AND_UNDERSCORES_REGEX, value
|
55
|
+
):
|
56
|
+
raise ValueError(
|
57
|
+
f"{field_name} must only contain lowercase letters, numbers, and"
|
58
|
+
" underscores."
|
59
|
+
)
|
60
|
+
return value
|
61
|
+
|
62
|
+
|
63
|
+
def raise_on_name_alphanumeric_dashes_underscores_only(
|
64
|
+
value: str, field_name: str = "value"
|
65
|
+
) -> str:
|
66
|
+
if not re.match(LOWERCASE_LETTERS_NUMBERS_AND_DASHES_OR_UNDERSCORES_REGEX, value):
|
67
|
+
raise ValueError(
|
68
|
+
f"{field_name} must only contain lowercase letters, numbers, and"
|
69
|
+
" dashes or underscores."
|
70
|
+
)
|
71
|
+
return value
|
72
|
+
|
73
|
+
|
74
|
+
BANNED_CHARACTERS = ["/", "%", "&", ">", "<"]
|
75
|
+
|
76
|
+
WITHOUT_BANNED_CHARACTERS = r"^[^" + "".join(BANNED_CHARACTERS) + "]+$"
|
77
|
+
Name = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS)]
|
78
|
+
|
79
|
+
WITHOUT_BANNED_CHARACTERS_EMPTY_OK = r"^[^" + "".join(BANNED_CHARACTERS) + "]*$"
|
80
|
+
NameOrEmpty = Annotated[str, Field(pattern=WITHOUT_BANNED_CHARACTERS_EMPTY_OK)]
|
81
|
+
|
82
|
+
|
83
|
+
def non_emptyish(value: str) -> str:
|
84
|
+
if not value.strip("' \""):
|
85
|
+
raise ValueError("name cannot be an empty string")
|
86
|
+
|
87
|
+
return value
|
88
|
+
|
89
|
+
|
90
|
+
NonEmptyishName = Annotated[
|
91
|
+
str,
|
92
|
+
Field(pattern=WITHOUT_BANNED_CHARACTERS),
|
93
|
+
BeforeValidator(non_emptyish),
|
94
|
+
]
|
95
|
+
|
96
|
+
|
97
|
+
### specific names
|
98
|
+
|
99
|
+
BlockDocumentName = Annotated[
|
100
|
+
Name,
|
101
|
+
AfterValidator(
|
102
|
+
partial(
|
103
|
+
raise_on_name_alphanumeric_dashes_only, field_name="Block document name"
|
104
|
+
)
|
105
|
+
),
|
106
|
+
]
|
107
|
+
|
108
|
+
|
109
|
+
BlockTypeSlug = Annotated[
|
110
|
+
str,
|
111
|
+
AfterValidator(
|
112
|
+
partial(raise_on_name_alphanumeric_dashes_only, field_name="Block type slug")
|
113
|
+
),
|
114
|
+
]
|
115
|
+
|
116
|
+
ArtifactKey = Annotated[
|
117
|
+
str,
|
118
|
+
AfterValidator(
|
119
|
+
partial(raise_on_name_alphanumeric_dashes_only, field_name="Artifact key")
|
120
|
+
),
|
121
|
+
]
|
122
|
+
|
123
|
+
MAX_VARIABLE_NAME_LENGTH = 255
|
124
|
+
|
125
|
+
|
126
|
+
VariableName = Annotated[
|
127
|
+
str,
|
128
|
+
AfterValidator(
|
129
|
+
partial(
|
130
|
+
raise_on_name_alphanumeric_dashes_underscores_only,
|
131
|
+
field_name="Variable name",
|
132
|
+
)
|
133
|
+
),
|
134
|
+
Field(
|
135
|
+
max_length=MAX_VARIABLE_NAME_LENGTH,
|
136
|
+
description="The name of the variable",
|
137
|
+
examples=["my_variable"],
|
138
|
+
),
|
139
|
+
]
|
prefect/utilities/dockerutils.py
CHANGED
@@ -495,10 +495,11 @@ def parse_image_tag(name: str) -> tuple[str, Optional[str]]:
|
|
495
495
|
"""
|
496
496
|
Parse Docker Image String
|
497
497
|
|
498
|
-
- If a tag exists, this function parses and returns the image registry and tag,
|
498
|
+
- If a tag or digest exists, this function parses and returns the image registry and tag/digest,
|
499
499
|
separately as a tuple.
|
500
500
|
- Example 1: 'prefecthq/prefect:latest' -> ('prefecthq/prefect', 'latest')
|
501
501
|
- Example 2: 'hostname.io:5050/folder/subfolder:latest' -> ('hostname.io:5050/folder/subfolder', 'latest')
|
502
|
+
- Example 3: 'prefecthq/prefect@sha256:abc123' -> ('prefecthq/prefect', 'sha256:abc123')
|
502
503
|
- Supports parsing Docker Image strings that follow Docker Image Specification v1.1.0
|
503
504
|
- Image building tools typically enforce this standard
|
504
505
|
|
@@ -506,26 +507,35 @@ def parse_image_tag(name: str) -> tuple[str, Optional[str]]:
|
|
506
507
|
name (str): Name of Docker Image
|
507
508
|
|
508
509
|
Return:
|
509
|
-
tuple: image registry, image tag
|
510
|
+
tuple: image registry, image tag/digest
|
510
511
|
"""
|
511
512
|
tag = None
|
512
513
|
name_parts = name.split("/")
|
513
|
-
|
514
|
-
# -
|
514
|
+
|
515
|
+
# First handles the simplest image names (DockerHub-based, index-free, potentially with a tag or digest)
|
516
|
+
# - Example: simplename:latest or simplename@sha256:abc123
|
515
517
|
if len(name_parts) == 1:
|
516
|
-
if "
|
518
|
+
if "@" in name_parts[0]:
|
519
|
+
image_name, tag = name_parts[0].split("@")
|
520
|
+
elif ":" in name_parts[0]:
|
517
521
|
image_name, tag = name_parts[0].split(":")
|
522
|
+
|
518
523
|
else:
|
519
524
|
image_name = name_parts[0]
|
520
525
|
else:
|
521
526
|
# 1. Separates index (hostname.io or prefecthq) from path:tag (folder/subfolder:latest or prefect:latest)
|
522
|
-
# 2. Separates path and tag (if
|
523
|
-
# 3. Reunites index and path (without tag) as image name
|
527
|
+
# 2. Separates path and tag/digest (if exists)
|
528
|
+
# 3. Reunites index and path (without tag/digest) as image name
|
524
529
|
index_name = name_parts[0]
|
525
530
|
image_path = "/".join(name_parts[1:])
|
526
|
-
|
531
|
+
|
532
|
+
if "@" in image_path:
|
533
|
+
image_path, tag = image_path.split("@")
|
534
|
+
elif ":" in image_path:
|
527
535
|
image_path, tag = image_path.split(":")
|
536
|
+
|
528
537
|
image_name = f"{index_name}/{image_path}"
|
538
|
+
|
529
539
|
return image_name, tag
|
530
540
|
|
531
541
|
|
prefect/utilities/importtools.py
CHANGED
@@ -145,17 +145,19 @@ def import_object(import_path: str) -> Any:
|
|
145
145
|
- module.object
|
146
146
|
- module:object
|
147
147
|
- /path/to/script.py:object
|
148
|
+
- module:object.method
|
149
|
+
- /path/to/script.py:object.method
|
148
150
|
|
149
151
|
This function is not thread safe as it modifies the 'sys' module during execution.
|
150
152
|
"""
|
151
153
|
if ".py:" in import_path:
|
152
|
-
script_path,
|
154
|
+
script_path, object_path = import_path.rsplit(":", 1)
|
153
155
|
module = load_script_as_module(script_path)
|
154
156
|
else:
|
155
157
|
if ":" in import_path:
|
156
|
-
module_name,
|
158
|
+
module_name, object_path = import_path.rsplit(":", 1)
|
157
159
|
elif "." in import_path:
|
158
|
-
module_name,
|
160
|
+
module_name, object_path = import_path.rsplit(".", 1)
|
159
161
|
else:
|
160
162
|
raise ValueError(
|
161
163
|
f"Invalid format for object import. Received {import_path!r}."
|
@@ -163,7 +165,13 @@ def import_object(import_path: str) -> Any:
|
|
163
165
|
|
164
166
|
module = load_module(module_name)
|
165
167
|
|
166
|
-
|
168
|
+
# Handle nested object/method access
|
169
|
+
parts = object_path.split(".")
|
170
|
+
obj = module
|
171
|
+
for part in parts:
|
172
|
+
obj = getattr(obj, part)
|
173
|
+
|
174
|
+
return obj
|
167
175
|
|
168
176
|
|
169
177
|
class DelayedImportErrorModule(ModuleType):
|
prefect/workers/base.py
CHANGED
@@ -697,6 +697,25 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
697
697
|
"Workers must implement a method for running submitted flow runs"
|
698
698
|
)
|
699
699
|
|
700
|
+
async def _initiate_run(
|
701
|
+
self,
|
702
|
+
flow_run: "FlowRun",
|
703
|
+
configuration: C,
|
704
|
+
) -> None:
|
705
|
+
"""
|
706
|
+
This method is called by the worker to initiate a flow run and should return as
|
707
|
+
soon as possible.
|
708
|
+
|
709
|
+
This method is used in `.submit` to allow non-blocking submission of flows. For
|
710
|
+
workers that wait for completion in their `run` method, this method should be
|
711
|
+
implemented to return immediately.
|
712
|
+
|
713
|
+
If this method is not implemented, `.submit` will fall back to the `.run` method.
|
714
|
+
"""
|
715
|
+
raise NotImplementedError(
|
716
|
+
"This worker has not implemented `_initiate_run`. Please use `run` instead."
|
717
|
+
)
|
718
|
+
|
700
719
|
async def submit(
|
701
720
|
self,
|
702
721
|
flow: "Flow[..., FR]",
|
@@ -866,16 +885,19 @@ class BaseWorker(abc.ABC, Generic[C, V, R]):
|
|
866
885
|
try:
|
867
886
|
# Call the implementation-specific run method with the constructed configuration. This is where the
|
868
887
|
# rubber meets the road.
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
await self.
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
888
|
+
try:
|
889
|
+
await self._initiate_run(flow_run, configuration)
|
890
|
+
except NotImplementedError:
|
891
|
+
result = await self.run(flow_run, configuration)
|
892
|
+
|
893
|
+
if result.status_code != 0:
|
894
|
+
await self._propose_crashed_state(
|
895
|
+
flow_run,
|
896
|
+
(
|
897
|
+
"Flow run infrastructure exited with non-zero status code"
|
898
|
+
f" {result.status_code}."
|
899
|
+
),
|
900
|
+
)
|
879
901
|
except Exception as exc:
|
880
902
|
# This flow run was being submitted and did not start successfully
|
881
903
|
logger.exception(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: prefect-client
|
3
|
-
Version: 3.4.
|
3
|
+
Version: 3.4.1
|
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
|
@@ -60,6 +60,7 @@ Requires-Dist: sniffio<2.0.0,>=1.3.0
|
|
60
60
|
Requires-Dist: toml>=0.10.0
|
61
61
|
Requires-Dist: typing-extensions<5.0.0,>=4.10.0
|
62
62
|
Requires-Dist: ujson<6.0.0,>=5.8.0
|
63
|
+
Requires-Dist: uuid7>=0.1.0
|
63
64
|
Requires-Dist: uvicorn!=0.29.0,>=0.14.0
|
64
65
|
Requires-Dist: websockets<16.0,>=13.0
|
65
66
|
Requires-Dist: whenever<0.9.0,>=0.7.3; python_version >= '3.13'
|