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.
- prefect/__init__.py +74 -110
- prefect/_internal/compatibility/deprecated.py +6 -115
- prefect/_internal/compatibility/experimental.py +4 -79
- prefect/_internal/compatibility/migration.py +166 -0
- prefect/_internal/concurrency/__init__.py +2 -2
- prefect/_internal/concurrency/api.py +1 -35
- prefect/_internal/concurrency/calls.py +0 -6
- prefect/_internal/concurrency/cancellation.py +0 -3
- prefect/_internal/concurrency/event_loop.py +0 -20
- prefect/_internal/concurrency/inspection.py +3 -3
- prefect/_internal/concurrency/primitives.py +1 -0
- prefect/_internal/concurrency/services.py +23 -0
- prefect/_internal/concurrency/threads.py +35 -0
- prefect/_internal/concurrency/waiters.py +0 -28
- prefect/_internal/integrations.py +7 -0
- prefect/_internal/pydantic/__init__.py +0 -45
- prefect/_internal/pydantic/annotations/pendulum.py +2 -2
- prefect/_internal/pydantic/v1_schema.py +21 -22
- prefect/_internal/pydantic/v2_schema.py +0 -2
- prefect/_internal/pydantic/v2_validated_func.py +18 -23
- prefect/_internal/pytz.py +1 -1
- prefect/_internal/retries.py +61 -0
- prefect/_internal/schemas/bases.py +45 -177
- prefect/_internal/schemas/fields.py +1 -43
- prefect/_internal/schemas/validators.py +47 -233
- prefect/agent.py +3 -695
- prefect/artifacts.py +173 -14
- prefect/automations.py +39 -4
- prefect/blocks/abstract.py +1 -1
- prefect/blocks/core.py +405 -153
- prefect/blocks/fields.py +2 -57
- prefect/blocks/notifications.py +43 -28
- prefect/blocks/redis.py +168 -0
- prefect/blocks/system.py +67 -20
- prefect/blocks/webhook.py +2 -9
- prefect/cache_policies.py +239 -0
- prefect/client/__init__.py +4 -0
- prefect/client/base.py +33 -27
- prefect/client/cloud.py +65 -20
- prefect/client/collections.py +1 -1
- prefect/client/orchestration.py +650 -442
- prefect/client/schemas/actions.py +115 -100
- prefect/client/schemas/filters.py +46 -52
- prefect/client/schemas/objects.py +228 -178
- prefect/client/schemas/responses.py +18 -36
- prefect/client/schemas/schedules.py +55 -36
- prefect/client/schemas/sorting.py +2 -0
- prefect/client/subscriptions.py +8 -7
- prefect/client/types/flexible_schedule_list.py +11 -0
- prefect/client/utilities.py +9 -6
- prefect/concurrency/asyncio.py +60 -11
- prefect/concurrency/context.py +24 -0
- prefect/concurrency/events.py +2 -2
- prefect/concurrency/services.py +46 -16
- prefect/concurrency/sync.py +51 -7
- prefect/concurrency/v1/asyncio.py +143 -0
- prefect/concurrency/v1/context.py +27 -0
- prefect/concurrency/v1/events.py +61 -0
- prefect/concurrency/v1/services.py +116 -0
- prefect/concurrency/v1/sync.py +92 -0
- prefect/context.py +246 -149
- prefect/deployments/__init__.py +33 -18
- prefect/deployments/base.py +10 -15
- prefect/deployments/deployments.py +2 -1048
- prefect/deployments/flow_runs.py +178 -0
- prefect/deployments/runner.py +72 -173
- prefect/deployments/schedules.py +31 -25
- prefect/deployments/steps/__init__.py +0 -1
- prefect/deployments/steps/core.py +7 -0
- prefect/deployments/steps/pull.py +15 -21
- prefect/deployments/steps/utility.py +2 -1
- prefect/docker/__init__.py +20 -0
- prefect/docker/docker_image.py +82 -0
- prefect/engine.py +15 -2475
- prefect/events/actions.py +17 -23
- prefect/events/cli/automations.py +20 -7
- prefect/events/clients.py +142 -80
- prefect/events/filters.py +14 -18
- prefect/events/related.py +74 -75
- prefect/events/schemas/__init__.py +0 -5
- prefect/events/schemas/automations.py +55 -46
- prefect/events/schemas/deployment_triggers.py +7 -197
- prefect/events/schemas/events.py +46 -65
- prefect/events/schemas/labelling.py +10 -14
- prefect/events/utilities.py +4 -5
- prefect/events/worker.py +23 -8
- prefect/exceptions.py +15 -0
- prefect/filesystems.py +30 -529
- prefect/flow_engine.py +827 -0
- prefect/flow_runs.py +379 -7
- prefect/flows.py +470 -360
- prefect/futures.py +382 -331
- prefect/infrastructure/__init__.py +5 -26
- prefect/infrastructure/base.py +3 -320
- prefect/infrastructure/provisioners/__init__.py +5 -3
- prefect/infrastructure/provisioners/cloud_run.py +13 -8
- prefect/infrastructure/provisioners/container_instance.py +14 -9
- prefect/infrastructure/provisioners/ecs.py +10 -8
- prefect/infrastructure/provisioners/modal.py +8 -5
- prefect/input/__init__.py +4 -0
- prefect/input/actions.py +2 -4
- prefect/input/run_input.py +9 -9
- prefect/logging/formatters.py +2 -4
- prefect/logging/handlers.py +9 -14
- prefect/logging/loggers.py +5 -5
- prefect/main.py +72 -0
- prefect/plugins.py +2 -64
- prefect/profiles.toml +16 -2
- prefect/records/__init__.py +1 -0
- prefect/records/base.py +223 -0
- prefect/records/filesystem.py +207 -0
- prefect/records/memory.py +178 -0
- prefect/records/result_store.py +64 -0
- prefect/results.py +577 -504
- prefect/runner/runner.py +117 -47
- prefect/runner/server.py +32 -34
- prefect/runner/storage.py +3 -12
- prefect/runner/submit.py +2 -10
- prefect/runner/utils.py +2 -2
- prefect/runtime/__init__.py +1 -0
- prefect/runtime/deployment.py +1 -0
- prefect/runtime/flow_run.py +40 -5
- prefect/runtime/task_run.py +1 -0
- prefect/serializers.py +28 -39
- prefect/server/api/collections_data/views/aggregate-worker-metadata.json +5 -14
- prefect/settings.py +209 -332
- prefect/states.py +160 -63
- prefect/task_engine.py +1478 -57
- prefect/task_runners.py +383 -287
- prefect/task_runs.py +240 -0
- prefect/task_worker.py +463 -0
- prefect/tasks.py +684 -374
- prefect/transactions.py +410 -0
- prefect/types/__init__.py +72 -86
- prefect/types/entrypoint.py +13 -0
- prefect/utilities/annotations.py +4 -3
- prefect/utilities/asyncutils.py +227 -148
- prefect/utilities/callables.py +137 -45
- prefect/utilities/collections.py +134 -86
- prefect/utilities/dispatch.py +27 -14
- prefect/utilities/dockerutils.py +11 -4
- prefect/utilities/engine.py +186 -32
- prefect/utilities/filesystem.py +4 -5
- prefect/utilities/importtools.py +26 -27
- prefect/utilities/pydantic.py +128 -38
- prefect/utilities/schema_tools/hydration.py +18 -1
- prefect/utilities/schema_tools/validation.py +30 -0
- prefect/utilities/services.py +35 -9
- prefect/utilities/templating.py +12 -2
- prefect/utilities/timeout.py +20 -5
- prefect/utilities/urls.py +195 -0
- prefect/utilities/visualization.py +1 -0
- prefect/variables.py +78 -59
- prefect/workers/__init__.py +0 -1
- prefect/workers/base.py +237 -244
- prefect/workers/block.py +5 -226
- prefect/workers/cloud.py +6 -0
- prefect/workers/process.py +265 -12
- prefect/workers/server.py +29 -11
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/METADATA +28 -24
- prefect_client-3.0.0.dist-info/RECORD +201 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/WHEEL +1 -1
- prefect/_internal/pydantic/_base_model.py +0 -51
- prefect/_internal/pydantic/_compat.py +0 -82
- prefect/_internal/pydantic/_flags.py +0 -20
- prefect/_internal/pydantic/_types.py +0 -8
- prefect/_internal/pydantic/utilities/config_dict.py +0 -72
- prefect/_internal/pydantic/utilities/field_validator.py +0 -150
- prefect/_internal/pydantic/utilities/model_construct.py +0 -56
- prefect/_internal/pydantic/utilities/model_copy.py +0 -55
- prefect/_internal/pydantic/utilities/model_dump.py +0 -136
- prefect/_internal/pydantic/utilities/model_dump_json.py +0 -112
- prefect/_internal/pydantic/utilities/model_fields.py +0 -50
- prefect/_internal/pydantic/utilities/model_fields_set.py +0 -29
- prefect/_internal/pydantic/utilities/model_json_schema.py +0 -82
- prefect/_internal/pydantic/utilities/model_rebuild.py +0 -80
- prefect/_internal/pydantic/utilities/model_validate.py +0 -75
- prefect/_internal/pydantic/utilities/model_validate_json.py +0 -68
- prefect/_internal/pydantic/utilities/model_validator.py +0 -87
- prefect/_internal/pydantic/utilities/type_adapter.py +0 -71
- prefect/_vendor/fastapi/__init__.py +0 -25
- prefect/_vendor/fastapi/applications.py +0 -946
- prefect/_vendor/fastapi/background.py +0 -3
- prefect/_vendor/fastapi/concurrency.py +0 -44
- prefect/_vendor/fastapi/datastructures.py +0 -58
- prefect/_vendor/fastapi/dependencies/__init__.py +0 -0
- prefect/_vendor/fastapi/dependencies/models.py +0 -64
- prefect/_vendor/fastapi/dependencies/utils.py +0 -877
- prefect/_vendor/fastapi/encoders.py +0 -177
- prefect/_vendor/fastapi/exception_handlers.py +0 -40
- prefect/_vendor/fastapi/exceptions.py +0 -46
- prefect/_vendor/fastapi/logger.py +0 -3
- prefect/_vendor/fastapi/middleware/__init__.py +0 -1
- prefect/_vendor/fastapi/middleware/asyncexitstack.py +0 -25
- prefect/_vendor/fastapi/middleware/cors.py +0 -3
- prefect/_vendor/fastapi/middleware/gzip.py +0 -3
- prefect/_vendor/fastapi/middleware/httpsredirect.py +0 -3
- prefect/_vendor/fastapi/middleware/trustedhost.py +0 -3
- prefect/_vendor/fastapi/middleware/wsgi.py +0 -3
- prefect/_vendor/fastapi/openapi/__init__.py +0 -0
- prefect/_vendor/fastapi/openapi/constants.py +0 -2
- prefect/_vendor/fastapi/openapi/docs.py +0 -203
- prefect/_vendor/fastapi/openapi/models.py +0 -480
- prefect/_vendor/fastapi/openapi/utils.py +0 -485
- prefect/_vendor/fastapi/param_functions.py +0 -340
- prefect/_vendor/fastapi/params.py +0 -453
- prefect/_vendor/fastapi/py.typed +0 -0
- prefect/_vendor/fastapi/requests.py +0 -4
- prefect/_vendor/fastapi/responses.py +0 -40
- prefect/_vendor/fastapi/routing.py +0 -1331
- prefect/_vendor/fastapi/security/__init__.py +0 -15
- prefect/_vendor/fastapi/security/api_key.py +0 -98
- prefect/_vendor/fastapi/security/base.py +0 -6
- prefect/_vendor/fastapi/security/http.py +0 -172
- prefect/_vendor/fastapi/security/oauth2.py +0 -227
- prefect/_vendor/fastapi/security/open_id_connect_url.py +0 -34
- prefect/_vendor/fastapi/security/utils.py +0 -10
- prefect/_vendor/fastapi/staticfiles.py +0 -1
- prefect/_vendor/fastapi/templating.py +0 -3
- prefect/_vendor/fastapi/testclient.py +0 -1
- prefect/_vendor/fastapi/types.py +0 -3
- prefect/_vendor/fastapi/utils.py +0 -235
- prefect/_vendor/fastapi/websockets.py +0 -7
- prefect/_vendor/starlette/__init__.py +0 -1
- prefect/_vendor/starlette/_compat.py +0 -28
- prefect/_vendor/starlette/_exception_handler.py +0 -80
- prefect/_vendor/starlette/_utils.py +0 -88
- prefect/_vendor/starlette/applications.py +0 -261
- prefect/_vendor/starlette/authentication.py +0 -159
- prefect/_vendor/starlette/background.py +0 -43
- prefect/_vendor/starlette/concurrency.py +0 -59
- prefect/_vendor/starlette/config.py +0 -151
- prefect/_vendor/starlette/convertors.py +0 -87
- prefect/_vendor/starlette/datastructures.py +0 -707
- prefect/_vendor/starlette/endpoints.py +0 -130
- prefect/_vendor/starlette/exceptions.py +0 -60
- prefect/_vendor/starlette/formparsers.py +0 -276
- prefect/_vendor/starlette/middleware/__init__.py +0 -17
- prefect/_vendor/starlette/middleware/authentication.py +0 -52
- prefect/_vendor/starlette/middleware/base.py +0 -220
- prefect/_vendor/starlette/middleware/cors.py +0 -176
- prefect/_vendor/starlette/middleware/errors.py +0 -265
- prefect/_vendor/starlette/middleware/exceptions.py +0 -74
- prefect/_vendor/starlette/middleware/gzip.py +0 -113
- prefect/_vendor/starlette/middleware/httpsredirect.py +0 -19
- prefect/_vendor/starlette/middleware/sessions.py +0 -82
- prefect/_vendor/starlette/middleware/trustedhost.py +0 -64
- prefect/_vendor/starlette/middleware/wsgi.py +0 -147
- prefect/_vendor/starlette/py.typed +0 -0
- prefect/_vendor/starlette/requests.py +0 -328
- prefect/_vendor/starlette/responses.py +0 -347
- prefect/_vendor/starlette/routing.py +0 -933
- prefect/_vendor/starlette/schemas.py +0 -154
- prefect/_vendor/starlette/staticfiles.py +0 -248
- prefect/_vendor/starlette/status.py +0 -199
- prefect/_vendor/starlette/templating.py +0 -231
- prefect/_vendor/starlette/testclient.py +0 -804
- prefect/_vendor/starlette/types.py +0 -30
- prefect/_vendor/starlette/websockets.py +0 -193
- prefect/blocks/kubernetes.py +0 -119
- prefect/deprecated/__init__.py +0 -0
- prefect/deprecated/data_documents.py +0 -350
- prefect/deprecated/packaging/__init__.py +0 -12
- prefect/deprecated/packaging/base.py +0 -96
- prefect/deprecated/packaging/docker.py +0 -146
- prefect/deprecated/packaging/file.py +0 -92
- prefect/deprecated/packaging/orion.py +0 -80
- prefect/deprecated/packaging/serializers.py +0 -171
- prefect/events/instrument.py +0 -135
- prefect/infrastructure/container.py +0 -824
- prefect/infrastructure/kubernetes.py +0 -920
- prefect/infrastructure/process.py +0 -289
- prefect/manifests.py +0 -20
- prefect/new_flow_engine.py +0 -449
- prefect/new_task_engine.py +0 -423
- prefect/pydantic/__init__.py +0 -76
- prefect/pydantic/main.py +0 -39
- prefect/software/__init__.py +0 -2
- prefect/software/base.py +0 -50
- prefect/software/conda.py +0 -199
- prefect/software/pip.py +0 -122
- prefect/software/python.py +0 -52
- prefect/task_server.py +0 -322
- prefect_client-2.20.4.dist-info/RECORD +0 -294
- /prefect/{_internal/pydantic/utilities → client/types}/__init__.py +0 -0
- /prefect/{_vendor → concurrency/v1}/__init__.py +0 -0
- {prefect_client-2.20.4.dist-info → prefect_client-3.0.0.dist-info}/LICENSE +0 -0
- {prefect_client-2.20.4.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
|
prefect/client/__init__.py
CHANGED
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
|
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.
|
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.
|
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
|
616
|
-
|
617
|
-
|
618
|
-
|
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
|
-
|
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
|
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
|
20
|
-
|
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.
|
91
|
-
|
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
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
177
|
+
raise
|
133
178
|
|
134
179
|
if res.status_code == status.HTTP_204_NO_CONTENT:
|
135
180
|
return
|
prefect/client/collections.py
CHANGED
@@ -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
|