prefect-client 3.1.11__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.
- prefect/_experimental/sla/__init__.py +0 -0
- prefect/_experimental/sla/client.py +66 -0
- prefect/_experimental/sla/objects.py +53 -0
- prefect/_version.py +3 -3
- prefect/automations.py +236 -30
- prefect/blocks/__init__.py +3 -3
- prefect/blocks/abstract.py +53 -30
- prefect/blocks/core.py +181 -82
- prefect/blocks/notifications.py +133 -73
- prefect/blocks/redis.py +13 -9
- prefect/blocks/system.py +24 -11
- prefect/blocks/webhook.py +7 -5
- prefect/cache_policies.py +3 -2
- prefect/client/orchestration/__init__.py +103 -2006
- prefect/client/orchestration/_automations/__init__.py +0 -0
- prefect/client/orchestration/_automations/client.py +329 -0
- prefect/client/orchestration/_blocks_documents/__init__.py +0 -0
- prefect/client/orchestration/_blocks_documents/client.py +334 -0
- prefect/client/orchestration/_blocks_schemas/__init__.py +0 -0
- prefect/client/orchestration/_blocks_schemas/client.py +200 -0
- prefect/client/orchestration/_blocks_types/__init__.py +0 -0
- prefect/client/orchestration/_blocks_types/client.py +380 -0
- prefect/client/orchestration/_deployments/__init__.py +0 -0
- prefect/client/orchestration/_deployments/client.py +1128 -0
- prefect/client/orchestration/_flow_runs/__init__.py +0 -0
- prefect/client/orchestration/_flow_runs/client.py +903 -0
- prefect/client/orchestration/_flows/__init__.py +0 -0
- prefect/client/orchestration/_flows/client.py +343 -0
- prefect/client/orchestration/_logs/client.py +16 -14
- prefect/client/schemas/__init__.py +68 -28
- prefect/client/schemas/objects.py +5 -5
- prefect/context.py +15 -1
- prefect/deployments/base.py +6 -0
- prefect/deployments/runner.py +42 -1
- prefect/engine.py +17 -4
- prefect/filesystems.py +6 -2
- prefect/flow_engine.py +47 -38
- prefect/flows.py +10 -1
- prefect/logging/logging.yml +1 -1
- prefect/runner/runner.py +4 -2
- prefect/settings/models/cloud.py +5 -0
- prefect/settings/models/experiments.py +0 -5
- prefect/states.py +57 -38
- prefect/task_runners.py +56 -55
- prefect/task_worker.py +2 -2
- prefect/tasks.py +6 -4
- prefect/telemetry/bootstrap.py +10 -9
- prefect/telemetry/services.py +4 -0
- prefect/utilities/templating.py +25 -1
- prefect/workers/base.py +6 -3
- prefect/workers/process.py +1 -1
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.12.dist-info}/METADATA +2 -2
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.12.dist-info}/RECORD +56 -39
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.12.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.12.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.11.dist-info → prefect_client-3.1.12.dist-info}/top_level.txt +0 -0
prefect/blocks/notifications.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import logging
|
2
4
|
from abc import ABC
|
3
|
-
from typing import
|
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
|
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
|
37
|
-
|
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
|
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:
|
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,
|
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 =
|
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:
|
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 =
|
124
|
-
|
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 =
|
150
|
-
|
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 :=
|
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 =
|
205
|
-
|
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[
|
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
|
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:
|
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 =
|
336
|
-
|
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:
|
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
|
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 =
|
405
|
-
|
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[
|
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[
|
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[
|
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[
|
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[
|
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[
|
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
|
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 =
|
524
|
-
|
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[
|
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
|
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 =
|
605
|
-
|
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=
|
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
|
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 =
|
704
|
-
|
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[
|
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[
|
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[
|
743
|
-
cookies: Optional[
|
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:
|
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:
|
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",
|
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 =
|
835
|
-
|
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:
|
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
|
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
|
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 =
|
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:
|
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:
|
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:
|
123
|
-
password:
|
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:
|
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:
|