prefect-client 3.1.10__py3-none-any.whl → 3.1.12__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 (141) hide show
  1. prefect/_experimental/lineage.py +7 -8
  2. prefect/_experimental/sla/__init__.py +0 -0
  3. prefect/_experimental/sla/client.py +66 -0
  4. prefect/_experimental/sla/objects.py +53 -0
  5. prefect/_internal/_logging.py +15 -3
  6. prefect/_internal/compatibility/async_dispatch.py +22 -16
  7. prefect/_internal/compatibility/deprecated.py +42 -18
  8. prefect/_internal/compatibility/migration.py +2 -2
  9. prefect/_internal/concurrency/inspection.py +12 -14
  10. prefect/_internal/concurrency/primitives.py +2 -2
  11. prefect/_internal/concurrency/services.py +154 -80
  12. prefect/_internal/concurrency/waiters.py +13 -9
  13. prefect/_internal/pydantic/annotations/pendulum.py +7 -7
  14. prefect/_internal/pytz.py +4 -3
  15. prefect/_internal/retries.py +10 -5
  16. prefect/_internal/schemas/bases.py +19 -10
  17. prefect/_internal/schemas/validators.py +227 -388
  18. prefect/_version.py +3 -3
  19. prefect/automations.py +236 -30
  20. prefect/blocks/__init__.py +3 -3
  21. prefect/blocks/abstract.py +53 -30
  22. prefect/blocks/core.py +183 -84
  23. prefect/blocks/notifications.py +133 -73
  24. prefect/blocks/redis.py +13 -9
  25. prefect/blocks/system.py +24 -11
  26. prefect/blocks/webhook.py +7 -5
  27. prefect/cache_policies.py +3 -2
  28. prefect/client/orchestration/__init__.py +1957 -0
  29. prefect/client/orchestration/_artifacts/__init__.py +0 -0
  30. prefect/client/orchestration/_artifacts/client.py +239 -0
  31. prefect/client/orchestration/_automations/__init__.py +0 -0
  32. prefect/client/orchestration/_automations/client.py +329 -0
  33. prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
  34. prefect/client/orchestration/_blocks_documents/client.py +334 -0
  35. prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
  36. prefect/client/orchestration/_blocks_schemas/client.py +200 -0
  37. prefect/client/orchestration/_blocks_types/__init__.py +0 -0
  38. prefect/client/orchestration/_blocks_types/client.py +380 -0
  39. prefect/client/orchestration/_concurrency_limits/__init__.py +0 -0
  40. prefect/client/orchestration/_concurrency_limits/client.py +762 -0
  41. prefect/client/orchestration/_deployments/__init__.py +0 -0
  42. prefect/client/orchestration/_deployments/client.py +1128 -0
  43. prefect/client/orchestration/_flow_runs/__init__.py +0 -0
  44. prefect/client/orchestration/_flow_runs/client.py +903 -0
  45. prefect/client/orchestration/_flows/__init__.py +0 -0
  46. prefect/client/orchestration/_flows/client.py +343 -0
  47. prefect/client/orchestration/_logs/__init__.py +0 -0
  48. prefect/client/orchestration/_logs/client.py +97 -0
  49. prefect/client/orchestration/_variables/__init__.py +0 -0
  50. prefect/client/orchestration/_variables/client.py +157 -0
  51. prefect/client/orchestration/base.py +46 -0
  52. prefect/client/orchestration/routes.py +145 -0
  53. prefect/client/schemas/__init__.py +68 -28
  54. prefect/client/schemas/actions.py +2 -2
  55. prefect/client/schemas/filters.py +5 -0
  56. prefect/client/schemas/objects.py +8 -15
  57. prefect/client/schemas/schedules.py +22 -10
  58. prefect/concurrency/_asyncio.py +87 -0
  59. prefect/concurrency/{events.py → _events.py} +10 -10
  60. prefect/concurrency/asyncio.py +20 -104
  61. prefect/concurrency/context.py +6 -4
  62. prefect/concurrency/services.py +26 -74
  63. prefect/concurrency/sync.py +23 -44
  64. prefect/concurrency/v1/_asyncio.py +63 -0
  65. prefect/concurrency/v1/{events.py → _events.py} +13 -15
  66. prefect/concurrency/v1/asyncio.py +27 -80
  67. prefect/concurrency/v1/context.py +6 -4
  68. prefect/concurrency/v1/services.py +33 -79
  69. prefect/concurrency/v1/sync.py +18 -37
  70. prefect/context.py +66 -45
  71. prefect/deployments/base.py +10 -144
  72. prefect/deployments/flow_runs.py +12 -2
  73. prefect/deployments/runner.py +53 -4
  74. prefect/deployments/steps/pull.py +13 -0
  75. prefect/engine.py +17 -4
  76. prefect/events/clients.py +7 -1
  77. prefect/events/schemas/events.py +3 -2
  78. prefect/filesystems.py +6 -2
  79. prefect/flow_engine.py +101 -85
  80. prefect/flows.py +10 -1
  81. prefect/input/run_input.py +2 -1
  82. prefect/logging/logging.yml +1 -1
  83. prefect/main.py +1 -3
  84. prefect/results.py +2 -307
  85. prefect/runner/runner.py +4 -2
  86. prefect/runner/storage.py +87 -21
  87. prefect/serializers.py +32 -25
  88. prefect/settings/legacy.py +4 -4
  89. prefect/settings/models/api.py +3 -3
  90. prefect/settings/models/cli.py +3 -3
  91. prefect/settings/models/client.py +5 -3
  92. prefect/settings/models/cloud.py +8 -3
  93. prefect/settings/models/deployments.py +3 -3
  94. prefect/settings/models/experiments.py +4 -7
  95. prefect/settings/models/flows.py +3 -3
  96. prefect/settings/models/internal.py +4 -2
  97. prefect/settings/models/logging.py +4 -3
  98. prefect/settings/models/results.py +3 -3
  99. prefect/settings/models/root.py +3 -2
  100. prefect/settings/models/runner.py +4 -4
  101. prefect/settings/models/server/api.py +3 -3
  102. prefect/settings/models/server/database.py +11 -4
  103. prefect/settings/models/server/deployments.py +6 -2
  104. prefect/settings/models/server/ephemeral.py +4 -2
  105. prefect/settings/models/server/events.py +3 -2
  106. prefect/settings/models/server/flow_run_graph.py +6 -2
  107. prefect/settings/models/server/root.py +3 -3
  108. prefect/settings/models/server/services.py +26 -11
  109. prefect/settings/models/server/tasks.py +6 -3
  110. prefect/settings/models/server/ui.py +3 -3
  111. prefect/settings/models/tasks.py +5 -5
  112. prefect/settings/models/testing.py +3 -3
  113. prefect/settings/models/worker.py +5 -3
  114. prefect/settings/profiles.py +15 -2
  115. prefect/states.py +61 -45
  116. prefect/task_engine.py +54 -75
  117. prefect/task_runners.py +56 -55
  118. prefect/task_worker.py +2 -2
  119. prefect/tasks.py +90 -36
  120. prefect/telemetry/bootstrap.py +10 -9
  121. prefect/telemetry/run_telemetry.py +13 -8
  122. prefect/telemetry/services.py +4 -0
  123. prefect/transactions.py +4 -15
  124. prefect/utilities/_git.py +34 -0
  125. prefect/utilities/asyncutils.py +1 -1
  126. prefect/utilities/engine.py +3 -19
  127. prefect/utilities/generics.py +18 -0
  128. prefect/utilities/templating.py +25 -1
  129. prefect/workers/base.py +6 -3
  130. prefect/workers/process.py +1 -1
  131. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/METADATA +2 -2
  132. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/RECORD +135 -109
  133. prefect/client/orchestration.py +0 -4523
  134. prefect/records/__init__.py +0 -1
  135. prefect/records/base.py +0 -235
  136. prefect/records/filesystem.py +0 -213
  137. prefect/records/memory.py +0 -184
  138. prefect/records/result_store.py +0 -70
  139. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/LICENSE +0 -0
  140. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/WHEEL +0 -0
  141. {prefect_client-3.1.10.dist-info → prefect_client-3.1.12.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
+ from __future__ import annotations
2
+
1
3
  import logging
2
4
  from abc import ABC
3
- from typing import Dict, List, Optional
5
+ from typing import Any, Optional, cast
4
6
 
5
- from pydantic import AnyHttpUrl, Field, SecretStr
7
+ from pydantic import AnyHttpUrl, Field, HttpUrl, SecretStr
6
8
  from typing_extensions import Literal
7
9
 
8
10
  from prefect.blocks.abstract import NotificationBlock, NotificationError
@@ -30,11 +32,13 @@ class AbstractAppriseNotificationBlock(NotificationBlock, ABC):
30
32
  ),
31
33
  )
32
34
 
33
- def __init__(self, *args, **kwargs):
34
- import apprise
35
+ def __init__(self, *args: Any, **kwargs: Any):
36
+ from apprise import (
37
+ NOTIFY_TYPES, # pyright: ignore[reportAttributeAccessIssue, reportUnknownVariableType] incomplete type hints in apprise
38
+ )
35
39
 
36
- if PREFECT_NOTIFY_TYPE_DEFAULT not in apprise.NOTIFY_TYPES:
37
- apprise.NOTIFY_TYPES += (PREFECT_NOTIFY_TYPE_DEFAULT,)
40
+ if PREFECT_NOTIFY_TYPE_DEFAULT not in NOTIFY_TYPES:
41
+ NOTIFY_TYPES += (PREFECT_NOTIFY_TYPE_DEFAULT,) # pyright: ignore[reportUnknownVariableType]
38
42
 
39
43
  super().__init__(*args, **kwargs)
40
44
 
@@ -50,20 +54,22 @@ class AbstractAppriseNotificationBlock(NotificationBlock, ABC):
50
54
  )
51
55
 
52
56
  self._apprise_client = Apprise(asset=prefect_app_data)
53
- self._apprise_client.add(url.get_secret_value())
57
+ self._apprise_client.add(servers=url.get_secret_value()) # pyright: ignore[reportUnknownMemberType]
54
58
 
55
59
  def block_initialization(self) -> None:
56
- self._start_apprise_client(self.url)
60
+ self._start_apprise_client(getattr(self, "url"))
57
61
 
58
62
  @sync_compatible
59
- async def notify(
63
+ async def notify( # pyright: ignore[reportIncompatibleMethodOverride] TODO: update to sync only once base class is updated
60
64
  self,
61
65
  body: str,
62
- subject: Optional[str] = None,
63
- ):
66
+ subject: str | None = None,
67
+ ) -> None:
64
68
  with LogEavesdropper("apprise", level=logging.DEBUG) as eavesdropper:
65
- result = await self._apprise_client.async_notify(
66
- body=body, title=subject, notify_type=self.notify_type
69
+ result = await self._apprise_client.async_notify( # pyright: ignore[reportUnknownMemberType] incomplete type hints in apprise
70
+ body=body,
71
+ title=subject or "",
72
+ notify_type=self.notify_type, # pyright: ignore[reportArgumentType]
67
73
  )
68
74
  if not result and self._raise_on_failure:
69
75
  raise NotificationError(log=eavesdropper.text())
@@ -74,7 +80,9 @@ class AppriseNotificationBlock(AbstractAppriseNotificationBlock, ABC):
74
80
  A base class for sending notifications using Apprise, through webhook URLs.
75
81
  """
76
82
 
77
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
83
+ _documentation_url = HttpUrl(
84
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
85
+ )
78
86
  url: SecretStr = Field(
79
87
  default=...,
80
88
  title="Webhook URL",
@@ -87,10 +95,10 @@ class AppriseNotificationBlock(AbstractAppriseNotificationBlock, ABC):
87
95
  )
88
96
 
89
97
  @sync_compatible
90
- async def notify(
98
+ async def notify( # pyright: ignore[reportIncompatibleMethodOverride] TODO: update to sync only once base class is updated
91
99
  self,
92
100
  body: str,
93
- subject: Optional[str] = None,
101
+ subject: str | None = None,
94
102
  ):
95
103
  if not self.allow_private_urls:
96
104
  try:
@@ -100,7 +108,7 @@ class AppriseNotificationBlock(AbstractAppriseNotificationBlock, ABC):
100
108
  raise NotificationError(str(exc))
101
109
  raise
102
110
 
103
- await super().notify(body, subject)
111
+ await super().notify(body, subject) # pyright: ignore[reportGeneralTypeIssues] TODO: update to sync only once base class is updated
104
112
 
105
113
 
106
114
  # TODO: Move to prefect-slack once collection block auto-registration is
@@ -120,8 +128,12 @@ class SlackWebhook(AppriseNotificationBlock):
120
128
  """
121
129
 
122
130
  _block_type_name = "Slack Webhook"
123
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/c1965ecbf8704ee1ea20d77786de9a41ce1087d1-500x500.png"
124
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
131
+ _logo_url = HttpUrl(
132
+ "https://cdn.sanity.io/images/3ugk85nk/production/c1965ecbf8704ee1ea20d77786de9a41ce1087d1-500x500.png"
133
+ )
134
+ _documentation_url = HttpUrl(
135
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
136
+ )
125
137
 
126
138
  url: SecretStr = Field(
127
139
  default=...,
@@ -146,8 +158,12 @@ class MicrosoftTeamsWebhook(AppriseNotificationBlock):
146
158
 
147
159
  _block_type_name = "Microsoft Teams Webhook"
148
160
  _block_type_slug = "ms-teams-webhook"
149
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/817efe008a57f0a24f3587414714b563e5e23658-250x250.png"
150
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
161
+ _logo_url = HttpUrl(
162
+ "https://cdn.sanity.io/images/3ugk85nk/production/817efe008a57f0a24f3587414714b563e5e23658-250x250.png"
163
+ )
164
+ _documentation_url = HttpUrl(
165
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
166
+ )
151
167
 
152
168
  url: SecretStr = Field(
153
169
  default=...,
@@ -173,13 +189,16 @@ class MicrosoftTeamsWebhook(AppriseNotificationBlock):
173
189
  from apprise.plugins.workflows import NotifyWorkflows
174
190
 
175
191
  if not (
176
- parsed_url := NotifyWorkflows.parse_native_url(self.url.get_secret_value())
192
+ parsed_url := cast(
193
+ dict[str, Any],
194
+ NotifyWorkflows.parse_native_url(self.url.get_secret_value()), # pyright: ignore[reportUnknownMemberType] incomplete type hints in apprise
195
+ )
177
196
  ):
178
197
  raise ValueError("Invalid Microsoft Teams Workflow URL provided.")
179
198
 
180
199
  parsed_url |= {"include_image": self.include_image, "wrap": self.wrap}
181
200
 
182
- self._start_apprise_client(SecretStr(NotifyWorkflows(**parsed_url).url()))
201
+ self._start_apprise_client(SecretStr(NotifyWorkflows(**parsed_url).url())) # pyright: ignore[reportUnknownMemberType] incomplete type hints in apprise
183
202
 
184
203
 
185
204
  class PagerDutyWebHook(AbstractAppriseNotificationBlock):
@@ -201,12 +220,16 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
201
220
 
202
221
  _block_type_name = "Pager Duty Webhook"
203
222
  _block_type_slug = "pager-duty-webhook"
204
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/8dbf37d17089c1ce531708eac2e510801f7b3aee-250x250.png"
205
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
223
+ _logo_url = HttpUrl(
224
+ "https://cdn.sanity.io/images/3ugk85nk/production/8dbf37d17089c1ce531708eac2e510801f7b3aee-250x250.png"
225
+ )
226
+ _documentation_url = HttpUrl(
227
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
228
+ )
206
229
 
207
230
  # The default cannot be prefect_default because NotifyPagerDuty's
208
231
  # PAGERDUTY_SEVERITY_MAP only has these notify types defined as keys
209
- notify_type: Literal["info", "success", "warning", "failure"] = Field(
232
+ notify_type: Literal["info", "success", "warning", "failure"] = Field( # pyright: ignore[reportIncompatibleVariableOverride]
210
233
  default="info", description="The severity of the notification."
211
234
  )
212
235
 
@@ -264,7 +287,7 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
264
287
  description="Associate the notification status via a represented icon.",
265
288
  )
266
289
 
267
- custom_details: Optional[Dict[str, str]] = Field(
290
+ custom_details: Optional[dict[str, str]] = Field(
268
291
  default=None,
269
292
  description="Additional details to include as part of the payload.",
270
293
  examples=['{"disk_space_left": "145GB"}'],
@@ -276,7 +299,9 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
276
299
  from apprise.plugins.pagerduty import NotifyPagerDuty
277
300
  except ImportError:
278
301
  # Fallback for versions apprise<1.18.0
279
- from apprise.plugins.NotifyPagerDuty import NotifyPagerDuty
302
+ from apprise.plugins.NotifyPagerDuty import ( # pyright: ignore[reportMissingImports] this is a fallback
303
+ NotifyPagerDuty, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
304
+ )
280
305
 
281
306
  url = SecretStr(
282
307
  NotifyPagerDuty(
@@ -290,15 +315,15 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
290
315
  click=self.clickable_url,
291
316
  include_image=self.include_image,
292
317
  details=self.custom_details,
293
- ).url()
318
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
294
319
  )
295
320
  self._start_apprise_client(url)
296
321
 
297
322
  @sync_compatible
298
- async def notify(
323
+ async def notify( # pyright: ignore[reportIncompatibleMethodOverride] TODO: update to sync only once base class is updated
299
324
  self,
300
325
  body: str,
301
- subject: Optional[str] = None,
326
+ subject: str | None = None,
302
327
  ):
303
328
  """
304
329
  Apprise will combine subject and body by default, so we need to move
@@ -313,7 +338,7 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
313
338
  body = " "
314
339
  self.block_initialization()
315
340
 
316
- await super().notify(body, subject)
341
+ await super().notify(body, subject) # pyright: ignore[reportGeneralTypeIssues] TODO: update to sync only once base class is updated
317
342
 
318
343
 
319
344
  class TwilioSMS(AbstractAppriseNotificationBlock):
@@ -332,8 +357,12 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
332
357
  _description = "Enables sending notifications via Twilio SMS."
333
358
  _block_type_name = "Twilio SMS"
334
359
  _block_type_slug = "twilio-sms"
335
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/8bd8777999f82112c09b9c8d57083ac75a4a0d65-250x250.png" # noqa
336
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
360
+ _logo_url = HttpUrl(
361
+ "https://cdn.sanity.io/images/3ugk85nk/production/8bd8777999f82112c09b9c8d57083ac75a4a0d65-250x250.png"
362
+ ) # noqa
363
+ _documentation_url = HttpUrl(
364
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
365
+ )
337
366
 
338
367
  account_sid: str = Field(
339
368
  default=...,
@@ -357,7 +386,7 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
357
386
  examples=["18001234567"],
358
387
  )
359
388
 
360
- to_phone_numbers: List[str] = Field(
389
+ to_phone_numbers: list[str] = Field(
361
390
  default=...,
362
391
  description="A list of valid Twilio phone number(s) to send the message to.",
363
392
  # not wrapped in brackets because of the way UI displays examples; in code should be ["18004242424"]
@@ -370,14 +399,17 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
370
399
  from apprise.plugins.twilio import NotifyTwilio
371
400
  except ImportError:
372
401
  # Fallback for versions apprise<1.18.0
373
- from apprise.plugins.NotifyTwilio import NotifyTwilio
402
+ from apprise.plugins.NotifyTwilio import ( # pyright: ignore[reportMissingImports] this is a fallback
403
+ NotifyTwilio, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
404
+ )
405
+
374
406
  url = SecretStr(
375
407
  NotifyTwilio(
376
408
  account_sid=self.account_sid,
377
409
  auth_token=self.auth_token.get_secret_value(),
378
410
  source=self.from_phone_number,
379
411
  targets=self.to_phone_numbers,
380
- ).url()
412
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
381
413
  )
382
414
  self._start_apprise_client(url)
383
415
 
@@ -401,8 +433,12 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
401
433
 
402
434
  _block_type_name = "Opsgenie Webhook"
403
435
  _block_type_slug = "opsgenie-webhook"
404
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/d8b5bc6244ae6cd83b62ec42f10d96e14d6e9113-280x280.png"
405
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
436
+ _logo_url = HttpUrl(
437
+ "https://cdn.sanity.io/images/3ugk85nk/production/d8b5bc6244ae6cd83b62ec42f10d96e14d6e9113-280x280.png"
438
+ )
439
+ _documentation_url = HttpUrl(
440
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
441
+ )
406
442
 
407
443
  apikey: SecretStr = Field(
408
444
  default=...,
@@ -410,19 +446,19 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
410
446
  description="The API Key associated with your Opsgenie account.",
411
447
  )
412
448
 
413
- target_user: Optional[List] = Field(
449
+ target_user: Optional[list[str]] = Field(
414
450
  default=None, description="The user(s) you wish to notify."
415
451
  )
416
452
 
417
- target_team: Optional[List] = Field(
453
+ target_team: Optional[list[str]] = Field(
418
454
  default=None, description="The team(s) you wish to notify."
419
455
  )
420
456
 
421
- target_schedule: Optional[List] = Field(
457
+ target_schedule: Optional[list[str]] = Field(
422
458
  default=None, description="The schedule(s) you wish to notify."
423
459
  )
424
460
 
425
- target_escalation: Optional[List] = Field(
461
+ target_escalation: Optional[list[str]] = Field(
426
462
  default=None, description="The escalation(s) you wish to notify."
427
463
  )
428
464
 
@@ -435,7 +471,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
435
471
  description="Notify all targets in batches (instead of individually).",
436
472
  )
437
473
 
438
- tags: Optional[List] = Field(
474
+ tags: Optional[list[str]] = Field(
439
475
  default=None,
440
476
  description=(
441
477
  "A comma-separated list of tags you can associate with your Opsgenie"
@@ -460,7 +496,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
460
496
  default=None, description="The entity to associate with the message."
461
497
  )
462
498
 
463
- details: Optional[Dict[str, str]] = Field(
499
+ details: Optional[dict[str, str]] = Field(
464
500
  default=None,
465
501
  description="Additional details composed of key/values pairs.",
466
502
  examples=['{"key1": "value1", "key2": "value2"}'],
@@ -472,9 +508,11 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
472
508
  from apprise.plugins.opsgenie import NotifyOpsgenie
473
509
  except ImportError:
474
510
  # Fallback for versions apprise<1.18.0
475
- from apprise.plugins.NotifyOpsgenie import NotifyOpsgenie
511
+ from apprise.plugins.NotifyOpsgenie import ( # pyright: ignore[reportMissingImports] this is a fallback
512
+ NotifyOpsgenie, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
513
+ )
476
514
 
477
- targets = []
515
+ targets: list[str] = []
478
516
  if self.target_user:
479
517
  [targets.append(f"@{x}") for x in self.target_user]
480
518
  if self.target_team:
@@ -495,7 +533,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
495
533
  batch=self.batch,
496
534
  tags=self.tags,
497
535
  action="new",
498
- ).url()
536
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
499
537
  )
500
538
  self._start_apprise_client(url)
501
539
 
@@ -520,8 +558,12 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
520
558
  _description = "Enables sending notifications via a provided Mattermost webhook."
521
559
  _block_type_name = "Mattermost Webhook"
522
560
  _block_type_slug = "mattermost-webhook"
523
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/1350a147130bf82cbc799a5f868d2c0116207736-250x250.png"
524
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
561
+ _logo_url = HttpUrl(
562
+ "https://cdn.sanity.io/images/3ugk85nk/production/1350a147130bf82cbc799a5f868d2c0116207736-250x250.png"
563
+ )
564
+ _documentation_url = HttpUrl(
565
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
566
+ )
525
567
 
526
568
  hostname: str = Field(
527
569
  default=...,
@@ -540,7 +582,7 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
540
582
  description="The name of the bot that will send the message.",
541
583
  )
542
584
 
543
- channels: Optional[List[str]] = Field(
585
+ channels: Optional[list[str]] = Field(
544
586
  default=None,
545
587
  description="The channel(s) you wish to notify.",
546
588
  )
@@ -566,7 +608,9 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
566
608
  from apprise.plugins.mattermost import NotifyMattermost
567
609
  except ImportError:
568
610
  # Fallback for versions apprise<1.18.0
569
- from apprise.plugins.NotifyMattermost import NotifyMattermost
611
+ from apprise.plugins.NotifyMattermost import ( # pyright: ignore[reportMissingImports] this is a fallback
612
+ NotifyMattermost, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
613
+ )
570
614
 
571
615
  url = SecretStr(
572
616
  NotifyMattermost(
@@ -577,7 +621,7 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
577
621
  channels=self.channels,
578
622
  include_image=self.include_image,
579
623
  port=self.port,
580
- ).url()
624
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
581
625
  )
582
626
  self._start_apprise_client(url)
583
627
 
@@ -601,8 +645,12 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
601
645
  _description = "Enables sending notifications via a provided Discord webhook."
602
646
  _block_type_name = "Discord Webhook"
603
647
  _block_type_slug = "discord-webhook"
604
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/9e94976c80ef925b66d24e5d14f0d47baa6b8f88-250x250.png"
605
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
648
+ _logo_url = HttpUrl(
649
+ "https://cdn.sanity.io/images/3ugk85nk/production/9e94976c80ef925b66d24e5d14f0d47baa6b8f88-250x250.png"
650
+ )
651
+ _documentation_url = HttpUrl(
652
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
653
+ )
606
654
 
607
655
  webhook_id: SecretStr = Field(
608
656
  default=...,
@@ -650,7 +698,7 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
650
698
 
651
699
  avatar_url: Optional[str] = Field(
652
700
  title="Avatar URL",
653
- default=False,
701
+ default=None,
654
702
  description=(
655
703
  "Over-ride the default discord avatar icon URL. By default this is not set"
656
704
  " and Apprise chooses the URL dynamically based on the type of message"
@@ -664,7 +712,9 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
664
712
  from apprise.plugins.discord import NotifyDiscord
665
713
  except ImportError:
666
714
  # Fallback for versions apprise<1.18.0
667
- from apprise.plugins.NotifyDiscord import NotifyDiscord
715
+ from apprise.plugins.NotifyDiscord import ( # pyright: ignore[reportMissingImports] this is a fallback
716
+ NotifyDiscord, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
717
+ )
668
718
 
669
719
  url = SecretStr(
670
720
  NotifyDiscord(
@@ -675,7 +725,7 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
675
725
  include_image=self.include_image,
676
726
  avatar=self.avatar,
677
727
  avatar_url=self.avatar_url,
678
- ).url()
728
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
679
729
  )
680
730
  self._start_apprise_client(url)
681
731
 
@@ -700,8 +750,12 @@ class CustomWebhookNotificationBlock(NotificationBlock):
700
750
  """
701
751
 
702
752
  _block_type_name = "Custom Webhook"
703
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/c7247cb359eb6cf276734d4b1fbf00fb8930e89e-250x250.png"
704
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
753
+ _logo_url = HttpUrl(
754
+ "https://cdn.sanity.io/images/3ugk85nk/production/c7247cb359eb6cf276734d4b1fbf00fb8930e89e-250x250.png"
755
+ )
756
+ _documentation_url = HttpUrl(
757
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
758
+ )
705
759
 
706
760
  name: str = Field(title="Name", description="Name of the webhook.")
707
761
 
@@ -715,10 +769,10 @@ class CustomWebhookNotificationBlock(NotificationBlock):
715
769
  default="POST", description="The webhook request method. Defaults to `POST`."
716
770
  )
717
771
 
718
- params: Optional[Dict[str, str]] = Field(
772
+ params: Optional[dict[str, str]] = Field(
719
773
  default=None, title="Query Params", description="Custom query params."
720
774
  )
721
- json_data: Optional[dict] = Field(
775
+ json_data: Optional[dict[str, Any]] = Field(
722
776
  default=None,
723
777
  title="JSON Data",
724
778
  description="Send json data as payload.",
@@ -727,7 +781,7 @@ class CustomWebhookNotificationBlock(NotificationBlock):
727
781
  ' "{{tokenFromSecrets}}"}'
728
782
  ],
729
783
  )
730
- form_data: Optional[Dict[str, str]] = Field(
784
+ form_data: Optional[dict[str, str]] = Field(
731
785
  default=None,
732
786
  title="Form Data",
733
787
  description=(
@@ -739,8 +793,8 @@ class CustomWebhookNotificationBlock(NotificationBlock):
739
793
  ],
740
794
  )
741
795
 
742
- headers: Optional[Dict[str, str]] = Field(None, description="Custom headers.")
743
- cookies: Optional[Dict[str, str]] = Field(None, description="Custom cookies.")
796
+ headers: Optional[dict[str, str]] = Field(None, description="Custom headers.")
797
+ cookies: Optional[dict[str, str]] = Field(None, description="Custom cookies.")
744
798
 
745
799
  timeout: float = Field(
746
800
  default=10, description="Request timeout in seconds. Defaults to 10."
@@ -753,7 +807,7 @@ class CustomWebhookNotificationBlock(NotificationBlock):
753
807
  examples=['{"tokenFromSecrets":"SomeSecretToken"}'],
754
808
  )
755
809
 
756
- def _build_request_args(self, body: str, subject: Optional[str]):
810
+ def _build_request_args(self, body: str, subject: str | None) -> dict[str, Any]:
757
811
  """Build kwargs for httpx.AsyncClient.request"""
758
812
  # prepare values
759
813
  values = self.secrets.get_secret_value()
@@ -799,11 +853,11 @@ class CustomWebhookNotificationBlock(NotificationBlock):
799
853
  raise KeyError(f"{name}/{placeholder}")
800
854
 
801
855
  @sync_compatible
802
- async def notify(self, body: str, subject: Optional[str] = None):
856
+ async def notify(self, body: str, subject: str | None = None) -> None: # pyright: ignore[reportIncompatibleMethodOverride]
803
857
  import httpx
804
858
 
805
859
  request_args = self._build_request_args(body, subject)
806
- cookies = request_args.pop("cookies", None)
860
+ cookies = request_args.pop("cookies", dict())
807
861
  # make request with httpx
808
862
  client = httpx.AsyncClient(
809
863
  headers={"user-agent": "Prefect Notifications"}, cookies=cookies
@@ -831,8 +885,12 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
831
885
  _description = "Enables sending notifications via Sendgrid email service."
832
886
  _block_type_name = "Sendgrid Email"
833
887
  _block_type_slug = "sendgrid-email"
834
- _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/82bc6ed16ca42a2252a5512c72233a253b8a58eb-250x250.png"
835
- _documentation_url = "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
888
+ _logo_url = HttpUrl(
889
+ "https://cdn.sanity.io/images/3ugk85nk/production/82bc6ed16ca42a2252a5512c72233a253b8a58eb-250x250.png"
890
+ )
891
+ _documentation_url = HttpUrl(
892
+ "https://docs.prefect.io/latest/automate/events/automations-triggers#sending-notifications-with-automations"
893
+ )
836
894
 
837
895
  api_key: SecretStr = Field(
838
896
  default=...,
@@ -846,7 +904,7 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
846
904
  examples=["test-support@gmail.com"],
847
905
  )
848
906
 
849
- to_emails: List[str] = Field(
907
+ to_emails: list[str] = Field(
850
908
  default=...,
851
909
  title="Recipient emails",
852
910
  description="Email ids of all recipients.",
@@ -859,14 +917,16 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
859
917
  from apprise.plugins.sendgrid import NotifySendGrid
860
918
  except ImportError:
861
919
  # Fallback for versions apprise<1.18.0
862
- from apprise.plugins.NotifySendGrid import NotifySendGrid
920
+ from apprise.plugins.NotifySendGrid import ( # pyright: ignore[reportMissingImports] this is a fallback
921
+ NotifySendGrid, # pyright: ignore[reportUnknownVariableType] incomplete type hints in apprise
922
+ )
863
923
 
864
924
  url = SecretStr(
865
925
  NotifySendGrid(
866
926
  apikey=self.api_key.get_secret_value(),
867
927
  from_email=self.sender_email,
868
928
  targets=self.to_emails,
869
- ).url()
929
+ ).url() # pyright: ignore[reportUnknownMemberType, reportUnknownArgumentType] incomplete type hints in apprise
870
930
  )
871
931
 
872
932
  self._start_apprise_client(url)
prefect/blocks/redis.py CHANGED
@@ -1,6 +1,8 @@
1
+ from __future__ import annotations
2
+
1
3
  from contextlib import asynccontextmanager
2
4
  from pathlib import Path
3
- from typing import AsyncGenerator, Optional, Union
5
+ from typing import AsyncGenerator, Optional
4
6
 
5
7
  try:
6
8
  import redis.asyncio as redis
@@ -10,7 +12,7 @@ except ImportError:
10
12
  "You can install it with `pip install redis>=5.0.1"
11
13
  )
12
14
 
13
- from pydantic import Field
15
+ from pydantic import Field, HttpUrl
14
16
  from pydantic.types import SecretStr
15
17
  from typing_extensions import Self
16
18
 
@@ -48,7 +50,9 @@ class RedisStorageContainer(WritableFileSystem):
48
50
  ```
49
51
  """
50
52
 
51
- _logo_url = "https://stprododpcmscdnendpoint.azureedge.net/assets/icons/redis.png"
53
+ _logo_url = HttpUrl(
54
+ "https://cdn.sanity.io/images/3ugk85nk/production/dfb02cfce09ce3ca88fea097659a83554dd7a850-596x512.png"
55
+ )
52
56
 
53
57
  host: Optional[str] = Field(default=None, description="Redis hostname")
54
58
  port: int = Field(default=6379, description="Redis port")
@@ -70,7 +74,7 @@ class RedisStorageContainer(WritableFileSystem):
70
74
  )
71
75
 
72
76
  @sync_compatible
73
- async def read_path(self, path: Union[Path, str]):
77
+ async def read_path(self, path: Path | str):
74
78
  """Read the redis content at `path`
75
79
 
76
80
  Args:
@@ -83,7 +87,7 @@ class RedisStorageContainer(WritableFileSystem):
83
87
  return await client.get(str(path))
84
88
 
85
89
  @sync_compatible
86
- async def write_path(self, path: Union[Path, str], content: bytes):
90
+ async def write_path(self, path: Path | str, content: bytes):
87
91
  """Write `content` to the redis at `path`
88
92
 
89
93
  Args:
@@ -97,7 +101,7 @@ class RedisStorageContainer(WritableFileSystem):
97
101
  @asynccontextmanager
98
102
  async def _client(self) -> AsyncGenerator[redis.Redis, None]:
99
103
  if self.connection_string:
100
- client = redis.Redis.from_url(self.connection_string.get_secret_value())
104
+ client = redis.Redis.from_url(self.connection_string.get_secret_value()) # pyright: ignore[reportUnknownMemberType] incomplete typing for redis-py
101
105
  else:
102
106
  assert self.host
103
107
  client = redis.Redis(
@@ -119,8 +123,8 @@ class RedisStorageContainer(WritableFileSystem):
119
123
  host: str,
120
124
  port: int = 6379,
121
125
  db: int = 0,
122
- username: Union[None, str, SecretStr] = None,
123
- password: Union[None, str, SecretStr] = None,
126
+ username: None | str | SecretStr = None,
127
+ password: None | str | SecretStr = None,
124
128
  ) -> Self:
125
129
  """Create block from hostname, username and password
126
130
 
@@ -140,7 +144,7 @@ class RedisStorageContainer(WritableFileSystem):
140
144
  return cls(host=host, port=port, db=db, username=username, password=password)
141
145
 
142
146
  @classmethod
143
- def from_connection_string(cls, connection_string: Union[str, SecretStr]) -> Self:
147
+ def from_connection_string(cls, connection_string: str | SecretStr) -> Self:
144
148
  """Create block from a Redis connection string
145
149
 
146
150
  Supports the following URL schemes: