prefect-client 2.20.4__py3-none-any.whl → 3.0.0__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 (288) hide show
  1. prefect/__init__.py +74 -110
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/compatibility/migration.py +166 -0
  5. prefect/_internal/concurrency/__init__.py +2 -2
  6. prefect/_internal/concurrency/api.py +1 -35
  7. prefect/_internal/concurrency/calls.py +0 -6
  8. prefect/_internal/concurrency/cancellation.py +0 -3
  9. prefect/_internal/concurrency/event_loop.py +0 -20
  10. prefect/_internal/concurrency/inspection.py +3 -3
  11. prefect/_internal/concurrency/primitives.py +1 -0
  12. prefect/_internal/concurrency/services.py +23 -0
  13. prefect/_internal/concurrency/threads.py +35 -0
  14. prefect/_internal/concurrency/waiters.py +0 -28
  15. prefect/_internal/integrations.py +7 -0
  16. prefect/_internal/pydantic/__init__.py +0 -45
  17. prefect/_internal/pydantic/annotations/pendulum.py +2 -2
  18. prefect/_internal/pydantic/v1_schema.py +21 -22
  19. prefect/_internal/pydantic/v2_schema.py +0 -2
  20. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  21. prefect/_internal/pytz.py +1 -1
  22. prefect/_internal/retries.py +61 -0
  23. prefect/_internal/schemas/bases.py +45 -177
  24. prefect/_internal/schemas/fields.py +1 -43
  25. prefect/_internal/schemas/validators.py +47 -233
  26. prefect/agent.py +3 -695
  27. prefect/artifacts.py +173 -14
  28. prefect/automations.py +39 -4
  29. prefect/blocks/abstract.py +1 -1
  30. prefect/blocks/core.py +405 -153
  31. prefect/blocks/fields.py +2 -57
  32. prefect/blocks/notifications.py +43 -28
  33. prefect/blocks/redis.py +168 -0
  34. prefect/blocks/system.py +67 -20
  35. prefect/blocks/webhook.py +2 -9
  36. prefect/cache_policies.py +239 -0
  37. prefect/client/__init__.py +4 -0
  38. prefect/client/base.py +33 -27
  39. prefect/client/cloud.py +65 -20
  40. prefect/client/collections.py +1 -1
  41. prefect/client/orchestration.py +650 -442
  42. prefect/client/schemas/actions.py +115 -100
  43. prefect/client/schemas/filters.py +46 -52
  44. prefect/client/schemas/objects.py +228 -178
  45. prefect/client/schemas/responses.py +18 -36
  46. prefect/client/schemas/schedules.py +55 -36
  47. prefect/client/schemas/sorting.py +2 -0
  48. prefect/client/subscriptions.py +8 -7
  49. prefect/client/types/flexible_schedule_list.py +11 -0
  50. prefect/client/utilities.py +9 -6
  51. prefect/concurrency/asyncio.py +60 -11
  52. prefect/concurrency/context.py +24 -0
  53. prefect/concurrency/events.py +2 -2
  54. prefect/concurrency/services.py +46 -16
  55. prefect/concurrency/sync.py +51 -7
  56. prefect/concurrency/v1/asyncio.py +143 -0
  57. prefect/concurrency/v1/context.py +27 -0
  58. prefect/concurrency/v1/events.py +61 -0
  59. prefect/concurrency/v1/services.py +116 -0
  60. prefect/concurrency/v1/sync.py +92 -0
  61. prefect/context.py +246 -149
  62. prefect/deployments/__init__.py +33 -18
  63. prefect/deployments/base.py +10 -15
  64. prefect/deployments/deployments.py +2 -1048
  65. prefect/deployments/flow_runs.py +178 -0
  66. prefect/deployments/runner.py +72 -173
  67. prefect/deployments/schedules.py +31 -25
  68. prefect/deployments/steps/__init__.py +0 -1
  69. prefect/deployments/steps/core.py +7 -0
  70. prefect/deployments/steps/pull.py +15 -21
  71. prefect/deployments/steps/utility.py +2 -1
  72. prefect/docker/__init__.py +20 -0
  73. prefect/docker/docker_image.py +82 -0
  74. prefect/engine.py +15 -2475
  75. prefect/events/actions.py +17 -23
  76. prefect/events/cli/automations.py +20 -7
  77. prefect/events/clients.py +142 -80
  78. prefect/events/filters.py +14 -18
  79. prefect/events/related.py +74 -75
  80. prefect/events/schemas/__init__.py +0 -5
  81. prefect/events/schemas/automations.py +55 -46
  82. prefect/events/schemas/deployment_triggers.py +7 -197
  83. prefect/events/schemas/events.py +46 -65
  84. prefect/events/schemas/labelling.py +10 -14
  85. prefect/events/utilities.py +4 -5
  86. prefect/events/worker.py +23 -8
  87. prefect/exceptions.py +15 -0
  88. prefect/filesystems.py +30 -529
  89. prefect/flow_engine.py +827 -0
  90. prefect/flow_runs.py +379 -7
  91. prefect/flows.py +470 -360
  92. prefect/futures.py +382 -331
  93. prefect/infrastructure/__init__.py +5 -26
  94. prefect/infrastructure/base.py +3 -320
  95. prefect/infrastructure/provisioners/__init__.py +5 -3
  96. prefect/infrastructure/provisioners/cloud_run.py +13 -8
  97. prefect/infrastructure/provisioners/container_instance.py +14 -9
  98. prefect/infrastructure/provisioners/ecs.py +10 -8
  99. prefect/infrastructure/provisioners/modal.py +8 -5
  100. prefect/input/__init__.py +4 -0
  101. prefect/input/actions.py +2 -4
  102. prefect/input/run_input.py +9 -9
  103. prefect/logging/formatters.py +2 -4
  104. prefect/logging/handlers.py +9 -14
  105. prefect/logging/loggers.py +5 -5
  106. prefect/main.py +72 -0
  107. prefect/plugins.py +2 -64
  108. prefect/profiles.toml +16 -2
  109. prefect/records/__init__.py +1 -0
  110. prefect/records/base.py +223 -0
  111. prefect/records/filesystem.py +207 -0
  112. prefect/records/memory.py +178 -0
  113. prefect/records/result_store.py +64 -0
  114. prefect/results.py +577 -504
  115. prefect/runner/runner.py +117 -47
  116. prefect/runner/server.py +32 -34
  117. prefect/runner/storage.py +3 -12
  118. prefect/runner/submit.py +2 -10
  119. prefect/runner/utils.py +2 -2
  120. prefect/runtime/__init__.py +1 -0
  121. prefect/runtime/deployment.py +1 -0
  122. prefect/runtime/flow_run.py +40 -5
  123. prefect/runtime/task_run.py +1 -0
  124. prefect/serializers.py +28 -39
  125. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  126. prefect/settings.py +209 -332
  127. prefect/states.py +160 -63
  128. prefect/task_engine.py +1478 -57
  129. prefect/task_runners.py +383 -287
  130. prefect/task_runs.py +240 -0
  131. prefect/task_worker.py +463 -0
  132. prefect/tasks.py +684 -374
  133. prefect/transactions.py +410 -0
  134. prefect/types/__init__.py +72 -86
  135. prefect/types/entrypoint.py +13 -0
  136. prefect/utilities/annotations.py +4 -3
  137. prefect/utilities/asyncutils.py +227 -148
  138. prefect/utilities/callables.py +137 -45
  139. prefect/utilities/collections.py +134 -86
  140. prefect/utilities/dispatch.py +27 -14
  141. prefect/utilities/dockerutils.py +11 -4
  142. prefect/utilities/engine.py +186 -32
  143. prefect/utilities/filesystem.py +4 -5
  144. prefect/utilities/importtools.py +26 -27
  145. prefect/utilities/pydantic.py +128 -38
  146. prefect/utilities/schema_tools/hydration.py +18 -1
  147. prefect/utilities/schema_tools/validation.py +30 -0
  148. prefect/utilities/services.py +35 -9
  149. prefect/utilities/templating.py +12 -2
  150. prefect/utilities/timeout.py +20 -5
  151. prefect/utilities/urls.py +195 -0
  152. prefect/utilities/visualization.py +1 -0
  153. prefect/variables.py +78 -59
  154. prefect/workers/__init__.py +0 -1
  155. prefect/workers/base.py +237 -244
  156. prefect/workers/block.py +5 -226
  157. prefect/workers/cloud.py +6 -0
  158. prefect/workers/process.py +265 -12
  159. prefect/workers/server.py +29 -11
  160. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
  163. prefect/_internal/pydantic/_base_model.py +0 -51
  164. prefect/_internal/pydantic/_compat.py +0 -82
  165. prefect/_internal/pydantic/_flags.py +0 -20
  166. prefect/_internal/pydantic/_types.py +0 -8
  167. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  168. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  169. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  170. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  171. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  172. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  173. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  174. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  175. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  176. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  177. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  178. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  179. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  180. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  181. prefect/_vendor/fastapi/__init__.py +0 -25
  182. prefect/_vendor/fastapi/applications.py +0 -946
  183. prefect/_vendor/fastapi/background.py +0 -3
  184. prefect/_vendor/fastapi/concurrency.py +0 -44
  185. prefect/_vendor/fastapi/datastructures.py +0 -58
  186. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  187. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  188. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  189. prefect/_vendor/fastapi/encoders.py +0 -177
  190. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  191. prefect/_vendor/fastapi/exceptions.py +0 -46
  192. prefect/_vendor/fastapi/logger.py +0 -3
  193. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  194. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  195. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  196. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  197. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  198. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  199. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  200. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  201. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  202. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  203. prefect/_vendor/fastapi/openapi/models.py +0 -480
  204. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  205. prefect/_vendor/fastapi/param_functions.py +0 -340
  206. prefect/_vendor/fastapi/params.py +0 -453
  207. prefect/_vendor/fastapi/py.typed +0 -0
  208. prefect/_vendor/fastapi/requests.py +0 -4
  209. prefect/_vendor/fastapi/responses.py +0 -40
  210. prefect/_vendor/fastapi/routing.py +0 -1331
  211. prefect/_vendor/fastapi/security/__init__.py +0 -15
  212. prefect/_vendor/fastapi/security/api_key.py +0 -98
  213. prefect/_vendor/fastapi/security/base.py +0 -6
  214. prefect/_vendor/fastapi/security/http.py +0 -172
  215. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  216. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  217. prefect/_vendor/fastapi/security/utils.py +0 -10
  218. prefect/_vendor/fastapi/staticfiles.py +0 -1
  219. prefect/_vendor/fastapi/templating.py +0 -3
  220. prefect/_vendor/fastapi/testclient.py +0 -1
  221. prefect/_vendor/fastapi/types.py +0 -3
  222. prefect/_vendor/fastapi/utils.py +0 -235
  223. prefect/_vendor/fastapi/websockets.py +0 -7
  224. prefect/_vendor/starlette/__init__.py +0 -1
  225. prefect/_vendor/starlette/_compat.py +0 -28
  226. prefect/_vendor/starlette/_exception_handler.py +0 -80
  227. prefect/_vendor/starlette/_utils.py +0 -88
  228. prefect/_vendor/starlette/applications.py +0 -261
  229. prefect/_vendor/starlette/authentication.py +0 -159
  230. prefect/_vendor/starlette/background.py +0 -43
  231. prefect/_vendor/starlette/concurrency.py +0 -59
  232. prefect/_vendor/starlette/config.py +0 -151
  233. prefect/_vendor/starlette/convertors.py +0 -87
  234. prefect/_vendor/starlette/datastructures.py +0 -707
  235. prefect/_vendor/starlette/endpoints.py +0 -130
  236. prefect/_vendor/starlette/exceptions.py +0 -60
  237. prefect/_vendor/starlette/formparsers.py +0 -276
  238. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  239. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  240. prefect/_vendor/starlette/middleware/base.py +0 -220
  241. prefect/_vendor/starlette/middleware/cors.py +0 -176
  242. prefect/_vendor/starlette/middleware/errors.py +0 -265
  243. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  244. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  245. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  246. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  247. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  248. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  249. prefect/_vendor/starlette/py.typed +0 -0
  250. prefect/_vendor/starlette/requests.py +0 -328
  251. prefect/_vendor/starlette/responses.py +0 -347
  252. prefect/_vendor/starlette/routing.py +0 -933
  253. prefect/_vendor/starlette/schemas.py +0 -154
  254. prefect/_vendor/starlette/staticfiles.py +0 -248
  255. prefect/_vendor/starlette/status.py +0 -199
  256. prefect/_vendor/starlette/templating.py +0 -231
  257. prefect/_vendor/starlette/testclient.py +0 -804
  258. prefect/_vendor/starlette/types.py +0 -30
  259. prefect/_vendor/starlette/websockets.py +0 -193
  260. prefect/blocks/kubernetes.py +0 -119
  261. prefect/deprecated/__init__.py +0 -0
  262. prefect/deprecated/data_documents.py +0 -350
  263. prefect/deprecated/packaging/__init__.py +0 -12
  264. prefect/deprecated/packaging/base.py +0 -96
  265. prefect/deprecated/packaging/docker.py +0 -146
  266. prefect/deprecated/packaging/file.py +0 -92
  267. prefect/deprecated/packaging/orion.py +0 -80
  268. prefect/deprecated/packaging/serializers.py +0 -171
  269. prefect/events/instrument.py +0 -135
  270. prefect/infrastructure/container.py +0 -824
  271. prefect/infrastructure/kubernetes.py +0 -920
  272. prefect/infrastructure/process.py +0 -289
  273. prefect/manifests.py +0 -20
  274. prefect/new_flow_engine.py +0 -449
  275. prefect/new_task_engine.py +0 -423
  276. prefect/pydantic/__init__.py +0 -76
  277. prefect/pydantic/main.py +0 -39
  278. prefect/software/__init__.py +0 -2
  279. prefect/software/base.py +0 -50
  280. prefect/software/conda.py +0 -199
  281. prefect/software/pip.py +0 -122
  282. prefect/software/python.py +0 -52
  283. prefect/task_server.py +0 -322
  284. prefect_client-2.20.4.dist-info/RECORD +0 -294
  285. /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
  286. /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
  287. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
prefect/blocks/fields.py CHANGED
@@ -1,58 +1,3 @@
1
- from typing import TYPE_CHECKING, Any, Dict
1
+ from prefect.types import SecretDict
2
2
 
3
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
4
-
5
- if HAS_PYDANTIC_V2:
6
- from pydantic.v1 import SecretField
7
- from pydantic.v1.utils import update_not_none
8
- from pydantic.v1.validators import dict_validator
9
-
10
- if TYPE_CHECKING:
11
- from pydantic.v1.typing import CallableGenerator
12
-
13
- else:
14
- from pydantic import SecretField
15
- from pydantic.utils import update_not_none
16
- from pydantic.validators import dict_validator
17
-
18
- if TYPE_CHECKING:
19
- from pydantic.typing import CallableGenerator
20
-
21
-
22
- class SecretDict(SecretField):
23
- @classmethod
24
- def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
25
- update_not_none(
26
- field_schema,
27
- type="object",
28
- )
29
-
30
- @classmethod
31
- def __get_validators__(cls) -> "CallableGenerator":
32
- yield cls.validate
33
-
34
- @classmethod
35
- def validate(cls, value: Any) -> "SecretDict":
36
- if isinstance(value, cls):
37
- return value
38
- value = dict_validator(value)
39
- return cls(value)
40
-
41
- def __init__(self, value: Dict[str, Any]):
42
- self._secret_value = value
43
-
44
- def __str__(self) -> str:
45
- return (
46
- str({key: "**********" for key in self.get_secret_value().keys()})
47
- if self.get_secret_value()
48
- else ""
49
- )
50
-
51
- def __repr__(self) -> str:
52
- return f"SecretDict('{self}')"
53
-
54
- def get_secret_value(self) -> Dict[str, Any]:
55
- return self._secret_value
56
-
57
- def dict(self) -> Dict:
58
- return {key: "**********" for key in self.get_secret_value().keys()}
3
+ __all__ = ["SecretDict"]
@@ -2,19 +2,12 @@ import logging
2
2
  from abc import ABC
3
3
  from typing import Dict, List, Optional
4
4
 
5
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
6
- from prefect.logging import LogEavesdropper
7
-
8
- if HAS_PYDANTIC_V2:
9
- from pydantic.v1 import AnyHttpUrl, Field, SecretStr
10
- else:
11
- from pydantic import AnyHttpUrl, Field, SecretStr
12
-
5
+ from pydantic import AnyHttpUrl, Field, SecretStr
13
6
  from typing_extensions import Literal
14
7
 
15
8
  from prefect.blocks.abstract import NotificationBlock, NotificationError
16
- from prefect.blocks.fields import SecretDict
17
- from prefect.events.instrument import instrument_instance_method_call
9
+ from prefect.logging import LogEavesdropper
10
+ from prefect.types import SecretDict
18
11
  from prefect.utilities.asyncutils import sync_compatible
19
12
  from prefect.utilities.templating import apply_values, find_placeholders
20
13
 
@@ -62,7 +55,6 @@ class AbstractAppriseNotificationBlock(NotificationBlock, ABC):
62
55
  self._start_apprise_client(self.url)
63
56
 
64
57
  @sync_compatible
65
- @instrument_instance_method_call
66
58
  async def notify(
67
59
  self,
68
60
  body: str,
@@ -137,11 +129,11 @@ class MicrosoftTeamsWebhook(AppriseNotificationBlock):
137
129
  _documentation_url = "https://docs.prefect.io/api-ref/prefect/blocks/notifications/#prefect.blocks.notifications.MicrosoftTeamsWebhook"
138
130
 
139
131
  url: SecretStr = Field(
140
- ...,
132
+ default=...,
141
133
  title="Webhook URL",
142
134
  description="The Microsoft Power Automate (Workflows) URL used to send notifications to Teams.",
143
135
  examples=[
144
- "https://prod-NO.LOCATION.logic.azure.com:443/workflows/WFID/triggers/manual/paths/invoke?sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=SIGNATURE"
136
+ "https://prod-NO.LOCATION.logic.azure.com:443/workflows/WFID/triggers/manual/paths/invoke?api-version=2016-06-01&sp=%2Ftriggers%2Fmanual%2Frun&sv=1.0&sig=SIGNATURE"
145
137
  ],
146
138
  )
147
139
 
@@ -164,12 +156,7 @@ class MicrosoftTeamsWebhook(AppriseNotificationBlock):
164
156
  ):
165
157
  raise ValueError("Invalid Microsoft Teams Workflow URL provided.")
166
158
 
167
- parsed_url.update(
168
- {
169
- "include_image": self.include_image,
170
- "wrap": self.wrap,
171
- }
172
- )
159
+ parsed_url |= {"include_image": self.include_image, "wrap": self.wrap}
173
160
 
174
161
  self._start_apprise_client(SecretStr(NotifyWorkflows(**parsed_url).url()))
175
162
 
@@ -263,7 +250,12 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
263
250
  )
264
251
 
265
252
  def block_initialization(self) -> None:
266
- from apprise.plugins.pagerduty import NotifyPagerDuty
253
+ try:
254
+ # Try importing for apprise>=1.18.0
255
+ from apprise.plugins.pagerduty import NotifyPagerDuty
256
+ except ImportError:
257
+ # Fallback for versions apprise<1.18.0
258
+ from apprise.plugins.NotifyPagerDuty import NotifyPagerDuty
267
259
 
268
260
  url = SecretStr(
269
261
  NotifyPagerDuty(
@@ -331,8 +323,12 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
331
323
  )
332
324
 
333
325
  def block_initialization(self) -> None:
334
- from apprise.plugins.twilio import NotifyTwilio
335
-
326
+ try:
327
+ # Try importing for apprise>=1.18.0
328
+ from apprise.plugins.twilio import NotifyTwilio
329
+ except ImportError:
330
+ # Fallback for versions apprise<1.18.0
331
+ from apprise.plugins.NotifyTwilio import NotifyTwilio
336
332
  url = SecretStr(
337
333
  NotifyTwilio(
338
334
  account_sid=self.account_sid,
@@ -406,7 +402,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
406
402
  examples=['["tag1", "tag2"]'],
407
403
  )
408
404
 
409
- priority: Optional[str] = Field(
405
+ priority: Optional[int] = Field(
410
406
  default=3,
411
407
  description=(
412
408
  "The priority to associate with the message. It is on a scale between 1"
@@ -429,7 +425,12 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
429
425
  )
430
426
 
431
427
  def block_initialization(self) -> None:
432
- from apprise.plugins.opsgenie import NotifyOpsgenie
428
+ try:
429
+ # Try importing for apprise>=1.18.0
430
+ from apprise.plugins.opsgenie import NotifyOpsgenie
431
+ except ImportError:
432
+ # Fallback for versions apprise<1.18.0
433
+ from apprise.plugins.NotifyOpsgenie import NotifyOpsgenie
433
434
 
434
435
  targets = []
435
436
  if self.target_user:
@@ -517,7 +518,12 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
517
518
  )
518
519
 
519
520
  def block_initialization(self) -> None:
520
- from apprise.plugins.mattermost import NotifyMattermost
521
+ try:
522
+ # Try importing for apprise>=1.18.0
523
+ from apprise.plugins.mattermost import NotifyMattermost
524
+ except ImportError:
525
+ # Fallback for versions apprise<1.18.0
526
+ from apprise.plugins.NotifyMattermost import NotifyMattermost
521
527
 
522
528
  url = SecretStr(
523
529
  NotifyMattermost(
@@ -610,7 +616,12 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
610
616
  )
611
617
 
612
618
  def block_initialization(self) -> None:
613
- from apprise.plugins.discord import NotifyDiscord
619
+ try:
620
+ # Try importing for apprise>=1.18.0
621
+ from apprise.plugins.discord import NotifyDiscord
622
+ except ImportError:
623
+ # Fallback for versions apprise<1.18.0
624
+ from apprise.plugins.NotifyDiscord import NotifyDiscord
614
625
 
615
626
  url = SecretStr(
616
627
  NotifyDiscord(
@@ -745,7 +756,6 @@ class CustomWebhookNotificationBlock(NotificationBlock):
745
756
  raise KeyError(f"{name}/{placeholder}")
746
757
 
747
758
  @sync_compatible
748
- @instrument_instance_method_call
749
759
  async def notify(self, body: str, subject: Optional[str] = None):
750
760
  import httpx
751
761
 
@@ -801,7 +811,12 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
801
811
  )
802
812
 
803
813
  def block_initialization(self) -> None:
804
- from apprise.plugins.sendgrid import NotifySendGrid
814
+ try:
815
+ # Try importing for apprise>=1.18.0
816
+ from apprise.plugins.sendgrid import NotifySendGrid
817
+ except ImportError:
818
+ # Fallback for versions apprise<1.18.0
819
+ from apprise.plugins.NotifySendGrid import NotifySendGrid
805
820
 
806
821
  url = SecretStr(
807
822
  NotifySendGrid(
@@ -0,0 +1,168 @@
1
+ from contextlib import asynccontextmanager
2
+ from pathlib import Path
3
+ from typing import AsyncGenerator, Optional, Union
4
+
5
+ try:
6
+ import redis.asyncio as redis
7
+ except ImportError:
8
+ raise ImportError(
9
+ "`redis-py` must be installed to use the `RedisStorageContainer` block. "
10
+ "You can install it with `pip install redis>=5.0.1"
11
+ )
12
+
13
+ from pydantic import Field
14
+ from pydantic.types import SecretStr
15
+ from typing_extensions import Self
16
+
17
+ from prefect.filesystems import WritableFileSystem
18
+ from prefect.utilities.asyncutils import sync_compatible
19
+
20
+
21
+ class RedisStorageContainer(WritableFileSystem):
22
+ """
23
+ Block used to interact with Redis as a filesystem
24
+
25
+ Attributes:
26
+ host (str): The value to store.
27
+ port (int): The value to store.
28
+ db (int): The value to store.
29
+ username (str): The value to store.
30
+ password (str): The value to store.
31
+ connection_string (str): The value to store.
32
+
33
+ Example:
34
+ Create a new block from hostname, username and password:
35
+ ```python
36
+ from prefect.blocks.redis import RedisStorageContainer
37
+
38
+ block = RedisStorageContainer.from_host(
39
+ host="myredishost.com", username="redis", password="SuperSecret")
40
+ block.save("BLOCK_NAME")
41
+ ```
42
+
43
+ Create a new block from a connection string
44
+ ```python
45
+ from prefect.blocks.redis import RedisStorageContainer
46
+ block = RedisStorageContainer.from_url(""redis://redis:SuperSecret@myredishost.com:6379")
47
+ block.save("BLOCK_NAME")
48
+ ```
49
+ """
50
+
51
+ _logo_url = "https://stprododpcmscdnendpoint.azureedge.net/assets/icons/redis.png"
52
+
53
+ host: Optional[str] = Field(default=None, description="Redis hostname")
54
+ port: int = Field(default=6379, description="Redis port")
55
+ db: int = Field(default=0, description="Redis DB index")
56
+ username: Optional[SecretStr] = Field(default=None, description="Redis username")
57
+ password: Optional[SecretStr] = Field(default=None, description="Redis password")
58
+ connection_string: Optional[SecretStr] = Field(
59
+ default=None, description="Redis connection string"
60
+ )
61
+
62
+ def block_initialization(self) -> None:
63
+ if self.connection_string:
64
+ return
65
+ if not self.host:
66
+ raise ValueError("Initialization error: 'host' is required but missing.")
67
+ if self.username and not self.password:
68
+ raise ValueError(
69
+ "Initialization error: 'username' is provided, but 'password' is missing. Both are required."
70
+ )
71
+
72
+ @sync_compatible
73
+ async def read_path(self, path: Union[Path, str]):
74
+ """Read the redis content at `path`
75
+
76
+ Args:
77
+ path: Redis key to read from
78
+
79
+ Returns:
80
+ Contents at key as bytes
81
+ """
82
+ async with self._client() as client:
83
+ return await client.get(str(path))
84
+
85
+ @sync_compatible
86
+ async def write_path(self, path: Union[Path, str], content: bytes):
87
+ """Write `content` to the redis at `path`
88
+
89
+ Args:
90
+ path: Redis key to write to
91
+ content: Binary object to write
92
+ """
93
+
94
+ async with self._client() as client:
95
+ return await client.set(str(path), content)
96
+
97
+ @asynccontextmanager
98
+ async def _client(self) -> AsyncGenerator[redis.Redis, None]:
99
+ if self.connection_string:
100
+ client = redis.Redis.from_url(self.connection_string.get_secret_value())
101
+ else:
102
+ assert self.host
103
+ client = redis.Redis(
104
+ host=self.host,
105
+ port=self.port,
106
+ username=self.username.get_secret_value() if self.username else None,
107
+ password=self.password.get_secret_value() if self.password else None,
108
+ db=self.db,
109
+ )
110
+
111
+ try:
112
+ yield client
113
+ finally:
114
+ await client.aclose()
115
+
116
+ @classmethod
117
+ def from_host(
118
+ cls,
119
+ host: str,
120
+ port: int = 6379,
121
+ db: int = 0,
122
+ username: Union[None, str, SecretStr] = None,
123
+ password: Union[None, str, SecretStr] = None,
124
+ ) -> Self:
125
+ """Create block from hostname, username and password
126
+
127
+ Args:
128
+ host: Redis hostname
129
+ username: Redis username
130
+ password: Redis password
131
+ port: Redis port
132
+
133
+ Returns:
134
+ `RedisStorageContainer` instance
135
+ """
136
+
137
+ username = SecretStr(username) if isinstance(username, str) else username
138
+ password = SecretStr(password) if isinstance(password, str) else password
139
+
140
+ return cls(host=host, port=port, db=db, username=username, password=password)
141
+
142
+ @classmethod
143
+ def from_connection_string(cls, connection_string: Union[str, SecretStr]) -> Self:
144
+ """Create block from a Redis connection string
145
+
146
+ Supports the following URL schemes:
147
+ - `redis://` creates a TCP socket connection
148
+ - `rediss://` creates a SSL wrapped TCP socket connection
149
+ - `unix://` creates a Unix Domain Socket connection
150
+
151
+ See [Redis docs](https://redis.readthedocs.io/en/stable/examples
152
+ /connection_examples.html#Connecting-to-Redis-instances-by-specifying-a-URL
153
+ -scheme.) for more info.
154
+
155
+ Args:
156
+ connection_string: Redis connection string
157
+
158
+ Returns:
159
+ `RedisStorageContainer` instance
160
+ """
161
+
162
+ connection_string = (
163
+ SecretStr(connection_string)
164
+ if isinstance(connection_string, str)
165
+ else connection_string
166
+ )
167
+
168
+ return cls(connection_string=connection_string)
prefect/blocks/system.py CHANGED
@@ -1,19 +1,35 @@
1
- from typing import Any
2
-
3
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
1
+ import json
2
+ from typing import Annotated, Any, Generic, TypeVar, Union
3
+
4
+ from pydantic import (
5
+ Field,
6
+ JsonValue,
7
+ SecretStr,
8
+ StrictStr,
9
+ field_validator,
10
+ )
11
+ from pydantic import Secret as PydanticSecret
12
+ from pydantic_extra_types.pendulum_dt import DateTime as PydanticDateTime
13
+
14
+ from prefect._internal.compatibility.deprecated import deprecated_class
15
+ from prefect.blocks.core import Block
4
16
 
5
- if HAS_PYDANTIC_V2:
6
- from pydantic.v1 import Field, SecretStr
7
- else:
8
- from pydantic import Field, SecretStr
17
+ _SecretValueType = Union[
18
+ Annotated[StrictStr, Field(title="string")],
19
+ Annotated[JsonValue, Field(title="JSON")],
20
+ ]
9
21
 
10
- from prefect._internal.schemas.fields import DateTimeTZ
11
- from prefect.blocks.core import Block
22
+ T = TypeVar("T", bound=_SecretValueType)
12
23
 
13
24
 
25
+ @deprecated_class(
26
+ start_date="Jun 2024",
27
+ end_date="Jun 2025",
28
+ help="Use Variables to store json data instead.",
29
+ )
14
30
  class JSON(Block):
15
31
  """
16
- A block that represents JSON
32
+ A block that represents JSON. Deprecated, please use Variables to store JSON data instead.
17
33
 
18
34
  Attributes:
19
35
  value: A JSON-compatible value.
@@ -33,9 +49,14 @@ class JSON(Block):
33
49
  value: Any = Field(default=..., description="A JSON-compatible value.")
34
50
 
35
51
 
52
+ @deprecated_class(
53
+ start_date="Jun 2024",
54
+ end_date="Jun 2025",
55
+ help="Use Variables to store string data instead.",
56
+ )
36
57
  class String(Block):
37
58
  """
38
- A block that represents a string
59
+ A block that represents a string. Deprecated, please use Variables to store string data instead.
39
60
 
40
61
  Attributes:
41
62
  value: A string value.
@@ -55,9 +76,14 @@ class String(Block):
55
76
  value: str = Field(default=..., description="A string value.")
56
77
 
57
78
 
79
+ @deprecated_class(
80
+ start_date="Jun 2024",
81
+ end_date="Jun 2025",
82
+ help="Use Variables to store datetime data instead.",
83
+ )
58
84
  class DateTime(Block):
59
85
  """
60
- A block that represents a datetime
86
+ A block that represents a datetime. Deprecated, please use Variables to store datetime data instead.
61
87
 
62
88
  Attributes:
63
89
  value: An ISO 8601-compatible datetime value.
@@ -75,24 +101,26 @@ class DateTime(Block):
75
101
  _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/8b3da9a6621e92108b8e6a75b82e15374e170ff7-48x48.png"
76
102
  _documentation_url = "https://docs.prefect.io/api-ref/prefect/blocks/system/#prefect.blocks.system.DateTime"
77
103
 
78
- value: DateTimeTZ = Field(
104
+ value: PydanticDateTime = Field(
79
105
  default=...,
80
106
  description="An ISO 8601-compatible datetime value.",
81
107
  )
82
108
 
83
109
 
84
- class Secret(Block):
110
+ class Secret(Block, Generic[T]):
85
111
  """
86
112
  A block that represents a secret value. The value stored in this block will be obfuscated when
87
- this block is logged or shown in the UI.
113
+ this block is viewed or edited in the UI.
88
114
 
89
115
  Attributes:
90
- value: A string value that should be kept secret.
116
+ value: A value that should be kept secret.
91
117
 
92
118
  Example:
93
119
  ```python
94
120
  from prefect.blocks.system import Secret
95
121
 
122
+ Secret(value="sk-1234567890").save("BLOCK_NAME", overwrite=True)
123
+
96
124
  secret_block = Secret.load("BLOCK_NAME")
97
125
 
98
126
  # Access the stored secret
@@ -103,9 +131,28 @@ class Secret(Block):
103
131
  _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/c6f20e556dd16effda9df16551feecfb5822092b-48x48.png"
104
132
  _documentation_url = "https://docs.prefect.io/api-ref/prefect/blocks/system/#prefect.blocks.system.Secret"
105
133
 
106
- value: SecretStr = Field(
107
- default=..., description="A string value that should be kept secret."
134
+ value: Union[SecretStr, PydanticSecret[T]] = Field(
135
+ default=...,
136
+ description="A value that should be kept secret.",
137
+ examples=["sk-1234567890", {"username": "johndoe", "password": "s3cr3t"}],
138
+ json_schema_extra={
139
+ "writeOnly": True,
140
+ "format": "password",
141
+ },
108
142
  )
109
143
 
110
- def get(self):
111
- return self.value.get_secret_value()
144
+ @field_validator("value", mode="before")
145
+ def validate_value(
146
+ cls, value: Union[T, SecretStr, PydanticSecret[T]]
147
+ ) -> Union[SecretStr, PydanticSecret[T]]:
148
+ if isinstance(value, (PydanticSecret, SecretStr)):
149
+ return value
150
+ else:
151
+ return PydanticSecret[type(value)](value)
152
+
153
+ def get(self) -> T:
154
+ try:
155
+ value = self.value.get_secret_value()
156
+ return json.loads(value)
157
+ except (TypeError, json.JSONDecodeError):
158
+ return value
prefect/blocks/webhook.py CHANGED
@@ -1,18 +1,11 @@
1
1
  from typing import Optional
2
2
 
3
3
  from httpx import AsyncClient, AsyncHTTPTransport, Response
4
-
5
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
6
-
7
- if HAS_PYDANTIC_V2:
8
- from pydantic.v1 import Field, SecretStr
9
- else:
10
- from pydantic import Field, SecretStr
11
-
4
+ from pydantic import Field, SecretStr
12
5
  from typing_extensions import Literal
13
6
 
14
7
  from prefect.blocks.core import Block
15
- from prefect.blocks.fields import SecretDict
8
+ from prefect.types import SecretDict
16
9
 
17
10
  # Use a global HTTP transport to maintain a process-wide connection pool for
18
11
  # interservice requests