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
@@ -1,37 +1,36 @@
|
|
1
1
|
import inspect
|
2
2
|
import typing
|
3
|
+
import warnings
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
if HAS_PYDANTIC_V2:
|
7
|
-
from pydantic.v1 import BaseModel as V1BaseModel
|
8
|
-
else:
|
9
|
-
from pydantic import BaseModel as V1BaseModel
|
5
|
+
import pydantic
|
6
|
+
from pydantic.v1 import BaseModel as V1BaseModel
|
10
7
|
|
11
8
|
|
12
9
|
def is_v1_model(v) -> bool:
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
with warnings.catch_warnings():
|
11
|
+
warnings.filterwarnings(
|
12
|
+
"ignore", category=pydantic.warnings.PydanticDeprecatedSince20
|
13
|
+
)
|
14
|
+
|
15
|
+
if isinstance(v, V1BaseModel):
|
17
16
|
return True
|
18
|
-
|
19
|
-
|
17
|
+
try:
|
18
|
+
if inspect.isclass(v) and issubclass(v, V1BaseModel):
|
19
|
+
return True
|
20
|
+
except TypeError:
|
21
|
+
pass
|
20
22
|
|
21
|
-
|
23
|
+
return False
|
22
24
|
|
23
25
|
|
24
26
|
def is_v1_type(v) -> bool:
|
25
|
-
if
|
26
|
-
|
27
|
-
return True
|
28
|
-
|
29
|
-
try:
|
30
|
-
return v.__module__.startswith("pydantic.v1.types")
|
31
|
-
except AttributeError:
|
32
|
-
return False
|
27
|
+
if is_v1_model(v):
|
28
|
+
return True
|
33
29
|
|
34
|
-
|
30
|
+
try:
|
31
|
+
return v.__module__.startswith("pydantic.v1.types")
|
32
|
+
except AttributeError:
|
33
|
+
return False
|
35
34
|
|
36
35
|
|
37
36
|
def has_v1_type_as_param(signature: inspect.Signature) -> bool:
|
@@ -1,16 +1,15 @@
|
|
1
1
|
"""
|
2
|
-
This module contains an implementation of pydantic v1's ValidateFunction
|
2
|
+
This module contains an implementation of pydantic v1's ValidateFunction
|
3
3
|
modified to validate function arguments and return a pydantic v2 model.
|
4
4
|
|
5
|
-
Specifically it allows for us to validate v2 models used as flow/task
|
5
|
+
Specifically it allows for us to validate v2 models used as flow/task
|
6
6
|
arguments.
|
7
7
|
"""
|
8
8
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type, Union
|
10
10
|
|
11
11
|
# importing directly from v2 to be able to create a v2 model
|
12
|
-
from pydantic import BaseModel, create_model
|
13
|
-
from pydantic.v1 import validator
|
12
|
+
from pydantic import BaseModel, ConfigDict, create_model, field_validator
|
14
13
|
from pydantic.v1.decorator import ValidatedFunction
|
15
14
|
from pydantic.v1.errors import ConfigError
|
16
15
|
from pydantic.v1.utils import to_camel
|
@@ -28,28 +27,25 @@ class V2ValidatedFunction(ValidatedFunction):
|
|
28
27
|
fields: Dict[str, Any],
|
29
28
|
takes_args: bool,
|
30
29
|
takes_kwargs: bool,
|
31
|
-
config:
|
30
|
+
config: ConfigDict,
|
32
31
|
) -> None:
|
33
32
|
pos_args = len(self.arg_mapping)
|
34
33
|
|
35
|
-
|
36
|
-
pass
|
37
|
-
|
38
|
-
if not TYPE_CHECKING: # pragma: no branch
|
39
|
-
if isinstance(config, dict):
|
40
|
-
CustomConfig = type("Config", (), config) # noqa: F811
|
41
|
-
elif config is not None:
|
42
|
-
CustomConfig = config # noqa: F811
|
43
|
-
|
44
|
-
if hasattr(CustomConfig, "fields") or hasattr(CustomConfig, "alias_generator"):
|
34
|
+
if config.get("fields") or config.get("alias_generator"):
|
45
35
|
raise ConfigError(
|
46
36
|
'Setting the "fields" and "alias_generator" property on custom Config'
|
47
37
|
" for @validate_arguments is not yet supported, please remove."
|
48
38
|
)
|
49
39
|
|
40
|
+
if "extra" not in config:
|
41
|
+
config["extra"] = "forbid"
|
42
|
+
|
50
43
|
# This is the key change -- inheriting the BaseModel class from v2
|
51
44
|
class DecoratorBaseModel(BaseModel):
|
52
|
-
|
45
|
+
model_config = config
|
46
|
+
|
47
|
+
@field_validator(self.v_args_name, check_fields=False)
|
48
|
+
@classmethod
|
53
49
|
def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]:
|
54
50
|
if takes_args or v is None:
|
55
51
|
return v
|
@@ -59,7 +55,8 @@ class V2ValidatedFunction(ValidatedFunction):
|
|
59
55
|
f" {pos_args + len(v)} given"
|
60
56
|
)
|
61
57
|
|
62
|
-
@
|
58
|
+
@field_validator(self.v_kwargs_name, check_fields=False)
|
59
|
+
@classmethod
|
63
60
|
def check_kwargs(
|
64
61
|
cls, v: Optional[Dict[str, Any]]
|
65
62
|
) -> Optional[Dict[str, Any]]:
|
@@ -70,7 +67,8 @@ class V2ValidatedFunction(ValidatedFunction):
|
|
70
67
|
keys = ", ".join(map(repr, v.keys()))
|
71
68
|
raise TypeError(f"unexpected keyword argument{plural}: {keys}")
|
72
69
|
|
73
|
-
@
|
70
|
+
@field_validator(V_POSITIONAL_ONLY_NAME, check_fields=False)
|
71
|
+
@classmethod
|
74
72
|
def check_positional_only(cls, v: Optional[List[str]]) -> None:
|
75
73
|
if v is None:
|
76
74
|
return
|
@@ -82,7 +80,8 @@ class V2ValidatedFunction(ValidatedFunction):
|
|
82
80
|
f" argument{plural}: {keys}"
|
83
81
|
)
|
84
82
|
|
85
|
-
@
|
83
|
+
@field_validator(V_DUPLICATE_KWARGS, check_fields=False)
|
84
|
+
@classmethod
|
86
85
|
def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None:
|
87
86
|
if v is None:
|
88
87
|
return
|
@@ -91,10 +90,6 @@ class V2ValidatedFunction(ValidatedFunction):
|
|
91
90
|
keys = ", ".join(map(repr, v))
|
92
91
|
raise TypeError(f"multiple values for argument{plural}: {keys}")
|
93
92
|
|
94
|
-
class Config(CustomConfig):
|
95
|
-
# extra = getattr(CustomConfig, "extra", Extra.forbid)
|
96
|
-
extra = getattr(CustomConfig, "extra", "forbid")
|
97
|
-
|
98
93
|
self.model = create_model(
|
99
94
|
to_camel(self.raw_function.__name__),
|
100
95
|
__base__=DecoratorBaseModel,
|
@@ -3,29 +3,18 @@ Utilities for creating and working with Prefect REST API schemas.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import datetime
|
6
|
-
import json
|
7
6
|
import os
|
8
|
-
from
|
9
|
-
from typing import Any, Dict, Generator, Optional, Set, TypeVar
|
7
|
+
from typing import Any, ClassVar, Optional, Set, TypeVar
|
10
8
|
from uuid import UUID, uuid4
|
11
9
|
|
12
|
-
import orjson
|
13
10
|
import pendulum
|
14
|
-
from
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
from pydantic.v1.json import custom_pydantic_encoder
|
22
|
-
else:
|
23
|
-
import pydantic
|
24
|
-
from pydantic import BaseModel, Field, SecretField
|
25
|
-
from pydantic.json import custom_pydantic_encoder
|
26
|
-
|
27
|
-
from prefect._internal.schemas.fields import DateTimeTZ
|
28
|
-
from prefect._internal.schemas.serializers import orjson_dumps_extra_compatible
|
11
|
+
from pydantic import (
|
12
|
+
BaseModel,
|
13
|
+
ConfigDict,
|
14
|
+
Field,
|
15
|
+
)
|
16
|
+
from pydantic_extra_types.pendulum_dt import DateTime
|
17
|
+
from typing_extensions import Self
|
29
18
|
|
30
19
|
T = TypeVar("T")
|
31
20
|
|
@@ -42,39 +31,16 @@ class PrefectBaseModel(BaseModel):
|
|
42
31
|
subtle unintentional testing errors.
|
43
32
|
"""
|
44
33
|
|
45
|
-
|
46
|
-
# extra attributes are forbidden in order to raise meaningful errors for
|
47
|
-
# bad API payloads
|
48
|
-
# We cannot load this setting through the normal pattern due to circular
|
49
|
-
# imports; instead just check if its a truthy setting directly
|
50
|
-
if os.getenv("PREFECT_TEST_MODE", "0").lower() in ["1", "true"]:
|
51
|
-
extra = "forbid"
|
52
|
-
else:
|
53
|
-
extra = "ignore"
|
54
|
-
|
55
|
-
json_encoders = {
|
56
|
-
# Uses secret fields and strange logic to avoid a circular import error
|
57
|
-
# for Secret dict in prefect.blocks.fields
|
58
|
-
SecretField: lambda v: v.dict() if getattr(v, "dict", None) else str(v)
|
59
|
-
}
|
60
|
-
|
61
|
-
pydantic_version = getattr(pydantic, "__version__", None)
|
62
|
-
if pydantic_version is not None and Version(pydantic_version) >= Version(
|
63
|
-
"1.9.2"
|
64
|
-
):
|
65
|
-
copy_on_model_validation = "none"
|
66
|
-
else:
|
67
|
-
copy_on_model_validation = False
|
34
|
+
_reset_fields: ClassVar[Set[str]] = set()
|
68
35
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
return set()
|
36
|
+
model_config = ConfigDict(
|
37
|
+
ser_json_timedelta="float",
|
38
|
+
extra=(
|
39
|
+
"ignore"
|
40
|
+
if os.getenv("PREFECT_TEST_MODE", "0").lower() not in ["true", "1"]
|
41
|
+
else "forbid"
|
42
|
+
),
|
43
|
+
)
|
78
44
|
|
79
45
|
def __eq__(self, other: Any) -> bool:
|
80
46
|
"""Equaltiy operator that ignores the resettable fields of the PrefectBaseModel.
|
@@ -82,124 +48,47 @@ class PrefectBaseModel(BaseModel):
|
|
82
48
|
NOTE: this equality operator will only be applied if the PrefectBaseModel is
|
83
49
|
the left-hand operand. This is a limitation of Python.
|
84
50
|
"""
|
85
|
-
copy_dict = self.
|
51
|
+
copy_dict = self.model_dump(exclude=self._reset_fields)
|
86
52
|
if isinstance(other, PrefectBaseModel):
|
87
|
-
return copy_dict == other.
|
53
|
+
return copy_dict == other.model_dump(exclude=other._reset_fields)
|
88
54
|
if isinstance(other, BaseModel):
|
89
|
-
return copy_dict == other.
|
55
|
+
return copy_dict == other.model_dump()
|
90
56
|
else:
|
91
57
|
return copy_dict == other
|
92
58
|
|
93
|
-
def json(self, *args, include_secrets: bool = False, **kwargs) -> str:
|
94
|
-
"""
|
95
|
-
Returns a representation of the model as JSON.
|
96
|
-
|
97
|
-
If `include_secrets=True`, then `SecretStr` and `SecretBytes` objects are
|
98
|
-
fully revealed. Otherwise they are obfuscated.
|
99
|
-
|
100
|
-
"""
|
101
|
-
if include_secrets:
|
102
|
-
if "encoder" in kwargs:
|
103
|
-
raise ValueError(
|
104
|
-
"Alternative encoder provided; can not set encoder for"
|
105
|
-
" SecretFields."
|
106
|
-
)
|
107
|
-
kwargs["encoder"] = partial(
|
108
|
-
custom_pydantic_encoder,
|
109
|
-
{SecretField: lambda v: v.get_secret_value() if v else None},
|
110
|
-
)
|
111
|
-
return super().json(*args, **kwargs)
|
112
|
-
|
113
|
-
def dict(
|
114
|
-
self, *args, shallow: bool = False, json_compatible: bool = False, **kwargs
|
115
|
-
) -> dict:
|
116
|
-
"""Returns a representation of the model as a Python dictionary.
|
117
|
-
|
118
|
-
For more information on this distinction please see
|
119
|
-
https://pydantic-docs.helpmanual.io/usage/exporting_models/#dictmodel-and-iteration
|
120
|
-
|
121
|
-
|
122
|
-
Args:
|
123
|
-
shallow (bool, optional): If True (default), nested Pydantic fields
|
124
|
-
are also coerced to dicts. If false, they are left as Pydantic
|
125
|
-
models.
|
126
|
-
json_compatible (bool, optional): if True, objects are converted
|
127
|
-
into json-compatible representations, similar to calling
|
128
|
-
`json.loads(self.json())`. Not compatible with shallow=True.
|
129
|
-
|
130
|
-
Returns:
|
131
|
-
dict
|
132
|
-
"""
|
133
|
-
|
134
|
-
if json_compatible and shallow:
|
135
|
-
raise ValueError(
|
136
|
-
"`json_compatible` can only be applied to the entire object."
|
137
|
-
)
|
138
|
-
|
139
|
-
# return a json-compatible representation of the object
|
140
|
-
elif json_compatible:
|
141
|
-
return json.loads(self.json(*args, **kwargs))
|
142
|
-
|
143
|
-
# if shallow wasn't requested, return the standard pydantic behavior
|
144
|
-
elif not shallow:
|
145
|
-
return super().dict(*args, **kwargs)
|
146
|
-
|
147
|
-
# if no options were requested, return simple dict transformation
|
148
|
-
# to apply shallow conversion
|
149
|
-
elif not args and not kwargs:
|
150
|
-
return dict(self)
|
151
|
-
|
152
|
-
# if options like include/exclude were provided, perform
|
153
|
-
# a full dict conversion then overwrite with any shallow
|
154
|
-
# differences
|
155
|
-
else:
|
156
|
-
deep_dict = super().dict(*args, **kwargs)
|
157
|
-
shallow_dict = dict(self)
|
158
|
-
for k, v in list(deep_dict.items()):
|
159
|
-
if isinstance(v, dict) and isinstance(shallow_dict[k], BaseModel):
|
160
|
-
deep_dict[k] = shallow_dict[k]
|
161
|
-
return deep_dict
|
162
|
-
|
163
|
-
def copy(
|
164
|
-
self: T, *, update: Dict = None, reset_fields: bool = False, **kwargs: Any
|
165
|
-
) -> T:
|
166
|
-
"""
|
167
|
-
Duplicate a model.
|
168
|
-
|
169
|
-
Args:
|
170
|
-
update: values to change/add to the model copy
|
171
|
-
reset_fields: if True, reset the fields specified in `self._reset_fields`
|
172
|
-
to their default value on the new model
|
173
|
-
kwargs: kwargs to pass to `pydantic.BaseModel.copy`
|
174
|
-
|
175
|
-
Returns:
|
176
|
-
A new copy of the model
|
177
|
-
"""
|
178
|
-
if reset_fields:
|
179
|
-
update = update or dict()
|
180
|
-
for field in self._reset_fields():
|
181
|
-
update.setdefault(field, self.__fields__[field].get_default())
|
182
|
-
return super().copy(update=update, **kwargs)
|
183
|
-
|
184
59
|
def __rich_repr__(self):
|
185
60
|
# Display all of the fields in the model if they differ from the default value
|
186
|
-
for name, field in self.
|
61
|
+
for name, field in self.model_fields.items():
|
187
62
|
value = getattr(self, name)
|
188
63
|
|
189
64
|
# Simplify the display of some common fields
|
190
|
-
if field.
|
65
|
+
if field.annotation == UUID and value:
|
191
66
|
value = str(value)
|
192
67
|
elif (
|
193
|
-
isinstance(field.
|
68
|
+
isinstance(field.annotation, datetime.datetime)
|
194
69
|
and name == "timestamp"
|
195
70
|
and value
|
196
71
|
):
|
197
72
|
value = pendulum.instance(value).isoformat()
|
198
|
-
elif isinstance(field.
|
73
|
+
elif isinstance(field.annotation, datetime.datetime) and value:
|
199
74
|
value = pendulum.instance(value).diff_for_humans()
|
200
75
|
|
201
76
|
yield name, value, field.get_default()
|
202
77
|
|
78
|
+
def reset_fields(self: Self) -> Self:
|
79
|
+
"""
|
80
|
+
Reset the fields of the model that are in the `_reset_fields` set.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
PrefectBaseModel: A new instance of the model with the reset fields.
|
84
|
+
"""
|
85
|
+
return self.model_copy(
|
86
|
+
update={
|
87
|
+
field: self.model_fields[field].get_default(call_default_factory=True)
|
88
|
+
for field in self._reset_fields
|
89
|
+
}
|
90
|
+
)
|
91
|
+
|
203
92
|
|
204
93
|
class IDBaseModel(PrefectBaseModel):
|
205
94
|
"""
|
@@ -208,11 +97,9 @@ class IDBaseModel(PrefectBaseModel):
|
|
208
97
|
The ID is reset on copy() and not included in equality comparisons.
|
209
98
|
"""
|
210
99
|
|
100
|
+
_reset_fields: ClassVar[Set[str]] = {"id"}
|
211
101
|
id: UUID = Field(default_factory=uuid4)
|
212
102
|
|
213
|
-
def _reset_fields(self) -> Set[str]:
|
214
|
-
return super()._reset_fields().union({"id"})
|
215
|
-
|
216
103
|
|
217
104
|
class ObjectBaseModel(IDBaseModel):
|
218
105
|
"""
|
@@ -223,32 +110,12 @@ class ObjectBaseModel(IDBaseModel):
|
|
223
110
|
equality comparisons.
|
224
111
|
"""
|
225
112
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
created: Optional[DateTimeTZ] = Field(default=None, repr=False)
|
230
|
-
updated: Optional[DateTimeTZ] = Field(default=None, repr=False)
|
113
|
+
_reset_fields: ClassVar[Set[str]] = {"id", "created", "updated"}
|
114
|
+
model_config = ConfigDict(from_attributes=True)
|
231
115
|
|
232
|
-
|
233
|
-
|
116
|
+
created: Optional[DateTime] = Field(default=None, repr=False)
|
117
|
+
updated: Optional[DateTime] = Field(default=None, repr=False)
|
234
118
|
|
235
119
|
|
236
120
|
class ActionBaseModel(PrefectBaseModel):
|
237
|
-
|
238
|
-
extra = "forbid"
|
239
|
-
|
240
|
-
def __iter__(self):
|
241
|
-
# By default, `pydantic.BaseModel.__iter__` yields from `self.__dict__` directly
|
242
|
-
# instead of going through `_iter`. We want tor retain our custom logic in
|
243
|
-
# `_iter` during `dict(model)` calls which is what Pydantic uses for
|
244
|
-
# `parse_obj(model)`
|
245
|
-
yield from self._iter(to_dict=True)
|
246
|
-
|
247
|
-
def _iter(self, *args, **kwargs) -> Generator[tuple, None, None]:
|
248
|
-
# Drop fields that are marked as `ignored` from json and dictionary outputs
|
249
|
-
exclude = kwargs.pop("exclude", None) or set()
|
250
|
-
for name, field in self.__fields__.items():
|
251
|
-
if field.field_info.extra.get("ignored"):
|
252
|
-
exclude.add(name)
|
253
|
-
|
254
|
-
return super()._iter(*args, **kwargs, exclude=exclude)
|
121
|
+
model_config: ConfigDict = ConfigDict(extra="forbid")
|
@@ -1,49 +1,7 @@
|
|
1
|
-
import datetime
|
2
1
|
from typing import Optional
|
3
2
|
from uuid import UUID
|
4
3
|
|
5
|
-
import
|
6
|
-
from typing_extensions import TypeAlias
|
7
|
-
|
8
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
9
|
-
|
10
|
-
if HAS_PYDANTIC_V2:
|
11
|
-
from pydantic.v1 import BaseModel, Field
|
12
|
-
else:
|
13
|
-
from pydantic import BaseModel, Field
|
14
|
-
|
15
|
-
|
16
|
-
# Rather than subclassing pendulum.DateTime to add our pydantic-specific validation,
|
17
|
-
# which will lead to a lot of funky typing issues, we'll just monkeypatch the pydantic
|
18
|
-
# validators onto the class. Retaining this type alias means that we can still use it
|
19
|
-
# as we have been in class definitions, also guaranteeing that we'll be applying these
|
20
|
-
# validators by importing this module.
|
21
|
-
|
22
|
-
DateTimeTZ: TypeAlias = pendulum.DateTime
|
23
|
-
|
24
|
-
|
25
|
-
def _datetime_patched_classmethod(function):
|
26
|
-
if hasattr(DateTimeTZ, function.__name__):
|
27
|
-
return function
|
28
|
-
setattr(DateTimeTZ, function.__name__, classmethod(function))
|
29
|
-
return function
|
30
|
-
|
31
|
-
|
32
|
-
@_datetime_patched_classmethod
|
33
|
-
def __get_validators__(cls):
|
34
|
-
yield getattr(cls, "validate")
|
35
|
-
|
36
|
-
|
37
|
-
@_datetime_patched_classmethod
|
38
|
-
def validate(cls, v) -> pendulum.DateTime:
|
39
|
-
if isinstance(v, str):
|
40
|
-
parsed = pendulum.parse(v)
|
41
|
-
assert isinstance(parsed, pendulum.DateTime)
|
42
|
-
return parsed
|
43
|
-
elif isinstance(v, datetime.datetime):
|
44
|
-
return pendulum.instance(v)
|
45
|
-
else:
|
46
|
-
raise ValueError("Unrecognized datetime.")
|
4
|
+
from pydantic import BaseModel, Field
|
47
5
|
|
48
6
|
|
49
7
|
class CreatedBy(BaseModel):
|