prefect-client 2.20.4__py3-none-any.whl → 3.0.0__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 +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- 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/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- 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/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- 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 +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- 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/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/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/py.typed +0 -0
- 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/py.typed +0 -0
- 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/blocks/kubernetes.py +0 -119
- 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/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- 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/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/utilities/pydantic.py
CHANGED
@@ -1,25 +1,38 @@
|
|
1
1
|
from functools import partial
|
2
|
-
from typing import
|
2
|
+
from typing import (
|
3
|
+
Any,
|
4
|
+
Callable,
|
5
|
+
Dict,
|
6
|
+
Generic,
|
7
|
+
Optional,
|
8
|
+
Type,
|
9
|
+
TypeVar,
|
10
|
+
cast,
|
11
|
+
get_origin,
|
12
|
+
overload,
|
13
|
+
)
|
3
14
|
|
4
15
|
from jsonpatch import JsonPatch as JsonPatchBase
|
5
|
-
from
|
6
|
-
|
16
|
+
from pydantic import (
|
17
|
+
BaseModel,
|
18
|
+
GetJsonSchemaHandler,
|
19
|
+
Secret,
|
20
|
+
TypeAdapter,
|
21
|
+
ValidationError,
|
22
|
+
)
|
23
|
+
from pydantic.json_schema import JsonSchemaValue
|
24
|
+
from pydantic_core import core_schema, to_jsonable_python
|
25
|
+
from typing_extensions import Literal
|
7
26
|
|
8
|
-
from prefect._internal.pydantic.utilities.model_dump import model_dump
|
9
|
-
from prefect.pydantic import HAS_PYDANTIC_V2
|
10
27
|
from prefect.utilities.dispatch import get_dispatch_key, lookup_type, register_base_type
|
11
28
|
from prefect.utilities.importtools import from_qualified_name, to_qualified_name
|
12
29
|
|
13
|
-
if HAS_PYDANTIC_V2:
|
14
|
-
import pydantic.v1 as pydantic_v1
|
15
|
-
else:
|
16
|
-
import pydantic as pydantic_v1
|
17
|
-
|
18
30
|
D = TypeVar("D", bound=Any)
|
19
|
-
M = TypeVar("M", bound=
|
31
|
+
M = TypeVar("M", bound=BaseModel)
|
32
|
+
T = TypeVar("T", bound=Any)
|
20
33
|
|
21
34
|
|
22
|
-
def _reduce_model(model:
|
35
|
+
def _reduce_model(model: BaseModel):
|
23
36
|
"""
|
24
37
|
Helper for serializing a cythonized model with cloudpickle.
|
25
38
|
|
@@ -30,7 +43,7 @@ def _reduce_model(model: pydantic_v1.BaseModel):
|
|
30
43
|
_unreduce_model,
|
31
44
|
(
|
32
45
|
to_qualified_name(type(model)),
|
33
|
-
model.
|
46
|
+
model.model_dump_json(**getattr(model, "__reduce_kwargs__", {})),
|
34
47
|
),
|
35
48
|
)
|
36
49
|
|
@@ -38,7 +51,7 @@ def _reduce_model(model: pydantic_v1.BaseModel):
|
|
38
51
|
def _unreduce_model(model_name, json):
|
39
52
|
"""Helper for restoring model after serialization"""
|
40
53
|
model = from_qualified_name(model_name)
|
41
|
-
return model.
|
54
|
+
return model.model_validate_json(json)
|
42
55
|
|
43
56
|
|
44
57
|
@overload
|
@@ -53,7 +66,7 @@ def add_cloudpickle_reduction(
|
|
53
66
|
...
|
54
67
|
|
55
68
|
|
56
|
-
def add_cloudpickle_reduction(__model_cls: Type[M] = None, **kwargs: Any):
|
69
|
+
def add_cloudpickle_reduction(__model_cls: Optional[Type[M]] = None, **kwargs: Any):
|
57
70
|
"""
|
58
71
|
Adds a `__reducer__` to the given class that ensures it is cloudpickle compatible.
|
59
72
|
|
@@ -83,7 +96,7 @@ def add_cloudpickle_reduction(__model_cls: Type[M] = None, **kwargs: Any):
|
|
83
96
|
)
|
84
97
|
|
85
98
|
|
86
|
-
def get_class_fields_only(model: Type[
|
99
|
+
def get_class_fields_only(model: Type[BaseModel]) -> set:
|
87
100
|
"""
|
88
101
|
Gets all the field names defined on the model class but not any parent classes.
|
89
102
|
Any fields that are on the parent but redefined on the subclass are included.
|
@@ -92,7 +105,7 @@ def get_class_fields_only(model: Type[pydantic_v1.BaseModel]) -> set:
|
|
92
105
|
parent_class_fields = set()
|
93
106
|
|
94
107
|
for base in model.__class__.__bases__:
|
95
|
-
if issubclass(base,
|
108
|
+
if issubclass(base, BaseModel):
|
96
109
|
parent_class_fields.update(base.__annotations__.keys())
|
97
110
|
|
98
111
|
return (subclass_class_fields - parent_class_fields) | (
|
@@ -125,7 +138,7 @@ def add_type_dispatch(model_cls: Type[M]) -> Type[M]:
|
|
125
138
|
model_cls, "__dispatch_key__"
|
126
139
|
) or "__dispatch_key__" in getattr(model_cls, "__annotations__", {})
|
127
140
|
|
128
|
-
defines_type_field = "type" in model_cls.
|
141
|
+
defines_type_field = "type" in model_cls.model_fields
|
129
142
|
|
130
143
|
if not defines_dispatch_key and not defines_type_field:
|
131
144
|
raise ValueError(
|
@@ -133,18 +146,8 @@ def add_type_dispatch(model_cls: Type[M]) -> Type[M]:
|
|
133
146
|
"or a type field. One of these is required for dispatch."
|
134
147
|
)
|
135
148
|
|
136
|
-
elif defines_dispatch_key and not defines_type_field:
|
137
|
-
# Add a type field to store the value of the dispatch key
|
138
|
-
model_cls.__fields__["type"] = pydantic_v1.fields.ModelField(
|
139
|
-
name="type",
|
140
|
-
type_=str,
|
141
|
-
required=True,
|
142
|
-
class_validators=None,
|
143
|
-
model_config=model_cls.__config__,
|
144
|
-
)
|
145
|
-
|
146
149
|
elif not defines_dispatch_key and defines_type_field:
|
147
|
-
field_type_annotation = model_cls.
|
150
|
+
field_type_annotation = model_cls.model_fields["type"].annotation
|
148
151
|
if field_type_annotation != str:
|
149
152
|
raise TypeError(
|
150
153
|
f"Model class {model_cls.__name__!r} defines a 'type' field with "
|
@@ -154,7 +157,7 @@ def add_type_dispatch(model_cls: Type[M]) -> Type[M]:
|
|
154
157
|
# Set the dispatch key to retrieve the value from the type field
|
155
158
|
@classmethod
|
156
159
|
def dispatch_key_from_type_field(cls):
|
157
|
-
return cls.
|
160
|
+
return cls.model_fields["type"].default
|
158
161
|
|
159
162
|
model_cls.__dispatch_key__ = dispatch_key_from_type_field
|
160
163
|
|
@@ -176,12 +179,12 @@ def add_type_dispatch(model_cls: Type[M]) -> Type[M]:
|
|
176
179
|
data.setdefault("type", type_string)
|
177
180
|
cls_init(__pydantic_self__, **data)
|
178
181
|
|
179
|
-
def __new__(cls: Type[
|
182
|
+
def __new__(cls: Type[M], **kwargs: Any) -> M:
|
180
183
|
if "type" in kwargs:
|
181
184
|
try:
|
182
185
|
subcls = lookup_type(cls, dispatch_key=kwargs["type"])
|
183
186
|
except KeyError as exc:
|
184
|
-
raise
|
187
|
+
raise ValidationError(errors=[exc], model=cls)
|
185
188
|
return cls_new(subcls)
|
186
189
|
else:
|
187
190
|
return cls_new(cls)
|
@@ -207,7 +210,7 @@ class PartialModel(Generic[M]):
|
|
207
210
|
a field already has a value.
|
208
211
|
|
209
212
|
Example:
|
210
|
-
>>> class MyModel(
|
213
|
+
>>> class MyModel(BaseModel):
|
211
214
|
>>> x: int
|
212
215
|
>>> y: str
|
213
216
|
>>> z: float
|
@@ -237,7 +240,7 @@ class PartialModel(Generic[M]):
|
|
237
240
|
raise ValueError(f"Field {name!r} has already been set.")
|
238
241
|
|
239
242
|
def raise_if_not_in_model(self, name):
|
240
|
-
if name not in self.model_cls.
|
243
|
+
if name not in self.model_cls.model_fields:
|
241
244
|
raise ValueError(f"Field {name!r} is not present in the model.")
|
242
245
|
|
243
246
|
def __setattr__(self, __name: str, __value: Any) -> None:
|
@@ -257,8 +260,22 @@ class PartialModel(Generic[M]):
|
|
257
260
|
|
258
261
|
class JsonPatch(JsonPatchBase):
|
259
262
|
@classmethod
|
260
|
-
def
|
261
|
-
|
263
|
+
def __get_pydantic_core_schema__(
|
264
|
+
cls, source_type: Any, handler: GetJsonSchemaHandler
|
265
|
+
) -> core_schema.CoreSchema:
|
266
|
+
return core_schema.typed_dict_schema(
|
267
|
+
{"patch": core_schema.typed_dict_field(core_schema.dict_schema())}
|
268
|
+
)
|
269
|
+
|
270
|
+
@classmethod
|
271
|
+
def __get_pydantic_json_schema__(
|
272
|
+
cls, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
|
273
|
+
) -> JsonSchemaValue:
|
274
|
+
json_schema = handler(core_schema)
|
275
|
+
json_schema = handler.resolve_ref_schema(json_schema)
|
276
|
+
json_schema.pop("required", None)
|
277
|
+
json_schema.pop("properties", None)
|
278
|
+
json_schema.update(
|
262
279
|
{
|
263
280
|
"type": "array",
|
264
281
|
"format": "rfc6902",
|
@@ -268,6 +285,7 @@ class JsonPatch(JsonPatchBase):
|
|
268
285
|
},
|
269
286
|
}
|
270
287
|
)
|
288
|
+
return json_schema
|
271
289
|
|
272
290
|
|
273
291
|
def custom_pydantic_encoder(
|
@@ -282,7 +300,79 @@ def custom_pydantic_encoder(
|
|
282
300
|
|
283
301
|
return encoder(obj)
|
284
302
|
else: # We have exited the for loop without finding a suitable encoder
|
285
|
-
if isinstance(obj,
|
286
|
-
return model_dump(
|
303
|
+
if isinstance(obj, BaseModel):
|
304
|
+
return obj.model_dump(mode="json")
|
287
305
|
else:
|
288
306
|
return to_jsonable_python(obj)
|
307
|
+
|
308
|
+
|
309
|
+
def parse_obj_as(
|
310
|
+
type_: type[T],
|
311
|
+
data: Any,
|
312
|
+
mode: Literal["python", "json", "strings"] = "python",
|
313
|
+
) -> T:
|
314
|
+
"""Parse a given data structure as a Pydantic model via `TypeAdapter`.
|
315
|
+
|
316
|
+
Read more about `TypeAdapter` [here](https://docs.pydantic.dev/latest/concepts/type_adapter/).
|
317
|
+
|
318
|
+
Args:
|
319
|
+
type_: The type to parse the data as.
|
320
|
+
data: The data to be parsed.
|
321
|
+
mode: The mode to use for parsing, either `python`, `json`, or `strings`.
|
322
|
+
Defaults to `python`, where `data` should be a Python object (e.g. `dict`).
|
323
|
+
|
324
|
+
Returns:
|
325
|
+
The parsed `data` as the given `type_`.
|
326
|
+
|
327
|
+
|
328
|
+
Example:
|
329
|
+
Basic Usage of `parse_as`
|
330
|
+
```python
|
331
|
+
from prefect.utilities.pydantic import parse_as
|
332
|
+
from pydantic import BaseModel
|
333
|
+
|
334
|
+
class ExampleModel(BaseModel):
|
335
|
+
name: str
|
336
|
+
|
337
|
+
# parsing python objects
|
338
|
+
parsed = parse_as(ExampleModel, {"name": "Marvin"})
|
339
|
+
assert isinstance(parsed, ExampleModel)
|
340
|
+
assert parsed.name == "Marvin"
|
341
|
+
|
342
|
+
# parsing json strings
|
343
|
+
parsed = parse_as(
|
344
|
+
list[ExampleModel],
|
345
|
+
'[{"name": "Marvin"}, {"name": "Arthur"}]',
|
346
|
+
mode="json"
|
347
|
+
)
|
348
|
+
assert all(isinstance(item, ExampleModel) for item in parsed)
|
349
|
+
assert parsed[0].name == "Marvin"
|
350
|
+
assert parsed[1].name == "Arthur"
|
351
|
+
|
352
|
+
# parsing raw strings
|
353
|
+
parsed = parse_as(int, '123', mode="strings")
|
354
|
+
assert isinstance(parsed, int)
|
355
|
+
assert parsed == 123
|
356
|
+
```
|
357
|
+
|
358
|
+
"""
|
359
|
+
adapter = TypeAdapter(type_)
|
360
|
+
|
361
|
+
if get_origin(type_) is list and isinstance(data, dict):
|
362
|
+
data = next(iter(data.values()))
|
363
|
+
|
364
|
+
parser: Callable[[Any], T] = getattr(adapter, f"validate_{mode}")
|
365
|
+
|
366
|
+
return parser(data)
|
367
|
+
|
368
|
+
|
369
|
+
def handle_secret_render(value: object, context: dict[str, Any]) -> object:
|
370
|
+
if hasattr(value, "get_secret_value"):
|
371
|
+
return (
|
372
|
+
cast(Secret[object], value).get_secret_value()
|
373
|
+
if context.get("include_secrets", False)
|
374
|
+
else "**********"
|
375
|
+
)
|
376
|
+
elif isinstance(value, BaseModel):
|
377
|
+
return value.model_dump(context=context)
|
378
|
+
return value
|
@@ -11,10 +11,14 @@ from prefect.server.utilities.user_templates import (
|
|
11
11
|
render_user_template_sync,
|
12
12
|
validate_user_template,
|
13
13
|
)
|
14
|
+
from prefect.types import StrictVariableValue
|
14
15
|
|
15
16
|
|
16
17
|
class HydrationContext(BaseModel):
|
17
|
-
workspace_variables: Dict[
|
18
|
+
workspace_variables: Dict[
|
19
|
+
str,
|
20
|
+
StrictVariableValue,
|
21
|
+
] = Field(default_factory=dict)
|
18
22
|
render_workspace_variables: bool = Field(default=False)
|
19
23
|
raise_on_error: bool = Field(default=False)
|
20
24
|
render_jinja: bool = Field(default=False)
|
@@ -198,6 +202,11 @@ def json_handler(obj: dict, ctx: HydrationContext):
|
|
198
202
|
dehydrated_json = _hydrate(obj["value"], ctx)
|
199
203
|
else:
|
200
204
|
dehydrated_json = obj["value"]
|
205
|
+
|
206
|
+
# If the result is a Placeholder, we should return it as is
|
207
|
+
if isinstance(dehydrated_json, Placeholder):
|
208
|
+
return dehydrated_json
|
209
|
+
|
201
210
|
try:
|
202
211
|
return json.loads(dehydrated_json)
|
203
212
|
except (json.decoder.JSONDecodeError, TypeError) as e:
|
@@ -220,6 +229,10 @@ def jinja_handler(obj: dict, ctx: HydrationContext):
|
|
220
229
|
else:
|
221
230
|
dehydrated_jinja = obj["template"]
|
222
231
|
|
232
|
+
# If the result is a Placeholder, we should return it as is
|
233
|
+
if isinstance(dehydrated_jinja, Placeholder):
|
234
|
+
return dehydrated_jinja
|
235
|
+
|
223
236
|
try:
|
224
237
|
validate_user_template(dehydrated_jinja)
|
225
238
|
except (jinja2.exceptions.TemplateSyntaxError, TemplateSecurityError) as exc:
|
@@ -241,6 +254,10 @@ def workspace_variable_handler(obj: dict, ctx: HydrationContext):
|
|
241
254
|
else:
|
242
255
|
dehydrated_variable = obj["variable_name"]
|
243
256
|
|
257
|
+
# If the result is a Placeholder, we should return it as is
|
258
|
+
if isinstance(dehydrated_variable, Placeholder):
|
259
|
+
return dehydrated_variable
|
260
|
+
|
244
261
|
if not ctx.render_workspace_variables:
|
245
262
|
return WorkspaceVariable(variable_name=obj["variable_name"])
|
246
263
|
|
@@ -253,5 +253,35 @@ def preprocess_schema(
|
|
253
253
|
process_properties(
|
254
254
|
definition["properties"], required_fields, allow_none_with_default
|
255
255
|
)
|
256
|
+
# Allow block types to be referenced by their id
|
257
|
+
if "block_type_slug" in definition:
|
258
|
+
schema["definitions"][definition["title"]] = {
|
259
|
+
"oneOf": [
|
260
|
+
definition,
|
261
|
+
{
|
262
|
+
"type": "object",
|
263
|
+
"properties": {
|
264
|
+
"$ref": {
|
265
|
+
"oneOf": [
|
266
|
+
{
|
267
|
+
"type": "string",
|
268
|
+
"format": "uuid",
|
269
|
+
},
|
270
|
+
{
|
271
|
+
"type": "object",
|
272
|
+
"additionalProperties": {
|
273
|
+
"type": "string",
|
274
|
+
},
|
275
|
+
"minProperties": 1,
|
276
|
+
},
|
277
|
+
]
|
278
|
+
}
|
279
|
+
},
|
280
|
+
"required": [
|
281
|
+
"$ref",
|
282
|
+
],
|
283
|
+
},
|
284
|
+
]
|
285
|
+
}
|
256
286
|
|
257
287
|
return schema
|
prefect/utilities/services.py
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
import sys
|
2
|
-
|
2
|
+
import threading
|
3
3
|
from collections import deque
|
4
4
|
from traceback import format_exception
|
5
5
|
from types import TracebackType
|
6
|
-
from typing import Callable, Coroutine, Deque, Tuple
|
6
|
+
from typing import Callable, Coroutine, Deque, Optional, Tuple
|
7
|
+
from wsgiref.simple_server import WSGIServer
|
7
8
|
|
8
9
|
import anyio
|
9
10
|
import httpx
|
10
11
|
|
11
12
|
from prefect.logging.loggers import get_logger
|
13
|
+
from prefect.settings import PREFECT_CLIENT_ENABLE_METRICS, PREFECT_CLIENT_METRICS_PORT
|
12
14
|
from prefect.utilities.collections import distinct
|
13
15
|
from prefect.utilities.math import clamped_poisson_interval
|
14
16
|
|
@@ -23,7 +25,7 @@ async def critical_service_loop(
|
|
23
25
|
backoff: int = 1,
|
24
26
|
printer: Callable[..., None] = print,
|
25
27
|
run_once: bool = False,
|
26
|
-
jitter_range: float = None,
|
28
|
+
jitter_range: Optional[float] = None,
|
27
29
|
):
|
28
30
|
"""
|
29
31
|
Runs the given `workload` function on the specified `interval`, while being
|
@@ -68,11 +70,6 @@ async def critical_service_loop(
|
|
68
70
|
backoff_count = 0
|
69
71
|
|
70
72
|
track_record.append(True)
|
71
|
-
except CancelledError as exc:
|
72
|
-
# Exit immediately because the task was cancelled, possibly due
|
73
|
-
# to a signal or timeout.
|
74
|
-
logger.debug(f"Run of {workload!r} cancelled", exc_info=exc)
|
75
|
-
return
|
76
73
|
except httpx.TransportError as exc:
|
77
74
|
# httpx.TransportError is the base class for any kind of communications
|
78
75
|
# error, like timeouts, connection failures, etc. This does _not_ cover
|
@@ -144,7 +141,7 @@ async def critical_service_loop(
|
|
144
141
|
failures.clear()
|
145
142
|
printer(
|
146
143
|
"Backing off due to consecutive errors, using increased interval of "
|
147
|
-
f" {interval * 2
|
144
|
+
f" {interval * 2**backoff_count}s."
|
148
145
|
)
|
149
146
|
|
150
147
|
if run_once:
|
@@ -156,3 +153,32 @@ async def critical_service_loop(
|
|
156
153
|
sleep = interval * 2**backoff_count
|
157
154
|
|
158
155
|
await anyio.sleep(sleep)
|
156
|
+
|
157
|
+
|
158
|
+
_metrics_server: Optional[Tuple[WSGIServer, threading.Thread]] = None
|
159
|
+
|
160
|
+
|
161
|
+
def start_client_metrics_server():
|
162
|
+
"""Start the process-wide Prometheus metrics server for client metrics (if enabled
|
163
|
+
with `PREFECT_CLIENT_ENABLE_METRICS`) on the port `PREFECT_CLIENT_METRICS_PORT`."""
|
164
|
+
if not PREFECT_CLIENT_ENABLE_METRICS:
|
165
|
+
return
|
166
|
+
|
167
|
+
global _metrics_server
|
168
|
+
if _metrics_server:
|
169
|
+
return
|
170
|
+
|
171
|
+
from prometheus_client import start_http_server
|
172
|
+
|
173
|
+
_metrics_server = start_http_server(port=PREFECT_CLIENT_METRICS_PORT.value())
|
174
|
+
|
175
|
+
|
176
|
+
def stop_client_metrics_server():
|
177
|
+
"""Start the process-wide Prometheus metrics server for client metrics, if it has
|
178
|
+
previously been started"""
|
179
|
+
global _metrics_server
|
180
|
+
if _metrics_server:
|
181
|
+
server, thread = _metrics_server
|
182
|
+
server.shutdown()
|
183
|
+
thread.join()
|
184
|
+
_metrics_server = None
|
prefect/utilities/templating.py
CHANGED
@@ -1,7 +1,17 @@
|
|
1
1
|
import enum
|
2
2
|
import os
|
3
3
|
import re
|
4
|
-
from typing import
|
4
|
+
from typing import (
|
5
|
+
TYPE_CHECKING,
|
6
|
+
Any,
|
7
|
+
Dict,
|
8
|
+
NamedTuple,
|
9
|
+
Optional,
|
10
|
+
Set,
|
11
|
+
Type,
|
12
|
+
TypeVar,
|
13
|
+
Union,
|
14
|
+
)
|
5
15
|
|
6
16
|
from prefect.client.utilities import inject_client
|
7
17
|
from prefect.utilities.annotations import NotSet
|
@@ -301,7 +311,7 @@ async def resolve_block_document_references(
|
|
301
311
|
|
302
312
|
|
303
313
|
@inject_client
|
304
|
-
async def resolve_variables(template: T, client: "PrefectClient" = None):
|
314
|
+
async def resolve_variables(template: T, client: Optional["PrefectClient"] = None):
|
305
315
|
"""
|
306
316
|
Resolve variables in a template by replacing each variable placeholder with the
|
307
317
|
value of the variable.
|
prefect/utilities/timeout.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from asyncio import CancelledError
|
2
2
|
from contextlib import contextmanager
|
3
|
-
from typing import Optional
|
3
|
+
from typing import Optional, Type
|
4
4
|
|
5
5
|
from prefect._internal.concurrency.cancellation import (
|
6
6
|
cancel_async_after,
|
@@ -8,8 +8,19 @@ from prefect._internal.concurrency.cancellation import (
|
|
8
8
|
)
|
9
9
|
|
10
10
|
|
11
|
+
def fail_if_not_timeout_error(timeout_exc_type: Type[Exception]) -> None:
|
12
|
+
if not issubclass(timeout_exc_type, TimeoutError):
|
13
|
+
raise ValueError(
|
14
|
+
"The `timeout_exc_type` argument must be a subclass of `TimeoutError`."
|
15
|
+
)
|
16
|
+
|
17
|
+
|
11
18
|
@contextmanager
|
12
|
-
def timeout_async(
|
19
|
+
def timeout_async(
|
20
|
+
seconds: Optional[float] = None, timeout_exc_type: Type[TimeoutError] = TimeoutError
|
21
|
+
):
|
22
|
+
fail_if_not_timeout_error(timeout_exc_type)
|
23
|
+
|
13
24
|
if seconds is None:
|
14
25
|
yield
|
15
26
|
return
|
@@ -18,11 +29,15 @@ def timeout_async(seconds: Optional[float] = None):
|
|
18
29
|
with cancel_async_after(timeout=seconds):
|
19
30
|
yield
|
20
31
|
except CancelledError:
|
21
|
-
raise
|
32
|
+
raise timeout_exc_type(f"Scope timed out after {seconds} second(s).")
|
22
33
|
|
23
34
|
|
24
35
|
@contextmanager
|
25
|
-
def timeout(
|
36
|
+
def timeout(
|
37
|
+
seconds: Optional[float] = None, timeout_exc_type: Type[TimeoutError] = TimeoutError
|
38
|
+
):
|
39
|
+
fail_if_not_timeout_error(timeout_exc_type)
|
40
|
+
|
26
41
|
if seconds is None:
|
27
42
|
yield
|
28
43
|
return
|
@@ -31,4 +46,4 @@ def timeout(seconds: Optional[float] = None):
|
|
31
46
|
with cancel_sync_after(timeout=seconds):
|
32
47
|
yield
|
33
48
|
except CancelledError:
|
34
|
-
raise
|
49
|
+
raise timeout_exc_type(f"Scope timed out after {seconds} second(s).")
|