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.
Files changed (239) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/system.py +22 -11
  26. prefect/blocks/webhook.py +2 -9
  27. prefect/client/base.py +4 -4
  28. prefect/client/cloud.py +8 -13
  29. prefect/client/orchestration.py +347 -341
  30. prefect/client/schemas/actions.py +92 -86
  31. prefect/client/schemas/filters.py +20 -40
  32. prefect/client/schemas/objects.py +151 -145
  33. prefect/client/schemas/responses.py +16 -24
  34. prefect/client/schemas/schedules.py +47 -35
  35. prefect/client/subscriptions.py +2 -2
  36. prefect/client/utilities.py +5 -2
  37. prefect/concurrency/asyncio.py +3 -1
  38. prefect/concurrency/events.py +1 -1
  39. prefect/concurrency/services.py +6 -3
  40. prefect/context.py +195 -27
  41. prefect/deployments/__init__.py +5 -6
  42. prefect/deployments/base.py +7 -5
  43. prefect/deployments/flow_runs.py +185 -0
  44. prefect/deployments/runner.py +50 -45
  45. prefect/deployments/schedules.py +28 -23
  46. prefect/deployments/steps/__init__.py +0 -1
  47. prefect/deployments/steps/core.py +1 -0
  48. prefect/deployments/steps/pull.py +7 -21
  49. prefect/engine.py +12 -2422
  50. prefect/events/actions.py +17 -23
  51. prefect/events/cli/automations.py +19 -6
  52. prefect/events/clients.py +14 -37
  53. prefect/events/filters.py +14 -18
  54. prefect/events/related.py +2 -2
  55. prefect/events/schemas/__init__.py +0 -5
  56. prefect/events/schemas/automations.py +55 -46
  57. prefect/events/schemas/deployment_triggers.py +7 -197
  58. prefect/events/schemas/events.py +34 -65
  59. prefect/events/schemas/labelling.py +10 -14
  60. prefect/events/utilities.py +2 -3
  61. prefect/events/worker.py +2 -3
  62. prefect/filesystems.py +6 -517
  63. prefect/{new_flow_engine.py → flow_engine.py} +313 -72
  64. prefect/flow_runs.py +377 -5
  65. prefect/flows.py +307 -166
  66. prefect/futures.py +186 -345
  67. prefect/infrastructure/__init__.py +0 -27
  68. prefect/infrastructure/provisioners/__init__.py +5 -3
  69. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  70. prefect/infrastructure/provisioners/container_instance.py +11 -7
  71. prefect/infrastructure/provisioners/ecs.py +6 -4
  72. prefect/infrastructure/provisioners/modal.py +8 -5
  73. prefect/input/actions.py +2 -4
  74. prefect/input/run_input.py +5 -7
  75. prefect/logging/formatters.py +0 -2
  76. prefect/logging/handlers.py +3 -11
  77. prefect/logging/loggers.py +2 -2
  78. prefect/manifests.py +2 -1
  79. prefect/records/__init__.py +1 -0
  80. prefect/records/result_store.py +42 -0
  81. prefect/records/store.py +9 -0
  82. prefect/results.py +43 -39
  83. prefect/runner/runner.py +19 -15
  84. prefect/runner/server.py +6 -10
  85. prefect/runner/storage.py +3 -8
  86. prefect/runner/submit.py +2 -2
  87. prefect/runner/utils.py +2 -2
  88. prefect/serializers.py +24 -35
  89. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  90. prefect/settings.py +70 -133
  91. prefect/states.py +17 -47
  92. prefect/task_engine.py +697 -58
  93. prefect/task_runners.py +269 -301
  94. prefect/task_server.py +53 -34
  95. prefect/tasks.py +327 -337
  96. prefect/transactions.py +220 -0
  97. prefect/types/__init__.py +61 -82
  98. prefect/utilities/asyncutils.py +195 -136
  99. prefect/utilities/callables.py +311 -43
  100. prefect/utilities/collections.py +23 -38
  101. prefect/utilities/dispatch.py +11 -3
  102. prefect/utilities/dockerutils.py +4 -0
  103. prefect/utilities/engine.py +140 -20
  104. prefect/utilities/importtools.py +97 -27
  105. prefect/utilities/pydantic.py +128 -38
  106. prefect/utilities/schema_tools/hydration.py +5 -1
  107. prefect/utilities/templating.py +12 -2
  108. prefect/variables.py +78 -61
  109. prefect/workers/__init__.py +0 -1
  110. prefect/workers/base.py +15 -17
  111. prefect/workers/process.py +3 -8
  112. prefect/workers/server.py +2 -2
  113. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/METADATA +22 -21
  114. prefect_client-3.0.0rc1.dist-info/RECORD +176 -0
  115. prefect/_internal/pydantic/_base_model.py +0 -51
  116. prefect/_internal/pydantic/_compat.py +0 -82
  117. prefect/_internal/pydantic/_flags.py +0 -20
  118. prefect/_internal/pydantic/_types.py +0 -8
  119. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  120. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  121. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  122. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  123. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  124. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  125. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  126. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  127. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  128. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  129. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  130. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  131. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  132. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  133. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  134. prefect/_vendor/__init__.py +0 -0
  135. prefect/_vendor/fastapi/__init__.py +0 -25
  136. prefect/_vendor/fastapi/applications.py +0 -946
  137. prefect/_vendor/fastapi/background.py +0 -3
  138. prefect/_vendor/fastapi/concurrency.py +0 -44
  139. prefect/_vendor/fastapi/datastructures.py +0 -58
  140. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  141. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  142. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  143. prefect/_vendor/fastapi/encoders.py +0 -177
  144. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  145. prefect/_vendor/fastapi/exceptions.py +0 -46
  146. prefect/_vendor/fastapi/logger.py +0 -3
  147. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  148. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  149. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  150. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  151. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  152. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  153. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  154. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  155. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  156. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  157. prefect/_vendor/fastapi/openapi/models.py +0 -480
  158. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  159. prefect/_vendor/fastapi/param_functions.py +0 -340
  160. prefect/_vendor/fastapi/params.py +0 -453
  161. prefect/_vendor/fastapi/requests.py +0 -4
  162. prefect/_vendor/fastapi/responses.py +0 -40
  163. prefect/_vendor/fastapi/routing.py +0 -1331
  164. prefect/_vendor/fastapi/security/__init__.py +0 -15
  165. prefect/_vendor/fastapi/security/api_key.py +0 -98
  166. prefect/_vendor/fastapi/security/base.py +0 -6
  167. prefect/_vendor/fastapi/security/http.py +0 -172
  168. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  169. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  170. prefect/_vendor/fastapi/security/utils.py +0 -10
  171. prefect/_vendor/fastapi/staticfiles.py +0 -1
  172. prefect/_vendor/fastapi/templating.py +0 -3
  173. prefect/_vendor/fastapi/testclient.py +0 -1
  174. prefect/_vendor/fastapi/types.py +0 -3
  175. prefect/_vendor/fastapi/utils.py +0 -235
  176. prefect/_vendor/fastapi/websockets.py +0 -7
  177. prefect/_vendor/starlette/__init__.py +0 -1
  178. prefect/_vendor/starlette/_compat.py +0 -28
  179. prefect/_vendor/starlette/_exception_handler.py +0 -80
  180. prefect/_vendor/starlette/_utils.py +0 -88
  181. prefect/_vendor/starlette/applications.py +0 -261
  182. prefect/_vendor/starlette/authentication.py +0 -159
  183. prefect/_vendor/starlette/background.py +0 -43
  184. prefect/_vendor/starlette/concurrency.py +0 -59
  185. prefect/_vendor/starlette/config.py +0 -151
  186. prefect/_vendor/starlette/convertors.py +0 -87
  187. prefect/_vendor/starlette/datastructures.py +0 -707
  188. prefect/_vendor/starlette/endpoints.py +0 -130
  189. prefect/_vendor/starlette/exceptions.py +0 -60
  190. prefect/_vendor/starlette/formparsers.py +0 -276
  191. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  192. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  193. prefect/_vendor/starlette/middleware/base.py +0 -220
  194. prefect/_vendor/starlette/middleware/cors.py +0 -176
  195. prefect/_vendor/starlette/middleware/errors.py +0 -265
  196. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  197. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  198. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  199. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  200. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  201. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  202. prefect/_vendor/starlette/requests.py +0 -328
  203. prefect/_vendor/starlette/responses.py +0 -347
  204. prefect/_vendor/starlette/routing.py +0 -933
  205. prefect/_vendor/starlette/schemas.py +0 -154
  206. prefect/_vendor/starlette/staticfiles.py +0 -248
  207. prefect/_vendor/starlette/status.py +0 -199
  208. prefect/_vendor/starlette/templating.py +0 -231
  209. prefect/_vendor/starlette/testclient.py +0 -804
  210. prefect/_vendor/starlette/types.py +0 -30
  211. prefect/_vendor/starlette/websockets.py +0 -193
  212. prefect/agent.py +0 -698
  213. prefect/deployments/deployments.py +0 -1042
  214. prefect/deprecated/__init__.py +0 -0
  215. prefect/deprecated/data_documents.py +0 -350
  216. prefect/deprecated/packaging/__init__.py +0 -12
  217. prefect/deprecated/packaging/base.py +0 -96
  218. prefect/deprecated/packaging/docker.py +0 -146
  219. prefect/deprecated/packaging/file.py +0 -92
  220. prefect/deprecated/packaging/orion.py +0 -80
  221. prefect/deprecated/packaging/serializers.py +0 -171
  222. prefect/events/instrument.py +0 -135
  223. prefect/infrastructure/base.py +0 -323
  224. prefect/infrastructure/container.py +0 -818
  225. prefect/infrastructure/kubernetes.py +0 -920
  226. prefect/infrastructure/process.py +0 -289
  227. prefect/new_task_engine.py +0 -423
  228. prefect/pydantic/__init__.py +0 -76
  229. prefect/pydantic/main.py +0 -39
  230. prefect/software/__init__.py +0 -2
  231. prefect/software/base.py +0 -50
  232. prefect/software/conda.py +0 -199
  233. prefect/software/pip.py +0 -122
  234. prefect/software/python.py +0 -52
  235. prefect/workers/block.py +0 -218
  236. prefect_client-2.19.2.dist-info/RECORD +0 -292
  237. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/LICENSE +0 -0
  238. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/WHEEL +0 -0
  239. {prefect_client-2.19.2.dist-info → prefect_client-3.0.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,38 @@
1
1
  from functools import partial
2
- from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast, overload
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 pydantic_core import to_jsonable_python
6
- from typing_extensions import Self
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=pydantic_v1.BaseModel)
31
+ M = TypeVar("M", bound=BaseModel)
32
+ T = TypeVar("T", bound=Any)
20
33
 
21
34
 
22
- def _reduce_model(model: pydantic_v1.BaseModel):
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.json(**getattr(model, "__reduce_kwargs__", {})),
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.parse_raw(json)
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[pydantic_v1.BaseModel]) -> set:
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, pydantic_v1.BaseModel):
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.__fields__
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.__fields__["type"].type_
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.__fields__["type"].default
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[Self], **kwargs) -> Self:
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 pydantic_v1.ValidationError(errors=[exc], model=cls)
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(pydantic_v1.BaseModel):
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.__fields__:
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 __modify_schema__(cls, field_schema):
261
- field_schema.update(
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, pydantic_v1.BaseModel):
286
- return model_dump(obj, mode="json")
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[str, str] = Field(default_factory=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)
@@ -1,7 +1,17 @@
1
1
  import enum
2
2
  import os
3
3
  import re
4
- from typing import TYPE_CHECKING, Any, Dict, NamedTuple, Set, Type, TypeVar, Union
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,107 @@ class Variable(VariableRequest):
23
25
  async def set(
24
26
  cls,
25
27
  name: str,
26
- value: str,
28
+ value: StrictVariableValue,
27
29
  tags: Optional[List[str]] = None,
28
30
  overwrite: bool = False,
29
- ) -> Optional[str]:
31
+ ):
30
32
  """
31
33
  Sets a new variable. If one exists with the same name, user must pass `overwrite=True`
32
- ```
33
- from prefect.variables import Variable
34
34
 
35
- @flow
36
- def my_flow():
37
- var = Variable.set(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
38
- ```
39
- or
40
- ```
35
+ Returns `True` if the variable was created or updated
36
+
37
+ Args:
38
+ - name: The name of the variable to set.
39
+ - value: The value of the variable to set.
40
+ - tags: An optional list of strings to associate with the variable.
41
+ - overwrite: Whether to overwrite the variable if it already exists.
42
+
43
+ Example:
44
+ Set a new variable and overwrite it if it already exists.
45
+ ```
41
46
  from prefect.variables import Variable
42
47
 
43
48
  @flow
44
- async def my_flow():
45
- var = await Variable.set(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
46
- ```
49
+ def my_flow():
50
+ Variable.set(name="my_var",value="test_value", tags=["hi", "there"], overwrite=True)
51
+ ```
47
52
  """
48
53
  client, _ = get_or_create_client()
49
54
  variable = await client.read_variable_by_name(name)
50
- var_dict = {"name": name, "value": value}
51
- var_dict["tags"] = tags or []
55
+ var_dict = {"name": name, "value": value, "tags": tags or []}
52
56
  if variable:
53
57
  if not overwrite:
54
58
  raise ValueError(
55
- "You are attempting to save a variable with a name that is already in use. If you would like to overwrite the values that are saved, then call .set with `overwrite=True`."
59
+ "You are attempting to set a variable with a name that is already in use. "
60
+ "If you would like to overwrite it, pass `overwrite=True`."
56
61
  )
57
- var = VariableUpdateRequest(**var_dict)
58
- await client.update_variable(variable=var)
59
- variable = await client.read_variable_by_name(name)
62
+ await client.update_variable(variable=VariableUpdateRequest(**var_dict))
60
63
  else:
61
- var = VariableRequest(**var_dict)
62
- variable = await client.create_variable(variable=var)
63
-
64
- return variable if variable else None
64
+ await client.create_variable(variable=VariableRequest(**var_dict))
65
65
 
66
66
  @classmethod
67
67
  @sync_compatible
68
- async def get(cls, name: str, default: Optional[str] = None) -> Optional[str]:
68
+ async def get(
69
+ cls,
70
+ name: str,
71
+ default: StrictVariableValue = None,
72
+ as_object: bool = False,
73
+ ) -> Union[StrictVariableValue, VariableResponse]:
69
74
  """
70
- Get a variable by name. If doesn't exist return the default.
71
- ```
75
+ Get a variable's value by name.
76
+
77
+ If the variable does not exist, return the default value.
78
+
79
+ If `as_object=True`, return the full variable object. `default` is ignored in this case.
80
+
81
+ Args:
82
+ - name: The name of the variable to get.
83
+ - default: The default value to return if the variable does not exist.
84
+ - as_object: Whether to return the full variable object.
85
+
86
+ Example:
87
+ Get a variable's value by name.
88
+ ```python
89
+ from prefect import flow
72
90
  from prefect.variables import Variable
73
91
 
74
92
  @flow
75
93
  def my_flow():
76
94
  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
- ```
95
+ ```
86
96
  """
87
97
  client, _ = get_or_create_client()
88
98
  variable = await client.read_variable_by_name(name)
89
- return variable if variable else default
99
+ if as_object:
100
+ return variable
90
101
 
102
+ return variable.value if variable else default
91
103
 
92
- @deprecated_callable(start_date="Apr 2024")
93
- @sync_compatible
94
- async def get(name: str, default: Optional[str] = None) -> Optional[str]:
95
- """
96
- Get a variable by name. If doesn't exist return the default.
97
- ```
98
- from prefect import variables
99
-
100
- @flow
101
- def my_flow():
102
- var = variables.get("my_var")
103
- ```
104
- or
105
- ```
106
- from prefect import variables
107
-
108
- @flow
109
- async def my_flow():
110
- var = await variables.get("my_var")
111
- ```
112
- """
113
- variable = await Variable.get(name)
114
- return variable.value if variable else default
104
+ @classmethod
105
+ @sync_compatible
106
+ async def unset(cls, name: str) -> bool:
107
+ """
108
+ Unset a variable by name.
109
+
110
+ Args:
111
+ - name: The name of the variable to unset.
112
+
113
+ Returns `True` if the variable was deleted, `False` if the variable did not exist.
114
+
115
+ Example:
116
+ Unset a variable by name.
117
+ ```python
118
+ from prefect import flow
119
+ from prefect.variables import Variable
120
+
121
+ @flow
122
+ def my_flow():
123
+ Variable.unset("my_var")
124
+ ```
125
+ """
126
+ client, _ = get_or_create_client()
127
+ try:
128
+ await client.delete_variable_by_name(name=name)
129
+ return True
130
+ except ObjectNotFound:
131
+ return False
@@ -1,2 +1 @@
1
1
  from .process import ProcessWorker
2
- from .block import BlockWorker
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,7 +101,8 @@ 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
- @validator("command")
104
+ @field_validator("command")
105
+ @classmethod
111
106
  def _coerce_command(cls, v):
112
107
  return return_v_or_none(v)
113
108
 
@@ -124,7 +119,10 @@ class BaseJobConfiguration(BaseModel):
124
119
  @classmethod
125
120
  @inject_client
126
121
  async def from_template_and_values(
127
- cls, base_job_template: dict, values: dict, client: "PrefectClient" = None
122
+ cls,
123
+ base_job_template: dict,
124
+ values: dict,
125
+ client: Optional["PrefectClient"] = None,
128
126
  ):
129
127
  """Creates a valid worker configuration object from the provided base
130
128
  configuration and overrides.
@@ -161,7 +159,7 @@ class BaseJobConfiguration(BaseModel):
161
159
  }
162
160
  """
163
161
  configuration = {}
164
- properties = cls.schema()["properties"]
162
+ properties = cls.model_json_schema()["properties"]
165
163
  for k, v in properties.items():
166
164
  if v.get("template"):
167
165
  template = v["template"]
@@ -420,7 +418,7 @@ class BaseWorker(abc.ABC):
420
418
  @classmethod
421
419
  def get_default_base_job_template(cls) -> Dict:
422
420
  if cls.job_configuration_variables is None:
423
- schema = cls.job_configuration.schema()
421
+ schema = cls.job_configuration.model_json_schema()
424
422
  # remove "template" key from all dicts in schema['properties'] because it is not a
425
423
  # relevant field
426
424
  for key, value in schema["properties"].items():
@@ -428,7 +426,7 @@ class BaseWorker(abc.ABC):
428
426
  schema["properties"][key].pop("template", None)
429
427
  variables_schema = schema
430
428
  else:
431
- variables_schema = cls.job_configuration_variables.schema()
429
+ variables_schema = cls.job_configuration_variables.model_json_schema()
432
430
  variables_schema.pop("title", None)
433
431
  return {
434
432
  "job_configuration": cls.job_configuration.json_template(),
@@ -963,7 +961,7 @@ class BaseWorker(abc.ABC):
963
961
  return {
964
962
  "name": self.name,
965
963
  "work_pool": (
966
- self._work_pool.dict(json_compatible=True)
964
+ self._work_pool.model_dump(mode="json")
967
965
  if self._work_pool is not None
968
966
  else None
969
967
  ),
@@ -1068,7 +1066,7 @@ class BaseWorker(abc.ABC):
1068
1066
  state_updates = state_updates or {}
1069
1067
  state_updates.setdefault("name", "Cancelled")
1070
1068
  state_updates.setdefault("type", StateType.CANCELLED)
1071
- state = flow_run.state.copy(update=state_updates)
1069
+ state = flow_run.state.model_copy(update=state_updates)
1072
1070
 
1073
1071
  await self._client.set_flow_run_state(flow_run.id, state, force=True)
1074
1072
 
@@ -1155,7 +1153,7 @@ class BaseWorker(abc.ABC):
1155
1153
  if include_self:
1156
1154
  worker_resource = self._event_resource()
1157
1155
  worker_resource["prefect.resource.role"] = "worker"
1158
- related.append(RelatedResource.parse_obj(worker_resource))
1156
+ related.append(RelatedResource.model_validate(worker_resource))
1159
1157
 
1160
1158
  return related
1161
1159
 
@@ -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
- @validator("working_dir")
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 prefect._vendor.fastapi import APIRouter, FastAPI, status
5
- from prefect._vendor.fastapi.responses import JSONResponse
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,