prefect-client 2.19.4__py3-none-any.whl → 3.0.0rc2__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 (242) hide show
  1. prefect/__init__.py +8 -56
  2. prefect/_internal/compatibility/deprecated.py +6 -115
  3. prefect/_internal/compatibility/experimental.py +4 -79
  4. prefect/_internal/concurrency/api.py +0 -34
  5. prefect/_internal/concurrency/calls.py +0 -6
  6. prefect/_internal/concurrency/cancellation.py +0 -3
  7. prefect/_internal/concurrency/event_loop.py +0 -20
  8. prefect/_internal/concurrency/inspection.py +3 -3
  9. prefect/_internal/concurrency/threads.py +35 -0
  10. prefect/_internal/concurrency/waiters.py +0 -28
  11. prefect/_internal/pydantic/__init__.py +0 -45
  12. prefect/_internal/pydantic/v1_schema.py +21 -22
  13. prefect/_internal/pydantic/v2_schema.py +0 -2
  14. prefect/_internal/pydantic/v2_validated_func.py +18 -23
  15. prefect/_internal/schemas/bases.py +44 -177
  16. prefect/_internal/schemas/fields.py +1 -43
  17. prefect/_internal/schemas/validators.py +60 -158
  18. prefect/artifacts.py +161 -14
  19. prefect/automations.py +39 -4
  20. prefect/blocks/abstract.py +1 -1
  21. prefect/blocks/core.py +268 -148
  22. prefect/blocks/fields.py +2 -57
  23. prefect/blocks/kubernetes.py +8 -12
  24. prefect/blocks/notifications.py +40 -20
  25. prefect/blocks/redis.py +168 -0
  26. prefect/blocks/system.py +22 -11
  27. prefect/blocks/webhook.py +2 -9
  28. prefect/client/base.py +4 -4
  29. prefect/client/cloud.py +8 -13
  30. prefect/client/orchestration.py +362 -340
  31. prefect/client/schemas/actions.py +92 -86
  32. prefect/client/schemas/filters.py +20 -40
  33. prefect/client/schemas/objects.py +158 -152
  34. prefect/client/schemas/responses.py +16 -24
  35. prefect/client/schemas/schedules.py +47 -35
  36. prefect/client/subscriptions.py +2 -2
  37. prefect/client/utilities.py +5 -2
  38. prefect/concurrency/asyncio.py +4 -2
  39. prefect/concurrency/events.py +1 -1
  40. prefect/concurrency/services.py +7 -4
  41. prefect/context.py +195 -27
  42. prefect/deployments/__init__.py +5 -6
  43. prefect/deployments/base.py +7 -5
  44. prefect/deployments/flow_runs.py +185 -0
  45. prefect/deployments/runner.py +50 -45
  46. prefect/deployments/schedules.py +28 -23
  47. prefect/deployments/steps/__init__.py +0 -1
  48. prefect/deployments/steps/core.py +1 -0
  49. prefect/deployments/steps/pull.py +7 -21
  50. prefect/engine.py +12 -2422
  51. prefect/events/actions.py +17 -23
  52. prefect/events/cli/automations.py +19 -6
  53. prefect/events/clients.py +14 -37
  54. prefect/events/filters.py +14 -18
  55. prefect/events/related.py +2 -2
  56. prefect/events/schemas/__init__.py +0 -5
  57. prefect/events/schemas/automations.py +55 -46
  58. prefect/events/schemas/deployment_triggers.py +7 -197
  59. prefect/events/schemas/events.py +36 -65
  60. prefect/events/schemas/labelling.py +10 -14
  61. prefect/events/utilities.py +2 -3
  62. prefect/events/worker.py +2 -3
  63. prefect/filesystems.py +6 -517
  64. prefect/{new_flow_engine.py → flow_engine.py} +315 -74
  65. prefect/flow_runs.py +379 -7
  66. prefect/flows.py +248 -165
  67. prefect/futures.py +187 -345
  68. prefect/infrastructure/__init__.py +0 -27
  69. prefect/infrastructure/provisioners/__init__.py +5 -3
  70. prefect/infrastructure/provisioners/cloud_run.py +11 -6
  71. prefect/infrastructure/provisioners/container_instance.py +11 -7
  72. prefect/infrastructure/provisioners/ecs.py +6 -4
  73. prefect/infrastructure/provisioners/modal.py +8 -5
  74. prefect/input/actions.py +2 -4
  75. prefect/input/run_input.py +9 -9
  76. prefect/logging/formatters.py +0 -2
  77. prefect/logging/handlers.py +3 -11
  78. prefect/logging/loggers.py +2 -2
  79. prefect/manifests.py +2 -1
  80. prefect/records/__init__.py +1 -0
  81. prefect/records/cache_policies.py +179 -0
  82. prefect/records/result_store.py +42 -0
  83. prefect/records/store.py +9 -0
  84. prefect/results.py +43 -39
  85. prefect/runner/runner.py +9 -9
  86. prefect/runner/server.py +6 -10
  87. prefect/runner/storage.py +3 -8
  88. prefect/runner/submit.py +2 -2
  89. prefect/runner/utils.py +2 -2
  90. prefect/serializers.py +24 -35
  91. prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
  92. prefect/settings.py +76 -136
  93. prefect/states.py +22 -50
  94. prefect/task_engine.py +666 -56
  95. prefect/task_runners.py +272 -300
  96. prefect/task_runs.py +203 -0
  97. prefect/{task_server.py → task_worker.py} +89 -60
  98. prefect/tasks.py +358 -341
  99. prefect/transactions.py +224 -0
  100. prefect/types/__init__.py +61 -82
  101. prefect/utilities/asyncutils.py +195 -136
  102. prefect/utilities/callables.py +121 -41
  103. prefect/utilities/collections.py +23 -38
  104. prefect/utilities/dispatch.py +11 -3
  105. prefect/utilities/dockerutils.py +4 -0
  106. prefect/utilities/engine.py +140 -20
  107. prefect/utilities/importtools.py +26 -27
  108. prefect/utilities/pydantic.py +128 -38
  109. prefect/utilities/schema_tools/hydration.py +5 -1
  110. prefect/utilities/templating.py +12 -2
  111. prefect/variables.py +84 -62
  112. prefect/workers/__init__.py +0 -1
  113. prefect/workers/base.py +26 -18
  114. prefect/workers/process.py +3 -8
  115. prefect/workers/server.py +2 -2
  116. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/METADATA +23 -21
  117. prefect_client-3.0.0rc2.dist-info/RECORD +179 -0
  118. prefect/_internal/pydantic/_base_model.py +0 -51
  119. prefect/_internal/pydantic/_compat.py +0 -82
  120. prefect/_internal/pydantic/_flags.py +0 -20
  121. prefect/_internal/pydantic/_types.py +0 -8
  122. prefect/_internal/pydantic/utilities/__init__.py +0 -0
  123. prefect/_internal/pydantic/utilities/config_dict.py +0 -72
  124. prefect/_internal/pydantic/utilities/field_validator.py +0 -150
  125. prefect/_internal/pydantic/utilities/model_construct.py +0 -56
  126. prefect/_internal/pydantic/utilities/model_copy.py +0 -55
  127. prefect/_internal/pydantic/utilities/model_dump.py +0 -136
  128. prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
  129. prefect/_internal/pydantic/utilities/model_fields.py +0 -50
  130. prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
  131. prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
  132. prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
  133. prefect/_internal/pydantic/utilities/model_validate.py +0 -75
  134. prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
  135. prefect/_internal/pydantic/utilities/model_validator.py +0 -87
  136. prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
  137. prefect/_vendor/__init__.py +0 -0
  138. prefect/_vendor/fastapi/__init__.py +0 -25
  139. prefect/_vendor/fastapi/applications.py +0 -946
  140. prefect/_vendor/fastapi/background.py +0 -3
  141. prefect/_vendor/fastapi/concurrency.py +0 -44
  142. prefect/_vendor/fastapi/datastructures.py +0 -58
  143. prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
  144. prefect/_vendor/fastapi/dependencies/models.py +0 -64
  145. prefect/_vendor/fastapi/dependencies/utils.py +0 -877
  146. prefect/_vendor/fastapi/encoders.py +0 -177
  147. prefect/_vendor/fastapi/exception_handlers.py +0 -40
  148. prefect/_vendor/fastapi/exceptions.py +0 -46
  149. prefect/_vendor/fastapi/logger.py +0 -3
  150. prefect/_vendor/fastapi/middleware/__init__.py +0 -1
  151. prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
  152. prefect/_vendor/fastapi/middleware/cors.py +0 -3
  153. prefect/_vendor/fastapi/middleware/gzip.py +0 -3
  154. prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
  155. prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
  156. prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
  157. prefect/_vendor/fastapi/openapi/__init__.py +0 -0
  158. prefect/_vendor/fastapi/openapi/constants.py +0 -2
  159. prefect/_vendor/fastapi/openapi/docs.py +0 -203
  160. prefect/_vendor/fastapi/openapi/models.py +0 -480
  161. prefect/_vendor/fastapi/openapi/utils.py +0 -485
  162. prefect/_vendor/fastapi/param_functions.py +0 -340
  163. prefect/_vendor/fastapi/params.py +0 -453
  164. prefect/_vendor/fastapi/requests.py +0 -4
  165. prefect/_vendor/fastapi/responses.py +0 -40
  166. prefect/_vendor/fastapi/routing.py +0 -1331
  167. prefect/_vendor/fastapi/security/__init__.py +0 -15
  168. prefect/_vendor/fastapi/security/api_key.py +0 -98
  169. prefect/_vendor/fastapi/security/base.py +0 -6
  170. prefect/_vendor/fastapi/security/http.py +0 -172
  171. prefect/_vendor/fastapi/security/oauth2.py +0 -227
  172. prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
  173. prefect/_vendor/fastapi/security/utils.py +0 -10
  174. prefect/_vendor/fastapi/staticfiles.py +0 -1
  175. prefect/_vendor/fastapi/templating.py +0 -3
  176. prefect/_vendor/fastapi/testclient.py +0 -1
  177. prefect/_vendor/fastapi/types.py +0 -3
  178. prefect/_vendor/fastapi/utils.py +0 -235
  179. prefect/_vendor/fastapi/websockets.py +0 -7
  180. prefect/_vendor/starlette/__init__.py +0 -1
  181. prefect/_vendor/starlette/_compat.py +0 -28
  182. prefect/_vendor/starlette/_exception_handler.py +0 -80
  183. prefect/_vendor/starlette/_utils.py +0 -88
  184. prefect/_vendor/starlette/applications.py +0 -261
  185. prefect/_vendor/starlette/authentication.py +0 -159
  186. prefect/_vendor/starlette/background.py +0 -43
  187. prefect/_vendor/starlette/concurrency.py +0 -59
  188. prefect/_vendor/starlette/config.py +0 -151
  189. prefect/_vendor/starlette/convertors.py +0 -87
  190. prefect/_vendor/starlette/datastructures.py +0 -707
  191. prefect/_vendor/starlette/endpoints.py +0 -130
  192. prefect/_vendor/starlette/exceptions.py +0 -60
  193. prefect/_vendor/starlette/formparsers.py +0 -276
  194. prefect/_vendor/starlette/middleware/__init__.py +0 -17
  195. prefect/_vendor/starlette/middleware/authentication.py +0 -52
  196. prefect/_vendor/starlette/middleware/base.py +0 -220
  197. prefect/_vendor/starlette/middleware/cors.py +0 -176
  198. prefect/_vendor/starlette/middleware/errors.py +0 -265
  199. prefect/_vendor/starlette/middleware/exceptions.py +0 -74
  200. prefect/_vendor/starlette/middleware/gzip.py +0 -113
  201. prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
  202. prefect/_vendor/starlette/middleware/sessions.py +0 -82
  203. prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
  204. prefect/_vendor/starlette/middleware/wsgi.py +0 -147
  205. prefect/_vendor/starlette/requests.py +0 -328
  206. prefect/_vendor/starlette/responses.py +0 -347
  207. prefect/_vendor/starlette/routing.py +0 -933
  208. prefect/_vendor/starlette/schemas.py +0 -154
  209. prefect/_vendor/starlette/staticfiles.py +0 -248
  210. prefect/_vendor/starlette/status.py +0 -199
  211. prefect/_vendor/starlette/templating.py +0 -231
  212. prefect/_vendor/starlette/testclient.py +0 -804
  213. prefect/_vendor/starlette/types.py +0 -30
  214. prefect/_vendor/starlette/websockets.py +0 -193
  215. prefect/agent.py +0 -698
  216. prefect/deployments/deployments.py +0 -1042
  217. prefect/deprecated/__init__.py +0 -0
  218. prefect/deprecated/data_documents.py +0 -350
  219. prefect/deprecated/packaging/__init__.py +0 -12
  220. prefect/deprecated/packaging/base.py +0 -96
  221. prefect/deprecated/packaging/docker.py +0 -146
  222. prefect/deprecated/packaging/file.py +0 -92
  223. prefect/deprecated/packaging/orion.py +0 -80
  224. prefect/deprecated/packaging/serializers.py +0 -171
  225. prefect/events/instrument.py +0 -135
  226. prefect/infrastructure/base.py +0 -323
  227. prefect/infrastructure/container.py +0 -818
  228. prefect/infrastructure/kubernetes.py +0 -920
  229. prefect/infrastructure/process.py +0 -289
  230. prefect/new_task_engine.py +0 -423
  231. prefect/pydantic/__init__.py +0 -76
  232. prefect/pydantic/main.py +0 -39
  233. prefect/software/__init__.py +0 -2
  234. prefect/software/base.py +0 -50
  235. prefect/software/conda.py +0 -199
  236. prefect/software/pip.py +0 -122
  237. prefect/software/python.py +0 -52
  238. prefect/workers/block.py +0 -218
  239. prefect_client-2.19.4.dist-info/RECORD +0 -292
  240. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/LICENSE +0 -0
  241. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.dist-info}/WHEEL +0 -0
  242. {prefect_client-2.19.4.dist-info → prefect_client-3.0.0rc2.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"]
@@ -1,19 +1,12 @@
1
1
  from pathlib import Path
2
- from typing import TYPE_CHECKING, Dict, Type
2
+ from typing import TYPE_CHECKING, Dict, Optional, Type
3
3
 
4
4
  import yaml
5
+ from pydantic import Field, field_validator
6
+ from typing_extensions import Self
5
7
 
6
8
  from prefect._internal.compatibility.deprecated import deprecated_class
7
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
9
  from prefect._internal.schemas.validators import validate_yaml
9
-
10
- if HAS_PYDANTIC_V2:
11
- from pydantic.v1 import Field, validator
12
- else:
13
- from pydantic import Field, validator
14
-
15
- from typing_extensions import Self
16
-
17
10
  from prefect.blocks.core import Block
18
11
  from prefect.utilities.collections import listrepr
19
12
  from prefect.utilities.importtools import lazy_import
@@ -59,12 +52,15 @@ class KubernetesClusterConfig(Block):
59
52
  default=..., description="The name of the kubectl context to use."
60
53
  )
61
54
 
62
- @validator("config", pre=True)
55
+ @field_validator("config", mode="before")
56
+ @classmethod
63
57
  def parse_yaml_config(cls, value):
64
58
  return validate_yaml(value)
65
59
 
66
60
  @classmethod
67
- def from_file(cls: Type[Self], path: Path = None, context_name: str = None) -> Self:
61
+ def from_file(
62
+ cls: Type[Self], path: Optional[Path] = None, context_name: Optional[str] = None
63
+ ) -> Self:
68
64
  """
69
65
  Create a cluster config from the a Kubernetes config file.
70
66
 
@@ -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,
@@ -235,7 +227,12 @@ class PagerDutyWebHook(AbstractAppriseNotificationBlock):
235
227
  )
236
228
 
237
229
  def block_initialization(self) -> None:
238
- from apprise.plugins.pagerduty import NotifyPagerDuty
230
+ try:
231
+ # Try importing for apprise>=1.18.0
232
+ from apprise.plugins.pagerduty import NotifyPagerDuty
233
+ except ImportError:
234
+ # Fallback for versions apprise<1.18.0
235
+ from apprise.plugins.NotifyPagerDuty import NotifyPagerDuty
239
236
 
240
237
  url = SecretStr(
241
238
  NotifyPagerDuty(
@@ -303,8 +300,12 @@ class TwilioSMS(AbstractAppriseNotificationBlock):
303
300
  )
304
301
 
305
302
  def block_initialization(self) -> None:
306
- from apprise.plugins.twilio import NotifyTwilio
307
-
303
+ try:
304
+ # Try importing for apprise>=1.18.0
305
+ from apprise.plugins.twilio import NotifyTwilio
306
+ except ImportError:
307
+ # Fallback for versions apprise<1.18.0
308
+ from apprise.plugins.NotifyTwilio import NotifyTwilio
308
309
  url = SecretStr(
309
310
  NotifyTwilio(
310
311
  account_sid=self.account_sid,
@@ -378,7 +379,7 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
378
379
  examples=['["tag1", "tag2"]'],
379
380
  )
380
381
 
381
- priority: Optional[str] = Field(
382
+ priority: Optional[int] = Field(
382
383
  default=3,
383
384
  description=(
384
385
  "The priority to associate with the message. It is on a scale between 1"
@@ -401,7 +402,12 @@ class OpsgenieWebhook(AbstractAppriseNotificationBlock):
401
402
  )
402
403
 
403
404
  def block_initialization(self) -> None:
404
- from apprise.plugins.opsgenie import NotifyOpsgenie
405
+ try:
406
+ # Try importing for apprise>=1.18.0
407
+ from apprise.plugins.opsgenie import NotifyOpsgenie
408
+ except ImportError:
409
+ # Fallback for versions apprise<1.18.0
410
+ from apprise.plugins.NotifyOpsgenie import NotifyOpsgenie
405
411
 
406
412
  targets = []
407
413
  if self.target_user:
@@ -489,7 +495,12 @@ class MattermostWebhook(AbstractAppriseNotificationBlock):
489
495
  )
490
496
 
491
497
  def block_initialization(self) -> None:
492
- from apprise.plugins.mattermost import NotifyMattermost
498
+ try:
499
+ # Try importing for apprise>=1.18.0
500
+ from apprise.plugins.mattermost import NotifyMattermost
501
+ except ImportError:
502
+ # Fallback for versions apprise<1.18.0
503
+ from apprise.plugins.NotifyMattermost import NotifyMattermost
493
504
 
494
505
  url = SecretStr(
495
506
  NotifyMattermost(
@@ -582,7 +593,12 @@ class DiscordWebhook(AbstractAppriseNotificationBlock):
582
593
  )
583
594
 
584
595
  def block_initialization(self) -> None:
585
- from apprise.plugins.discord import NotifyDiscord
596
+ try:
597
+ # Try importing for apprise>=1.18.0
598
+ from apprise.plugins.discord import NotifyDiscord
599
+ except ImportError:
600
+ # Fallback for versions apprise<1.18.0
601
+ from apprise.plugins.NotifyDiscord import NotifyDiscord
586
602
 
587
603
  url = SecretStr(
588
604
  NotifyDiscord(
@@ -717,7 +733,6 @@ class CustomWebhookNotificationBlock(NotificationBlock):
717
733
  raise KeyError(f"{name}/{placeholder}")
718
734
 
719
735
  @sync_compatible
720
- @instrument_instance_method_call
721
736
  async def notify(self, body: str, subject: Optional[str] = None):
722
737
  import httpx
723
738
 
@@ -773,7 +788,12 @@ class SendgridEmail(AbstractAppriseNotificationBlock):
773
788
  )
774
789
 
775
790
  def block_initialization(self) -> None:
776
- from apprise.plugins.sendgrid import NotifySendGrid
791
+ try:
792
+ # Try importing for apprise>=1.18.0
793
+ from apprise.plugins.sendgrid import NotifySendGrid
794
+ except ImportError:
795
+ # Fallback for versions apprise<1.18.0
796
+ from apprise.plugins.NotifySendGrid import NotifySendGrid
777
797
 
778
798
  url = SecretStr(
779
799
  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,20 @@
1
1
  from typing import Any
2
2
 
3
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
3
+ from pydantic import Field, SecretStr
4
+ from pydantic_extra_types.pendulum_dt import DateTime
4
5
 
5
- if HAS_PYDANTIC_V2:
6
- from pydantic.v1 import Field, SecretStr
7
- else:
8
- from pydantic import Field, SecretStr
9
-
10
- from prefect._internal.schemas.fields import DateTimeTZ
6
+ from prefect._internal.compatibility.deprecated import deprecated_class
11
7
  from prefect.blocks.core import Block
12
8
 
13
9
 
10
+ @deprecated_class(
11
+ start_date="Jun 2024",
12
+ end_date="Jun 2025",
13
+ help="Use Variables to store json data instead.",
14
+ )
14
15
  class JSON(Block):
15
16
  """
16
- A block that represents JSON
17
+ A block that represents JSON. Deprecated, please use Variables to store JSON data instead.
17
18
 
18
19
  Attributes:
19
20
  value: A JSON-compatible value.
@@ -33,9 +34,14 @@ class JSON(Block):
33
34
  value: Any = Field(default=..., description="A JSON-compatible value.")
34
35
 
35
36
 
37
+ @deprecated_class(
38
+ start_date="Jun 2024",
39
+ end_date="Jun 2025",
40
+ help="Use Variables to store string data instead.",
41
+ )
36
42
  class String(Block):
37
43
  """
38
- A block that represents a string
44
+ A block that represents a string. Deprecated, please use Variables to store string data instead.
39
45
 
40
46
  Attributes:
41
47
  value: A string value.
@@ -55,9 +61,14 @@ class String(Block):
55
61
  value: str = Field(default=..., description="A string value.")
56
62
 
57
63
 
64
+ @deprecated_class(
65
+ start_date="Jun 2024",
66
+ end_date="Jun 2025",
67
+ help="Use Variables to store datetime data instead.",
68
+ )
58
69
  class DateTime(Block):
59
70
  """
60
- A block that represents a datetime
71
+ A block that represents a datetime. Deprecated, please use Variables to store datetime data instead.
61
72
 
62
73
  Attributes:
63
74
  value: An ISO 8601-compatible datetime value.
@@ -75,7 +86,7 @@ class DateTime(Block):
75
86
  _logo_url = "https://cdn.sanity.io/images/3ugk85nk/production/8b3da9a6621e92108b8e6a75b82e15374e170ff7-48x48.png"
76
87
  _documentation_url = "https://docs.prefect.io/api-ref/prefect/blocks/system/#prefect.blocks.system.DateTime"
77
88
 
78
- value: DateTimeTZ = Field(
89
+ value: DateTime = Field(
79
90
  default=...,
80
91
  description="An ISO 8601-compatible datetime value.",
81
92
  )
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
prefect/client/base.py CHANGED
@@ -25,8 +25,8 @@ import anyio
25
25
  import httpx
26
26
  from asgi_lifespan import LifespanManager
27
27
  from httpx import HTTPStatusError, Request, Response
28
- from prefect._vendor.starlette import status
29
- from prefect._vendor.starlette.testclient import TestClient
28
+ from starlette import status
29
+ from starlette.testclient import TestClient
30
30
  from typing_extensions import Self
31
31
 
32
32
  import prefect
@@ -390,7 +390,7 @@ class PrefectHttpxAsyncClient(httpx.AsyncClient):
390
390
 
391
391
  raise
392
392
 
393
- token: CsrfToken = CsrfToken.parse_obj(token_response.json())
393
+ token: CsrfToken = CsrfToken.model_validate(token_response.json())
394
394
  self.csrf_token = token.token
395
395
  self.csrf_token_expiration = token.expiration
396
396
 
@@ -604,7 +604,7 @@ class PrefectHttpxSyncClient(httpx.Client):
604
604
 
605
605
  raise
606
606
 
607
- token: CsrfToken = CsrfToken.parse_obj(token_response.json())
607
+ token: CsrfToken = CsrfToken.model_validate(token_response.json())
608
608
  self.csrf_token = token.token
609
609
  self.csrf_token_expiration = token.expiration
610
610
 
prefect/client/cloud.py CHANGED
@@ -3,21 +3,14 @@ from typing import Any, Dict, List, Optional
3
3
 
4
4
  import anyio
5
5
  import httpx
6
-
7
- from prefect._internal.pydantic import HAS_PYDANTIC_V2
8
-
9
- if HAS_PYDANTIC_V2:
10
- import pydantic.v1 as pydantic
11
- else:
12
- import pydantic
13
-
14
- from prefect._vendor.starlette import status
6
+ import pydantic
7
+ from starlette import status
15
8
 
16
9
  import prefect.context
17
10
  import prefect.settings
18
11
  from prefect.client.base import PrefectHttpxAsyncClient
19
12
  from prefect.client.schemas import Workspace
20
- from prefect.exceptions import PrefectException
13
+ from prefect.exceptions import ObjectNotFound, PrefectException
21
14
  from prefect.settings import (
22
15
  PREFECT_API_KEY,
23
16
  PREFECT_CLOUD_API_URL,
@@ -87,8 +80,8 @@ class CloudClient:
87
80
  await self.read_workspaces()
88
81
 
89
82
  async def read_workspaces(self) -> List[Workspace]:
90
- workspaces = pydantic.parse_obj_as(
91
- List[Workspace], await self.get("/me/workspaces")
83
+ workspaces = pydantic.TypeAdapter(List[Workspace]).validate_python(
84
+ await self.get("/me/workspaces")
92
85
  )
93
86
  return workspaces
94
87
 
@@ -128,8 +121,10 @@ class CloudClient:
128
121
  status.HTTP_403_FORBIDDEN,
129
122
  ):
130
123
  raise CloudUnauthorizedError
124
+ elif exc.response.status_code == status.HTTP_404_NOT_FOUND:
125
+ raise ObjectNotFound(http_exc=exc) from exc
131
126
  else:
132
- raise exc
127
+ raise
133
128
 
134
129
  if res.status_code == status.HTTP_204_NO_CONTENT:
135
130
  return