prefect-client 2.19.4__py3-none-any.whl → 3.0.0rc2__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/redis.py +168 -0
- 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 +362 -340
- prefect/client/schemas/actions.py +92 -86
- prefect/client/schemas/filters.py +20 -40
- prefect/client/schemas/objects.py +158 -152
- 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 +4 -2
- prefect/concurrency/events.py +1 -1
- prefect/concurrency/services.py +7 -4
- 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 +36 -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} +315 -74
- prefect/flow_runs.py +379 -7
- prefect/flows.py +248 -165
- prefect/futures.py +187 -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 +9 -9
- 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/cache_policies.py +179 -0
- prefect/records/result_store.py +42 -0
- prefect/records/store.py +9 -0
- prefect/results.py +43 -39
- prefect/runner/runner.py +9 -9
- 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 +76 -136
- prefect/states.py +22 -50
- prefect/task_engine.py +666 -56
- prefect/task_runners.py +272 -300
- prefect/task_runs.py +203 -0
- prefect/{task_server.py → task_worker.py} +89 -60
- prefect/tasks.py +358 -341
- prefect/transactions.py +224 -0
- prefect/types/__init__.py +61 -82
- prefect/utilities/asyncutils.py +195 -136
- prefect/utilities/callables.py +121 -41
- 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 +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +5 -1
- prefect/utilities/templating.py +12 -2
- prefect/variables.py +84 -62
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +26 -18
- prefect/workers/process.py +3 -8
- prefect/workers/server.py +2 -2
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/METADATA +23 -21
- prefect_client-3.0.0rc2.dist-info/RECORD +179 -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.4.dist-info/RECORD +0 -292
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/LICENSE +0 -0
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/WHEEL +0 -0
- {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.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)
|
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/variables.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
from typing import List, Optional
|
1
|
+
from typing import List, Optional, Union
|
2
2
|
|
3
|
-
from prefect._internal.compatibility.deprecated import deprecated_callable
|
4
3
|
from prefect.client.schemas.actions import VariableCreate as VariableRequest
|
5
4
|
from prefect.client.schemas.actions import VariableUpdate as VariableUpdateRequest
|
5
|
+
from prefect.client.schemas.objects import Variable as VariableResponse
|
6
6
|
from prefect.client.utilities import get_or_create_client
|
7
|
+
from prefect.exceptions import ObjectNotFound
|
8
|
+
from prefect.types import StrictVariableValue
|
7
9
|
from prefect.utilities.asyncutils import sync_compatible
|
8
10
|
|
9
11
|
|
@@ -23,92 +25,112 @@ class Variable(VariableRequest):
|
|
23
25
|
async def set(
|
24
26
|
cls,
|
25
27
|
name: str,
|
26
|
-
value:
|
28
|
+
value: StrictVariableValue,
|
27
29
|
tags: Optional[List[str]] = None,
|
28
30
|
overwrite: bool = False,
|
29
|
-
|
31
|
+
as_object: bool = False,
|
32
|
+
):
|
30
33
|
"""
|
31
|
-
Sets a new variable. If one exists with the same name,
|
32
|
-
```
|
33
|
-
from prefect.variables import Variable
|
34
|
+
Sets a new variable. If one exists with the same name, must pass `overwrite=True`
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
Returns the newly set value. If `as_object=True`, return the full Variable object
|
37
|
+
|
38
|
+
Args:
|
39
|
+
- name: The name of the variable to set.
|
40
|
+
- value: The value of the variable to set.
|
41
|
+
- tags: An optional list of strings to associate with the variable.
|
42
|
+
- overwrite: Whether to overwrite the variable if it already exists.
|
43
|
+
- as_object: Whether to return the full Variable object.
|
44
|
+
|
45
|
+
Example:
|
46
|
+
Set a new variable and overwrite it if it already exists.
|
47
|
+
```
|
41
48
|
from prefect.variables import Variable
|
42
49
|
|
43
50
|
@flow
|
44
|
-
|
45
|
-
|
46
|
-
|
51
|
+
def my_flow():
|
52
|
+
Variable.set(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
|
53
|
+
```
|
47
54
|
"""
|
48
55
|
client, _ = get_or_create_client()
|
49
|
-
|
50
|
-
var_dict = {"name": name, "value": value}
|
51
|
-
|
52
|
-
if
|
56
|
+
variable_exists = await client.read_variable_by_name(name)
|
57
|
+
var_dict = {"name": name, "value": value, "tags": tags or []}
|
58
|
+
|
59
|
+
if variable_exists:
|
53
60
|
if not overwrite:
|
54
61
|
raise ValueError(
|
55
|
-
"
|
62
|
+
f"Variable {name!r} already exists. Use `overwrite=True` to update it."
|
56
63
|
)
|
57
|
-
|
58
|
-
await client.update_variable(variable=var)
|
64
|
+
await client.update_variable(variable=VariableUpdateRequest(**var_dict))
|
59
65
|
variable = await client.read_variable_by_name(name)
|
60
66
|
else:
|
61
|
-
|
62
|
-
|
67
|
+
variable = await client.create_variable(
|
68
|
+
variable=VariableRequest(**var_dict)
|
69
|
+
)
|
63
70
|
|
64
|
-
return variable if
|
71
|
+
return variable if as_object else variable.value
|
65
72
|
|
66
73
|
@classmethod
|
67
74
|
@sync_compatible
|
68
|
-
async def get(
|
75
|
+
async def get(
|
76
|
+
cls,
|
77
|
+
name: str,
|
78
|
+
default: StrictVariableValue = None,
|
79
|
+
as_object: bool = False,
|
80
|
+
) -> Union[StrictVariableValue, VariableResponse]:
|
69
81
|
"""
|
70
|
-
Get a variable by name.
|
71
|
-
|
82
|
+
Get a variable's value by name.
|
83
|
+
|
84
|
+
If the variable does not exist, return the default value.
|
85
|
+
|
86
|
+
If `as_object=True`, return the full variable object. `default` is ignored in this case.
|
87
|
+
|
88
|
+
Args:
|
89
|
+
- name: The name of the variable to get.
|
90
|
+
- default: The default value to return if the variable does not exist.
|
91
|
+
- as_object: Whether to return the full variable object.
|
92
|
+
|
93
|
+
Example:
|
94
|
+
Get a variable's value by name.
|
95
|
+
```python
|
96
|
+
from prefect import flow
|
72
97
|
from prefect.variables import Variable
|
73
98
|
|
74
99
|
@flow
|
75
100
|
def my_flow():
|
76
101
|
var = Variable.get("my_var")
|
77
|
-
|
78
|
-
or
|
79
|
-
```
|
80
|
-
from prefect.variables import Variable
|
81
|
-
|
82
|
-
@flow
|
83
|
-
async def my_flow():
|
84
|
-
var = await Variable.get("my_var")
|
85
|
-
```
|
102
|
+
```
|
86
103
|
"""
|
87
104
|
client, _ = get_or_create_client()
|
88
105
|
variable = await client.read_variable_by_name(name)
|
89
|
-
return variable if variable else default
|
90
106
|
|
107
|
+
return variable if as_object else (variable.value if variable else default)
|
91
108
|
|
92
|
-
@
|
93
|
-
@sync_compatible
|
94
|
-
async def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
109
|
+
@classmethod
|
110
|
+
@sync_compatible
|
111
|
+
async def unset(cls, name: str) -> bool:
|
112
|
+
"""
|
113
|
+
Unset a variable by name.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
- name: The name of the variable to unset.
|
117
|
+
|
118
|
+
Returns `True` if the variable was deleted, `False` if the variable did not exist.
|
119
|
+
|
120
|
+
Example:
|
121
|
+
Unset a variable by name.
|
122
|
+
```python
|
123
|
+
from prefect import flow
|
124
|
+
from prefect.variables import Variable
|
125
|
+
|
126
|
+
@flow
|
127
|
+
def my_flow():
|
128
|
+
Variable.unset("my_var")
|
129
|
+
```
|
130
|
+
"""
|
131
|
+
client, _ = get_or_create_client()
|
132
|
+
try:
|
133
|
+
await client.delete_variable_by_name(name=name)
|
134
|
+
return True
|
135
|
+
except ObjectNotFound:
|
136
|
+
return False
|
prefect/workers/__init__.py
CHANGED
prefect/workers/base.py
CHANGED
@@ -7,14 +7,7 @@ from uuid import uuid4
|
|
7
7
|
import anyio
|
8
8
|
import anyio.abc
|
9
9
|
import pendulum
|
10
|
-
|
11
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
12
|
-
from prefect._internal.schemas.validators import return_v_or_none
|
13
|
-
|
14
|
-
if HAS_PYDANTIC_V2:
|
15
|
-
from pydantic.v1 import BaseModel, Field, PrivateAttr, validator
|
16
|
-
else:
|
17
|
-
from pydantic import BaseModel, Field, PrivateAttr, validator
|
10
|
+
from pydantic import BaseModel, Field, PrivateAttr, field_validator
|
18
11
|
|
19
12
|
import prefect
|
20
13
|
from prefect._internal.compatibility.experimental import (
|
@@ -22,6 +15,7 @@ from prefect._internal.compatibility.experimental import (
|
|
22
15
|
ExperimentalFeature,
|
23
16
|
experiment_enabled,
|
24
17
|
)
|
18
|
+
from prefect._internal.schemas.validators import return_v_or_none
|
25
19
|
from prefect.client.orchestration import PrefectClient, get_client
|
26
20
|
from prefect.client.schemas.actions import WorkPoolCreate, WorkPoolUpdate
|
27
21
|
from prefect.client.schemas.filters import (
|
@@ -37,7 +31,6 @@ from prefect.client.schemas.filters import (
|
|
37
31
|
)
|
38
32
|
from prefect.client.schemas.objects import StateType, WorkPool
|
39
33
|
from prefect.client.utilities import inject_client
|
40
|
-
from prefect.engine import propose_state
|
41
34
|
from prefect.events import Event, RelatedResource, emit_event
|
42
35
|
from prefect.events.related import object_as_related_resource, tags_as_related_resources
|
43
36
|
from prefect.exceptions import (
|
@@ -57,6 +50,7 @@ from prefect.settings import (
|
|
57
50
|
)
|
58
51
|
from prefect.states import Crashed, Pending, exception_to_failed_state
|
59
52
|
from prefect.utilities.dispatch import get_registry_for_type, register_base_type
|
53
|
+
from prefect.utilities.engine import propose_state
|
60
54
|
from prefect.utilities.slugify import slugify
|
61
55
|
from prefect.utilities.templating import (
|
62
56
|
apply_values,
|
@@ -107,16 +101,27 @@ class BaseJobConfiguration(BaseModel):
|
|
107
101
|
def is_using_a_runner(self):
|
108
102
|
return self.command is not None and "prefect flow-run execute" in self.command
|
109
103
|
|
110
|
-
@
|
104
|
+
@field_validator("command")
|
105
|
+
@classmethod
|
111
106
|
def _coerce_command(cls, v):
|
112
107
|
return return_v_or_none(v)
|
113
108
|
|
109
|
+
@field_validator("env", mode="before")
|
110
|
+
@classmethod
|
111
|
+
def _coerce_env(cls, v):
|
112
|
+
return {k: str(v) if v is not None else None for k, v in v.items()}
|
113
|
+
|
114
114
|
@staticmethod
|
115
115
|
def _get_base_config_defaults(variables: dict) -> dict:
|
116
116
|
"""Get default values from base config for all variables that have them."""
|
117
117
|
defaults = dict()
|
118
118
|
for variable_name, attrs in variables.items():
|
119
|
-
|
119
|
+
# We remote `None` values because we don't want to use them in templating.
|
120
|
+
# The currently logic depends on keys not existing to populate the correct value
|
121
|
+
# in some cases.
|
122
|
+
# Pydantic will provide default values if the keys are missing when creating
|
123
|
+
# a configuration class.
|
124
|
+
if "default" in attrs and attrs.get("default") is not None:
|
120
125
|
defaults[variable_name] = attrs["default"]
|
121
126
|
|
122
127
|
return defaults
|
@@ -124,7 +129,10 @@ class BaseJobConfiguration(BaseModel):
|
|
124
129
|
@classmethod
|
125
130
|
@inject_client
|
126
131
|
async def from_template_and_values(
|
127
|
-
cls,
|
132
|
+
cls,
|
133
|
+
base_job_template: dict,
|
134
|
+
values: dict,
|
135
|
+
client: Optional["PrefectClient"] = None,
|
128
136
|
):
|
129
137
|
"""Creates a valid worker configuration object from the provided base
|
130
138
|
configuration and overrides.
|
@@ -161,7 +169,7 @@ class BaseJobConfiguration(BaseModel):
|
|
161
169
|
}
|
162
170
|
"""
|
163
171
|
configuration = {}
|
164
|
-
properties = cls.
|
172
|
+
properties = cls.model_json_schema()["properties"]
|
165
173
|
for k, v in properties.items():
|
166
174
|
if v.get("template"):
|
167
175
|
template = v["template"]
|
@@ -420,7 +428,7 @@ class BaseWorker(abc.ABC):
|
|
420
428
|
@classmethod
|
421
429
|
def get_default_base_job_template(cls) -> Dict:
|
422
430
|
if cls.job_configuration_variables is None:
|
423
|
-
schema = cls.job_configuration.
|
431
|
+
schema = cls.job_configuration.model_json_schema()
|
424
432
|
# remove "template" key from all dicts in schema['properties'] because it is not a
|
425
433
|
# relevant field
|
426
434
|
for key, value in schema["properties"].items():
|
@@ -428,7 +436,7 @@ class BaseWorker(abc.ABC):
|
|
428
436
|
schema["properties"][key].pop("template", None)
|
429
437
|
variables_schema = schema
|
430
438
|
else:
|
431
|
-
variables_schema = cls.job_configuration_variables.
|
439
|
+
variables_schema = cls.job_configuration_variables.model_json_schema()
|
432
440
|
variables_schema.pop("title", None)
|
433
441
|
return {
|
434
442
|
"job_configuration": cls.job_configuration.json_template(),
|
@@ -963,7 +971,7 @@ class BaseWorker(abc.ABC):
|
|
963
971
|
return {
|
964
972
|
"name": self.name,
|
965
973
|
"work_pool": (
|
966
|
-
self._work_pool.
|
974
|
+
self._work_pool.model_dump(mode="json")
|
967
975
|
if self._work_pool is not None
|
968
976
|
else None
|
969
977
|
),
|
@@ -1068,7 +1076,7 @@ class BaseWorker(abc.ABC):
|
|
1068
1076
|
state_updates = state_updates or {}
|
1069
1077
|
state_updates.setdefault("name", "Cancelled")
|
1070
1078
|
state_updates.setdefault("type", StateType.CANCELLED)
|
1071
|
-
state = flow_run.state.
|
1079
|
+
state = flow_run.state.model_copy(update=state_updates)
|
1072
1080
|
|
1073
1081
|
await self._client.set_flow_run_state(flow_run.id, state, force=True)
|
1074
1082
|
|
@@ -1155,7 +1163,7 @@ class BaseWorker(abc.ABC):
|
|
1155
1163
|
if include_self:
|
1156
1164
|
worker_resource = self._event_resource()
|
1157
1165
|
worker_resource["prefect.resource.role"] = "worker"
|
1158
|
-
related.append(RelatedResource.
|
1166
|
+
related.append(RelatedResource.model_validate(worker_resource))
|
1159
1167
|
|
1160
1168
|
return related
|
1161
1169
|
|
prefect/workers/process.py
CHANGED
@@ -26,13 +26,7 @@ from typing import TYPE_CHECKING, Dict, Optional, Tuple
|
|
26
26
|
|
27
27
|
import anyio
|
28
28
|
import anyio.abc
|
29
|
-
|
30
|
-
from prefect._internal.pydantic import HAS_PYDANTIC_V2
|
31
|
-
|
32
|
-
if HAS_PYDANTIC_V2:
|
33
|
-
from pydantic.v1 import Field, validator
|
34
|
-
else:
|
35
|
-
from pydantic import Field, validator
|
29
|
+
from pydantic import Field, field_validator
|
36
30
|
|
37
31
|
from prefect._internal.schemas.validators import validate_command
|
38
32
|
from prefect.client.schemas import FlowRun
|
@@ -68,7 +62,8 @@ class ProcessJobConfiguration(BaseJobConfiguration):
|
|
68
62
|
stream_output: bool = Field(default=True)
|
69
63
|
working_dir: Optional[Path] = Field(default=None)
|
70
64
|
|
71
|
-
@
|
65
|
+
@field_validator("working_dir")
|
66
|
+
@classmethod
|
72
67
|
def validate_command(cls, v):
|
73
68
|
return validate_command(v)
|
74
69
|
|
prefect/workers/server.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
from typing import Union
|
2
2
|
|
3
3
|
import uvicorn
|
4
|
-
from
|
5
|
-
from
|
4
|
+
from fastapi import APIRouter, FastAPI, status
|
5
|
+
from fastapi.responses import JSONResponse
|
6
6
|
|
7
7
|
from prefect.settings import (
|
8
8
|
PREFECT_WORKER_WEBSERVER_HOST,
|