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
prefect/states.py CHANGED
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import datetime
2
3
  import sys
3
4
  import traceback
@@ -13,28 +14,31 @@ from typing_extensions import TypeGuard
13
14
 
14
15
  from prefect.client.schemas import State as State
15
16
  from prefect.client.schemas import StateDetails, StateType
16
- from prefect.deprecated.data_documents import (
17
- DataDocument,
18
- result_from_state_with_data_document,
19
- )
20
17
  from prefect.exceptions import (
21
18
  CancelledRun,
22
19
  CrashedRun,
23
20
  FailedRun,
21
+ MissingContextError,
24
22
  MissingResult,
25
23
  PausedRun,
26
24
  TerminationSignal,
27
25
  UnfinishedRun,
28
26
  )
29
- from prefect.results import BaseResult, R, ResultFactory
27
+ from prefect.logging.loggers import get_logger, get_run_logger
28
+ from prefect.results import BaseResult, R, ResultStore
30
29
  from prefect.settings import PREFECT_ASYNC_FETCH_STATE_RESULT
31
30
  from prefect.utilities.annotations import BaseAnnotation
32
31
  from prefect.utilities.asyncutils import in_async_main_thread, sync_compatible
33
32
  from prefect.utilities.collections import ensure_iterable
34
33
 
34
+ logger = get_logger("states")
35
+
35
36
 
36
37
  def get_state_result(
37
- state: State[R], raise_on_failure: bool = True, fetch: Optional[bool] = None
38
+ state: State[R],
39
+ raise_on_failure: bool = True,
40
+ fetch: Optional[bool] = None,
41
+ retry_result_failure: bool = True,
38
42
  ) -> R:
39
43
  """
40
44
  Get the result from a state.
@@ -47,7 +51,6 @@ def get_state_result(
47
51
  ):
48
52
  # Fetch defaults to `True` for sync users or async users who have opted in
49
53
  fetch = True
50
-
51
54
  if not fetch:
52
55
  if fetch is None and in_async_main_thread():
53
56
  warnings.warn(
@@ -60,19 +63,53 @@ def get_state_result(
60
63
  DeprecationWarning,
61
64
  stacklevel=2,
62
65
  )
63
- # Backwards compatibility
64
- if isinstance(state.data, DataDocument):
65
- return result_from_state_with_data_document(
66
- state, raise_on_failure=raise_on_failure
67
- )
68
- else:
69
- return state.data
66
+
67
+ return state.data
70
68
  else:
71
- return _get_state_result(state, raise_on_failure=raise_on_failure)
69
+ return _get_state_result(
70
+ state,
71
+ raise_on_failure=raise_on_failure,
72
+ retry_result_failure=retry_result_failure,
73
+ )
74
+
75
+
76
+ RESULT_READ_MAXIMUM_ATTEMPTS = 10
77
+ RESULT_READ_RETRY_DELAY = 0.25
78
+
79
+
80
+ async def _get_state_result_data_with_retries(
81
+ state: State[R], retry_result_failure: bool = True
82
+ ) -> R:
83
+ # Results may be written asynchronously, possibly after their corresponding
84
+ # state has been written and events have been emitted, so we should give some
85
+ # grace here about missing results. The exception below could come in the form
86
+ # of a missing file, a short read, or other types of errors depending on the
87
+ # result storage backend.
88
+ if retry_result_failure is False:
89
+ max_attempts = 1
90
+ else:
91
+ max_attempts = RESULT_READ_MAXIMUM_ATTEMPTS
92
+
93
+ for i in range(1, max_attempts + 1):
94
+ try:
95
+ return await state.data.get()
96
+ except Exception as e:
97
+ if i == max_attempts:
98
+ raise
99
+ logger.debug(
100
+ "Exception %r while reading result, retry %s/%s in %ss...",
101
+ e,
102
+ i,
103
+ max_attempts,
104
+ RESULT_READ_RETRY_DELAY,
105
+ )
106
+ await asyncio.sleep(RESULT_READ_RETRY_DELAY)
72
107
 
73
108
 
74
109
  @sync_compatible
75
- async def _get_state_result(state: State[R], raise_on_failure: bool) -> R:
110
+ async def _get_state_result(
111
+ state: State[R], raise_on_failure: bool, retry_result_failure: bool = True
112
+ ) -> R:
76
113
  """
77
114
  Internal implementation for `get_state_result` without async backwards compatibility
78
115
  """
@@ -90,12 +127,11 @@ async def _get_state_result(state: State[R], raise_on_failure: bool) -> R:
90
127
  ):
91
128
  raise await get_state_exception(state)
92
129
 
93
- if isinstance(state.data, DataDocument):
94
- result = result_from_state_with_data_document(
95
- state, raise_on_failure=raise_on_failure
130
+ if isinstance(state.data, BaseResult):
131
+ result = await _get_state_result_data_with_retries(
132
+ state, retry_result_failure=retry_result_failure
96
133
  )
97
- elif isinstance(state.data, BaseResult):
98
- result = await state.data.get()
134
+
99
135
  elif state.data is None:
100
136
  if state.is_failed() or state.is_crashed() or state.is_cancelled():
101
137
  return await get_state_exception(state)
@@ -131,7 +167,7 @@ def format_exception(exc: BaseException, tb: TracebackType = None) -> str:
131
167
 
132
168
  async def exception_to_crashed_state(
133
169
  exc: BaseException,
134
- result_factory: Optional[ResultFactory] = None,
170
+ result_store: Optional[ResultStore] = None,
135
171
  ) -> State:
136
172
  """
137
173
  Takes an exception that occurs _outside_ of user code and converts it to a
@@ -170,8 +206,8 @@ async def exception_to_crashed_state(
170
206
  f" {format_exception(exc)}"
171
207
  )
172
208
 
173
- if result_factory:
174
- data = await result_factory.create_result(exc)
209
+ if result_store:
210
+ data = await result_store.create_result(exc)
175
211
  else:
176
212
  # Attach the exception for local usage, will not be available when retrieved
177
213
  # from the API
@@ -182,12 +218,18 @@ async def exception_to_crashed_state(
182
218
 
183
219
  async def exception_to_failed_state(
184
220
  exc: Optional[BaseException] = None,
185
- result_factory: Optional[ResultFactory] = None,
221
+ result_store: Optional[ResultStore] = None,
222
+ write_result: bool = False,
186
223
  **kwargs,
187
224
  ) -> State:
188
225
  """
189
226
  Convenience function for creating `Failed` states from exceptions
190
227
  """
228
+ try:
229
+ local_logger = get_run_logger()
230
+ except MissingContextError:
231
+ local_logger = logger
232
+
191
233
  if not exc:
192
234
  _, exc, _ = sys.exc_info()
193
235
  if exc is None:
@@ -197,8 +239,16 @@ async def exception_to_failed_state(
197
239
  else:
198
240
  pass
199
241
 
200
- if result_factory:
201
- data = await result_factory.create_result(exc)
242
+ if result_store:
243
+ data = await result_store.create_result(exc)
244
+ if write_result:
245
+ try:
246
+ await data.write()
247
+ except Exception as exc:
248
+ local_logger.warning(
249
+ "Failed to write result: %s Execution will continue, but the result has not been written",
250
+ exc,
251
+ )
202
252
  else:
203
253
  # Attach the exception for local usage, will not be available when retrieved
204
254
  # from the API
@@ -212,10 +262,19 @@ async def exception_to_failed_state(
212
262
  # excluded from messages for now
213
263
  message = existing_message + format_exception(exc)
214
264
 
215
- return Failed(data=data, message=message, **kwargs)
265
+ state = Failed(data=data, message=message, **kwargs)
266
+ state.state_details.retriable = False
267
+
268
+ return state
216
269
 
217
270
 
218
- async def return_value_to_state(retval: R, result_factory: ResultFactory) -> State[R]:
271
+ async def return_value_to_state(
272
+ retval: R,
273
+ result_store: ResultStore,
274
+ key: Optional[str] = None,
275
+ expiration: Optional[datetime.datetime] = None,
276
+ write_result: bool = False,
277
+ ) -> State[R]:
219
278
  """
220
279
  Given a return value from a user's function, create a `State` the run should
221
280
  be placed in.
@@ -236,28 +295,39 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
236
295
  Callers should resolve all futures into states before passing return values to this
237
296
  function.
238
297
  """
298
+ try:
299
+ local_logger = get_run_logger()
300
+ except MissingContextError:
301
+ local_logger = logger
239
302
 
240
303
  if (
241
- is_state(retval)
304
+ isinstance(retval, State)
242
305
  # Check for manual creation
243
306
  and not retval.state_details.flow_run_id
244
307
  and not retval.state_details.task_run_id
245
308
  ):
246
309
  state = retval
247
-
248
- # Do not modify states with data documents attached; backwards compatibility
249
- if isinstance(state.data, DataDocument):
250
- return state
251
-
252
- # Unless the user has already constructed a result explicitly, use the factory
310
+ # Unless the user has already constructed a result explicitly, use the store
253
311
  # to update the data to the correct type
254
312
  if not isinstance(state.data, BaseResult):
255
- state.data = await result_factory.create_result(state.data)
256
-
313
+ result = await result_store.create_result(
314
+ state.data,
315
+ key=key,
316
+ expiration=expiration,
317
+ )
318
+ if write_result:
319
+ try:
320
+ await result.write()
321
+ except Exception as exc:
322
+ local_logger.warning(
323
+ "Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
324
+ exc,
325
+ )
326
+ state.data = result
257
327
  return state
258
328
 
259
329
  # Determine a new state from the aggregate of contained states
260
- if is_state(retval) or is_state_iterable(retval):
330
+ if isinstance(retval, State) or is_state_iterable(retval):
261
331
  states = StateGroup(ensure_iterable(retval))
262
332
 
263
333
  # Determine the new state type
@@ -289,10 +359,23 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
289
359
  # TODO: We may actually want to set the data to a `StateGroup` object and just
290
360
  # allow it to be unpacked into a tuple and such so users can interact with
291
361
  # it
362
+ result = await result_store.create_result(
363
+ retval,
364
+ key=key,
365
+ expiration=expiration,
366
+ )
367
+ if write_result:
368
+ try:
369
+ await result.write()
370
+ except Exception as exc:
371
+ local_logger.warning(
372
+ "Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
373
+ exc,
374
+ )
292
375
  return State(
293
376
  type=new_state_type,
294
377
  message=message,
295
- data=await result_factory.create_result(retval),
378
+ data=result,
296
379
  )
297
380
 
298
381
  # Generators aren't portable, implicitly convert them to a list.
@@ -302,7 +385,23 @@ async def return_value_to_state(retval: R, result_factory: ResultFactory) -> Sta
302
385
  data = retval
303
386
 
304
387
  # Otherwise, they just gave data and this is a completed retval
305
- return Completed(data=await result_factory.create_result(data))
388
+ if isinstance(data, BaseResult):
389
+ return Completed(data=data)
390
+ else:
391
+ result = await result_store.create_result(
392
+ data,
393
+ key=key,
394
+ expiration=expiration,
395
+ )
396
+ if write_result:
397
+ try:
398
+ await result.write()
399
+ except Exception as exc:
400
+ local_logger.warning(
401
+ "Encountered an error while persisting result: %s Execution will continue, but the result has not been persisted",
402
+ exc,
403
+ )
404
+ return Completed(data=result)
306
405
 
307
406
 
308
407
  @sync_compatible
@@ -342,7 +441,7 @@ async def get_state_exception(state: State) -> BaseException:
342
441
  raise ValueError(f"Expected failed or crashed state got {state!r}.")
343
442
 
344
443
  if isinstance(state.data, BaseResult):
345
- result = await state.data.get()
444
+ result = await _get_state_result_data_with_retries(state)
346
445
  elif state.data is None:
347
446
  result = None
348
447
  else:
@@ -360,7 +459,7 @@ async def get_state_exception(state: State) -> BaseException:
360
459
  elif isinstance(result, str):
361
460
  return wrapper(result)
362
461
 
363
- elif is_state(result):
462
+ elif isinstance(result, State):
364
463
  # Return the exception from the inner state
365
464
  return await get_state_exception(result)
366
465
 
@@ -392,23 +491,6 @@ async def raise_state_exception(state: State) -> None:
392
491
  raise await get_state_exception(state)
393
492
 
394
493
 
395
- def is_state(obj: Any) -> TypeGuard[State]:
396
- """
397
- Check if the given object is a state instance
398
- """
399
- # We may want to narrow this to client-side state types but for now this provides
400
- # backwards compatibility
401
- try:
402
- from prefect.server.schemas.states import State as State_
403
-
404
- classes_ = (State, State_)
405
- except ImportError:
406
- classes_ = State
407
-
408
- # return isinstance(obj, (State, State_))
409
- return isinstance(obj, classes_)
410
-
411
-
412
494
  def is_state_iterable(obj: Any) -> TypeGuard[Iterable[State]]:
413
495
  """
414
496
  Check if a the given object is an iterable of states types
@@ -427,7 +509,7 @@ def is_state_iterable(obj: Any) -> TypeGuard[Iterable[State]]:
427
509
  and isinstance(obj, (list, set, tuple))
428
510
  and obj
429
511
  ):
430
- return all([is_state(o) for o in obj])
512
+ return all([isinstance(o, State) for o in obj])
431
513
  else:
432
514
  return False
433
515
 
@@ -495,7 +577,7 @@ def Scheduled(
495
577
  Returns:
496
578
  State: a Scheduled state
497
579
  """
498
- state_details = StateDetails.parse_obj(kwargs.pop("state_details", {}))
580
+ state_details = StateDetails.model_validate(kwargs.pop("state_details", {}))
499
581
  if scheduled_time is None:
500
582
  scheduled_time = pendulum.now("UTC")
501
583
  elif state_details.scheduled_time:
@@ -581,7 +663,7 @@ def Paused(
581
663
  Returns:
582
664
  State: a Paused state
583
665
  """
584
- state_details = StateDetails.parse_obj(kwargs.pop("state_details", {}))
666
+ state_details = StateDetails.model_validate(kwargs.pop("state_details", {}))
585
667
 
586
668
  if state_details.pause_timeout:
587
669
  raise ValueError("An extra pause timeout was provided in state_details")
@@ -642,6 +724,21 @@ def AwaitingRetry(
642
724
  )
643
725
 
644
726
 
727
+ def AwaitingConcurrencySlot(
728
+ cls: Type[State[R]] = State,
729
+ scheduled_time: Optional[datetime.datetime] = None,
730
+ **kwargs: Any,
731
+ ) -> State[R]:
732
+ """Convenience function for creating `AwaitingConcurrencySlot` states.
733
+
734
+ Returns:
735
+ State: a AwaitingConcurrencySlot state
736
+ """
737
+ return Scheduled(
738
+ cls=cls, scheduled_time=scheduled_time, name="AwaitingConcurrencySlot", **kwargs
739
+ )
740
+
741
+
645
742
  def Retrying(cls: Type[State[R]] = State, **kwargs: Any) -> State[R]:
646
743
  """Convenience function for creating `Retrying` states.
647
744