prefect-client 2.20.2__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.
Files changed (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +423 -164
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +667 -440
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2466
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +124 -51
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +138 -48
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.2.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.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)
@@ -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
@@ -1,14 +1,16 @@
1
1
  import sys
2
- from asyncio import CancelledError
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 ** backoff_count}s."
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
@@ -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.
@@ -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(seconds: Optional[float] = None):
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 TimeoutError(f"Scope timed out after {seconds} second(s).")
32
+ raise timeout_exc_type(f"Scope timed out after {seconds} second(s).")
22
33
 
23
34
 
24
35
  @contextmanager
25
- def timeout(seconds: Optional[float] = None):
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 TimeoutError(f"Scope timed out after {seconds} second(s).")
49
+ raise timeout_exc_type(f"Scope timed out after {seconds} second(s).")