prefect-client 2.20.2__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 +423 -164
  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 +667 -440
  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 -2466
  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 +124 -51
  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 +138 -48
  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.2.dist-info → prefect_client-3.0.0.dist-info}/METADATA +30 -26
  161. prefect_client-3.0.0.dist-info/RECORD +201 -0
  162. {prefect_client-2.20.2.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.2.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.2.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
  288. {prefect_client-2.20.2.dist-info → prefect_client-3.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,239 @@
1
+ import inspect
2
+ from dataclasses import dataclass
3
+ from typing import Any, Callable, Dict, Optional
4
+
5
+ from prefect.context import TaskRunContext
6
+ from prefect.utilities.hashing import hash_objects
7
+
8
+
9
+ @dataclass
10
+ class CachePolicy:
11
+ """
12
+ Base class for all cache policies.
13
+ """
14
+
15
+ @classmethod
16
+ def from_cache_key_fn(
17
+ cls, cache_key_fn: Callable[["TaskRunContext", Dict[str, Any]], Optional[str]]
18
+ ) -> "CacheKeyFnPolicy":
19
+ """
20
+ Given a function generates a key policy.
21
+ """
22
+ return CacheKeyFnPolicy(cache_key_fn=cache_key_fn)
23
+
24
+ def compute_key(
25
+ self,
26
+ task_ctx: TaskRunContext,
27
+ inputs: Dict[str, Any],
28
+ flow_parameters: Dict[str, Any],
29
+ **kwargs,
30
+ ) -> Optional[str]:
31
+ raise NotImplementedError
32
+
33
+ def __sub__(self, other: str) -> "CompoundCachePolicy":
34
+ if not isinstance(other, str):
35
+ raise TypeError("Can only subtract strings from key policies.")
36
+ if isinstance(self, Inputs):
37
+ exclude = self.exclude or []
38
+ return Inputs(exclude=exclude + [other])
39
+ elif isinstance(self, CompoundCachePolicy):
40
+ new = Inputs(exclude=[other])
41
+ policies = self.policies or []
42
+ return CompoundCachePolicy(policies=policies + [new])
43
+ else:
44
+ new = Inputs(exclude=[other])
45
+ return CompoundCachePolicy(policies=[self, new])
46
+
47
+ def __add__(self, other: "CachePolicy") -> "CompoundCachePolicy":
48
+ # adding _None is a no-op
49
+ if isinstance(other, _None):
50
+ return self
51
+ elif isinstance(self, _None):
52
+ return other
53
+
54
+ if isinstance(self, CompoundCachePolicy):
55
+ policies = self.policies or []
56
+ return CompoundCachePolicy(policies=policies + [other])
57
+ elif isinstance(other, CompoundCachePolicy):
58
+ policies = other.policies or []
59
+ return CompoundCachePolicy(policies=policies + [self])
60
+ else:
61
+ return CompoundCachePolicy(policies=[self, other])
62
+
63
+
64
+ @dataclass
65
+ class CacheKeyFnPolicy(CachePolicy):
66
+ """
67
+ This policy accepts a custom function with signature f(task_run_context, task_parameters, flow_parameters) -> str
68
+ and uses it to compute a task run cache key.
69
+ """
70
+
71
+ # making it optional for tests
72
+ cache_key_fn: Optional[
73
+ Callable[["TaskRunContext", Dict[str, Any]], Optional[str]]
74
+ ] = None
75
+
76
+ def compute_key(
77
+ self,
78
+ task_ctx: TaskRunContext,
79
+ inputs: Dict[str, Any],
80
+ flow_parameters: Dict[str, Any],
81
+ **kwargs,
82
+ ) -> Optional[str]:
83
+ if self.cache_key_fn:
84
+ return self.cache_key_fn(task_ctx, inputs)
85
+
86
+
87
+ @dataclass
88
+ class CompoundCachePolicy(CachePolicy):
89
+ """
90
+ This policy is constructed from two or more other cache policies and works by computing the keys
91
+ for each policy individually, and then hashing a sorted tuple of all computed keys.
92
+
93
+ Any keys that return `None` will be ignored.
94
+ """
95
+
96
+ policies: Optional[list] = None
97
+
98
+ def compute_key(
99
+ self,
100
+ task_ctx: TaskRunContext,
101
+ inputs: Dict[str, Any],
102
+ flow_parameters: Dict[str, Any],
103
+ **kwargs,
104
+ ) -> Optional[str]:
105
+ keys = []
106
+ for policy in self.policies or []:
107
+ policy_key = policy.compute_key(
108
+ task_ctx=task_ctx,
109
+ inputs=inputs,
110
+ flow_parameters=flow_parameters,
111
+ **kwargs,
112
+ )
113
+ if policy_key is not None:
114
+ keys.append(policy_key)
115
+ if not keys:
116
+ return None
117
+ return hash_objects(*keys)
118
+
119
+
120
+ @dataclass
121
+ class _None(CachePolicy):
122
+ """
123
+ Policy that always returns `None` for the computed cache key.
124
+ This policy prevents persistence.
125
+ """
126
+
127
+ def compute_key(
128
+ self,
129
+ task_ctx: TaskRunContext,
130
+ inputs: Dict[str, Any],
131
+ flow_parameters: Dict[str, Any],
132
+ **kwargs,
133
+ ) -> Optional[str]:
134
+ return None
135
+
136
+
137
+ @dataclass
138
+ class TaskSource(CachePolicy):
139
+ """
140
+ Policy for computing a cache key based on the source code of the task.
141
+ """
142
+
143
+ def compute_key(
144
+ self,
145
+ task_ctx: TaskRunContext,
146
+ inputs: Optional[Dict[str, Any]],
147
+ flow_parameters: Optional[Dict[str, Any]],
148
+ **kwargs,
149
+ ) -> Optional[str]:
150
+ if not task_ctx:
151
+ return None
152
+ try:
153
+ lines = inspect.getsource(task_ctx.task)
154
+ except TypeError:
155
+ lines = inspect.getsource(task_ctx.task.fn.__class__)
156
+ except OSError as exc:
157
+ if "could not get source code" in str(exc):
158
+ lines = task_ctx.task.fn.__code__.co_code
159
+ else:
160
+ raise
161
+
162
+ return hash_objects(lines)
163
+
164
+
165
+ @dataclass
166
+ class FlowParameters(CachePolicy):
167
+ """
168
+ Policy that computes the cache key based on a hash of the flow parameters.
169
+ """
170
+
171
+ def compute_key(
172
+ self,
173
+ task_ctx: TaskRunContext,
174
+ inputs: Dict[str, Any],
175
+ flow_parameters: Dict[str, Any],
176
+ **kwargs,
177
+ ) -> Optional[str]:
178
+ if not flow_parameters:
179
+ return None
180
+ return hash_objects(flow_parameters)
181
+
182
+
183
+ @dataclass
184
+ class RunId(CachePolicy):
185
+ """
186
+ Returns either the prevailing flow run ID, or if not found, the prevailing task
187
+ run ID.
188
+ """
189
+
190
+ def compute_key(
191
+ self,
192
+ task_ctx: TaskRunContext,
193
+ inputs: Dict[str, Any],
194
+ flow_parameters: Dict[str, Any],
195
+ **kwargs,
196
+ ) -> Optional[str]:
197
+ if not task_ctx:
198
+ return None
199
+ run_id = task_ctx.task_run.flow_run_id
200
+ if run_id is None:
201
+ run_id = task_ctx.task_run.id
202
+ return str(run_id)
203
+
204
+
205
+ @dataclass
206
+ class Inputs(CachePolicy):
207
+ """
208
+ Policy that computes a cache key based on a hash of the runtime inputs provided to the task..
209
+ """
210
+
211
+ exclude: Optional[list] = None
212
+
213
+ def compute_key(
214
+ self,
215
+ task_ctx: TaskRunContext,
216
+ inputs: Dict[str, Any],
217
+ flow_parameters: Dict[str, Any],
218
+ **kwargs,
219
+ ) -> Optional[str]:
220
+ hashed_inputs = {}
221
+ inputs = inputs or {}
222
+ exclude = self.exclude or []
223
+
224
+ if not inputs:
225
+ return None
226
+
227
+ for key, val in inputs.items():
228
+ if key not in exclude:
229
+ hashed_inputs[key] = val
230
+
231
+ return hash_objects(hashed_inputs)
232
+
233
+
234
+ INPUTS = Inputs()
235
+ NONE = _None()
236
+ TASK_SOURCE = TaskSource()
237
+ FLOW_PARAMETERS = FlowParameters()
238
+ RUN_ID = RunId()
239
+ DEFAULT = INPUTS + TASK_SOURCE + RUN_ID
@@ -15,3 +15,7 @@ $ python -m asyncio
15
15
  ```
16
16
  </div>
17
17
  """
18
+
19
+ from prefect._internal.compatibility.migration import getattr_migration
20
+
21
+ __getattr__ = getattr_migration(__name__)
prefect/client/base.py CHANGED
@@ -25,8 +25,7 @@ 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
30
29
  from typing_extensions import Self
31
30
 
32
31
  import prefect
@@ -35,10 +34,14 @@ from prefect.client.schemas.objects import CsrfToken
35
34
  from prefect.exceptions import PrefectHTTPStatusError
36
35
  from prefect.logging import get_logger
37
36
  from prefect.settings import (
37
+ PREFECT_API_URL,
38
38
  PREFECT_CLIENT_MAX_RETRIES,
39
39
  PREFECT_CLIENT_RETRY_EXTRA_CODES,
40
40
  PREFECT_CLIENT_RETRY_JITTER_FACTOR,
41
+ PREFECT_CLOUD_API_URL,
42
+ PREFECT_SERVER_ALLOW_EPHEMERAL_MODE,
41
43
  )
44
+ from prefect.utilities.collections import AutoEnum
42
45
  from prefect.utilities.math import bounded_poisson_interval, clamped_poisson_interval
43
46
 
44
47
  # Datastores for lifespan management, keys should be a tuple of thread and app
@@ -390,7 +393,7 @@ class PrefectHttpxAsyncClient(httpx.AsyncClient):
390
393
 
391
394
  raise
392
395
 
393
- token: CsrfToken = CsrfToken.parse_obj(token_response.json())
396
+ token: CsrfToken = CsrfToken.model_validate(token_response.json())
394
397
  self.csrf_token = token.token
395
398
  self.csrf_token_expiration = token.expiration
396
399
 
@@ -604,7 +607,7 @@ class PrefectHttpxSyncClient(httpx.Client):
604
607
 
605
608
  raise
606
609
 
607
- token: CsrfToken = CsrfToken.parse_obj(token_response.json())
610
+ token: CsrfToken = CsrfToken.model_validate(token_response.json())
608
611
  self.csrf_token = token.token
609
612
  self.csrf_token_expiration = token.expiration
610
613
 
@@ -612,28 +615,31 @@ class PrefectHttpxSyncClient(httpx.Client):
612
615
  request.headers["Prefect-Csrf-Client"] = str(self.csrf_client_id)
613
616
 
614
617
 
615
- class PrefectHttpxSyncEphemeralClient(TestClient, PrefectHttpxSyncClient):
616
- """
617
- This client is a synchronous httpx client that can be used to talk directly
618
- to an ASGI app, such as an ephemeral Prefect API.
619
-
620
- It is a subclass of both Starlette's `TestClient` and Prefect's
621
- `PrefectHttpxSyncClient`, so it combines the synchronous testing
622
- capabilities of `TestClient` with the Prefect-specific behaviors of
623
- `PrefectHttpxSyncClient`.
624
- """
618
+ class ServerType(AutoEnum):
619
+ EPHEMERAL = AutoEnum.auto()
620
+ SERVER = AutoEnum.auto()
621
+ CLOUD = AutoEnum.auto()
622
+ UNCONFIGURED = AutoEnum.auto()
625
623
 
626
- def __init__(
627
- self,
628
- *args,
629
- # override TestClient default
630
- raise_server_exceptions=False,
631
- **kwargs,
632
- ):
633
- super().__init__(
634
- *args,
635
- raise_server_exceptions=raise_server_exceptions,
636
- **kwargs,
637
- )
638
624
 
639
- pass
625
+ def determine_server_type() -> ServerType:
626
+ """
627
+ Determine the server type based on the current settings.
628
+
629
+ Returns:
630
+ - `ServerType.EPHEMERAL` if the ephemeral server is enabled
631
+ - `ServerType.SERVER` if a API URL is configured and it is not a cloud URL
632
+ - `ServerType.CLOUD` if an API URL is configured and it is a cloud URL
633
+ - `ServerType.UNCONFIGURED` if no API URL is configured and ephemeral mode is
634
+ not enabled
635
+ """
636
+ api_url = PREFECT_API_URL.value()
637
+ if api_url is None:
638
+ if PREFECT_SERVER_ALLOW_EPHEMERAL_MODE.value():
639
+ return ServerType.EPHEMERAL
640
+ else:
641
+ return ServerType.UNCONFIGURED
642
+ if api_url.startswith(PREFECT_CLOUD_API_URL.value()):
643
+ return ServerType.CLOUD
644
+ else:
645
+ return ServerType.SERVER
prefect/client/cloud.py CHANGED
@@ -1,23 +1,20 @@
1
1
  import re
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, cast
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
- from prefect.client.schemas import Workspace
20
- from prefect.exceptions import PrefectException
12
+ from prefect.client.schemas.objects import (
13
+ IPAllowlist,
14
+ IPAllowlistMyAccessResponse,
15
+ Workspace,
16
+ )
17
+ from prefect.exceptions import ObjectNotFound, PrefectException
21
18
  from prefect.settings import (
22
19
  PREFECT_API_KEY,
23
20
  PREFECT_CLOUD_API_URL,
@@ -76,6 +73,27 @@ class CloudClient:
76
73
  **httpx_settings, enable_csrf_support=False
77
74
  )
78
75
 
76
+ api_url = prefect.settings.PREFECT_API_URL.value() or ""
77
+ if match := (
78
+ re.search(PARSE_API_URL_REGEX, host)
79
+ or re.search(PARSE_API_URL_REGEX, api_url)
80
+ ):
81
+ self.account_id, self.workspace_id = match.groups()
82
+
83
+ @property
84
+ def account_base_url(self) -> str:
85
+ if not self.account_id:
86
+ raise ValueError("Account ID not set")
87
+
88
+ return f"accounts/{self.account_id}"
89
+
90
+ @property
91
+ def workspace_base_url(self) -> str:
92
+ if not self.workspace_id:
93
+ raise ValueError("Workspace ID not set")
94
+
95
+ return f"{self.account_base_url}/workspaces/{self.workspace_id}"
96
+
79
97
  async def api_healthcheck(self):
80
98
  """
81
99
  Attempts to connect to the Cloud API and raises the encountered exception if not
@@ -87,18 +105,43 @@ class CloudClient:
87
105
  await self.read_workspaces()
88
106
 
89
107
  async def read_workspaces(self) -> List[Workspace]:
90
- workspaces = pydantic.parse_obj_as(
91
- List[Workspace], await self.get("/me/workspaces")
108
+ workspaces = pydantic.TypeAdapter(List[Workspace]).validate_python(
109
+ await self.get("/me/workspaces")
92
110
  )
93
111
  return workspaces
94
112
 
95
113
  async def read_worker_metadata(self) -> Dict[str, Any]:
96
- configured_url = prefect.settings.PREFECT_API_URL.value()
97
- account_id, workspace_id = re.findall(PARSE_API_URL_REGEX, configured_url)[0]
98
- return await self.get(
99
- f"accounts/{account_id}/workspaces/{workspace_id}/collections/work_pool_types"
114
+ response = await self.get(
115
+ f"{self.workspace_base_url}/collections/work_pool_types"
116
+ )
117
+ return cast(Dict[str, Any], response)
118
+
119
+ async def read_account_settings(self) -> Dict[str, Any]:
120
+ response = await self.get(f"{self.account_base_url}/settings")
121
+ return cast(Dict[str, Any], response)
122
+
123
+ async def update_account_settings(self, settings: Dict[str, Any]):
124
+ await self.request(
125
+ "PATCH",
126
+ f"{self.account_base_url}/settings",
127
+ json=settings,
100
128
  )
101
129
 
130
+ async def read_account_ip_allowlist(self) -> IPAllowlist:
131
+ response = await self.get(f"{self.account_base_url}/ip_allowlist")
132
+ return IPAllowlist.model_validate(response)
133
+
134
+ async def update_account_ip_allowlist(self, updated_allowlist: IPAllowlist):
135
+ await self.request(
136
+ "PUT",
137
+ f"{self.account_base_url}/ip_allowlist",
138
+ json=updated_allowlist.model_dump(mode="json"),
139
+ )
140
+
141
+ async def check_ip_allowlist_access(self) -> IPAllowlistMyAccessResponse:
142
+ response = await self.get(f"{self.account_base_url}/ip_allowlist/my_access")
143
+ return IPAllowlistMyAccessResponse.model_validate(response)
144
+
102
145
  async def __aenter__(self):
103
146
  await self._client.__aenter__()
104
147
  return self
@@ -127,9 +170,11 @@ class CloudClient:
127
170
  status.HTTP_401_UNAUTHORIZED,
128
171
  status.HTTP_403_FORBIDDEN,
129
172
  ):
130
- raise CloudUnauthorizedError
173
+ raise CloudUnauthorizedError(str(exc)) from exc
174
+ elif exc.response.status_code == status.HTTP_404_NOT_FOUND:
175
+ raise ObjectNotFound(http_exc=exc) from exc
131
176
  else:
132
- raise exc
177
+ raise
133
178
 
134
179
  if res.status_code == status.HTTP_204_NO_CONTENT:
135
180
  return
@@ -29,6 +29,6 @@ def get_collections_metadata_client(
29
29
  """
30
30
  orchestration_client = get_client(httpx_settings=httpx_settings)
31
31
  if orchestration_client.server_type == ServerType.CLOUD:
32
- return get_cloud_client(httpx_settings=httpx_settings)
32
+ return get_cloud_client(httpx_settings=httpx_settings, infer_cloud_url=True)
33
33
  else:
34
34
  return orchestration_client