prefect-client 3.4.1.dev4__py3-none-any.whl → 3.4.1.dev5__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/_internal/schemas/validators.py +0 -102
- prefect/client/schemas/actions.py +13 -35
- prefect/client/schemas/objects.py +14 -15
- prefect/types/__init__.py +23 -35
- prefect/types/names.py +139 -0
- prefect/workers/base.py +32 -10
- {prefect_client-3.4.1.dev4.dist-info → prefect_client-3.4.1.dev5.dist-info}/METADATA +1 -1
- {prefect_client-3.4.1.dev4.dist-info → prefect_client-3.4.1.dev5.dist-info}/RECORD +11 -10
- {prefect_client-3.4.1.dev4.dist-info → prefect_client-3.4.1.dev5.dist-info}/WHEEL +0 -0
- {prefect_client-3.4.1.dev4.dist-info → prefect_client-3.4.1.dev5.dist-info}/licenses/LICENSE +0 -0
prefect/_build_info.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Generated by versioningit
|
2
|
-
__version__ = "3.4.1.
|
3
|
-
__build_date__ = "2025-05-
|
4
|
-
__git_commit__ = "
|
2
|
+
__version__ = "3.4.1.dev5"
|
3
|
+
__build_date__ = "2025-05-08 08:08:59.726919+00:00"
|
4
|
+
__git_commit__ = "7e7a18743f5fd0bd4e513ca22c5408e7176390b8"
|
5
5
|
__dirty__ = False
|
@@ -10,7 +10,6 @@ from __future__ import annotations
|
|
10
10
|
|
11
11
|
import datetime
|
12
12
|
import os
|
13
|
-
import re
|
14
13
|
import urllib.parse
|
15
14
|
import warnings
|
16
15
|
from collections.abc import Iterable, Mapping, MutableMapping
|
@@ -36,60 +35,6 @@ M = TypeVar("M", bound=Mapping[str, Any])
|
|
36
35
|
MM = TypeVar("MM", bound=MutableMapping[str, Any])
|
37
36
|
|
38
37
|
|
39
|
-
LOWERCASE_LETTERS_NUMBERS_AND_DASHES_ONLY_REGEX = "^[a-z0-9-]*$"
|
40
|
-
LOWERCASE_LETTERS_NUMBERS_AND_UNDERSCORES_REGEX = "^[a-z0-9_]*$"
|
41
|
-
SLUG_REGEX = "^[a-z0-9]+([_-]+[a-z0-9]+)*$"
|
42
|
-
|
43
|
-
|
44
|
-
@overload
|
45
|
-
def raise_on_name_alphanumeric_dashes_only(
|
46
|
-
value: str, field_name: str = ...
|
47
|
-
) -> str: ...
|
48
|
-
|
49
|
-
|
50
|
-
@overload
|
51
|
-
def raise_on_name_alphanumeric_dashes_only(
|
52
|
-
value: None, field_name: str = ...
|
53
|
-
) -> None: ...
|
54
|
-
|
55
|
-
|
56
|
-
def raise_on_name_alphanumeric_dashes_only(
|
57
|
-
value: Optional[str], field_name: str = "value"
|
58
|
-
) -> Optional[str]:
|
59
|
-
if value is not None and not bool(
|
60
|
-
re.match(LOWERCASE_LETTERS_NUMBERS_AND_DASHES_ONLY_REGEX, value)
|
61
|
-
):
|
62
|
-
raise ValueError(
|
63
|
-
f"{field_name} must only contain lowercase letters, numbers, and dashes."
|
64
|
-
)
|
65
|
-
return value
|
66
|
-
|
67
|
-
|
68
|
-
@overload
|
69
|
-
def raise_on_name_alphanumeric_underscores_only(
|
70
|
-
value: str, field_name: str = ...
|
71
|
-
) -> str: ...
|
72
|
-
|
73
|
-
|
74
|
-
@overload
|
75
|
-
def raise_on_name_alphanumeric_underscores_only(
|
76
|
-
value: None, field_name: str = ...
|
77
|
-
) -> None: ...
|
78
|
-
|
79
|
-
|
80
|
-
def raise_on_name_alphanumeric_underscores_only(
|
81
|
-
value: Optional[str], field_name: str = "value"
|
82
|
-
) -> Optional[str]:
|
83
|
-
if value is not None and not re.match(
|
84
|
-
LOWERCASE_LETTERS_NUMBERS_AND_UNDERSCORES_REGEX, value
|
85
|
-
):
|
86
|
-
raise ValueError(
|
87
|
-
f"{field_name} must only contain lowercase letters, numbers, and"
|
88
|
-
" underscores."
|
89
|
-
)
|
90
|
-
return value
|
91
|
-
|
92
|
-
|
93
38
|
def validate_values_conform_to_schema(
|
94
39
|
values: Optional[Mapping[str, Any]],
|
95
40
|
schema: Optional[Mapping[str, Any]],
|
@@ -611,50 +556,3 @@ def validate_working_dir(v: Optional[Path | str]) -> Optional[Path]:
|
|
611
556
|
if isinstance(v, str):
|
612
557
|
return relative_path_to_current_platform(v)
|
613
558
|
return v
|
614
|
-
|
615
|
-
|
616
|
-
### UNCATEGORIZED VALIDATORS ###
|
617
|
-
|
618
|
-
# the above categories seem to be getting a bit unwieldy, so this is a temporary
|
619
|
-
# catch-all for validators until we organize these into files
|
620
|
-
|
621
|
-
|
622
|
-
@overload
|
623
|
-
def validate_block_document_name(value: str) -> str: ...
|
624
|
-
|
625
|
-
|
626
|
-
@overload
|
627
|
-
def validate_block_document_name(value: None) -> None: ...
|
628
|
-
|
629
|
-
|
630
|
-
def validate_block_document_name(value: Optional[str]) -> Optional[str]:
|
631
|
-
if value is not None:
|
632
|
-
raise_on_name_alphanumeric_dashes_only(value, field_name="Block document name")
|
633
|
-
return value
|
634
|
-
|
635
|
-
|
636
|
-
def validate_artifact_key(value: str) -> str:
|
637
|
-
raise_on_name_alphanumeric_dashes_only(value, field_name="Artifact key")
|
638
|
-
return value
|
639
|
-
|
640
|
-
|
641
|
-
@overload
|
642
|
-
def validate_variable_name(value: str) -> str: ...
|
643
|
-
|
644
|
-
|
645
|
-
@overload
|
646
|
-
def validate_variable_name(value: None) -> None: ...
|
647
|
-
|
648
|
-
|
649
|
-
def validate_variable_name(value: Optional[str]) -> Optional[str]:
|
650
|
-
if value is not None:
|
651
|
-
if not bool(re.match(SLUG_REGEX, value)):
|
652
|
-
raise ValueError(
|
653
|
-
"Variable name must only contain lowercase letters, numbers, dashes, and underscores"
|
654
|
-
)
|
655
|
-
return value
|
656
|
-
|
657
|
-
|
658
|
-
def validate_block_type_slug(value: str):
|
659
|
-
raise_on_name_alphanumeric_dashes_only(value, field_name="Block type slug")
|
660
|
-
return value
|
@@ -12,12 +12,8 @@ from prefect._internal.schemas.bases import ActionBaseModel
|
|
12
12
|
from prefect._internal.schemas.validators import (
|
13
13
|
convert_to_strings,
|
14
14
|
remove_old_deployment_fields,
|
15
|
-
validate_artifact_key,
|
16
|
-
validate_block_document_name,
|
17
|
-
validate_block_type_slug,
|
18
15
|
validate_name_present_on_nonanonymous_blocks,
|
19
16
|
validate_schedule_max_scheduled_runs,
|
20
|
-
validate_variable_name,
|
21
17
|
)
|
22
18
|
from prefect.client.schemas.objects import (
|
23
19
|
StateDetails,
|
@@ -34,7 +30,6 @@ from prefect.client.schemas.schedules import (
|
|
34
30
|
from prefect.schedules import Schedule
|
35
31
|
from prefect.settings import PREFECT_DEPLOYMENT_SCHEDULE_MAX_SCHEDULED_RUNS
|
36
32
|
from prefect.types import (
|
37
|
-
MAX_VARIABLE_NAME_LENGTH,
|
38
33
|
DateTime,
|
39
34
|
KeyValueLabelsField,
|
40
35
|
Name,
|
@@ -45,6 +40,12 @@ from prefect.types import (
|
|
45
40
|
PositiveInteger,
|
46
41
|
StrictVariableValue,
|
47
42
|
)
|
43
|
+
from prefect.types.names import (
|
44
|
+
ArtifactKey,
|
45
|
+
BlockDocumentName,
|
46
|
+
BlockTypeSlug,
|
47
|
+
VariableName,
|
48
|
+
)
|
48
49
|
from prefect.utilities.collections import visit_collection
|
49
50
|
from prefect.utilities.pydantic import get_class_fields_only
|
50
51
|
|
@@ -216,7 +217,7 @@ class DeploymentCreate(ActionBaseModel):
|
|
216
217
|
flow_id: UUID = Field(..., description="The ID of the flow to deploy.")
|
217
218
|
paused: Optional[bool] = Field(default=None)
|
218
219
|
schedules: list[DeploymentScheduleCreate] = Field(
|
219
|
-
default_factory=
|
220
|
+
default_factory=lambda: [],
|
220
221
|
description="A list of schedules for the deployment.",
|
221
222
|
)
|
222
223
|
concurrency_limit: Optional[int] = Field(
|
@@ -537,7 +538,7 @@ class SavedSearchCreate(ActionBaseModel):
|
|
537
538
|
|
538
539
|
name: str = Field(default=..., description="The name of the saved search.")
|
539
540
|
filters: list[objects.SavedSearchFilter] = Field(
|
540
|
-
default_factory=
|
541
|
+
default_factory=lambda: [], description="The filter set for the saved search."
|
541
542
|
)
|
542
543
|
|
543
544
|
|
@@ -585,7 +586,7 @@ class BlockTypeCreate(ActionBaseModel):
|
|
585
586
|
"""Data used by the Prefect REST API to create a block type."""
|
586
587
|
|
587
588
|
name: str = Field(default=..., description="A block type's name")
|
588
|
-
slug:
|
589
|
+
slug: BlockTypeSlug = Field(default=..., description="A block type's slug")
|
589
590
|
logo_url: Optional[objects.HttpUrl] = Field(
|
590
591
|
default=None, description="Web URL for the block type's logo"
|
591
592
|
)
|
@@ -601,9 +602,6 @@ class BlockTypeCreate(ActionBaseModel):
|
|
601
602
|
description="A code snippet demonstrating use of the corresponding block",
|
602
603
|
)
|
603
604
|
|
604
|
-
# validators
|
605
|
-
_validate_slug_format = field_validator("slug")(validate_block_type_slug)
|
606
|
-
|
607
605
|
|
608
606
|
class BlockTypeUpdate(ActionBaseModel):
|
609
607
|
"""Data used by the Prefect REST API to update a block type."""
|
@@ -638,7 +636,7 @@ class BlockSchemaCreate(ActionBaseModel):
|
|
638
636
|
class BlockDocumentCreate(ActionBaseModel):
|
639
637
|
"""Data used by the Prefect REST API to create a block document."""
|
640
638
|
|
641
|
-
name: Optional[
|
639
|
+
name: Optional[BlockDocumentName] = Field(
|
642
640
|
default=None, description="The name of the block document"
|
643
641
|
)
|
644
642
|
data: dict[str, Any] = Field(
|
@@ -658,8 +656,6 @@ class BlockDocumentCreate(ActionBaseModel):
|
|
658
656
|
),
|
659
657
|
)
|
660
658
|
|
661
|
-
_validate_name_format = field_validator("name")(validate_block_document_name)
|
662
|
-
|
663
659
|
@model_validator(mode="before")
|
664
660
|
def validate_name_is_present_if_not_anonymous(
|
665
661
|
cls, values: dict[str, Any]
|
@@ -814,7 +810,7 @@ class WorkQueueUpdate(ActionBaseModel):
|
|
814
810
|
class ArtifactCreate(ActionBaseModel):
|
815
811
|
"""Data used by the Prefect REST API to create an artifact."""
|
816
812
|
|
817
|
-
key: Optional[
|
813
|
+
key: Optional[ArtifactKey] = Field(default=None)
|
818
814
|
type: Optional[str] = Field(default=None)
|
819
815
|
description: Optional[str] = Field(default=None)
|
820
816
|
data: Optional[Union[dict[str, Any], Any]] = Field(default=None)
|
@@ -822,8 +818,6 @@ class ArtifactCreate(ActionBaseModel):
|
|
822
818
|
flow_run_id: Optional[UUID] = Field(default=None)
|
823
819
|
task_run_id: Optional[UUID] = Field(default=None)
|
824
820
|
|
825
|
-
_validate_artifact_format = field_validator("key")(validate_artifact_key)
|
826
|
-
|
827
821
|
|
828
822
|
class ArtifactUpdate(ActionBaseModel):
|
829
823
|
"""Data used by the Prefect REST API to update an artifact."""
|
@@ -836,12 +830,7 @@ class ArtifactUpdate(ActionBaseModel):
|
|
836
830
|
class VariableCreate(ActionBaseModel):
|
837
831
|
"""Data used by the Prefect REST API to create a Variable."""
|
838
832
|
|
839
|
-
name:
|
840
|
-
default=...,
|
841
|
-
description="The name of the variable",
|
842
|
-
examples=["my_variable"],
|
843
|
-
max_length=MAX_VARIABLE_NAME_LENGTH,
|
844
|
-
)
|
833
|
+
name: VariableName = Field(default=...)
|
845
834
|
value: StrictVariableValue = Field(
|
846
835
|
default=...,
|
847
836
|
description="The value of the variable",
|
@@ -849,19 +838,11 @@ class VariableCreate(ActionBaseModel):
|
|
849
838
|
)
|
850
839
|
tags: Optional[list[str]] = Field(default=None)
|
851
840
|
|
852
|
-
# validators
|
853
|
-
_validate_name_format = field_validator("name")(validate_variable_name)
|
854
|
-
|
855
841
|
|
856
842
|
class VariableUpdate(ActionBaseModel):
|
857
843
|
"""Data used by the Prefect REST API to update a Variable."""
|
858
844
|
|
859
|
-
name: Optional[
|
860
|
-
default=None,
|
861
|
-
description="The name of the variable",
|
862
|
-
examples=["my_variable"],
|
863
|
-
max_length=MAX_VARIABLE_NAME_LENGTH,
|
864
|
-
)
|
845
|
+
name: Optional[VariableName] = Field(default=None)
|
865
846
|
value: StrictVariableValue = Field(
|
866
847
|
default=None,
|
867
848
|
description="The value of the variable",
|
@@ -869,9 +850,6 @@ class VariableUpdate(ActionBaseModel):
|
|
869
850
|
)
|
870
851
|
tags: Optional[list[str]] = Field(default=None)
|
871
852
|
|
872
|
-
# validators
|
873
|
-
_validate_name_format = field_validator("name")(validate_variable_name)
|
874
|
-
|
875
853
|
|
876
854
|
class GlobalConcurrencyLimitCreate(ActionBaseModel):
|
877
855
|
"""Data used by the Prefect REST API to create a global concurrency limit."""
|
@@ -19,6 +19,7 @@ from uuid import UUID, uuid4
|
|
19
19
|
|
20
20
|
import orjson
|
21
21
|
from pydantic import (
|
22
|
+
AfterValidator,
|
22
23
|
ConfigDict,
|
23
24
|
Discriminator,
|
24
25
|
Field,
|
@@ -40,9 +41,7 @@ from prefect._internal.schemas.fields import CreatedBy, UpdatedBy
|
|
40
41
|
from prefect._internal.schemas.validators import (
|
41
42
|
get_or_create_run_name,
|
42
43
|
list_length_50_or_less,
|
43
|
-
raise_on_name_alphanumeric_dashes_only,
|
44
44
|
set_run_policy_deprecated_fields,
|
45
|
-
validate_block_document_name,
|
46
45
|
validate_default_queue_id_not_none,
|
47
46
|
validate_max_metadata_length,
|
48
47
|
validate_name_present_on_nonanonymous_blocks,
|
@@ -61,6 +60,10 @@ from prefect.types import (
|
|
61
60
|
StrictVariableValue,
|
62
61
|
)
|
63
62
|
from prefect.types._datetime import DateTime, now
|
63
|
+
from prefect.types.names import (
|
64
|
+
BlockDocumentName,
|
65
|
+
raise_on_name_alphanumeric_dashes_only,
|
66
|
+
)
|
64
67
|
from prefect.utilities.asyncutils import run_coro_as_sync
|
65
68
|
from prefect.utilities.collections import AutoEnum, visit_collection
|
66
69
|
from prefect.utilities.names import generate_slug
|
@@ -1000,7 +1003,7 @@ class BlockSchema(ObjectBaseModel):
|
|
1000
1003
|
class BlockDocument(ObjectBaseModel):
|
1001
1004
|
"""An ORM representation of a block document."""
|
1002
1005
|
|
1003
|
-
name: Optional[
|
1006
|
+
name: Optional[BlockDocumentName] = Field(
|
1004
1007
|
default=None,
|
1005
1008
|
description=(
|
1006
1009
|
"The block document's name. Not required for anonymous block documents."
|
@@ -1029,8 +1032,6 @@ class BlockDocument(ObjectBaseModel):
|
|
1029
1032
|
),
|
1030
1033
|
)
|
1031
1034
|
|
1032
|
-
_validate_name_format = field_validator("name")(validate_block_document_name)
|
1033
|
-
|
1034
1035
|
@model_validator(mode="before")
|
1035
1036
|
@classmethod
|
1036
1037
|
def validate_name_is_present_if_not_anonymous(
|
@@ -1133,7 +1134,8 @@ class Deployment(ObjectBaseModel):
|
|
1133
1134
|
default=None, description="The concurrency limit for the deployment."
|
1134
1135
|
)
|
1135
1136
|
schedules: list[DeploymentSchedule] = Field(
|
1136
|
-
default_factory=
|
1137
|
+
default_factory=lambda: [],
|
1138
|
+
description="A list of schedules for the deployment.",
|
1137
1139
|
)
|
1138
1140
|
job_variables: dict[str, Any] = Field(
|
1139
1141
|
default_factory=dict,
|
@@ -1219,7 +1221,7 @@ class ConcurrencyLimit(ObjectBaseModel):
|
|
1219
1221
|
)
|
1220
1222
|
concurrency_limit: int = Field(default=..., description="The concurrency limit.")
|
1221
1223
|
active_slots: list[UUID] = Field(
|
1222
|
-
default_factory=
|
1224
|
+
default_factory=lambda: [],
|
1223
1225
|
description="A list of active run ids using a concurrency slot",
|
1224
1226
|
)
|
1225
1227
|
|
@@ -1300,7 +1302,8 @@ class SavedSearch(ObjectBaseModel):
|
|
1300
1302
|
|
1301
1303
|
name: str = Field(default=..., description="The name of the saved search.")
|
1302
1304
|
filters: list[SavedSearchFilter] = Field(
|
1303
|
-
default_factory=
|
1305
|
+
default_factory=lambda: [],
|
1306
|
+
description="The filter set for the saved search.",
|
1304
1307
|
)
|
1305
1308
|
|
1306
1309
|
|
@@ -1644,7 +1647,9 @@ class Variable(ObjectBaseModel):
|
|
1644
1647
|
|
1645
1648
|
class FlowRunInput(ObjectBaseModel):
|
1646
1649
|
flow_run_id: UUID = Field(description="The flow run ID associated with the input.")
|
1647
|
-
key: str = Field(
|
1650
|
+
key: Annotated[str, AfterValidator(raise_on_name_alphanumeric_dashes_only)] = Field(
|
1651
|
+
description="The key of the input."
|
1652
|
+
)
|
1648
1653
|
value: str = Field(description="The value of the input.")
|
1649
1654
|
sender: Optional[str] = Field(default=None, description="The sender of the input.")
|
1650
1655
|
|
@@ -1658,12 +1663,6 @@ class FlowRunInput(ObjectBaseModel):
|
|
1658
1663
|
"""
|
1659
1664
|
return orjson.loads(self.value)
|
1660
1665
|
|
1661
|
-
@field_validator("key", check_fields=False)
|
1662
|
-
@classmethod
|
1663
|
-
def validate_name_characters(cls, v: str) -> str:
|
1664
|
-
raise_on_name_alphanumeric_dashes_only(v)
|
1665
|
-
return v
|
1666
|
-
|
1667
1666
|
|
1668
1667
|
class GlobalConcurrencyLimit(ObjectBaseModel):
|
1669
1668
|
"""An ORM representation of a global concurrency limit"""
|
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
|
5
5
|
from typing_extensions import Literal
|
6
6
|
import orjson
|
7
7
|
import pydantic
|
8
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/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,7 +1,7 @@
|
|
1
1
|
prefect/.prefectignore,sha256=awSprvKT0vI8a64mEOLrMxhxqcO-b0ERQeYpA2rNKVQ,390
|
2
2
|
prefect/__init__.py,sha256=iCdcC5ZmeewikCdnPEP6YBAjPNV5dvfxpYCTpw30Hkw,3685
|
3
3
|
prefect/__main__.py,sha256=WFjw3kaYJY6pOTA7WDOgqjsz8zUEUZHCcj3P5wyVa-g,66
|
4
|
-
prefect/_build_info.py,sha256=
|
4
|
+
prefect/_build_info.py,sha256=X9CjltSx8CpEa5y-u1RGzeOI83l9BsXPZ3myZoXzOTg,185
|
5
5
|
prefect/_result_records.py,sha256=S6QmsODkehGVSzbMm6ig022PYbI6gNKz671p_8kBYx4,7789
|
6
6
|
prefect/_versioning.py,sha256=YqR5cxXrY4P6LM1Pmhd8iMo7v_G2KJpGNdsf4EvDFQ0,14132
|
7
7
|
prefect/_waiters.py,sha256=Ia2ITaXdHzevtyWIgJoOg95lrEXQqNEOquHvw3T33UQ,9026
|
@@ -66,7 +66,7 @@ prefect/_internal/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
|
|
66
66
|
prefect/_internal/schemas/bases.py,sha256=JqcZazL5Cp2hZ8Hssu8R2SVXRxHfbdRbTqmvwDYSzyk,4291
|
67
67
|
prefect/_internal/schemas/fields.py,sha256=m4LrFNz8rA9uBhMk9VyQT6FIXmV_EVAW92hdXeSvHbY,837
|
68
68
|
prefect/_internal/schemas/serializers.py,sha256=G_RGHfObjisUiRvd29p-zc6W4bwt5rE1OdR6TXNrRhQ,825
|
69
|
-
prefect/_internal/schemas/validators.py,sha256=
|
69
|
+
prefect/_internal/schemas/validators.py,sha256=bOtuOYHWfRo-i6zqkE-wvCMXQ3Yww-itj86QLp3yu3Y,16681
|
70
70
|
prefect/_vendor/croniter/__init__.py,sha256=NUFzdbyPcTQhIOFtzmFM0nbClAvBbKh2mlnTBa6NfHU,523
|
71
71
|
prefect/_vendor/croniter/croniter.py,sha256=eJ2HzStNAYV-vNiLOgDXl4sYWWHOsSA0dgwbkQoguhY,53009
|
72
72
|
prefect/blocks/__init__.py,sha256=D0hB72qMfgqnBB2EMZRxUxlX9yLfkab5zDChOwJZmkY,220
|
@@ -112,9 +112,9 @@ prefect/client/orchestration/_variables/client.py,sha256=wKBbZBLGgs5feDCil-xxKt3
|
|
112
112
|
prefect/client/orchestration/_work_pools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
113
113
|
prefect/client/orchestration/_work_pools/client.py,sha256=s1DfUQQBgB2sLiVVPhLNTlkueUDE6uFsh4mAzcSA1OE,19881
|
114
114
|
prefect/client/schemas/__init__.py,sha256=InZcDzdeWA2oaV0TlyvoMcyLcbi_aaqU1U9D6Gx-eoU,2747
|
115
|
-
prefect/client/schemas/actions.py,sha256=
|
115
|
+
prefect/client/schemas/actions.py,sha256=E46Mdq7vAq8hhYmMj6zqUF20uAPXZricViZcIYmgEf0,32443
|
116
116
|
prefect/client/schemas/filters.py,sha256=qa--NNZduuSOcL1xw-YMd4FVIKMrDnBwPPY4m5Di0GA,35963
|
117
|
-
prefect/client/schemas/objects.py,sha256=
|
117
|
+
prefect/client/schemas/objects.py,sha256=pmu3CGQ62LYHgS0bEDS_s2XDwtkuR17BYbM5_6vGcWg,57755
|
118
118
|
prefect/client/schemas/responses.py,sha256=Zdcx7jlIaluEa2uYIOE5mK1HsJvWPErRAcaWM20oY_I,17336
|
119
119
|
prefect/client/schemas/schedules.py,sha256=sxLFk0SmFY7X1Y9R9HyGDqOS3U5NINBWTciUU7vTTic,14836
|
120
120
|
prefect/client/schemas/sorting.py,sha256=L-2Mx-igZPtsUoRUguTcG3nIEstMEMPD97NwPM2Ox5s,2579
|
@@ -276,9 +276,10 @@ prefect/telemetry/logging.py,sha256=ktIVTXbdZ46v6fUhoHNidFrpvpNJR-Pj-hQ4V9b40W4,
|
|
276
276
|
prefect/telemetry/processors.py,sha256=jw6j6LviOVxw3IBJe7cSjsxFk0zzY43jUmy6C9pcfCE,2272
|
277
277
|
prefect/telemetry/run_telemetry.py,sha256=_FbjiPqPemu4xvZuI2YBPwXeRJ2BcKRJ6qgO4UMzKKE,8571
|
278
278
|
prefect/telemetry/services.py,sha256=DxgNNDTeWNtHBtioX8cjua4IrCbTiJJdYecx-gugg-w,2358
|
279
|
-
prefect/types/__init__.py,sha256=
|
279
|
+
prefect/types/__init__.py,sha256=vzFQspL0xeqQVW3rtXdBk1hKi_nlzvg8Qaf4jyQ95v0,4261
|
280
280
|
prefect/types/_datetime.py,sha256=ZE-4YK5XJuyxnp5pqldZwtIjkxCpxDGnCSfZiTl7-TU,7566
|
281
281
|
prefect/types/entrypoint.py,sha256=2FF03-wLPgtnqR_bKJDB2BsXXINPdu8ptY9ZYEZnXg8,328
|
282
|
+
prefect/types/names.py,sha256=CMMZD928iiod2UvB0qrsfXEBC5jj_bO0ge1fFXcrtgM,3450
|
282
283
|
prefect/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
283
284
|
prefect/utilities/_ast.py,sha256=sgEPUWElih-3cp4PoAy1IOyPtu8E27lL0Dldf3ijnYY,4905
|
284
285
|
prefect/utilities/_deprecated.py,sha256=b3pqRSoFANdVJAc8TJkygBcP-VjZtLJUxVIWC7kwspI,1303
|
@@ -313,13 +314,13 @@ prefect/utilities/schema_tools/__init__.py,sha256=At3rMHd2g_Em2P3_dFQlFgqR_EpBwr
|
|
313
314
|
prefect/utilities/schema_tools/hydration.py,sha256=NkRhWkNfxxFmVGhNDfmxdK_xeKaEhs3a42q83Sg9cT4,9436
|
314
315
|
prefect/utilities/schema_tools/validation.py,sha256=Wix26IVR-ZJ32-6MX2pHhrwm3reB-Q4iB6_phn85OKE,10743
|
315
316
|
prefect/workers/__init__.py,sha256=EaM1F0RZ-XIJaGeTKLsXDnfOPHzVWk5bk0_c4BVS44M,64
|
316
|
-
prefect/workers/base.py,sha256=
|
317
|
+
prefect/workers/base.py,sha256=_Puzm_f2Q7YLI89G_u9oM3esvwUWIKZ3fpfPqi-KMQk,62358
|
317
318
|
prefect/workers/block.py,sha256=dPvG1jDGD5HSH7aM2utwtk6RaJ9qg13XjkA0lAIgQmY,287
|
318
319
|
prefect/workers/cloud.py,sha256=dPvG1jDGD5HSH7aM2utwtk6RaJ9qg13XjkA0lAIgQmY,287
|
319
320
|
prefect/workers/process.py,sha256=Yi5D0U5AQ51wHT86GdwtImXSefe0gJf3LGq4r4z9zwM,11090
|
320
321
|
prefect/workers/server.py,sha256=2pmVeJZiVbEK02SO6BEZaBIvHMsn6G8LzjW8BXyiTtk,1952
|
321
322
|
prefect/workers/utilities.py,sha256=VfPfAlGtTuDj0-Kb8WlMgAuOfgXCdrGAnKMapPSBrwc,2483
|
322
|
-
prefect_client-3.4.1.
|
323
|
-
prefect_client-3.4.1.
|
324
|
-
prefect_client-3.4.1.
|
325
|
-
prefect_client-3.4.1.
|
323
|
+
prefect_client-3.4.1.dev5.dist-info/METADATA,sha256=PUcsY0sXpjiBspwz8PP-QToO8lAjsegJsS62iCWM1so,7471
|
324
|
+
prefect_client-3.4.1.dev5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
325
|
+
prefect_client-3.4.1.dev5.dist-info/licenses/LICENSE,sha256=MCxsn8osAkzfxKC4CC_dLcUkU8DZLkyihZ8mGs3Ah3Q,11357
|
326
|
+
prefect_client-3.4.1.dev5.dist-info/RECORD,,
|
File without changes
|
{prefect_client-3.4.1.dev4.dist-info → prefect_client-3.4.1.dev5.dist-info}/licenses/LICENSE
RENAMED
File without changes
|