prefect-client 3.1.15__py3-none-any.whl → 3.2.1__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 (89) hide show
  1. prefect/_experimental/sla/objects.py +29 -1
  2. prefect/_internal/compatibility/deprecated.py +4 -4
  3. prefect/_internal/compatibility/migration.py +1 -1
  4. prefect/_internal/concurrency/calls.py +1 -2
  5. prefect/_internal/concurrency/cancellation.py +2 -4
  6. prefect/_internal/concurrency/threads.py +3 -3
  7. prefect/_internal/schemas/bases.py +3 -11
  8. prefect/_internal/schemas/validators.py +36 -60
  9. prefect/_result_records.py +235 -0
  10. prefect/_version.py +3 -3
  11. prefect/agent.py +1 -0
  12. prefect/automations.py +4 -8
  13. prefect/blocks/notifications.py +8 -8
  14. prefect/cache_policies.py +2 -0
  15. prefect/client/base.py +7 -8
  16. prefect/client/collections.py +3 -6
  17. prefect/client/orchestration/__init__.py +15 -263
  18. prefect/client/orchestration/_deployments/client.py +14 -6
  19. prefect/client/orchestration/_flow_runs/client.py +10 -6
  20. prefect/client/orchestration/_work_pools/__init__.py +0 -0
  21. prefect/client/orchestration/_work_pools/client.py +598 -0
  22. prefect/client/orchestration/base.py +9 -2
  23. prefect/client/schemas/actions.py +66 -2
  24. prefect/client/schemas/objects.py +22 -50
  25. prefect/client/schemas/schedules.py +7 -18
  26. prefect/client/types/flexible_schedule_list.py +2 -1
  27. prefect/context.py +2 -3
  28. prefect/deployments/flow_runs.py +1 -1
  29. prefect/deployments/runner.py +119 -43
  30. prefect/deployments/schedules.py +7 -1
  31. prefect/engine.py +4 -9
  32. prefect/events/schemas/automations.py +4 -2
  33. prefect/events/utilities.py +15 -13
  34. prefect/exceptions.py +1 -1
  35. prefect/flow_engine.py +19 -10
  36. prefect/flow_runs.py +4 -8
  37. prefect/flows.py +53 -22
  38. prefect/infrastructure/__init__.py +1 -0
  39. prefect/infrastructure/base.py +1 -0
  40. prefect/infrastructure/provisioners/__init__.py +3 -6
  41. prefect/infrastructure/provisioners/coiled.py +3 -3
  42. prefect/infrastructure/provisioners/container_instance.py +1 -0
  43. prefect/infrastructure/provisioners/ecs.py +6 -6
  44. prefect/infrastructure/provisioners/modal.py +3 -3
  45. prefect/input/run_input.py +5 -7
  46. prefect/locking/filesystem.py +4 -3
  47. prefect/main.py +1 -1
  48. prefect/results.py +42 -249
  49. prefect/runner/runner.py +9 -4
  50. prefect/runner/server.py +5 -5
  51. prefect/runner/storage.py +12 -10
  52. prefect/runner/submit.py +2 -4
  53. prefect/schedules.py +231 -0
  54. prefect/serializers.py +5 -5
  55. prefect/settings/__init__.py +2 -1
  56. prefect/settings/base.py +3 -3
  57. prefect/settings/models/root.py +4 -0
  58. prefect/settings/models/server/services.py +50 -9
  59. prefect/settings/sources.py +8 -4
  60. prefect/states.py +42 -11
  61. prefect/task_engine.py +10 -10
  62. prefect/task_runners.py +11 -22
  63. prefect/task_worker.py +9 -9
  64. prefect/tasks.py +22 -41
  65. prefect/telemetry/bootstrap.py +4 -6
  66. prefect/telemetry/services.py +2 -4
  67. prefect/types/__init__.py +2 -1
  68. prefect/types/_datetime.py +28 -1
  69. prefect/utilities/_engine.py +0 -1
  70. prefect/utilities/asyncutils.py +4 -8
  71. prefect/utilities/collections.py +13 -22
  72. prefect/utilities/dispatch.py +2 -4
  73. prefect/utilities/dockerutils.py +6 -6
  74. prefect/utilities/importtools.py +1 -68
  75. prefect/utilities/names.py +1 -1
  76. prefect/utilities/processutils.py +3 -6
  77. prefect/utilities/pydantic.py +4 -6
  78. prefect/utilities/schema_tools/hydration.py +6 -5
  79. prefect/utilities/templating.py +16 -10
  80. prefect/utilities/visualization.py +2 -4
  81. prefect/workers/base.py +3 -3
  82. prefect/workers/block.py +1 -0
  83. prefect/workers/cloud.py +1 -0
  84. prefect/workers/process.py +1 -0
  85. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/METADATA +1 -1
  86. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/RECORD +89 -85
  87. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/LICENSE +0 -0
  88. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/WHEEL +0 -0
  89. {prefect_client-3.1.15.dist-info → prefect_client-3.2.1.dist-info}/top_level.txt +0 -0
@@ -57,7 +57,7 @@ from prefect._internal.schemas.validators import (
57
57
  )
58
58
  from prefect.client.base import ServerType
59
59
  from prefect.client.orchestration import PrefectClient, get_client
60
- from prefect.client.schemas.actions import DeploymentScheduleCreate
60
+ from prefect.client.schemas.actions import DeploymentScheduleCreate, DeploymentUpdate
61
61
  from prefect.client.schemas.filters import WorkerFilter, WorkerFilterStatus
62
62
  from prefect.client.schemas.objects import (
63
63
  ConcurrencyLimitConfig,
@@ -77,6 +77,7 @@ from prefect.exceptions import (
77
77
  PrefectHTTPStatusError,
78
78
  )
79
79
  from prefect.runner.storage import RunnerStorage
80
+ from prefect.schedules import Schedule
80
81
  from prefect.settings import (
81
82
  PREFECT_DEFAULT_WORK_POOL_NAME,
82
83
  PREFECT_UI_URL,
@@ -113,7 +114,7 @@ class RunnerDeployment(BaseModel):
113
114
  description: An optional description of the deployment; defaults to the flow's
114
115
  description
115
116
  tags: An optional list of tags to associate with this deployment; note that tags
116
- are used only for organizational purposes. For delegating work to agents,
117
+ are used only for organizational purposes. For delegating work to workers,
117
118
  see `work_queue_name`.
118
119
  schedule: A schedule to run this deployment on, once registered
119
120
  parameters: A dictionary of parameter values to pass to runs created from this
@@ -229,6 +230,10 @@ class RunnerDeployment(BaseModel):
229
230
  def entrypoint_type(self) -> EntrypointType:
230
231
  return self._entrypoint_type
231
232
 
233
+ @property
234
+ def full_name(self) -> str:
235
+ return f"{self.flow_name}/{self.name}"
236
+
232
237
  @field_validator("name", mode="before")
233
238
  @classmethod
234
239
  def validate_name(cls, value: str) -> str:
@@ -255,24 +260,9 @@ class RunnerDeployment(BaseModel):
255
260
  def reconcile_schedules(cls, values):
256
261
  return reconcile_schedules_runner(values)
257
262
 
258
- @sync_compatible
259
- async def apply(
263
+ async def _create(
260
264
  self, work_pool_name: Optional[str] = None, image: Optional[str] = None
261
265
  ) -> UUID:
262
- """
263
- Registers this deployment with the API and returns the deployment's ID.
264
-
265
- Args:
266
- work_pool_name: The name of the work pool to use for this
267
- deployment.
268
- image: The registry, name, and tag of the Docker image to
269
- use for this deployment. Only used when the deployment is
270
- deployed to a work pool.
271
-
272
- Returns:
273
- The ID of the created deployment.
274
- """
275
-
276
266
  work_pool_name = work_pool_name or self.work_pool_name
277
267
 
278
268
  if image and not work_pool_name:
@@ -324,9 +314,14 @@ class RunnerDeployment(BaseModel):
324
314
  if image:
325
315
  create_payload["job_variables"]["image"] = image
326
316
  create_payload["path"] = None if self.storage else self._path
327
- create_payload["pull_steps"] = (
328
- [self.storage.to_pull_step()] if self.storage else []
329
- )
317
+ if self.storage:
318
+ pull_steps = self.storage.to_pull_step()
319
+ if isinstance(pull_steps, list):
320
+ create_payload["pull_steps"] = pull_steps
321
+ else:
322
+ create_payload["pull_steps"] = [pull_steps]
323
+ else:
324
+ create_payload["pull_steps"] = []
330
325
 
331
326
  try:
332
327
  deployment_id = await client.create_deployment(**create_payload)
@@ -339,25 +334,7 @@ class RunnerDeployment(BaseModel):
339
334
  f"Error while applying deployment: {str(exc)}"
340
335
  ) from exc
341
336
 
342
- try:
343
- # The triggers defined in the deployment spec are, essentially,
344
- # anonymous and attempting truly sync them with cloud is not
345
- # feasible. Instead, we remove all automations that are owned
346
- # by the deployment, meaning that they were created via this
347
- # mechanism below, and then recreate them.
348
- await client.delete_resource_owned_automations(
349
- f"prefect.deployment.{deployment_id}"
350
- )
351
- except PrefectHTTPStatusError as e:
352
- if e.response.status_code == 404:
353
- # This Prefect server does not support automations, so we can safely
354
- # ignore this 404 and move on.
355
- return deployment_id
356
- raise e
357
-
358
- for trigger in self.triggers:
359
- trigger.set_deployment_id(deployment_id)
360
- await client.create_automation(trigger.as_automation())
337
+ await self._create_triggers(deployment_id, client)
361
338
 
362
339
  # We plan to support SLA configuration on the Prefect Server in the future.
363
340
  # For now, we only support it on Prefect Cloud.
@@ -370,6 +347,86 @@ class RunnerDeployment(BaseModel):
370
347
 
371
348
  return deployment_id
372
349
 
350
+ async def _update(self, deployment_id: UUID, client: PrefectClient):
351
+ parameter_openapi_schema = self._parameter_openapi_schema.model_dump(
352
+ exclude_unset=True
353
+ )
354
+ await client.update_deployment(
355
+ deployment_id,
356
+ deployment=DeploymentUpdate(
357
+ parameter_openapi_schema=parameter_openapi_schema,
358
+ **self.model_dump(
359
+ mode="json",
360
+ exclude_unset=True,
361
+ exclude={"storage", "name", "flow_name", "triggers"},
362
+ ),
363
+ ),
364
+ )
365
+
366
+ await self._create_triggers(deployment_id, client)
367
+
368
+ # We plan to support SLA configuration on the Prefect Server in the future.
369
+ # For now, we only support it on Prefect Cloud.
370
+
371
+ # If we're provided with an empty list, we will call the apply endpoint
372
+ # to remove existing SLAs for the deployment. If the argument is not provided,
373
+ # we will not call the endpoint.
374
+ if self._sla or self._sla == []:
375
+ await self._create_slas(deployment_id, client)
376
+
377
+ return deployment_id
378
+
379
+ async def _create_triggers(self, deployment_id: UUID, client: PrefectClient):
380
+ try:
381
+ # The triggers defined in the deployment spec are, essentially,
382
+ # anonymous and attempting truly sync them with cloud is not
383
+ # feasible. Instead, we remove all automations that are owned
384
+ # by the deployment, meaning that they were created via this
385
+ # mechanism below, and then recreate them.
386
+ await client.delete_resource_owned_automations(
387
+ f"prefect.deployment.{deployment_id}"
388
+ )
389
+ except PrefectHTTPStatusError as e:
390
+ if e.response.status_code == 404:
391
+ # This Prefect server does not support automations, so we can safely
392
+ # ignore this 404 and move on.
393
+ return deployment_id
394
+ raise e
395
+
396
+ for trigger in self.triggers:
397
+ trigger.set_deployment_id(deployment_id)
398
+ await client.create_automation(trigger.as_automation())
399
+
400
+ @sync_compatible
401
+ async def apply(
402
+ self, work_pool_name: Optional[str] = None, image: Optional[str] = None
403
+ ) -> UUID:
404
+ """
405
+ Registers this deployment with the API and returns the deployment's ID.
406
+
407
+ Args:
408
+ work_pool_name: The name of the work pool to use for this
409
+ deployment.
410
+ image: The registry, name, and tag of the Docker image to
411
+ use for this deployment. Only used when the deployment is
412
+ deployed to a work pool.
413
+
414
+ Returns:
415
+ The ID of the created deployment.
416
+ """
417
+
418
+ async with get_client() as client:
419
+ try:
420
+ deployment = await client.read_deployment_by_name(self.full_name)
421
+ except ObjectNotFound:
422
+ return await self._create(work_pool_name, image)
423
+ else:
424
+ if image:
425
+ self.job_variables["image"] = image
426
+ if work_pool_name:
427
+ self.work_pool_name = work_pool_name
428
+ return await self._update(deployment.id, client)
429
+
373
430
  async def _create_slas(self, deployment_id: UUID, client: PrefectClient):
374
431
  if not isinstance(self._sla, list):
375
432
  self._sla = [self._sla]
@@ -390,7 +447,7 @@ class RunnerDeployment(BaseModel):
390
447
  cron: Optional[Union[Iterable[str], str]] = None,
391
448
  rrule: Optional[Union[Iterable[str], str]] = None,
392
449
  timezone: Optional[str] = None,
393
- schedule: Optional[SCHEDULE_TYPES] = None,
450
+ schedule: Union[SCHEDULE_TYPES, Schedule, None] = None,
394
451
  schedules: Optional["FlexibleScheduleList"] = None,
395
452
  ) -> Union[List[DeploymentScheduleCreate], "FlexibleScheduleList"]:
396
453
  """
@@ -422,7 +479,6 @@ class RunnerDeployment(BaseModel):
422
479
  this list is returned as-is, bypassing other schedule construction
423
480
  logic.
424
481
  """
425
-
426
482
  num_schedules = sum(
427
483
  1
428
484
  for entry in (interval, cron, rrule, schedule, schedules)
@@ -430,7 +486,7 @@ class RunnerDeployment(BaseModel):
430
486
  )
431
487
  if num_schedules > 1:
432
488
  raise ValueError(
433
- "Only one of interval, cron, rrule, or schedules can be provided."
489
+ "Only one of interval, cron, rrule, schedule, or schedules can be provided."
434
490
  )
435
491
  elif num_schedules == 0:
436
492
  return []
@@ -483,6 +539,7 @@ class RunnerDeployment(BaseModel):
483
539
  cron: Optional[Union[Iterable[str], str]] = None,
484
540
  rrule: Optional[Union[Iterable[str], str]] = None,
485
541
  paused: Optional[bool] = None,
542
+ schedule: Optional[Schedule] = None,
486
543
  schedules: Optional["FlexibleScheduleList"] = None,
487
544
  concurrency_limit: Optional[Union[int, ConcurrencyLimitConfig, None]] = None,
488
545
  parameters: Optional[dict[str, Any]] = None,
@@ -508,6 +565,8 @@ class RunnerDeployment(BaseModel):
508
565
  cron: A cron schedule of when to execute runs of this flow.
509
566
  rrule: An rrule schedule of when to execute runs of this flow.
510
567
  paused: Whether or not to set this deployment as paused.
568
+ schedule: A schedule object defining when to execute runs of this deployment.
569
+ Used to provide additional scheduling options like `timezone` or `parameters`.
511
570
  schedules: A list of schedule objects defining when to execute runs of this deployment.
512
571
  Used to define multiple schedules or additional scheduling options like `timezone`.
513
572
  concurrency_limit: The maximum number of concurrent runs this deployment will allow.
@@ -533,6 +592,7 @@ class RunnerDeployment(BaseModel):
533
592
  cron=cron,
534
593
  rrule=rrule,
535
594
  schedules=schedules,
595
+ schedule=schedule,
536
596
  )
537
597
 
538
598
  job_variables = job_variables or {}
@@ -627,6 +687,7 @@ class RunnerDeployment(BaseModel):
627
687
  cron: Optional[Union[Iterable[str], str]] = None,
628
688
  rrule: Optional[Union[Iterable[str], str]] = None,
629
689
  paused: Optional[bool] = None,
690
+ schedule: Optional[Schedule] = None,
630
691
  schedules: Optional["FlexibleScheduleList"] = None,
631
692
  concurrency_limit: Optional[Union[int, ConcurrencyLimitConfig, None]] = None,
632
693
  parameters: Optional[dict[str, Any]] = None,
@@ -682,6 +743,7 @@ class RunnerDeployment(BaseModel):
682
743
  cron=cron,
683
744
  rrule=rrule,
684
745
  schedules=schedules,
746
+ schedule=schedule,
685
747
  )
686
748
 
687
749
  if isinstance(concurrency_limit, ConcurrencyLimitConfig):
@@ -730,6 +792,7 @@ class RunnerDeployment(BaseModel):
730
792
  cron: Optional[Union[Iterable[str], str]] = None,
731
793
  rrule: Optional[Union[Iterable[str], str]] = None,
732
794
  paused: Optional[bool] = None,
795
+ schedule: Optional[Schedule] = None,
733
796
  schedules: Optional["FlexibleScheduleList"] = None,
734
797
  concurrency_limit: Optional[Union[int, ConcurrencyLimitConfig, None]] = None,
735
798
  parameters: Optional[dict[str, Any]] = None,
@@ -758,6 +821,11 @@ class RunnerDeployment(BaseModel):
758
821
  or a timedelta object. If a number is given, it will be interpreted as seconds.
759
822
  cron: A cron schedule of when to execute runs of this flow.
760
823
  rrule: An rrule schedule of when to execute runs of this flow.
824
+ paused: Whether or not the deployment is paused.
825
+ schedule: A schedule object defining when to execute runs of this deployment.
826
+ Used to provide additional scheduling options like `timezone` or `parameters`.
827
+ schedules: A list of schedule objects defining when to execute runs of this deployment.
828
+ Used to provide additional scheduling options like `timezone` or `parameters`.
761
829
  triggers: A list of triggers that should kick of a run of this flow.
762
830
  parameters: A dictionary of default parameter values to pass to runs of this flow.
763
831
  description: A description for the created deployment. Defaults to the flow's
@@ -782,6 +850,7 @@ class RunnerDeployment(BaseModel):
782
850
  cron=cron,
783
851
  rrule=rrule,
784
852
  schedules=schedules,
853
+ schedule=schedule,
785
854
  )
786
855
 
787
856
  if isinstance(concurrency_limit, ConcurrencyLimitConfig):
@@ -845,6 +914,7 @@ class RunnerDeployment(BaseModel):
845
914
  cron: Optional[Union[Iterable[str], str]] = None,
846
915
  rrule: Optional[Union[Iterable[str], str]] = None,
847
916
  paused: Optional[bool] = None,
917
+ schedule: Optional[Schedule] = None,
848
918
  schedules: Optional["FlexibleScheduleList"] = None,
849
919
  concurrency_limit: Optional[Union[int, ConcurrencyLimitConfig, None]] = None,
850
920
  parameters: Optional[dict[str, Any]] = None,
@@ -873,6 +943,11 @@ class RunnerDeployment(BaseModel):
873
943
  or a timedelta object. If a number is given, it will be interpreted as seconds.
874
944
  cron: A cron schedule of when to execute runs of this flow.
875
945
  rrule: An rrule schedule of when to execute runs of this flow.
946
+ paused: Whether or not the deployment is paused.
947
+ schedule: A schedule object defining when to execute runs of this deployment.
948
+ Used to provide additional scheduling options like `timezone` or `parameters`.
949
+ schedules: A list of schedule objects defining when to execute runs of this deployment.
950
+ Used to provide additional scheduling options like `timezone` or `parameters`.
876
951
  triggers: A list of triggers that should kick of a run of this flow.
877
952
  parameters: A dictionary of default parameter values to pass to runs of this flow.
878
953
  description: A description for the created deployment. Defaults to the flow's
@@ -897,6 +972,7 @@ class RunnerDeployment(BaseModel):
897
972
  cron=cron,
898
973
  rrule=rrule,
899
974
  schedules=schedules,
975
+ schedule=schedule,
900
976
  )
901
977
 
902
978
  if isinstance(concurrency_limit, ConcurrencyLimitConfig):
@@ -2,6 +2,7 @@ from typing import TYPE_CHECKING, Any, List, Optional, Sequence, Union
2
2
 
3
3
  from prefect.client.schemas.actions import DeploymentScheduleCreate
4
4
  from prefect.client.schemas.schedules import is_schedule_type
5
+ from prefect.schedules import Schedule
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from prefect.client.schemas.schedules import SCHEDULE_TYPES
@@ -12,10 +13,13 @@ FlexibleScheduleList = Sequence[
12
13
 
13
14
 
14
15
  def create_deployment_schedule_create(
15
- schedule: "SCHEDULE_TYPES",
16
+ schedule: Union["SCHEDULE_TYPES", "Schedule"],
16
17
  active: Optional[bool] = True,
17
18
  ) -> DeploymentScheduleCreate:
18
19
  """Create a DeploymentScheduleCreate object from common schedule parameters."""
20
+
21
+ if isinstance(schedule, Schedule):
22
+ return DeploymentScheduleCreate.from_schedule(schedule)
19
23
  return DeploymentScheduleCreate(
20
24
  schedule=schedule,
21
25
  active=active if active is not None else True,
@@ -30,6 +34,8 @@ def normalize_to_deployment_schedule_create(
30
34
  for obj in schedules:
31
35
  if is_schedule_type(obj):
32
36
  normalized.append(create_deployment_schedule_create(obj))
37
+ elif isinstance(obj, Schedule):
38
+ normalized.append(create_deployment_schedule_create(obj))
33
39
  elif isinstance(obj, dict):
34
40
  normalized.append(create_deployment_schedule_create(**obj))
35
41
  elif isinstance(obj, DeploymentScheduleCreate):
prefect/engine.py CHANGED
@@ -62,18 +62,13 @@ if __name__ == "__main__":
62
62
  else:
63
63
  run_flow(flow, flow_run=flow_run, error_logger=run_logger)
64
64
 
65
- except Abort as abort_signal:
66
- abort_signal: Abort
65
+ except Abort:
67
66
  engine_logger.info(
68
- f"Engine execution of flow run '{flow_run_id}' aborted by orchestrator:"
69
- f" {abort_signal}"
67
+ "Engine execution of flow run '{flow_run_id}' aborted by orchestrator."
70
68
  )
71
69
  exit(0)
72
- except Pause as pause_signal:
73
- pause_signal: Pause
74
- engine_logger.info(
75
- f"Engine execution of flow run '{flow_run_id}' is paused: {pause_signal}"
76
- )
70
+ except Pause:
71
+ engine_logger.info(f"Engine execution of flow run '{flow_run_id}' is paused.")
77
72
  exit(0)
78
73
  except Exception:
79
74
  engine_logger.error(
@@ -198,7 +198,9 @@ class EventTrigger(ResourceTrigger):
198
198
  "10 seconds"
199
199
  )
200
200
 
201
- return data | {"within": within} if within else data
201
+ if within:
202
+ data = {**data, "within": within}
203
+ return data
202
204
 
203
205
  def describe_for_cli(self, indent: int = 0) -> str:
204
206
  """Return a human-readable description of this trigger for the CLI"""
@@ -248,7 +250,7 @@ class MetricTriggerQuery(PrefectBaseModel):
248
250
  threshold: float = Field(
249
251
  ...,
250
252
  description=(
251
- "The threshold value against which we'll compare " "the query result."
253
+ "The threshold value against which we'll compare the query result."
252
254
  ),
253
255
  )
254
256
  operator: MetricTriggerOperator = Field(
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  from datetime import timedelta
2
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any
3
5
  from uuid import UUID
4
6
 
5
- import pendulum
7
+ from prefect.types import DateTime
6
8
 
7
9
  from .clients import (
8
10
  AssertingEventsClient,
@@ -18,16 +20,16 @@ TIGHT_TIMING = timedelta(minutes=5)
18
20
 
19
21
  def emit_event(
20
22
  event: str,
21
- resource: Dict[str, str],
22
- occurred: Optional[pendulum.DateTime] = None,
23
- related: Optional[Union[List[Dict[str, str]], List[RelatedResource]]] = None,
24
- payload: Optional[Dict[str, Any]] = None,
25
- id: Optional[UUID] = None,
26
- follows: Optional[Event] = None,
27
- **kwargs: Optional[Dict[str, Any]],
28
- ) -> Optional[Event]:
23
+ resource: dict[str, str],
24
+ occurred: DateTime | None = None,
25
+ related: list[dict[str, str]] | list[RelatedResource] | None = None,
26
+ payload: dict[str, Any] | None = None,
27
+ id: UUID | None = None,
28
+ follows: Event | None = None,
29
+ **kwargs: dict[str, Any] | None,
30
+ ) -> Event | None:
29
31
  """
30
- Send an event to Prefect Cloud.
32
+ Send an event to Prefect.
31
33
 
32
34
  Args:
33
35
  event: The name of the event that happened.
@@ -60,14 +62,14 @@ def emit_event(
60
62
  if worker_instance.client_type not in operational_clients:
61
63
  return None
62
64
 
63
- event_kwargs: Dict[str, Any] = {
65
+ event_kwargs: dict[str, Any] = {
64
66
  "event": event,
65
67
  "resource": resource,
66
68
  **kwargs,
67
69
  }
68
70
 
69
71
  if occurred is None:
70
- occurred = pendulum.now("UTC")
72
+ occurred = DateTime.now("UTC")
71
73
  event_kwargs["occurred"] = occurred
72
74
 
73
75
  if related is not None:
prefect/exceptions.py CHANGED
@@ -161,7 +161,7 @@ class ParameterTypeError(PrefectException):
161
161
  @classmethod
162
162
  def from_validation_error(cls, exc: ValidationError) -> Self:
163
163
  bad_params = [
164
- f'{".".join(str(item) for item in err["loc"])}: {err["msg"]}'
164
+ f"{'.'.join(str(item) for item in err['loc'])}: {err['msg']}"
165
165
  for err in exc.errors()
166
166
  ]
167
167
  msg = "Flow run received invalid parameters:\n - " + "\n - ".join(bad_params)
prefect/flow_engine.py CHANGED
@@ -49,12 +49,18 @@ from prefect.context import (
49
49
  )
50
50
  from prefect.exceptions import (
51
51
  Abort,
52
+ MissingFlowError,
52
53
  Pause,
53
54
  PrefectException,
54
55
  TerminationSignal,
55
56
  UpstreamTaskError,
56
57
  )
57
- from prefect.flows import Flow, load_flow_from_entrypoint, load_flow_from_flow_run
58
+ from prefect.flows import (
59
+ Flow,
60
+ load_flow_from_entrypoint,
61
+ load_flow_from_flow_run,
62
+ load_function_and_convert_to_flow,
63
+ )
58
64
  from prefect.futures import PrefectFuture, resolve_futures_to_states
59
65
  from prefect.logging.loggers import (
60
66
  flow_run_logger,
@@ -125,7 +131,10 @@ def load_flow(flow_run: FlowRun) -> Flow[..., Any]:
125
131
 
126
132
  if entrypoint:
127
133
  # we should not accept a placeholder flow at runtime
128
- flow = load_flow_from_entrypoint(entrypoint, use_placeholder_flow=False)
134
+ try:
135
+ flow = load_flow_from_entrypoint(entrypoint, use_placeholder_flow=False)
136
+ except MissingFlowError:
137
+ flow = load_function_and_convert_to_flow(entrypoint)
129
138
  else:
130
139
  flow = run_coro_as_sync(
131
140
  load_flow_from_flow_run(flow_run, use_placeholder_flow=False)
@@ -1518,6 +1527,8 @@ def run_flow(
1518
1527
  ret_val = run_flow_async(**kwargs)
1519
1528
  else:
1520
1529
  ret_val = run_flow_sync(**kwargs)
1530
+ except (Abort, Pause):
1531
+ raise
1521
1532
  except:
1522
1533
  if error_logger:
1523
1534
  error_logger.error(
@@ -1597,20 +1608,18 @@ def run_flow_in_subprocess(
1597
1608
  # This is running in a brand new process, so there won't be an existing
1598
1609
  # event loop.
1599
1610
  asyncio.run(maybe_coro)
1600
- except Abort as abort_signal:
1601
- abort_signal: Abort
1611
+ except Abort:
1602
1612
  if flow_run:
1603
- msg = f"Execution of flow run '{flow_run.id}' aborted by orchestrator: {abort_signal}"
1613
+ msg = f"Execution of flow run '{flow_run.id}' aborted by orchestrator."
1604
1614
  else:
1605
- msg = f"Execution aborted by orchestrator: {abort_signal}"
1615
+ msg = "Execution aborted by orchestrator."
1606
1616
  engine_logger.info(msg)
1607
1617
  exit(0)
1608
- except Pause as pause_signal:
1609
- pause_signal: Pause
1618
+ except Pause:
1610
1619
  if flow_run:
1611
- msg = f"Execution of flow run '{flow_run.id}' is paused: {pause_signal}"
1620
+ msg = f"Execution of flow run '{flow_run.id}' is paused."
1612
1621
  else:
1613
- msg = f"Execution is paused: {pause_signal}"
1622
+ msg = "Execution is paused."
1614
1623
  engine_logger.info(msg)
1615
1624
  exit(0)
1616
1625
  except Exception:
prefect/flow_runs.py CHANGED
@@ -139,8 +139,7 @@ async def pause_flow_run(
139
139
  timeout: int = 3600,
140
140
  poll_interval: int = 10,
141
141
  key: Optional[str] = None,
142
- ) -> None:
143
- ...
142
+ ) -> None: ...
144
143
 
145
144
 
146
145
  @overload
@@ -149,8 +148,7 @@ async def pause_flow_run(
149
148
  timeout: int = 3600,
150
149
  poll_interval: int = 10,
151
150
  key: Optional[str] = None,
152
- ) -> T:
153
- ...
151
+ ) -> T: ...
154
152
 
155
153
 
156
154
  @sync_compatible
@@ -308,8 +306,7 @@ async def suspend_flow_run(
308
306
  timeout: Optional[int] = 3600,
309
307
  key: Optional[str] = None,
310
308
  client: Optional[PrefectClient] = None,
311
- ) -> None:
312
- ...
309
+ ) -> None: ...
313
310
 
314
311
 
315
312
  @overload
@@ -319,8 +316,7 @@ async def suspend_flow_run(
319
316
  timeout: Optional[int] = 3600,
320
317
  key: Optional[str] = None,
321
318
  client: Optional[PrefectClient] = None,
322
- ) -> T:
323
- ...
319
+ ) -> T: ...
324
320
 
325
321
 
326
322
  @sync_compatible