prefect-client 3.4.1.dev4__py3-none-any.whl → 3.4.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
prefect/events/filters.py CHANGED
@@ -1,5 +1,7 @@
1
+ from __future__ import annotations
2
+
1
3
  import datetime
2
- from typing import Optional
4
+ from typing import Optional, Union
3
5
  from uuid import UUID
4
6
 
5
7
  from pydantic import Field
@@ -43,11 +45,21 @@ class EventDataFilter(PrefectBaseModel, extra="forbid"): # type: ignore[call-ar
43
45
  """A base class for filtering event data."""
44
46
 
45
47
  def get_filters(self) -> list["EventDataFilter"]:
46
- filters: list["EventDataFilter"] = [
47
- filter
48
- for filter in [getattr(self, name) for name in type(self).model_fields]
49
- if isinstance(filter, EventDataFilter)
50
- ]
48
+ filters: list[EventDataFilter] = []
49
+ for filter in [
50
+ getattr(self, name) for name in self.__class__.model_fields.keys()
51
+ ]:
52
+ # Any embedded list of filters are flattened and thus ANDed together
53
+ subfilters: list[EventDataFilter] = (
54
+ filter if isinstance(filter, list) else [filter]
55
+ )
56
+
57
+ for subfilter in subfilters:
58
+ if not isinstance(subfilter, EventDataFilter):
59
+ continue
60
+
61
+ filters.append(subfilter)
62
+
51
63
  return filters
52
64
 
53
65
  def includes(self, event: Event) -> bool:
@@ -233,18 +245,20 @@ class EventFilter(EventDataFilter):
233
245
  default=None,
234
246
  description="Filter criteria for the event name",
235
247
  )
236
- any_resource: Optional[EventAnyResourceFilter] = Field(
237
- default=None,
238
- description="Filter criteria for any resource involved in the event",
239
- )
240
248
  resource: Optional[EventResourceFilter] = Field(
241
249
  default=None,
242
250
  description="Filter criteria for the resource of the event",
243
251
  )
244
- related: Optional[EventRelatedFilter] = Field(
252
+ related: Optional[Union[EventRelatedFilter, list[EventRelatedFilter]]] = Field(
245
253
  default=None,
246
254
  description="Filter criteria for the related resources of the event",
247
255
  )
256
+ any_resource: Optional[
257
+ Union[EventAnyResourceFilter, list[EventAnyResourceFilter]]
258
+ ] = Field(
259
+ default=None,
260
+ description="Filter criteria for any resource involved in the event",
261
+ )
248
262
  id: EventIDFilter = Field(
249
263
  default_factory=lambda: EventIDFilter(id=[]),
250
264
  description="Filter criteria for the events' ID",
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
4
  import textwrap
3
5
  from datetime import timedelta
@@ -103,7 +105,7 @@ class ResourceTrigger(Trigger, abc.ABC):
103
105
  default_factory=lambda: ResourceSpecification.model_validate({}),
104
106
  description="Labels for resources which this trigger will match.",
105
107
  )
106
- match_related: ResourceSpecification = Field(
108
+ match_related: Union[ResourceSpecification, list[ResourceSpecification]] = Field(
107
109
  default_factory=lambda: ResourceSpecification.model_validate({}),
108
110
  description="Labels for related resources which this trigger will match.",
109
111
  )
@@ -13,7 +13,7 @@ from typing import (
13
13
  Tuple,
14
14
  Union,
15
15
  )
16
- from uuid import UUID, uuid4
16
+ from uuid import UUID
17
17
 
18
18
  from pydantic import (
19
19
  AfterValidator,
@@ -26,6 +26,7 @@ from typing_extensions import Annotated, Self
26
26
 
27
27
  import prefect.types._datetime
28
28
  from prefect._internal.schemas.bases import PrefectBaseModel
29
+ from prefect._internal.uuid7 import uuid7
29
30
  from prefect.logging import get_logger
30
31
  from prefect.settings import (
31
32
  PREFECT_EVENTS_MAXIMUM_LABELS_PER_RESOURCE,
@@ -135,7 +136,7 @@ class Event(PrefectBaseModel):
135
136
  description="An open-ended set of data describing what happened",
136
137
  )
137
138
  id: UUID = Field(
138
- default_factory=uuid4,
139
+ default_factory=uuid7,
139
140
  description="The client-provided identifier of this event",
140
141
  )
141
142
  follows: Optional[UUID] = Field(
prefect/flows.py CHANGED
@@ -404,7 +404,7 @@ class Flow(Generic[P, R]):
404
404
  module_name = inspect.getfile(fn)
405
405
  module = module_name if module_name != "__main__" else module
406
406
 
407
- self._entrypoint = f"{module}:{fn.__name__}"
407
+ self._entrypoint = f"{module}:{getattr(fn, '__qualname__', fn.__name__)}"
408
408
 
409
409
  @property
410
410
  def ismethod(self) -> bool:
prefect/results.py CHANGED
@@ -1,11 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import asyncio
4
- import inspect
5
4
  import os
6
5
  import socket
7
6
  import threading
8
7
  import uuid
8
+ from datetime import datetime
9
9
  from functools import partial
10
10
  from operator import methodcaller
11
11
  from pathlib import Path
@@ -34,6 +34,8 @@ from typing_extensions import ParamSpec, Self
34
34
  import prefect
35
35
  import prefect.types._datetime
36
36
  from prefect._internal.compatibility.async_dispatch import async_dispatch
37
+ from prefect._internal.compatibility.blocks import call_explicitly_async_block_method
38
+ from prefect._internal.compatibility.deprecated import deprecated_callable
37
39
  from prefect._internal.concurrency.event_loop import get_running_loop
38
40
  from prefect._result_records import R, ResultRecord, ResultRecordMetadata
39
41
  from prefect.blocks.core import Block
@@ -285,29 +287,6 @@ def _format_user_supplied_storage_key(key: str) -> str:
285
287
  return key.format(**runtime_vars, parameters=prefect.runtime.task_run.parameters)
286
288
 
287
289
 
288
- async def _call_explicitly_async_block_method(
289
- block: WritableFileSystem | NullFileSystem,
290
- method: str,
291
- args: tuple[Any, ...],
292
- kwargs: dict[str, Any],
293
- ) -> Any:
294
- """
295
- TODO: remove this once we have explicit async methods on all storage blocks
296
-
297
- see https://github.com/PrefectHQ/prefect/issues/15008
298
- """
299
- if hasattr(block, f"a{method}"): # explicit async method
300
- return await getattr(block, f"a{method}")(*args, **kwargs)
301
- elif hasattr(getattr(block, method, None), "aio"): # sync_compatible
302
- return await getattr(block, method).aio(block, *args, **kwargs)
303
- else: # should not happen in prefect, but users can override impls
304
- maybe_coro = getattr(block, method)(*args, **kwargs)
305
- if inspect.isawaitable(maybe_coro):
306
- return await maybe_coro
307
- else:
308
- return maybe_coro
309
-
310
-
311
290
  T = TypeVar("T")
312
291
 
313
292
 
@@ -505,7 +484,7 @@ class ResultStore(BaseModel):
505
484
  # TODO: Add an `exists` method to commonly used storage blocks
506
485
  # so the entire payload doesn't need to be read
507
486
  try:
508
- metadata_content = await _call_explicitly_async_block_method(
487
+ metadata_content = await call_explicitly_async_block_method(
509
488
  self.metadata_storage, "read_path", (key,), {}
510
489
  )
511
490
  if metadata_content is None:
@@ -516,7 +495,7 @@ class ResultStore(BaseModel):
516
495
  return False
517
496
  else:
518
497
  try:
519
- content = await _call_explicitly_async_block_method(
498
+ content = await call_explicitly_async_block_method(
520
499
  self.result_storage, "read_path", (key,), {}
521
500
  )
522
501
  if content is None:
@@ -601,7 +580,7 @@ class ResultStore(BaseModel):
601
580
  self.result_storage = await aget_default_result_storage()
602
581
 
603
582
  if self.metadata_storage is not None:
604
- metadata_content = await _call_explicitly_async_block_method(
583
+ metadata_content = await call_explicitly_async_block_method(
605
584
  self.metadata_storage,
606
585
  "read_path",
607
586
  (key,),
@@ -611,7 +590,7 @@ class ResultStore(BaseModel):
611
590
  assert metadata.storage_key is not None, (
612
591
  "Did not find storage key in metadata"
613
592
  )
614
- result_content = await _call_explicitly_async_block_method(
593
+ result_content = await call_explicitly_async_block_method(
615
594
  self.result_storage,
616
595
  "read_path",
617
596
  (metadata.storage_key,),
@@ -624,7 +603,7 @@ class ResultStore(BaseModel):
624
603
  )
625
604
  await emit_result_read_event(self, resolved_key_path)
626
605
  else:
627
- content = await _call_explicitly_async_block_method(
606
+ content = await call_explicitly_async_block_method(
628
607
  self.result_storage,
629
608
  "read_path",
630
609
  (key,),
@@ -806,13 +785,13 @@ class ResultStore(BaseModel):
806
785
 
807
786
  # If metadata storage is configured, write result and metadata separately
808
787
  if self.metadata_storage is not None:
809
- await _call_explicitly_async_block_method(
788
+ await call_explicitly_async_block_method(
810
789
  self.result_storage,
811
790
  "write_path",
812
791
  (result_record.metadata.storage_key,),
813
792
  {"content": result_record.serialize_result()},
814
793
  )
815
- await _call_explicitly_async_block_method(
794
+ await call_explicitly_async_block_method(
816
795
  self.metadata_storage,
817
796
  "write_path",
818
797
  (base_key,),
@@ -821,7 +800,7 @@ class ResultStore(BaseModel):
821
800
  await emit_result_write_event(self, result_record.metadata.storage_key)
822
801
  # Otherwise, write the result metadata and result together
823
802
  else:
824
- await _call_explicitly_async_block_method(
803
+ await call_explicitly_async_block_method(
825
804
  self.result_storage,
826
805
  "write_path",
827
806
  (result_record.metadata.storage_key,),
@@ -998,6 +977,11 @@ class ResultStore(BaseModel):
998
977
 
999
978
  # TODO: These two methods need to find a new home
1000
979
 
980
+ @deprecated_callable(
981
+ start_date=datetime(2025, 5, 10),
982
+ end_date=datetime(2025, 11, 10),
983
+ help="Use `store_parameters` from `prefect.task_worker` instead.",
984
+ )
1001
985
  @sync_compatible
1002
986
  async def store_parameters(self, identifier: UUID, parameters: dict[str, Any]):
1003
987
  record = ResultRecord(
@@ -1007,21 +991,26 @@ class ResultStore(BaseModel):
1007
991
  ),
1008
992
  )
1009
993
 
1010
- await _call_explicitly_async_block_method(
994
+ await call_explicitly_async_block_method(
1011
995
  self.result_storage,
1012
996
  "write_path",
1013
997
  (f"parameters/{identifier}",),
1014
998
  {"content": record.serialize()},
1015
999
  )
1016
1000
 
1001
+ @deprecated_callable(
1002
+ start_date=datetime(2025, 5, 10),
1003
+ end_date=datetime(2025, 11, 10),
1004
+ help="Use `read_parameters` from `prefect.task_worker` instead.",
1005
+ )
1017
1006
  @sync_compatible
1018
1007
  async def read_parameters(self, identifier: UUID) -> dict[str, Any]:
1019
1008
  if self.result_storage is None:
1020
1009
  raise ValueError(
1021
1010
  "Result store is not configured - must have a result storage block to read parameters"
1022
1011
  )
1023
- record: ResultRecord[Any] = ResultRecord.deserialize(
1024
- await _call_explicitly_async_block_method(
1012
+ record: ResultRecord[Any] = ResultRecord[Any].deserialize(
1013
+ await call_explicitly_async_block_method(
1025
1014
  self.result_storage,
1026
1015
  "read_path",
1027
1016
  (f"parameters/{identifier}",),
prefect/runner/runner.py CHANGED
@@ -968,16 +968,9 @@ class Runner:
968
968
 
969
969
  pid = process_map_entry.get("pid") if process_map_entry else None
970
970
  if not pid:
971
- if flow_run.state:
972
- await self._run_on_cancellation_hooks(flow_run, flow_run.state)
973
- await self._mark_flow_run_as_cancelled(
974
- flow_run,
975
- state_updates={
976
- "message": (
977
- "Could not find process ID for flow run"
978
- " and cancellation cannot be guaranteed."
979
- )
980
- },
971
+ self._logger.debug(
972
+ "Received cancellation request for flow run %s but no process was found.",
973
+ flow_run.id,
981
974
  )
982
975
  return
983
976
 
prefect/serializers.py CHANGED
@@ -12,6 +12,7 @@ bytes to an object respectively.
12
12
  """
13
13
 
14
14
  import base64
15
+ import io
15
16
  from typing import Any, ClassVar, Generic, Optional, Union, overload
16
17
 
17
18
  from pydantic import (
@@ -46,6 +47,14 @@ def prefect_json_object_encoder(obj: Any) -> Any:
46
47
  """
47
48
  if isinstance(obj, BaseException):
48
49
  return {"__exc_type__": to_qualified_name(obj.__class__), "message": str(obj)}
50
+ elif isinstance(obj, io.IOBase):
51
+ return {
52
+ "__class__": to_qualified_name(obj.__class__),
53
+ "data": (
54
+ f"<Prefect IOStream Placeholder: type={obj.__class__.__name__}, "
55
+ f"repr={repr(obj)} (original content not read)>"
56
+ ),
57
+ }
49
58
  else:
50
59
  return {
51
60
  "__class__": to_qualified_name(obj.__class__),
@@ -3,7 +3,7 @@ Routes for interacting with work queue objects.
3
3
  """
4
4
 
5
5
  from typing import TYPE_CHECKING, List, Optional
6
- from uuid import UUID, uuid4
6
+ from uuid import UUID
7
7
 
8
8
  import sqlalchemy as sa
9
9
  from fastapi import (
@@ -20,6 +20,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
20
20
  import prefect.server.api.dependencies as dependencies
21
21
  import prefect.server.models as models
22
22
  import prefect.server.schemas as schemas
23
+ from prefect._internal.uuid7 import uuid7
23
24
  from prefect.server.api.validation import validate_job_variable_defaults_for_work_pool
24
25
  from prefect.server.database import PrefectDBInterface, provide_database_interface
25
26
  from prefect.server.models.deployments import mark_deployments_ready
@@ -184,7 +185,7 @@ async def create_work_pool(
184
185
  )
185
186
 
186
187
  await emit_work_pool_status_event(
187
- event_id=uuid4(),
188
+ event_id=uuid7(),
188
189
  occurred=now("UTC"),
189
190
  pre_update_work_pool=None,
190
191
  work_pool=model,
@@ -1,9 +1,10 @@
1
- from typing import ClassVar, Optional, Union
1
+ from typing import ClassVar, Optional
2
2
 
3
3
  from pydantic import AliasChoices, AliasPath, Field
4
4
  from pydantic_settings import SettingsConfigDict
5
5
 
6
6
  from prefect.settings.base import PrefectBaseSettings, build_settings_config
7
+ from prefect.types import TaskRetryDelaySeconds
7
8
 
8
9
 
9
10
  class TasksRunnerSettings(PrefectBaseSettings):
@@ -73,7 +74,7 @@ class TasksSettings(PrefectBaseSettings):
73
74
  ),
74
75
  )
75
76
 
76
- default_retry_delay_seconds: Union[int, float, list[float]] = Field(
77
+ default_retry_delay_seconds: TaskRetryDelaySeconds = Field(
77
78
  default=0,
78
79
  description="This value sets the default retry delay seconds for all tasks.",
79
80
  validation_alias=AliasChoices(
prefect/task_runners.py CHANGED
@@ -21,6 +21,7 @@ from typing import (
21
21
 
22
22
  from typing_extensions import ParamSpec, Self, TypeVar
23
23
 
24
+ from prefect._internal.uuid7 import uuid7
24
25
  from prefect.client.schemas.objects import TaskRunInput
25
26
  from prefect.exceptions import MappingLengthMismatch, MappingMissingIterable
26
27
  from prefect.futures import (
@@ -290,7 +291,7 @@ class ThreadPoolTaskRunner(TaskRunner[PrefectConcurrentFuture[R]]):
290
291
  from prefect.context import FlowRunContext
291
292
  from prefect.task_engine import run_task_async, run_task_sync
292
293
 
293
- task_run_id = uuid.uuid4()
294
+ task_run_id = uuid7()
294
295
  cancel_event = threading.Event()
295
296
  self._cancel_events[task_run_id] = cancel_event
296
297
  context = copy_context()
prefect/task_worker.py CHANGED
@@ -22,17 +22,20 @@ from websockets.exceptions import InvalidStatus
22
22
 
23
23
  import prefect.types._datetime
24
24
  from prefect import Task
25
+ from prefect._internal.compatibility.blocks import call_explicitly_async_block_method
25
26
  from prefect._internal.concurrency.api import create_call, from_sync
26
27
  from prefect.cache_policies import DEFAULT, NO_CACHE
27
28
  from prefect.client.orchestration import get_client
28
29
  from prefect.client.schemas.objects import TaskRun
29
30
  from prefect.client.subscriptions import Subscription
30
31
  from prefect.logging.loggers import get_logger
31
- from prefect.results import ResultStore, get_or_create_default_task_scheduling_storage
32
- from prefect.settings import (
33
- PREFECT_API_URL,
34
- PREFECT_TASK_SCHEDULING_DELETE_FAILED_SUBMISSIONS,
32
+ from prefect.results import (
33
+ ResultRecord,
34
+ ResultRecordMetadata,
35
+ ResultStore,
36
+ get_or_create_default_task_scheduling_storage,
35
37
  )
38
+ from prefect.settings import get_current_settings
36
39
  from prefect.states import Pending
37
40
  from prefect.task_engine import run_task_async, run_task_sync
38
41
  from prefect.types import DateTime
@@ -43,6 +46,7 @@ from prefect.utilities.processutils import (
43
46
  _register_signal, # pyright: ignore[reportPrivateUsage]
44
47
  )
45
48
  from prefect.utilities.services import start_client_metrics_server
49
+ from prefect.utilities.timeout import timeout_async
46
50
  from prefect.utilities.urls import url_for
47
51
 
48
52
  if TYPE_CHECKING:
@@ -170,9 +174,13 @@ class TaskWorker:
170
174
  sys.exit(0)
171
175
 
172
176
  @sync_compatible
173
- async def start(self) -> None:
177
+ async def start(self, timeout: Optional[float] = None) -> None:
174
178
  """
175
179
  Starts a task worker, which runs the tasks provided in the constructor.
180
+
181
+ Args:
182
+ timeout: If provided, the task worker will exit after the given number of
183
+ seconds. Defaults to None, meaning the task worker will run indefinitely.
176
184
  """
177
185
  _register_signal(signal.SIGTERM, self.handle_sigterm)
178
186
 
@@ -181,14 +189,16 @@ class TaskWorker:
181
189
  async with asyncnullcontext() if self.started else self:
182
190
  logger.info("Starting task worker...")
183
191
  try:
184
- await self._subscribe_to_task_scheduling()
192
+ with timeout_async(timeout):
193
+ await self._subscribe_to_task_scheduling()
185
194
  except InvalidStatus as exc:
186
195
  if exc.response.status_code == 403:
187
196
  logger.error(
188
197
  "403: Could not establish a connection to the `/task_runs/subscriptions/scheduled`"
189
- f" endpoint found at:\n\n {PREFECT_API_URL.value()}"
190
- "\n\nPlease double-check the values of your"
191
- " `PREFECT_API_URL` and `PREFECT_API_KEY` environment variables."
198
+ f" endpoint found at:\n\n {get_current_settings().api.url}"
199
+ "\n\nPlease double-check the values of"
200
+ " `PREFECT_API_AUTH_STRING` and `PREFECT_SERVER_API_AUTH_STRING` if running a Prefect server "
201
+ "or `PREFECT_API_URL` and `PREFECT_API_KEY` environment variables if using Prefect Cloud."
192
202
  )
193
203
  else:
194
204
  raise
@@ -228,7 +238,7 @@ class TaskWorker:
228
238
  return True
229
239
 
230
240
  async def _subscribe_to_task_scheduling(self):
231
- base_url = PREFECT_API_URL.value()
241
+ base_url = get_current_settings().api.url
232
242
  if base_url is None:
233
243
  raise ValueError(
234
244
  "`PREFECT_API_URL` must be set to use the task worker. "
@@ -282,7 +292,7 @@ class TaskWorker:
282
292
  task = next((t for t in self.tasks if t.task_key == task_run.task_key), None)
283
293
 
284
294
  if not task:
285
- if PREFECT_TASK_SCHEDULING_DELETE_FAILED_SUBMISSIONS:
295
+ if get_current_settings().tasks.scheduling.delete_failed_submissions:
286
296
  logger.warning(
287
297
  f"Task {task_run.name!r} not found in task worker registry."
288
298
  )
@@ -298,12 +308,18 @@ class TaskWorker:
298
308
  run_context = None
299
309
  if should_try_to_read_parameters(task, task_run):
300
310
  parameters_id = task_run.state.state_details.task_parameters_id
311
+ if parameters_id is None:
312
+ logger.warning(
313
+ f"Task run {task_run.id!r} has no parameters ID. Skipping parameter retrieval."
314
+ )
315
+ return
316
+
301
317
  task.persist_result = True
302
318
  store = await ResultStore(
303
319
  result_storage=await get_or_create_default_task_scheduling_storage()
304
320
  ).update_for_task(task)
305
321
  try:
306
- run_data: dict[str, Any] = await store.read_parameters(parameters_id)
322
+ run_data: dict[str, Any] = await read_parameters(store, parameters_id)
307
323
  parameters = run_data.get("parameters", {})
308
324
  wait_for = run_data.get("wait_for", [])
309
325
  run_context = run_data.get("context", None)
@@ -312,7 +328,7 @@ class TaskWorker:
312
328
  f"Failed to read parameters for task run {task_run.id!r}",
313
329
  exc_info=exc,
314
330
  )
315
- if PREFECT_TASK_SCHEDULING_DELETE_FAILED_SUBMISSIONS.value():
331
+ if get_current_settings().tasks.scheduling.delete_failed_submissions:
316
332
  logger.info(
317
333
  f"Deleting task run {task_run.id!r} because it failed to submit"
318
334
  )
@@ -421,6 +437,7 @@ async def serve(
421
437
  *tasks: Task[P, R],
422
438
  limit: Optional[int] = 10,
423
439
  status_server_port: Optional[int] = None,
440
+ timeout: Optional[float] = None,
424
441
  ):
425
442
  """Serve the provided tasks so that their runs may be submitted to
426
443
  and executed in the engine. Tasks do not need to be within a flow run context to be
@@ -434,6 +451,8 @@ async def serve(
434
451
  - status_server_port: An optional port on which to start an HTTP server
435
452
  exposing status information about the task worker. If not provided, no
436
453
  status server will run.
454
+ - timeout: If provided, the task worker will exit after the given number of
455
+ seconds. Defaults to None, meaning the task worker will run indefinitely.
437
456
 
438
457
  Example:
439
458
  ```python
@@ -469,7 +488,13 @@ async def serve(
469
488
  status_server_task = loop.create_task(server.serve())
470
489
 
471
490
  try:
472
- await task_worker.start()
491
+ await task_worker.start(timeout=timeout)
492
+
493
+ except TimeoutError:
494
+ if timeout is not None:
495
+ logger.info(f"Task worker timed out after {timeout} seconds. Exiting...")
496
+ else:
497
+ raise
473
498
 
474
499
  except BaseExceptionGroup as exc: # novermin
475
500
  exceptions = exc.exceptions
@@ -492,3 +517,59 @@ async def serve(
492
517
  await status_server_task
493
518
  except asyncio.CancelledError:
494
519
  pass
520
+
521
+
522
+ async def store_parameters(
523
+ result_store: ResultStore, identifier: UUID, parameters: dict[str, Any]
524
+ ) -> None:
525
+ """Store parameters for a task run in the result store.
526
+
527
+ Args:
528
+ result_store: The result store to store the parameters in.
529
+ identifier: The identifier of the task run.
530
+ parameters: The parameters to store.
531
+ """
532
+ if result_store.result_storage is None:
533
+ raise ValueError(
534
+ "Result store is not configured - must have a result storage block to store parameters"
535
+ )
536
+ record = ResultRecord(
537
+ result=parameters,
538
+ metadata=ResultRecordMetadata(
539
+ serializer=result_store.serializer, storage_key=str(identifier)
540
+ ),
541
+ )
542
+
543
+ await call_explicitly_async_block_method(
544
+ result_store.result_storage,
545
+ "write_path",
546
+ (f"parameters/{identifier}",),
547
+ {"content": record.serialize()},
548
+ )
549
+
550
+
551
+ async def read_parameters(
552
+ result_store: ResultStore, identifier: UUID
553
+ ) -> dict[str, Any]:
554
+ """Read parameters for a task run from the result store.
555
+
556
+ Args:
557
+ result_store: The result store to read the parameters from.
558
+ identifier: The identifier of the task run.
559
+
560
+ Returns:
561
+ The parameters for the task run.
562
+ """
563
+ if result_store.result_storage is None:
564
+ raise ValueError(
565
+ "Result store is not configured - must have a result storage block to read parameters"
566
+ )
567
+ record: ResultRecord[Any] = ResultRecord[Any].deserialize(
568
+ await call_explicitly_async_block_method(
569
+ result_store.result_storage,
570
+ "read_path",
571
+ (f"parameters/{identifier}",),
572
+ {},
573
+ )
574
+ )
575
+ return record.result
prefect/tasks.py CHANGED
@@ -32,6 +32,7 @@ from uuid import UUID, uuid4
32
32
  from typing_extensions import Literal, ParamSpec, Self, TypeAlias, TypeIs
33
33
 
34
34
  import prefect.states
35
+ from prefect._internal.uuid7 import uuid7
35
36
  from prefect.cache_policies import DEFAULT, NO_CACHE, CachePolicy
36
37
  from prefect.client.orchestration import get_client
37
38
  from prefect.client.schemas import TaskRun
@@ -809,6 +810,8 @@ class Task(Generic[P, R]):
809
810
  # store parameters for background tasks so that task worker
810
811
  # can retrieve them at runtime
811
812
  if deferred and (parameters or wait_for):
813
+ from prefect.task_worker import store_parameters
814
+
812
815
  parameters_id = uuid4()
813
816
  state.state_details.task_parameters_id = parameters_id
814
817
 
@@ -824,7 +827,7 @@ class Task(Generic[P, R]):
824
827
  data["parameters"] = parameters
825
828
  if wait_for:
826
829
  data["wait_for"] = wait_for
827
- await store.store_parameters(parameters_id, data)
830
+ await store_parameters(store, parameters_id, data)
828
831
 
829
832
  # collect task inputs
830
833
  task_inputs = {
@@ -910,6 +913,8 @@ class Task(Generic[P, R]):
910
913
  # store parameters for background tasks so that task worker
911
914
  # can retrieve them at runtime
912
915
  if deferred and (parameters or wait_for):
916
+ from prefect.task_worker import store_parameters
917
+
913
918
  parameters_id = uuid4()
914
919
  state.state_details.task_parameters_id = parameters_id
915
920
 
@@ -925,7 +930,7 @@ class Task(Generic[P, R]):
925
930
  data["parameters"] = parameters
926
931
  if wait_for:
927
932
  data["wait_for"] = wait_for
928
- await store.store_parameters(parameters_id, data)
933
+ await store_parameters(store, parameters_id, data)
929
934
 
930
935
  # collect task inputs
931
936
  task_inputs = {
@@ -953,7 +958,7 @@ class Task(Generic[P, R]):
953
958
  if flow_run_context and flow_run_context.flow_run
954
959
  else None
955
960
  )
956
- task_run_id = id or uuid4()
961
+ task_run_id = id or uuid7()
957
962
  state = prefect.states.Pending(
958
963
  state_details=StateDetails(
959
964
  task_run_id=task_run_id,