prefect-client 3.1.11__py3-none-any.whl → 3.1.13__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 (133) hide show
  1. prefect/_experimental/sla/__init__.py +0 -0
  2. prefect/_experimental/sla/client.py +92 -0
  3. prefect/_experimental/sla/objects.py +61 -0
  4. prefect/_internal/concurrency/services.py +2 -2
  5. prefect/_internal/concurrency/threads.py +6 -0
  6. prefect/_internal/retries.py +6 -3
  7. prefect/_internal/schemas/validators.py +6 -4
  8. prefect/_version.py +3 -3
  9. prefect/artifacts.py +4 -1
  10. prefect/automations.py +236 -30
  11. prefect/blocks/__init__.py +3 -3
  12. prefect/blocks/abstract.py +57 -31
  13. prefect/blocks/core.py +181 -82
  14. prefect/blocks/notifications.py +134 -73
  15. prefect/blocks/redis.py +13 -9
  16. prefect/blocks/system.py +24 -11
  17. prefect/blocks/webhook.py +7 -5
  18. prefect/cache_policies.py +23 -22
  19. prefect/client/orchestration/__init__.py +103 -2006
  20. prefect/client/orchestration/_automations/__init__.py +0 -0
  21. prefect/client/orchestration/_automations/client.py +329 -0
  22. prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
  23. prefect/client/orchestration/_blocks_documents/client.py +334 -0
  24. prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
  25. prefect/client/orchestration/_blocks_schemas/client.py +200 -0
  26. prefect/client/orchestration/_blocks_types/__init__.py +0 -0
  27. prefect/client/orchestration/_blocks_types/client.py +380 -0
  28. prefect/client/orchestration/_deployments/__init__.py +0 -0
  29. prefect/client/orchestration/_deployments/client.py +1128 -0
  30. prefect/client/orchestration/_flow_runs/__init__.py +0 -0
  31. prefect/client/orchestration/_flow_runs/client.py +903 -0
  32. prefect/client/orchestration/_flows/__init__.py +0 -0
  33. prefect/client/orchestration/_flows/client.py +343 -0
  34. prefect/client/orchestration/_logs/client.py +16 -14
  35. prefect/client/schemas/__init__.py +68 -28
  36. prefect/client/schemas/objects.py +5 -5
  37. prefect/client/utilities.py +3 -3
  38. prefect/context.py +15 -1
  39. prefect/deployments/base.py +13 -4
  40. prefect/deployments/flow_runs.py +5 -1
  41. prefect/deployments/runner.py +37 -1
  42. prefect/deployments/steps/core.py +1 -1
  43. prefect/deployments/steps/pull.py +8 -3
  44. prefect/deployments/steps/utility.py +2 -2
  45. prefect/docker/docker_image.py +13 -9
  46. prefect/engine.py +33 -11
  47. prefect/events/cli/automations.py +4 -4
  48. prefect/events/clients.py +17 -14
  49. prefect/events/schemas/automations.py +12 -8
  50. prefect/events/schemas/events.py +5 -1
  51. prefect/events/worker.py +1 -1
  52. prefect/filesystems.py +7 -3
  53. prefect/flow_engine.py +64 -47
  54. prefect/flows.py +128 -74
  55. prefect/futures.py +14 -7
  56. prefect/infrastructure/provisioners/__init__.py +2 -0
  57. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  58. prefect/infrastructure/provisioners/coiled.py +249 -0
  59. prefect/infrastructure/provisioners/container_instance.py +4 -3
  60. prefect/infrastructure/provisioners/ecs.py +55 -43
  61. prefect/infrastructure/provisioners/modal.py +5 -4
  62. prefect/input/actions.py +5 -1
  63. prefect/input/run_input.py +157 -43
  64. prefect/logging/configuration.py +3 -3
  65. prefect/logging/filters.py +2 -2
  66. prefect/logging/formatters.py +15 -11
  67. prefect/logging/handlers.py +24 -14
  68. prefect/logging/highlighters.py +5 -5
  69. prefect/logging/loggers.py +28 -18
  70. prefect/logging/logging.yml +1 -1
  71. prefect/main.py +3 -1
  72. prefect/results.py +166 -86
  73. prefect/runner/runner.py +38 -29
  74. prefect/runner/server.py +3 -1
  75. prefect/runner/storage.py +18 -18
  76. prefect/runner/submit.py +19 -12
  77. prefect/runtime/deployment.py +15 -8
  78. prefect/runtime/flow_run.py +19 -6
  79. prefect/runtime/task_run.py +7 -3
  80. prefect/settings/base.py +17 -7
  81. prefect/settings/legacy.py +4 -4
  82. prefect/settings/models/api.py +4 -3
  83. prefect/settings/models/cli.py +4 -3
  84. prefect/settings/models/client.py +7 -4
  85. prefect/settings/models/cloud.py +9 -3
  86. prefect/settings/models/deployments.py +4 -3
  87. prefect/settings/models/experiments.py +4 -8
  88. prefect/settings/models/flows.py +4 -3
  89. prefect/settings/models/internal.py +4 -3
  90. prefect/settings/models/logging.py +8 -6
  91. prefect/settings/models/results.py +4 -3
  92. prefect/settings/models/root.py +11 -16
  93. prefect/settings/models/runner.py +8 -5
  94. prefect/settings/models/server/api.py +6 -3
  95. prefect/settings/models/server/database.py +120 -25
  96. prefect/settings/models/server/deployments.py +4 -3
  97. prefect/settings/models/server/ephemeral.py +7 -4
  98. prefect/settings/models/server/events.py +6 -3
  99. prefect/settings/models/server/flow_run_graph.py +4 -3
  100. prefect/settings/models/server/root.py +4 -3
  101. prefect/settings/models/server/services.py +15 -12
  102. prefect/settings/models/server/tasks.py +7 -4
  103. prefect/settings/models/server/ui.py +4 -3
  104. prefect/settings/models/tasks.py +10 -5
  105. prefect/settings/models/testing.py +4 -3
  106. prefect/settings/models/worker.py +7 -4
  107. prefect/settings/profiles.py +13 -12
  108. prefect/settings/sources.py +20 -19
  109. prefect/states.py +74 -51
  110. prefect/task_engine.py +43 -33
  111. prefect/task_runners.py +85 -72
  112. prefect/task_runs.py +20 -11
  113. prefect/task_worker.py +14 -9
  114. prefect/tasks.py +36 -28
  115. prefect/telemetry/bootstrap.py +13 -9
  116. prefect/telemetry/run_telemetry.py +15 -13
  117. prefect/telemetry/services.py +4 -0
  118. prefect/transactions.py +3 -3
  119. prefect/types/__init__.py +3 -1
  120. prefect/utilities/_deprecated.py +38 -0
  121. prefect/utilities/engine.py +11 -4
  122. prefect/utilities/filesystem.py +2 -2
  123. prefect/utilities/generics.py +1 -1
  124. prefect/utilities/pydantic.py +21 -36
  125. prefect/utilities/templating.py +25 -1
  126. prefect/workers/base.py +58 -33
  127. prefect/workers/process.py +20 -15
  128. prefect/workers/server.py +4 -5
  129. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/METADATA +3 -3
  130. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/RECORD +133 -114
  131. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
  132. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
  133. {prefect_client-3.1.11.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
@@ -29,7 +29,11 @@ prefect.client.schemas.StateCreate.model_rebuild(
29
29
  }
30
30
  )
31
31
 
32
- logger = get_logger(__name__)
32
+
33
+ if TYPE_CHECKING:
34
+ import logging
35
+
36
+ logger: "logging.Logger" = get_logger(__name__)
33
37
 
34
38
 
35
39
  @sync_compatible
@@ -48,12 +48,14 @@ from rich.console import Console
48
48
  from rich.progress import Progress, SpinnerColumn, TextColumn, track
49
49
  from rich.table import Table
50
50
 
51
+ from prefect._experimental.sla.objects import SlaTypes
51
52
  from prefect._internal.concurrency.api import create_call, from_async
52
53
  from prefect._internal.schemas.validators import (
53
54
  reconcile_paused_deployment,
54
55
  reconcile_schedules_runner,
55
56
  )
56
- from prefect.client.orchestration import get_client
57
+ from prefect.client.base import ServerType
58
+ from prefect.client.orchestration import PrefectClient, get_client
57
59
  from prefect.client.schemas.actions import DeploymentScheduleCreate
58
60
  from prefect.client.schemas.filters import WorkerFilter, WorkerFilterStatus
59
61
  from prefect.client.schemas.objects import (
@@ -128,6 +130,7 @@ class RunnerDeployment(BaseModel):
128
130
  job_variables: Settings used to override the values specified default base job template
129
131
  of the chosen work pool. Refer to the base job template of the chosen work pool for
130
132
  available settings.
133
+ _sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
131
134
  """
132
135
 
133
136
  model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
@@ -207,6 +210,10 @@ class RunnerDeployment(BaseModel):
207
210
  " a built runner."
208
211
  ),
209
212
  )
213
+ # (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
214
+ _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = PrivateAttr(
215
+ default=None,
216
+ )
210
217
  _entrypoint_type: EntrypointType = PrivateAttr(
211
218
  default=EntrypointType.FILE_PATH,
212
219
  )
@@ -351,8 +358,28 @@ class RunnerDeployment(BaseModel):
351
358
  trigger.set_deployment_id(deployment_id)
352
359
  await client.create_automation(trigger.as_automation())
353
360
 
361
+ # We plan to support SLA configuration on the Prefect Server in the future.
362
+ # For now, we only support it on Prefect Cloud.
363
+
364
+ # If we're provided with an empty list, we will call the apply endpoint
365
+ # to remove existing SLAs for the deployment. If the argument is not provided,
366
+ # we will not call the endpoint.
367
+ if self._sla or self._sla == []:
368
+ await self._create_slas(deployment_id, client)
369
+
354
370
  return deployment_id
355
371
 
372
+ async def _create_slas(self, deployment_id: UUID, client: PrefectClient):
373
+ if not isinstance(self._sla, list):
374
+ self._sla = [self._sla]
375
+
376
+ if client.server_type == ServerType.CLOUD:
377
+ await client.apply_slas_for_deployment(deployment_id, self._sla)
378
+ else:
379
+ raise ValueError(
380
+ "SLA configuration is currently only supported on Prefect Cloud."
381
+ )
382
+
356
383
  @staticmethod
357
384
  def _construct_deployment_schedules(
358
385
  interval: Optional[
@@ -467,6 +494,7 @@ class RunnerDeployment(BaseModel):
467
494
  work_queue_name: Optional[str] = None,
468
495
  job_variables: Optional[dict[str, Any]] = None,
469
496
  entrypoint_type: EntrypointType = EntrypointType.FILE_PATH,
497
+ _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
470
498
  ) -> "RunnerDeployment":
471
499
  """
472
500
  Configure a deployment for a given flow.
@@ -497,6 +525,7 @@ class RunnerDeployment(BaseModel):
497
525
  job_variables: Settings used to override the values specified default base job template
498
526
  of the chosen work pool. Refer to the base job template of the chosen work pool for
499
527
  available settings.
528
+ _sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
500
529
  """
501
530
  constructed_schedules = cls._construct_deployment_schedules(
502
531
  interval=interval,
@@ -532,6 +561,7 @@ class RunnerDeployment(BaseModel):
532
561
  work_queue_name=work_queue_name,
533
562
  job_variables=job_variables,
534
563
  )
564
+ deployment._sla = _sla
535
565
 
536
566
  if not deployment.entrypoint:
537
567
  no_file_location_error = (
@@ -607,6 +637,7 @@ class RunnerDeployment(BaseModel):
607
637
  work_pool_name: Optional[str] = None,
608
638
  work_queue_name: Optional[str] = None,
609
639
  job_variables: Optional[dict[str, Any]] = None,
640
+ _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
610
641
  ) -> "RunnerDeployment":
611
642
  """
612
643
  Configure a deployment for a given flow located at a given entrypoint.
@@ -638,6 +669,7 @@ class RunnerDeployment(BaseModel):
638
669
  job_variables: Settings used to override the values specified default base job template
639
670
  of the chosen work pool. Refer to the base job template of the chosen work pool for
640
671
  available settings.
672
+ _sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
641
673
  """
642
674
  from prefect.flows import load_flow_from_entrypoint
643
675
 
@@ -677,6 +709,7 @@ class RunnerDeployment(BaseModel):
677
709
  work_queue_name=work_queue_name,
678
710
  job_variables=job_variables,
679
711
  )
712
+ deployment._sla = _sla
680
713
  deployment._path = str(Path.cwd())
681
714
 
682
715
  cls._set_defaults_from_flow(deployment, flow)
@@ -708,6 +741,7 @@ class RunnerDeployment(BaseModel):
708
741
  work_pool_name: Optional[str] = None,
709
742
  work_queue_name: Optional[str] = None,
710
743
  job_variables: Optional[dict[str, Any]] = None,
744
+ _sla: Optional[Union[SlaTypes, list[SlaTypes]]] = None, # experimental
711
745
  ):
712
746
  """
713
747
  Create a RunnerDeployment from a flow located at a given entrypoint and stored in a
@@ -739,6 +773,7 @@ class RunnerDeployment(BaseModel):
739
773
  job_variables: Settings used to override the values specified default base job template
740
774
  of the chosen work pool. Refer to the base job template of the chosen work pool for
741
775
  available settings.
776
+ _sla: (Experimental) SLA configuration for the deployment. May be removed or modified at any time. Currently only supported on Prefect Cloud.
742
777
  """
743
778
  from prefect.flows import load_flow_from_entrypoint
744
779
 
@@ -787,6 +822,7 @@ class RunnerDeployment(BaseModel):
787
822
  work_queue_name=work_queue_name,
788
823
  job_variables=job_variables,
789
824
  )
825
+ deployment._sla = _sla
790
826
  deployment._path = str(storage.destination).replace(
791
827
  tmpdir, "$STORAGE_BASE_PATH"
792
828
  )
@@ -141,7 +141,7 @@ async def run_steps(
141
141
  steps: List[Dict[str, Any]],
142
142
  upstream_outputs: Optional[Dict[str, Any]] = None,
143
143
  print_function: Any = print,
144
- ):
144
+ ) -> dict[str, Any]:
145
145
  upstream_outputs = deepcopy(upstream_outputs) if upstream_outputs else {}
146
146
  for step in steps:
147
147
  if not step:
@@ -12,7 +12,10 @@ from prefect.logging.loggers import get_logger
12
12
  from prefect.runner.storage import BlockStorageAdapter, GitRepository, RemoteStorage
13
13
  from prefect.utilities.asyncutils import run_coro_as_sync
14
14
 
15
- deployment_logger = get_logger("deployment")
15
+ if TYPE_CHECKING:
16
+ import logging
17
+
18
+ deployment_logger: "logging.Logger" = get_logger("deployment")
16
19
 
17
20
  if TYPE_CHECKING:
18
21
  from prefect.blocks.core import Block
@@ -197,7 +200,7 @@ def git_clone(
197
200
  return dict(directory=str(storage.destination.relative_to(Path.cwd())))
198
201
 
199
202
 
200
- async def pull_from_remote_storage(url: str, **settings: Any):
203
+ async def pull_from_remote_storage(url: str, **settings: Any) -> dict[str, Any]:
201
204
  """
202
205
  Pulls code from a remote storage location into the current working directory.
203
206
 
@@ -239,7 +242,9 @@ async def pull_from_remote_storage(url: str, **settings: Any):
239
242
  return {"directory": directory}
240
243
 
241
244
 
242
- async def pull_with_block(block_document_name: str, block_type_slug: str):
245
+ async def pull_with_block(
246
+ block_document_name: str, block_type_slug: str
247
+ ) -> dict[str, Any]:
243
248
  """
244
249
  Pulls code using a block.
245
250
 
@@ -26,7 +26,7 @@ import shlex
26
26
  import string
27
27
  import subprocess
28
28
  import sys
29
- from typing import Dict, Optional
29
+ from typing import Any, Dict, Optional
30
30
 
31
31
  from anyio import create_task_group
32
32
  from anyio.streams.text import TextReceiveStream
@@ -205,7 +205,7 @@ async def pip_install_requirements(
205
205
  directory: Optional[str] = None,
206
206
  requirements_file: str = "requirements.txt",
207
207
  stream_output: bool = True,
208
- ):
208
+ ) -> dict[str, Any]:
209
209
  """
210
210
  Installs dependencies from a requirements.txt file.
211
211
 
@@ -1,5 +1,5 @@
1
1
  from pathlib import Path
2
- from typing import Optional
2
+ from typing import Any, Optional
3
3
 
4
4
  from pendulum import now as pendulum_now
5
5
 
@@ -34,7 +34,11 @@ class DockerImage:
34
34
  """
35
35
 
36
36
  def __init__(
37
- self, name: str, tag: Optional[str] = None, dockerfile="auto", **build_kwargs
37
+ self,
38
+ name: str,
39
+ tag: Optional[str] = None,
40
+ dockerfile: str = "auto",
41
+ **build_kwargs: Any,
38
42
  ):
39
43
  image_name, image_tag = parse_image_tag(name)
40
44
  if tag and image_tag:
@@ -49,16 +53,16 @@ class DockerImage:
49
53
  namespace = PREFECT_DEFAULT_DOCKER_BUILD_NAMESPACE.value()
50
54
  # join the namespace and repository to create the full image name
51
55
  # ignore namespace if it is None
52
- self.name = "/".join(filter(None, [namespace, repository]))
53
- self.tag = tag or image_tag or slugify(pendulum_now("utc").isoformat())
54
- self.dockerfile = dockerfile
55
- self.build_kwargs = build_kwargs
56
+ self.name: str = "/".join(filter(None, [namespace, repository]))
57
+ self.tag: str = tag or image_tag or slugify(pendulum_now("utc").isoformat())
58
+ self.dockerfile: str = dockerfile
59
+ self.build_kwargs: dict[str, Any] = build_kwargs
56
60
 
57
61
  @property
58
- def reference(self):
62
+ def reference(self) -> str:
59
63
  return f"{self.name}:{self.tag}"
60
64
 
61
- def build(self):
65
+ def build(self) -> None:
62
66
  full_image_name = self.reference
63
67
  build_kwargs = self.build_kwargs.copy()
64
68
  build_kwargs["context"] = Path.cwd()
@@ -72,7 +76,7 @@ class DockerImage:
72
76
  build_kwargs["dockerfile"] = self.dockerfile
73
77
  build_image(**build_kwargs)
74
78
 
75
- def push(self):
79
+ def push(self) -> None:
76
80
  with docker_client() as client:
77
81
  events = client.api.push(
78
82
  repository=self.name, tag=self.tag, stream=True, decode=True
prefect/engine.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import sys
3
- from typing import Any, Callable
3
+ from typing import TYPE_CHECKING, Any, Callable
4
4
  from uuid import UUID
5
5
 
6
6
  from prefect._internal.compatibility.migration import getattr_migration
@@ -15,12 +15,19 @@ from prefect.utilities.asyncutils import (
15
15
  run_coro_as_sync,
16
16
  )
17
17
 
18
- engine_logger = get_logger("engine")
18
+ if TYPE_CHECKING:
19
+ import logging
20
+
21
+ from prefect.flow_engine import FlowRun
22
+ from prefect.flows import Flow
23
+ from prefect.logging.loggers import LoggingAdapter
24
+
25
+ engine_logger: "logging.Logger" = get_logger("engine")
19
26
 
20
27
 
21
28
  if __name__ == "__main__":
22
29
  try:
23
- flow_run_id = UUID(
30
+ flow_run_id: UUID = UUID(
24
31
  sys.argv[1] if len(sys.argv) > 1 else os.environ.get("PREFECT__FLOW_RUN_ID")
25
32
  )
26
33
  except Exception:
@@ -31,26 +38,41 @@ if __name__ == "__main__":
31
38
 
32
39
  try:
33
40
  from prefect.flow_engine import (
34
- load_flow_and_flow_run,
41
+ flow_run_logger,
42
+ load_flow,
43
+ load_flow_run,
35
44
  run_flow,
36
45
  )
37
46
 
38
- flow_run, flow = load_flow_and_flow_run(flow_run_id=flow_run_id)
47
+ flow_run: "FlowRun" = load_flow_run(flow_run_id=flow_run_id)
48
+ run_logger: "LoggingAdapter" = flow_run_logger(flow_run=flow_run)
49
+
50
+ try:
51
+ flow: "Flow[..., Any]" = load_flow(flow_run)
52
+ except Exception:
53
+ run_logger.error(
54
+ "Unexpected exception encountered when trying to load flow",
55
+ exc_info=True,
56
+ )
57
+ raise
58
+
39
59
  # run the flow
40
60
  if flow.isasync:
41
- run_coro_as_sync(run_flow(flow, flow_run=flow_run))
61
+ run_coro_as_sync(run_flow(flow, flow_run=flow_run, error_logger=run_logger))
42
62
  else:
43
- run_flow(flow, flow_run=flow_run)
63
+ run_flow(flow, flow_run=flow_run, error_logger=run_logger)
44
64
 
45
- except Abort as exc:
65
+ except Abort as abort_signal:
66
+ abort_signal: Abort
46
67
  engine_logger.info(
47
68
  f"Engine execution of flow run '{flow_run_id}' aborted by orchestrator:"
48
- f" {exc}"
69
+ f" {abort_signal}"
49
70
  )
50
71
  exit(0)
51
- except Pause as exc:
72
+ except Pause as pause_signal:
73
+ pause_signal: Pause
52
74
  engine_logger.info(
53
- f"Engine execution of flow run '{flow_run_id}' is paused: {exc}"
75
+ f"Engine execution of flow run '{flow_run_id}' is paused: {pause_signal}"
54
76
  )
55
77
  exit(0)
56
78
  except Exception:
@@ -3,7 +3,7 @@ Command line interface for working with automations.
3
3
  """
4
4
 
5
5
  import functools
6
- from typing import Optional, Type
6
+ from typing import Any, Callable, Optional, Type
7
7
  from uuid import UUID
8
8
 
9
9
  import orjson
@@ -21,16 +21,16 @@ from prefect.client.orchestration import get_client
21
21
  from prefect.events.schemas.automations import Automation
22
22
  from prefect.exceptions import PrefectHTTPStatusError
23
23
 
24
- automations_app = PrefectTyper(
24
+ automations_app: PrefectTyper = PrefectTyper(
25
25
  name="automation",
26
26
  help="Manage automations.",
27
27
  )
28
28
  app.add_typer(automations_app, aliases=["automations"])
29
29
 
30
30
 
31
- def requires_automations(func):
31
+ def requires_automations(func: Callable[..., Any]) -> Callable[..., Any]:
32
32
  @functools.wraps(func)
33
- async def wrapper(*args, **kwargs):
33
+ async def wrapper(*args: Any, **kwargs: Any) -> Any:
34
34
  try:
35
35
  return await func(*args, **kwargs)
36
36
  except RuntimeError as exc:
prefect/events/clients.py CHANGED
@@ -70,18 +70,21 @@ EVENT_WEBSOCKET_CHECKPOINTS = Counter(
70
70
  labelnames=["client"],
71
71
  )
72
72
 
73
- logger = get_logger(__name__)
73
+ if TYPE_CHECKING:
74
+ import logging
75
+
76
+ logger: "logging.Logger" = get_logger(__name__)
74
77
 
75
78
 
76
- def http_to_ws(url: str):
79
+ def http_to_ws(url: str) -> str:
77
80
  return url.replace("https://", "wss://").replace("http://", "ws://").rstrip("/")
78
81
 
79
82
 
80
- def events_in_socket_from_api_url(url: str):
83
+ def events_in_socket_from_api_url(url: str) -> str:
81
84
  return http_to_ws(url) + "/events/in"
82
85
 
83
86
 
84
- def events_out_socket_from_api_url(url: str):
87
+ def events_out_socket_from_api_url(url: str) -> str:
85
88
  return http_to_ws(url) + "/events/out"
86
89
 
87
90
 
@@ -250,11 +253,11 @@ class AssertingEventsClient(EventsClient):
250
253
  last: ClassVar["Optional[AssertingEventsClient]"] = None
251
254
  all: ClassVar[List["AssertingEventsClient"]] = []
252
255
 
253
- args: Tuple
254
- kwargs: Dict[str, Any]
255
- events: List[Event]
256
+ args: tuple[Any, ...]
257
+ kwargs: dict[str, Any]
258
+ events: list[Event]
256
259
 
257
- def __init__(self, *args, **kwargs):
260
+ def __init__(self, *args: Any, **kwargs: Any):
258
261
  AssertingEventsClient.last = self
259
262
  AssertingEventsClient.all.append(self)
260
263
  self.args = args
@@ -431,13 +434,13 @@ class AssertingPassthroughEventsClient(PrefectEventsClient):
431
434
  during tests AND sends them to a Prefect server."""
432
435
 
433
436
  last: ClassVar["Optional[AssertingPassthroughEventsClient]"] = None
434
- all: ClassVar[List["AssertingPassthroughEventsClient"]] = []
437
+ all: ClassVar[list["AssertingPassthroughEventsClient"]] = []
435
438
 
436
- args: Tuple
437
- kwargs: Dict[str, Any]
438
- events: List[Event]
439
+ args: tuple[Any, ...]
440
+ kwargs: dict[str, Any]
441
+ events: list[Event]
439
442
 
440
- def __init__(self, *args, **kwargs):
443
+ def __init__(self, *args: Any, **kwargs: Any):
441
444
  super().__init__(*args, **kwargs)
442
445
  AssertingPassthroughEventsClient.last = self
443
446
  AssertingPassthroughEventsClient.all.append(self)
@@ -449,7 +452,7 @@ class AssertingPassthroughEventsClient(PrefectEventsClient):
449
452
  cls.last = None
450
453
  cls.all = []
451
454
 
452
- def pop_events(self) -> List[Event]:
455
+ def pop_events(self) -> list[Event]:
453
456
  events = self.events
454
457
  self.events = []
455
458
  return events
@@ -52,7 +52,7 @@ class Trigger(PrefectBaseModel, abc.ABC, extra="ignore"): # type: ignore[call-a
52
52
 
53
53
  _deployment_id: Optional[UUID] = PrivateAttr(default=None)
54
54
 
55
- def set_deployment_id(self, deployment_id: UUID):
55
+ def set_deployment_id(self, deployment_id: UUID) -> None:
56
56
  self._deployment_id = deployment_id
57
57
 
58
58
  def owner_resource(self) -> Optional[str]:
@@ -277,7 +277,7 @@ class MetricTriggerQuery(PrefectBaseModel):
277
277
  )
278
278
 
279
279
  @field_validator("range", "firing_for")
280
- def enforce_minimum_range(cls, value: timedelta):
280
+ def enforce_minimum_range(cls, value: timedelta) -> timedelta:
281
281
  if value < timedelta(seconds=300):
282
282
  raise ValueError("The minimum range is 300 seconds (5 minutes)")
283
283
  return value
@@ -404,13 +404,17 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
404
404
  """Defines an action a user wants to take when a certain number of events
405
405
  do or don't happen to the matching resources"""
406
406
 
407
- name: str = Field(..., description="The name of this automation")
408
- description: str = Field("", description="A longer description of this automation")
407
+ name: str = Field(default=..., description="The name of this automation")
408
+ description: str = Field(
409
+ default="", description="A longer description of this automation"
410
+ )
409
411
 
410
- enabled: bool = Field(True, description="Whether this automation will be evaluated")
412
+ enabled: bool = Field(
413
+ default=True, description="Whether this automation will be evaluated"
414
+ )
411
415
 
412
416
  trigger: TriggerTypes = Field(
413
- ...,
417
+ default=...,
414
418
  description=(
415
419
  "The criteria for which events this Automation covers and how it will "
416
420
  "respond to the presence or absence of those events"
@@ -418,7 +422,7 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
418
422
  )
419
423
 
420
424
  actions: List[ActionTypes] = Field(
421
- ...,
425
+ default=...,
422
426
  description="The actions to perform when this Automation triggers",
423
427
  )
424
428
 
@@ -438,4 +442,4 @@ class AutomationCore(PrefectBaseModel, extra="ignore"): # type: ignore[call-arg
438
442
 
439
443
 
440
444
  class Automation(AutomationCore):
441
- id: UUID = Field(..., description="The ID of this automation")
445
+ id: UUID = Field(default=..., description="The ID of this automation")
@@ -1,6 +1,7 @@
1
1
  import copy
2
2
  from collections import defaultdict
3
3
  from typing import (
4
+ TYPE_CHECKING,
4
5
  Any,
5
6
  ClassVar,
6
7
  Dict,
@@ -32,7 +33,10 @@ from prefect.types import DateTime
32
33
 
33
34
  from .labelling import Labelled
34
35
 
35
- logger = get_logger(__name__)
36
+ if TYPE_CHECKING:
37
+ import logging
38
+
39
+ logger: "logging.Logger" = get_logger(__name__)
36
40
 
37
41
 
38
42
  class Resource(Labelled):
prefect/events/worker.py CHANGED
@@ -82,7 +82,7 @@ class EventsWorker(QueueService[Event]):
82
82
 
83
83
  await self._client.emit(event)
84
84
 
85
- async def attach_related_resources_from_context(self, event: Event):
85
+ async def attach_related_resources_from_context(self, event: Event) -> None:
86
86
  if "prefect.resource.lineage-group" in event.resource:
87
87
  # We attach related resources to lineage events in `emit_lineage_event`,
88
88
  # instead of the worker, because not all run-related resources are
prefect/filesystems.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  import abc
2
4
  import urllib.parse
3
5
  from pathlib import Path
@@ -92,7 +94,9 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
92
94
  )
93
95
 
94
96
  @field_validator("basepath", mode="before")
95
- def cast_pathlib(cls, value):
97
+ def cast_pathlib(cls, value: str | Path | None) -> str | None:
98
+ if value is None:
99
+ return value
96
100
  return stringify_path(value)
97
101
 
98
102
  def _resolve_path(self, path: str, validate: bool = False) -> Path:
@@ -132,7 +136,7 @@ class LocalFileSystem(WritableFileSystem, WritableDeploymentStorage):
132
136
  Defaults to copying the entire contents of the block's basepath to the current working directory.
133
137
  """
134
138
  if not from_path:
135
- from_path = Path(self.basepath).expanduser().resolve()
139
+ from_path = Path(self.basepath or ".").expanduser().resolve()
136
140
  else:
137
141
  from_path = self._resolve_path(from_path)
138
142
 
@@ -277,7 +281,7 @@ class RemoteFileSystem(WritableFileSystem, WritableDeploymentStorage):
277
281
  _filesystem: fsspec.AbstractFileSystem = None
278
282
 
279
283
  @field_validator("basepath")
280
- def check_basepath(cls, value):
284
+ def check_basepath(cls, value: str) -> str:
281
285
  return validate_basepath(value)
282
286
 
283
287
  def _resolve_path(self, path: str) -> str: