prefect-client 3.1.12__py3-none-any.whl → 3.1.14__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 (111) hide show
  1. prefect/_experimental/lineage.py +63 -0
  2. prefect/_experimental/sla/client.py +53 -27
  3. prefect/_experimental/sla/objects.py +10 -2
  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 +1 -1
  11. prefect/blocks/abstract.py +5 -2
  12. prefect/blocks/notifications.py +1 -0
  13. prefect/cache_policies.py +70 -22
  14. prefect/client/orchestration/_automations/client.py +4 -0
  15. prefect/client/orchestration/_deployments/client.py +3 -3
  16. prefect/client/utilities.py +3 -3
  17. prefect/context.py +16 -6
  18. prefect/deployments/base.py +7 -4
  19. prefect/deployments/flow_runs.py +5 -1
  20. prefect/deployments/runner.py +6 -11
  21. prefect/deployments/steps/core.py +1 -1
  22. prefect/deployments/steps/pull.py +8 -3
  23. prefect/deployments/steps/utility.py +2 -2
  24. prefect/docker/docker_image.py +13 -9
  25. prefect/engine.py +19 -10
  26. prefect/events/cli/automations.py +4 -4
  27. prefect/events/clients.py +17 -14
  28. prefect/events/filters.py +34 -34
  29. prefect/events/schemas/automations.py +12 -8
  30. prefect/events/schemas/events.py +5 -1
  31. prefect/events/worker.py +1 -1
  32. prefect/filesystems.py +1 -1
  33. prefect/flow_engine.py +172 -123
  34. prefect/flows.py +119 -74
  35. prefect/futures.py +14 -7
  36. prefect/infrastructure/provisioners/__init__.py +2 -0
  37. prefect/infrastructure/provisioners/cloud_run.py +4 -4
  38. prefect/infrastructure/provisioners/coiled.py +249 -0
  39. prefect/infrastructure/provisioners/container_instance.py +4 -3
  40. prefect/infrastructure/provisioners/ecs.py +55 -43
  41. prefect/infrastructure/provisioners/modal.py +5 -4
  42. prefect/input/actions.py +5 -1
  43. prefect/input/run_input.py +157 -43
  44. prefect/logging/configuration.py +5 -8
  45. prefect/logging/filters.py +2 -2
  46. prefect/logging/formatters.py +15 -11
  47. prefect/logging/handlers.py +24 -14
  48. prefect/logging/highlighters.py +5 -5
  49. prefect/logging/loggers.py +29 -20
  50. prefect/main.py +3 -1
  51. prefect/results.py +166 -86
  52. prefect/runner/runner.py +112 -84
  53. prefect/runner/server.py +3 -1
  54. prefect/runner/storage.py +18 -18
  55. prefect/runner/submit.py +19 -12
  56. prefect/runtime/deployment.py +15 -8
  57. prefect/runtime/flow_run.py +19 -6
  58. prefect/runtime/task_run.py +7 -3
  59. prefect/settings/base.py +17 -7
  60. prefect/settings/legacy.py +4 -4
  61. prefect/settings/models/api.py +4 -3
  62. prefect/settings/models/cli.py +4 -3
  63. prefect/settings/models/client.py +7 -4
  64. prefect/settings/models/cloud.py +4 -3
  65. prefect/settings/models/deployments.py +4 -3
  66. prefect/settings/models/experiments.py +4 -3
  67. prefect/settings/models/flows.py +4 -3
  68. prefect/settings/models/internal.py +4 -3
  69. prefect/settings/models/logging.py +8 -6
  70. prefect/settings/models/results.py +4 -3
  71. prefect/settings/models/root.py +11 -16
  72. prefect/settings/models/runner.py +8 -5
  73. prefect/settings/models/server/api.py +6 -3
  74. prefect/settings/models/server/database.py +120 -25
  75. prefect/settings/models/server/deployments.py +4 -3
  76. prefect/settings/models/server/ephemeral.py +7 -4
  77. prefect/settings/models/server/events.py +6 -3
  78. prefect/settings/models/server/flow_run_graph.py +4 -3
  79. prefect/settings/models/server/root.py +4 -3
  80. prefect/settings/models/server/services.py +15 -12
  81. prefect/settings/models/server/tasks.py +7 -4
  82. prefect/settings/models/server/ui.py +4 -3
  83. prefect/settings/models/tasks.py +10 -5
  84. prefect/settings/models/testing.py +4 -3
  85. prefect/settings/models/worker.py +7 -4
  86. prefect/settings/profiles.py +13 -12
  87. prefect/settings/sources.py +20 -19
  88. prefect/states.py +17 -13
  89. prefect/task_engine.py +43 -33
  90. prefect/task_runners.py +35 -23
  91. prefect/task_runs.py +20 -11
  92. prefect/task_worker.py +12 -7
  93. prefect/tasks.py +67 -25
  94. prefect/telemetry/bootstrap.py +4 -1
  95. prefect/telemetry/run_telemetry.py +15 -13
  96. prefect/transactions.py +3 -3
  97. prefect/types/__init__.py +9 -6
  98. prefect/types/_datetime.py +19 -0
  99. prefect/utilities/_deprecated.py +38 -0
  100. prefect/utilities/engine.py +11 -4
  101. prefect/utilities/filesystem.py +2 -2
  102. prefect/utilities/generics.py +1 -1
  103. prefect/utilities/pydantic.py +21 -36
  104. prefect/workers/base.py +52 -30
  105. prefect/workers/process.py +20 -15
  106. prefect/workers/server.py +4 -5
  107. {prefect_client-3.1.12.dist-info → prefect_client-3.1.14.dist-info}/METADATA +2 -2
  108. {prefect_client-3.1.12.dist-info → prefect_client-3.1.14.dist-info}/RECORD +111 -108
  109. {prefect_client-3.1.12.dist-info → prefect_client-3.1.14.dist-info}/LICENSE +0 -0
  110. {prefect_client-3.1.12.dist-info → prefect_client-3.1.14.dist-info}/WHEEL +0 -0
  111. {prefect_client-3.1.12.dist-info → prefect_client-3.1.14.dist-info}/top_level.txt +0 -0
prefect/context.py CHANGED
@@ -337,10 +337,15 @@ class EngineContext(RunContext):
337
337
  flow: The flow instance associated with the run
338
338
  flow_run: The API metadata for the flow run
339
339
  task_runner: The task runner instance being used for the flow run
340
- task_run_futures: A list of futures for task runs submitted within this flow run
341
- task_run_states: A list of states for task runs created within this flow run
342
340
  task_run_results: A mapping of result ids to task run states for this flow run
343
- flow_run_states: A list of states for flow runs created within this flow run
341
+ log_prints: Whether to log print statements from the flow run
342
+ parameters: The parameters passed to the flow run
343
+ detached: Flag indicating if context has been serialized and sent to remote infrastructure
344
+ result_store: The result store used to persist results
345
+ persist_result: Whether to persist the flow run result
346
+ task_run_dynamic_keys: Counter for task calls allowing unique keys
347
+ observed_flow_pauses: Counter for flow pauses
348
+ events: Events worker to emit events
344
349
  """
345
350
 
346
351
  flow: Optional["Flow[Any, Any]"] = None
@@ -373,7 +378,7 @@ class EngineContext(RunContext):
373
378
  __var__: ClassVar[ContextVar[Self]] = ContextVar("flow_run")
374
379
 
375
380
  def serialize(self: Self, include_secrets: bool = True) -> dict[str, Any]:
376
- return self.model_dump(
381
+ serialized = self.model_dump(
377
382
  include={
378
383
  "flow_run",
379
384
  "flow",
@@ -381,13 +386,18 @@ class EngineContext(RunContext):
381
386
  "log_prints",
382
387
  "start_time",
383
388
  "input_keyset",
384
- "result_store",
385
389
  "persist_result",
386
390
  },
387
391
  exclude_unset=True,
388
- serialize_as_any=True,
389
392
  context={"include_secrets": include_secrets},
390
393
  )
394
+ if self.result_store:
395
+ serialized["result_store"] = self.result_store.model_dump(
396
+ serialize_as_any=True,
397
+ exclude_unset=True,
398
+ context={"include_secrets": include_secrets},
399
+ )
400
+ return serialized
391
401
 
392
402
 
393
403
  FlowRunContext = EngineContext # for backwards compatibility
@@ -19,6 +19,7 @@ from prefect.client.schemas.actions import DeploymentScheduleCreate
19
19
  from prefect.client.schemas.objects import ConcurrencyLimitStrategy
20
20
  from prefect.client.schemas.schedules import IntervalSchedule
21
21
  from prefect.utilities._git import get_git_branch, get_git_remote_origin_url
22
+ from prefect.utilities.annotations import NotSet
22
23
  from prefect.utilities.filesystem import create_default_ignore_file
23
24
  from prefect.utilities.templating import apply_values
24
25
 
@@ -113,7 +114,9 @@ def create_default_prefect_yaml(
113
114
  return True
114
115
 
115
116
 
116
- def configure_project_by_recipe(recipe: str, **formatting_kwargs) -> dict:
117
+ def configure_project_by_recipe(
118
+ recipe: str, **formatting_kwargs: Any
119
+ ) -> dict[str, Any] | type[NotSet]:
117
120
  """
118
121
  Given a recipe name, returns a dictionary representing base configuration options.
119
122
 
@@ -131,13 +134,13 @@ def configure_project_by_recipe(recipe: str, **formatting_kwargs) -> dict:
131
134
  raise ValueError(f"Unknown recipe {recipe!r} provided.")
132
135
 
133
136
  with recipe_path.open(mode="r") as f:
134
- config = yaml.safe_load(f)
137
+ config: dict[str, Any] = yaml.safe_load(f)
135
138
 
136
- config = apply_values(
139
+ templated_config = apply_values(
137
140
  template=config, values=formatting_kwargs, remove_notset=False
138
141
  )
139
142
 
140
- return config
143
+ return templated_config
141
144
 
142
145
 
143
146
  def initialize_project(
@@ -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
@@ -36,7 +36,6 @@ from pathlib import Path
36
36
  from typing import TYPE_CHECKING, Any, ClassVar, Iterable, List, Optional, Union
37
37
  from uuid import UUID
38
38
 
39
- from exceptiongroup import ExceptionGroup # novermin
40
39
  from pydantic import (
41
40
  BaseModel,
42
41
  ConfigDict,
@@ -361,7 +360,11 @@ class RunnerDeployment(BaseModel):
361
360
 
362
361
  # We plan to support SLA configuration on the Prefect Server in the future.
363
362
  # For now, we only support it on Prefect Cloud.
364
- if self._sla:
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 == []:
365
368
  await self._create_slas(deployment_id, client)
366
369
 
367
370
  return deployment_id
@@ -371,15 +374,7 @@ class RunnerDeployment(BaseModel):
371
374
  self._sla = [self._sla]
372
375
 
373
376
  if client.server_type == ServerType.CLOUD:
374
- exceptions = []
375
- for sla in self._sla:
376
- try:
377
- sla.set_deployment_id(deployment_id)
378
- await client.create_sla(sla)
379
- except Exception as e:
380
- exceptions.append(e)
381
- if exceptions:
382
- raise ExceptionGroup("Failed to create SLAs", exceptions) # novermin
377
+ await client.apply_slas_for_deployment(deployment_id, self._sla)
383
378
  else:
384
379
  raise ValueError(
385
380
  "SLA configuration is currently only supported on Prefect Cloud."
@@ -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:
@@ -37,11 +44,11 @@ if __name__ == "__main__":
37
44
  run_flow,
38
45
  )
39
46
 
40
- flow_run = load_flow_run(flow_run_id=flow_run_id)
41
- run_logger = flow_run_logger(flow_run=flow_run)
47
+ flow_run: "FlowRun" = load_flow_run(flow_run_id=flow_run_id)
48
+ run_logger: "LoggingAdapter" = flow_run_logger(flow_run=flow_run)
42
49
 
43
50
  try:
44
- flow = load_flow(flow_run)
51
+ flow: "Flow[..., Any]" = load_flow(flow_run)
45
52
  except Exception:
46
53
  run_logger.error(
47
54
  "Unexpected exception encountered when trying to load flow",
@@ -55,15 +62,17 @@ if __name__ == "__main__":
55
62
  else:
56
63
  run_flow(flow, flow_run=flow_run, error_logger=run_logger)
57
64
 
58
- except Abort as exc:
65
+ except Abort as abort_signal:
66
+ abort_signal: Abort
59
67
  engine_logger.info(
60
68
  f"Engine execution of flow run '{flow_run_id}' aborted by orchestrator:"
61
- f" {exc}"
69
+ f" {abort_signal}"
62
70
  )
63
71
  exit(0)
64
- except Pause as exc:
72
+ except Pause as pause_signal:
73
+ pause_signal: Pause
65
74
  engine_logger.info(
66
- 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}"
67
76
  )
68
77
  exit(0)
69
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
prefect/events/filters.py CHANGED
@@ -1,7 +1,6 @@
1
- from typing import List, Optional, Tuple, cast
1
+ from typing import Optional
2
2
  from uuid import UUID
3
3
 
4
- import pendulum
5
4
  from pydantic import Field
6
5
 
7
6
  from prefect._internal.schemas.bases import PrefectBaseModel
@@ -23,7 +22,7 @@ class AutomationFilterCreated(PrefectBaseModel):
23
22
  class AutomationFilterName(PrefectBaseModel):
24
23
  """Filter by `Automation.created`."""
25
24
 
26
- any_: Optional[List[str]] = Field(
25
+ any_: Optional[list[str]] = Field(
27
26
  default=None,
28
27
  description="Only include automations with names that match any of these strings",
29
28
  )
@@ -41,8 +40,8 @@ class AutomationFilter(PrefectBaseModel):
41
40
  class EventDataFilter(PrefectBaseModel, extra="forbid"): # type: ignore[call-arg]
42
41
  """A base class for filtering event data."""
43
42
 
44
- def get_filters(self) -> List["EventDataFilter"]:
45
- filters: List["EventDataFilter"] = [
43
+ def get_filters(self) -> list["EventDataFilter"]:
44
+ filters: list["EventDataFilter"] = [
46
45
  filter
47
46
  for filter in [getattr(self, name) for name in self.model_fields]
48
47
  if isinstance(filter, EventDataFilter)
@@ -60,14 +59,11 @@ class EventDataFilter(PrefectBaseModel, extra="forbid"): # type: ignore[call-ar
60
59
 
61
60
  class EventOccurredFilter(EventDataFilter):
62
61
  since: DateTime = Field(
63
- default_factory=lambda: cast(
64
- DateTime,
65
- pendulum.now("UTC").start_of("day").subtract(days=180),
66
- ),
62
+ default_factory=lambda: DateTime.now("UTC").start_of("day").subtract(days=180),
67
63
  description="Only include events after this time (inclusive)",
68
64
  )
69
65
  until: DateTime = Field(
70
- default_factory=lambda: cast(DateTime, pendulum.now("UTC")),
66
+ default_factory=lambda: DateTime.now("UTC"),
71
67
  description="Only include events prior to this time (inclusive)",
72
68
  )
73
69
 
@@ -76,18 +72,18 @@ class EventOccurredFilter(EventDataFilter):
76
72
 
77
73
 
78
74
  class EventNameFilter(EventDataFilter):
79
- prefix: Optional[List[str]] = Field(
75
+ prefix: Optional[list[str]] = Field(
80
76
  default=None, description="Only include events matching one of these prefixes"
81
77
  )
82
- exclude_prefix: Optional[List[str]] = Field(
78
+ exclude_prefix: Optional[list[str]] = Field(
83
79
  default=None, description="Exclude events matching one of these prefixes"
84
80
  )
85
81
 
86
- name: Optional[List[str]] = Field(
82
+ name: Optional[list[str]] = Field(
87
83
  default=None,
88
84
  description="Only include events matching one of these names exactly",
89
85
  )
90
- exclude_name: Optional[List[str]] = Field(
86
+ exclude_name: Optional[list[str]] = Field(
91
87
  default=None, description="Exclude events matching one of these names exactly"
92
88
  )
93
89
 
@@ -112,20 +108,20 @@ class EventNameFilter(EventDataFilter):
112
108
 
113
109
 
114
110
  class EventResourceFilter(EventDataFilter):
115
- id: Optional[List[str]] = Field(
116
- None, description="Only include events for resources with these IDs"
111
+ id: Optional[list[str]] = Field(
112
+ default=None, description="Only include events for resources with these IDs"
117
113
  )
118
- id_prefix: Optional[List[str]] = Field(
119
- None,
114
+ id_prefix: Optional[list[str]] = Field(
115
+ default=None,
120
116
  description=(
121
117
  "Only include events for resources with IDs starting with these prefixes."
122
118
  ),
123
119
  )
124
120
  labels: Optional[ResourceSpecification] = Field(
125
- None, description="Only include events for resources with these labels"
121
+ default=None, description="Only include events for resources with these labels"
126
122
  )
127
123
  distinct: bool = Field(
128
- False,
124
+ default=False,
129
125
  description="Only include events for distinct resources",
130
126
  )
131
127
 
@@ -148,35 +144,39 @@ class EventResourceFilter(EventDataFilter):
148
144
 
149
145
 
150
146
  class EventRelatedFilter(EventDataFilter):
151
- id: Optional[List[str]] = Field(
152
- None, description="Only include events for related resources with these IDs"
147
+ id: Optional[list[str]] = Field(
148
+ default=None,
149
+ description="Only include events for related resources with these IDs",
153
150
  )
154
- role: Optional[List[str]] = Field(
155
- None, description="Only include events for related resources in these roles"
151
+ role: Optional[list[str]] = Field(
152
+ default=None,
153
+ description="Only include events for related resources in these roles",
156
154
  )
157
- resources_in_roles: Optional[List[Tuple[str, str]]] = Field(
158
- None,
155
+ resources_in_roles: Optional[list[tuple[str, str]]] = Field(
156
+ default=None,
159
157
  description=(
160
158
  "Only include events with specific related resources in specific roles"
161
159
  ),
162
160
  )
163
161
  labels: Optional[ResourceSpecification] = Field(
164
- None, description="Only include events for related resources with these labels"
162
+ default=None,
163
+ description="Only include events for related resources with these labels",
165
164
  )
166
165
 
167
166
 
168
167
  class EventAnyResourceFilter(EventDataFilter):
169
- id: Optional[List[str]] = Field(
170
- None, description="Only include events for resources with these IDs"
168
+ id: Optional[list[str]] = Field(
169
+ default=None, description="Only include events for resources with these IDs"
171
170
  )
172
- id_prefix: Optional[List[str]] = Field(
173
- None,
171
+ id_prefix: Optional[list[str]] = Field(
172
+ default=None,
174
173
  description=(
175
174
  "Only include events for resources with IDs starting with these prefixes"
176
175
  ),
177
176
  )
178
177
  labels: Optional[ResourceSpecification] = Field(
179
- None, description="Only include events for related resources with these labels"
178
+ default=None,
179
+ description="Only include events for related resources with these labels",
180
180
  )
181
181
 
182
182
  def includes(self, event: Event) -> bool:
@@ -202,8 +202,8 @@ class EventAnyResourceFilter(EventDataFilter):
202
202
 
203
203
 
204
204
  class EventIDFilter(EventDataFilter):
205
- id: Optional[List[UUID]] = Field(
206
- None, description="Only include events with one of these IDs"
205
+ id: Optional[list[UUID]] = Field(
206
+ default=None, description="Only include events with one of these IDs"
207
207
  )
208
208
 
209
209
  def includes(self, event: Event) -> bool:
@@ -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
@@ -281,7 +281,7 @@ class RemoteFileSystem(WritableFileSystem, WritableDeploymentStorage):
281
281
  _filesystem: fsspec.AbstractFileSystem = None
282
282
 
283
283
  @field_validator("basepath")
284
- def check_basepath(cls, value):
284
+ def check_basepath(cls, value: str) -> str:
285
285
  return validate_basepath(value)
286
286
 
287
287
  def _resolve_path(self, path: str) -> str: