prefect-client 3.1.12__py3-none-any.whl → 3.1.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prefect/_experimental/sla/client.py +53 -27
- prefect/_experimental/sla/objects.py +10 -2
- prefect/_internal/concurrency/services.py +2 -2
- prefect/_internal/concurrency/threads.py +6 -0
- prefect/_internal/retries.py +6 -3
- prefect/_internal/schemas/validators.py +6 -4
- prefect/_version.py +3 -3
- prefect/artifacts.py +4 -1
- prefect/automations.py +1 -1
- prefect/blocks/abstract.py +5 -2
- prefect/blocks/notifications.py +1 -0
- prefect/cache_policies.py +20 -20
- prefect/client/utilities.py +3 -3
- prefect/deployments/base.py +7 -4
- prefect/deployments/flow_runs.py +5 -1
- prefect/deployments/runner.py +6 -11
- prefect/deployments/steps/core.py +1 -1
- prefect/deployments/steps/pull.py +8 -3
- prefect/deployments/steps/utility.py +2 -2
- prefect/docker/docker_image.py +13 -9
- prefect/engine.py +19 -10
- prefect/events/cli/automations.py +4 -4
- prefect/events/clients.py +17 -14
- prefect/events/schemas/automations.py +12 -8
- prefect/events/schemas/events.py +5 -1
- prefect/events/worker.py +1 -1
- prefect/filesystems.py +1 -1
- prefect/flow_engine.py +17 -9
- prefect/flows.py +118 -73
- prefect/futures.py +14 -7
- prefect/infrastructure/provisioners/__init__.py +2 -0
- prefect/infrastructure/provisioners/cloud_run.py +4 -4
- prefect/infrastructure/provisioners/coiled.py +249 -0
- prefect/infrastructure/provisioners/container_instance.py +4 -3
- prefect/infrastructure/provisioners/ecs.py +55 -43
- prefect/infrastructure/provisioners/modal.py +5 -4
- prefect/input/actions.py +5 -1
- prefect/input/run_input.py +157 -43
- prefect/logging/configuration.py +3 -3
- prefect/logging/filters.py +2 -2
- prefect/logging/formatters.py +15 -11
- prefect/logging/handlers.py +24 -14
- prefect/logging/highlighters.py +5 -5
- prefect/logging/loggers.py +28 -18
- prefect/main.py +3 -1
- prefect/results.py +166 -86
- prefect/runner/runner.py +34 -27
- prefect/runner/server.py +3 -1
- prefect/runner/storage.py +18 -18
- prefect/runner/submit.py +19 -12
- prefect/runtime/deployment.py +15 -8
- prefect/runtime/flow_run.py +19 -6
- prefect/runtime/task_run.py +7 -3
- prefect/settings/base.py +17 -7
- prefect/settings/legacy.py +4 -4
- prefect/settings/models/api.py +4 -3
- prefect/settings/models/cli.py +4 -3
- prefect/settings/models/client.py +7 -4
- prefect/settings/models/cloud.py +4 -3
- prefect/settings/models/deployments.py +4 -3
- prefect/settings/models/experiments.py +4 -3
- prefect/settings/models/flows.py +4 -3
- prefect/settings/models/internal.py +4 -3
- prefect/settings/models/logging.py +8 -6
- prefect/settings/models/results.py +4 -3
- prefect/settings/models/root.py +11 -16
- prefect/settings/models/runner.py +8 -5
- prefect/settings/models/server/api.py +6 -3
- prefect/settings/models/server/database.py +120 -25
- prefect/settings/models/server/deployments.py +4 -3
- prefect/settings/models/server/ephemeral.py +7 -4
- prefect/settings/models/server/events.py +6 -3
- prefect/settings/models/server/flow_run_graph.py +4 -3
- prefect/settings/models/server/root.py +4 -3
- prefect/settings/models/server/services.py +15 -12
- prefect/settings/models/server/tasks.py +7 -4
- prefect/settings/models/server/ui.py +4 -3
- prefect/settings/models/tasks.py +10 -5
- prefect/settings/models/testing.py +4 -3
- prefect/settings/models/worker.py +7 -4
- prefect/settings/profiles.py +13 -12
- prefect/settings/sources.py +20 -19
- prefect/states.py +17 -13
- prefect/task_engine.py +43 -33
- prefect/task_runners.py +35 -23
- prefect/task_runs.py +20 -11
- prefect/task_worker.py +12 -7
- prefect/tasks.py +30 -24
- prefect/telemetry/bootstrap.py +4 -1
- prefect/telemetry/run_telemetry.py +15 -13
- prefect/transactions.py +3 -3
- prefect/types/__init__.py +3 -1
- prefect/utilities/_deprecated.py +38 -0
- prefect/utilities/engine.py +11 -4
- prefect/utilities/filesystem.py +2 -2
- prefect/utilities/generics.py +1 -1
- prefect/utilities/pydantic.py +21 -36
- prefect/workers/base.py +52 -30
- prefect/workers/process.py +20 -15
- prefect/workers/server.py +4 -5
- {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/METADATA +2 -2
- {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/RECORD +105 -103
- {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/LICENSE +0 -0
- {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/WHEEL +0 -0
- {prefect_client-3.1.12.dist-info → prefect_client-3.1.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,249 @@
|
|
1
|
+
import importlib
|
2
|
+
import shlex
|
3
|
+
import sys
|
4
|
+
from copy import deepcopy
|
5
|
+
from types import ModuleType
|
6
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
7
|
+
|
8
|
+
from anyio import run_process
|
9
|
+
from rich.console import Console
|
10
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn
|
11
|
+
from rich.prompt import Confirm
|
12
|
+
|
13
|
+
from prefect.client.schemas.actions import BlockDocumentCreate
|
14
|
+
from prefect.client.schemas.objects import BlockDocument
|
15
|
+
from prefect.client.utilities import inject_client
|
16
|
+
from prefect.exceptions import ObjectNotFound
|
17
|
+
from prefect.utilities.importtools import lazy_import
|
18
|
+
|
19
|
+
if TYPE_CHECKING:
|
20
|
+
from prefect.client.orchestration import PrefectClient
|
21
|
+
|
22
|
+
|
23
|
+
coiled: ModuleType = lazy_import("coiled")
|
24
|
+
|
25
|
+
|
26
|
+
class CoiledPushProvisioner:
|
27
|
+
"""
|
28
|
+
A infrastructure provisioner for Coiled push work pools.
|
29
|
+
"""
|
30
|
+
|
31
|
+
def __init__(self, client: Optional["PrefectClient"] = None):
|
32
|
+
self._console = Console()
|
33
|
+
|
34
|
+
@property
|
35
|
+
def console(self) -> Console:
|
36
|
+
return self._console
|
37
|
+
|
38
|
+
@console.setter
|
39
|
+
def console(self, value: Console) -> None:
|
40
|
+
self._console = value
|
41
|
+
|
42
|
+
@staticmethod
|
43
|
+
def _is_coiled_installed() -> bool:
|
44
|
+
"""
|
45
|
+
Checks if the coiled package is installed.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
True if the coiled package is installed, False otherwise
|
49
|
+
"""
|
50
|
+
try:
|
51
|
+
importlib.import_module("coiled")
|
52
|
+
return True
|
53
|
+
except ModuleNotFoundError:
|
54
|
+
return False
|
55
|
+
|
56
|
+
async def _install_coiled(self):
|
57
|
+
"""
|
58
|
+
Installs the coiled package.
|
59
|
+
"""
|
60
|
+
with Progress(
|
61
|
+
SpinnerColumn(),
|
62
|
+
TextColumn("[bold blue]Installing coiled..."),
|
63
|
+
transient=True,
|
64
|
+
console=self.console,
|
65
|
+
) as progress:
|
66
|
+
task = progress.add_task("coiled install")
|
67
|
+
progress.start()
|
68
|
+
global coiled
|
69
|
+
await run_process(
|
70
|
+
[shlex.quote(sys.executable), "-m", "pip", "install", "coiled"]
|
71
|
+
)
|
72
|
+
coiled = importlib.import_module("coiled")
|
73
|
+
progress.advance(task)
|
74
|
+
|
75
|
+
async def _get_coiled_token(self) -> str:
|
76
|
+
"""
|
77
|
+
Gets a Coiled API token from the current Coiled configuration.
|
78
|
+
"""
|
79
|
+
import dask.config
|
80
|
+
|
81
|
+
return dask.config.get("coiled.token", "")
|
82
|
+
|
83
|
+
async def _create_new_coiled_token(self):
|
84
|
+
"""
|
85
|
+
Triggers a Coiled login via the browser if no current token. Will create a new token.
|
86
|
+
"""
|
87
|
+
await run_process(["coiled", "login"])
|
88
|
+
|
89
|
+
async def _create_coiled_credentials_block(
|
90
|
+
self,
|
91
|
+
block_document_name: str,
|
92
|
+
coiled_token: str,
|
93
|
+
client: "PrefectClient",
|
94
|
+
) -> BlockDocument:
|
95
|
+
"""
|
96
|
+
Creates a CoiledCredentials block containing the provided token.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
block_document_name: The name of the block document to create
|
100
|
+
coiled_token: The Coiled API token
|
101
|
+
|
102
|
+
Returns:
|
103
|
+
The ID of the created block
|
104
|
+
"""
|
105
|
+
assert client is not None, "client injection failed"
|
106
|
+
try:
|
107
|
+
credentials_block_type = await client.read_block_type_by_slug(
|
108
|
+
"coiled-credentials"
|
109
|
+
)
|
110
|
+
except ObjectNotFound:
|
111
|
+
# Shouldn't happen, but just in case
|
112
|
+
raise RuntimeError(
|
113
|
+
"Unable to find CoiledCredentials block type. Please ensure you are"
|
114
|
+
" using Prefect Cloud."
|
115
|
+
)
|
116
|
+
credentials_block_schema = (
|
117
|
+
await client.get_most_recent_block_schema_for_block_type(
|
118
|
+
block_type_id=credentials_block_type.id
|
119
|
+
)
|
120
|
+
)
|
121
|
+
assert (
|
122
|
+
credentials_block_schema is not None
|
123
|
+
), f"Unable to find schema for block type {credentials_block_type.slug}"
|
124
|
+
|
125
|
+
block_doc = await client.create_block_document(
|
126
|
+
block_document=BlockDocumentCreate(
|
127
|
+
name=block_document_name,
|
128
|
+
data={
|
129
|
+
"api_token": coiled_token,
|
130
|
+
},
|
131
|
+
block_type_id=credentials_block_type.id,
|
132
|
+
block_schema_id=credentials_block_schema.id,
|
133
|
+
)
|
134
|
+
)
|
135
|
+
return block_doc
|
136
|
+
|
137
|
+
@inject_client
|
138
|
+
async def provision(
|
139
|
+
self,
|
140
|
+
work_pool_name: str,
|
141
|
+
base_job_template: Dict[str, Any],
|
142
|
+
client: Optional["PrefectClient"] = None,
|
143
|
+
) -> Dict[str, Any]:
|
144
|
+
"""
|
145
|
+
Provisions resources necessary for a Coiled push work pool.
|
146
|
+
|
147
|
+
Provisioned resources:
|
148
|
+
- A CoiledCredentials block containing a Coiled API token
|
149
|
+
|
150
|
+
Args:
|
151
|
+
work_pool_name: The name of the work pool to provision resources for
|
152
|
+
base_job_template: The base job template to update
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
A copy of the provided base job template with the provisioned resources
|
156
|
+
"""
|
157
|
+
credentials_block_name = f"{work_pool_name}-coiled-credentials"
|
158
|
+
base_job_template_copy = deepcopy(base_job_template)
|
159
|
+
assert client is not None, "client injection failed"
|
160
|
+
try:
|
161
|
+
block_doc = await client.read_block_document_by_name(
|
162
|
+
credentials_block_name, "coiled-credentials"
|
163
|
+
)
|
164
|
+
self.console.print(
|
165
|
+
f"Work pool [blue]{work_pool_name!r}[/] will reuse the existing Coiled"
|
166
|
+
f" credentials block [blue]{credentials_block_name!r}[/blue]"
|
167
|
+
)
|
168
|
+
except ObjectNotFound:
|
169
|
+
if self._console.is_interactive and not Confirm.ask(
|
170
|
+
(
|
171
|
+
"\n"
|
172
|
+
"To configure your Coiled push work pool we'll need to store a Coiled"
|
173
|
+
" API token with Prefect Cloud as a block. We'll pull the token from"
|
174
|
+
" your local Coiled configuration or create a new token if we"
|
175
|
+
" can't find one.\n"
|
176
|
+
"\n"
|
177
|
+
"Would you like to continue?"
|
178
|
+
),
|
179
|
+
console=self.console,
|
180
|
+
default=True,
|
181
|
+
):
|
182
|
+
self.console.print(
|
183
|
+
"No problem! You can always configure your Coiled push work pool"
|
184
|
+
" later via the Prefect UI."
|
185
|
+
)
|
186
|
+
return base_job_template
|
187
|
+
|
188
|
+
if not self._is_coiled_installed():
|
189
|
+
if self.console.is_interactive and Confirm.ask(
|
190
|
+
(
|
191
|
+
"The [blue]coiled[/] package is required to configure"
|
192
|
+
" authentication for your work pool.\n"
|
193
|
+
"\n"
|
194
|
+
"Would you like to install it now?"
|
195
|
+
),
|
196
|
+
console=self.console,
|
197
|
+
default=True,
|
198
|
+
):
|
199
|
+
await self._install_coiled()
|
200
|
+
|
201
|
+
if not self._is_coiled_installed():
|
202
|
+
raise RuntimeError(
|
203
|
+
"The coiled package is not installed.\n\nPlease try installing coiled,"
|
204
|
+
" or you can use the Prefect UI to create your Coiled push work pool."
|
205
|
+
)
|
206
|
+
|
207
|
+
# Get the current Coiled API token
|
208
|
+
coiled_api_token = await self._get_coiled_token()
|
209
|
+
if not coiled_api_token:
|
210
|
+
# Create a new token one wasn't found
|
211
|
+
if self.console.is_interactive and Confirm.ask(
|
212
|
+
"Coiled credentials not found. Would you like to create a new token?",
|
213
|
+
console=self.console,
|
214
|
+
default=True,
|
215
|
+
):
|
216
|
+
await self._create_new_coiled_token()
|
217
|
+
coiled_api_token = await self._get_coiled_token()
|
218
|
+
else:
|
219
|
+
raise RuntimeError(
|
220
|
+
"Coiled credentials not found. Please create a new token by"
|
221
|
+
" running [blue]coiled login[/] and try again."
|
222
|
+
)
|
223
|
+
|
224
|
+
# Create the credentials block
|
225
|
+
with Progress(
|
226
|
+
SpinnerColumn(),
|
227
|
+
TextColumn("[bold blue]Saving Coiled credentials..."),
|
228
|
+
transient=True,
|
229
|
+
console=self.console,
|
230
|
+
) as progress:
|
231
|
+
task = progress.add_task("create coiled credentials block")
|
232
|
+
progress.start()
|
233
|
+
block_doc = await self._create_coiled_credentials_block(
|
234
|
+
credentials_block_name,
|
235
|
+
coiled_api_token,
|
236
|
+
client=client,
|
237
|
+
)
|
238
|
+
progress.advance(task)
|
239
|
+
|
240
|
+
base_job_template_copy["variables"]["properties"]["credentials"]["default"] = {
|
241
|
+
"$ref": {"block_document_id": str(block_doc.id)}
|
242
|
+
}
|
243
|
+
if "image" in base_job_template_copy["variables"]["properties"]:
|
244
|
+
base_job_template_copy["variables"]["properties"]["image"]["default"] = ""
|
245
|
+
self.console.print(
|
246
|
+
f"Successfully configured Coiled push work pool {work_pool_name!r}!",
|
247
|
+
style="green",
|
248
|
+
)
|
249
|
+
return base_job_template_copy
|
@@ -10,6 +10,7 @@ Classes:
|
|
10
10
|
ContainerInstancePushProvisioner: A class for provisioning infrastructure using Azure Container Instances.
|
11
11
|
|
12
12
|
"""
|
13
|
+
from __future__ import annotations
|
13
14
|
|
14
15
|
import json
|
15
16
|
import random
|
@@ -64,7 +65,7 @@ class AzureCLI:
|
|
64
65
|
failure_message: Optional[str] = None,
|
65
66
|
ignore_if_exists: bool = False,
|
66
67
|
return_json: bool = False,
|
67
|
-
):
|
68
|
+
) -> str | dict[str, Any] | None:
|
68
69
|
"""
|
69
70
|
Runs an Azure CLI command and processes the output.
|
70
71
|
|
@@ -156,7 +157,7 @@ class ContainerInstancePushProvisioner:
|
|
156
157
|
self._subscription_name = None
|
157
158
|
self._location = "eastus"
|
158
159
|
self._identity_name = "prefect-acr-identity"
|
159
|
-
self.azure_cli = AzureCLI(self.console)
|
160
|
+
self.azure_cli: AzureCLI = AzureCLI(self.console)
|
160
161
|
self._credentials_block_name = None
|
161
162
|
self._resource_group_name = "prefect-aci-push-pool-rg"
|
162
163
|
self._app_registration_name = "prefect-aci-push-pool-app"
|
@@ -170,7 +171,7 @@ class ContainerInstancePushProvisioner:
|
|
170
171
|
def console(self, value: Console) -> None:
|
171
172
|
self._console = value
|
172
173
|
|
173
|
-
async def set_location(self):
|
174
|
+
async def set_location(self) -> None:
|
174
175
|
"""
|
175
176
|
Set the Azure resource deployment location to the default or 'eastus' on failure.
|
176
177
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
1
3
|
import base64
|
2
4
|
import contextlib
|
3
5
|
import contextvars
|
@@ -9,9 +11,11 @@ import sys
|
|
9
11
|
from copy import deepcopy
|
10
12
|
from functools import partial
|
11
13
|
from textwrap import dedent
|
12
|
-
from
|
14
|
+
from types import ModuleType
|
15
|
+
from typing import TYPE_CHECKING, Any, Callable, Dict, Generator, List, Optional
|
13
16
|
|
14
17
|
import anyio
|
18
|
+
import anyio.to_thread
|
15
19
|
from anyio import run_process
|
16
20
|
from rich.console import Console
|
17
21
|
from rich.panel import Panel
|
@@ -33,13 +37,15 @@ from prefect.utilities.importtools import lazy_import
|
|
33
37
|
if TYPE_CHECKING:
|
34
38
|
from prefect.client.orchestration import PrefectClient
|
35
39
|
|
36
|
-
boto3 = lazy_import("boto3")
|
40
|
+
boto3: ModuleType = lazy_import("boto3")
|
37
41
|
|
38
|
-
current_console = contextvars.ContextVar(
|
42
|
+
current_console: contextvars.ContextVar[Console] = contextvars.ContextVar(
|
43
|
+
"console", default=Console()
|
44
|
+
)
|
39
45
|
|
40
46
|
|
41
47
|
@contextlib.contextmanager
|
42
|
-
def console_context(value: Console):
|
48
|
+
def console_context(value: Console) -> Generator[None, None, None]:
|
43
49
|
token = current_console.set(value)
|
44
50
|
try:
|
45
51
|
yield
|
@@ -73,7 +79,7 @@ class IamPolicyResource:
|
|
73
79
|
"""
|
74
80
|
return 1 if await self.requires_provisioning() else 0
|
75
81
|
|
76
|
-
def _get_policy_by_name(self, name):
|
82
|
+
def _get_policy_by_name(self, name: str) -> dict[str, Any] | None:
|
77
83
|
paginator = self._iam_client.get_paginator("list_policies")
|
78
84
|
page_iterator = paginator.paginate(Scope="Local")
|
79
85
|
|
@@ -119,9 +125,9 @@ class IamPolicyResource:
|
|
119
125
|
|
120
126
|
async def provision(
|
121
127
|
self,
|
122
|
-
policy_document:
|
128
|
+
policy_document: dict[str, Any],
|
123
129
|
advance: Callable[[], None],
|
124
|
-
):
|
130
|
+
) -> str:
|
125
131
|
"""
|
126
132
|
Provisions an IAM policy.
|
127
133
|
|
@@ -153,7 +159,7 @@ class IamPolicyResource:
|
|
153
159
|
return policy["Arn"]
|
154
160
|
|
155
161
|
@property
|
156
|
-
def next_steps(self):
|
162
|
+
def next_steps(self) -> list[str]:
|
157
163
|
return []
|
158
164
|
|
159
165
|
|
@@ -215,7 +221,7 @@ class IamUserResource:
|
|
215
221
|
async def provision(
|
216
222
|
self,
|
217
223
|
advance: Callable[[], None],
|
218
|
-
):
|
224
|
+
) -> None:
|
219
225
|
"""
|
220
226
|
Provisions an IAM user.
|
221
227
|
|
@@ -231,7 +237,7 @@ class IamUserResource:
|
|
231
237
|
advance()
|
232
238
|
|
233
239
|
@property
|
234
|
-
def next_steps(self):
|
240
|
+
def next_steps(self) -> list[str]:
|
235
241
|
return []
|
236
242
|
|
237
243
|
|
@@ -241,7 +247,7 @@ class CredentialsBlockResource:
|
|
241
247
|
self._user_name = user_name
|
242
248
|
self._requires_provisioning = None
|
243
249
|
|
244
|
-
async def get_task_count(self):
|
250
|
+
async def get_task_count(self) -> int:
|
245
251
|
"""
|
246
252
|
Returns the number of tasks that will be executed to provision this resource.
|
247
253
|
|
@@ -357,7 +363,7 @@ class CredentialsBlockResource:
|
|
357
363
|
}
|
358
364
|
|
359
365
|
@property
|
360
|
-
def next_steps(self):
|
366
|
+
def next_steps(self) -> list[str]:
|
361
367
|
return []
|
362
368
|
|
363
369
|
|
@@ -374,7 +380,7 @@ class AuthenticationResource:
|
|
374
380
|
credentials_block_name or f"{work_pool_name}-aws-credentials"
|
375
381
|
)
|
376
382
|
self._policy_name = policy_name
|
377
|
-
self._policy_document = {
|
383
|
+
self._policy_document: dict[str, Any] = {
|
378
384
|
"Version": "2012-10-17",
|
379
385
|
"Statement": [
|
380
386
|
{
|
@@ -417,7 +423,11 @@ class AuthenticationResource:
|
|
417
423
|
self._execution_role_resource = ExecutionRoleResource()
|
418
424
|
|
419
425
|
@property
|
420
|
-
def resources(
|
426
|
+
def resources(
|
427
|
+
self,
|
428
|
+
) -> list[
|
429
|
+
"ExecutionRoleResource | IamUserResource | IamPolicyResource | CredentialsBlockResource"
|
430
|
+
]:
|
421
431
|
return [
|
422
432
|
self._execution_role_resource,
|
423
433
|
self._iam_user_resource,
|
@@ -425,7 +435,7 @@ class AuthenticationResource:
|
|
425
435
|
self._credentials_block_resource,
|
426
436
|
]
|
427
437
|
|
428
|
-
async def get_task_count(self):
|
438
|
+
async def get_task_count(self) -> int:
|
429
439
|
"""
|
430
440
|
Returns the number of tasks that will be executed to provision this resource.
|
431
441
|
|
@@ -461,9 +471,9 @@ class AuthenticationResource:
|
|
461
471
|
|
462
472
|
async def provision(
|
463
473
|
self,
|
464
|
-
base_job_template:
|
474
|
+
base_job_template: dict[str, Any],
|
465
475
|
advance: Callable[[], None],
|
466
|
-
):
|
476
|
+
) -> None:
|
467
477
|
"""
|
468
478
|
Provisions the authentication resources.
|
469
479
|
|
@@ -507,7 +517,7 @@ class AuthenticationResource:
|
|
507
517
|
)
|
508
518
|
|
509
519
|
@property
|
510
|
-
def next_steps(self):
|
520
|
+
def next_steps(self) -> list[str]:
|
511
521
|
return [
|
512
522
|
next_step
|
513
523
|
for resource in self.resources
|
@@ -521,7 +531,7 @@ class ClusterResource:
|
|
521
531
|
self._cluster_name = cluster_name
|
522
532
|
self._requires_provisioning = None
|
523
533
|
|
524
|
-
async def get_task_count(self):
|
534
|
+
async def get_task_count(self) -> int:
|
525
535
|
"""
|
526
536
|
Returns the number of tasks that will be executed to provision this resource.
|
527
537
|
|
@@ -566,9 +576,9 @@ class ClusterResource:
|
|
566
576
|
|
567
577
|
async def provision(
|
568
578
|
self,
|
569
|
-
base_job_template:
|
579
|
+
base_job_template: dict[str, Any],
|
570
580
|
advance: Callable[[], None],
|
571
|
-
):
|
581
|
+
) -> None:
|
572
582
|
"""
|
573
583
|
Provisions an ECS cluster.
|
574
584
|
|
@@ -592,7 +602,7 @@ class ClusterResource:
|
|
592
602
|
] = self._cluster_name
|
593
603
|
|
594
604
|
@property
|
595
|
-
def next_steps(self):
|
605
|
+
def next_steps(self) -> list[str]:
|
596
606
|
return []
|
597
607
|
|
598
608
|
|
@@ -608,7 +618,7 @@ class VpcResource:
|
|
608
618
|
self._requires_provisioning = None
|
609
619
|
self._ecs_security_group_name = ecs_security_group_name
|
610
620
|
|
611
|
-
async def get_task_count(self):
|
621
|
+
async def get_task_count(self) -> int:
|
612
622
|
"""
|
613
623
|
Returns the number of tasks that will be executed to provision this resource.
|
614
624
|
|
@@ -642,7 +652,9 @@ class VpcResource:
|
|
642
652
|
response = await anyio.to_thread.run_sync(self._ec2_client.describe_vpcs)
|
643
653
|
return [vpc["CidrBlock"] for vpc in response["Vpcs"]]
|
644
654
|
|
645
|
-
async def _find_non_overlapping_cidr(
|
655
|
+
async def _find_non_overlapping_cidr(
|
656
|
+
self, default_cidr: str = "172.31.0.0/16"
|
657
|
+
) -> str:
|
646
658
|
"""Find a non-overlapping CIDR block"""
|
647
659
|
response = await anyio.to_thread.run_sync(self._ec2_client.describe_vpcs)
|
648
660
|
existing_cidrs = [vpc["CidrBlock"] for vpc in response["Vpcs"]]
|
@@ -708,9 +720,9 @@ class VpcResource:
|
|
708
720
|
|
709
721
|
async def provision(
|
710
722
|
self,
|
711
|
-
base_job_template:
|
723
|
+
base_job_template: dict[str, Any],
|
712
724
|
advance: Callable[[], None],
|
713
|
-
):
|
725
|
+
) -> None:
|
714
726
|
"""
|
715
727
|
Provisions a VPC.
|
716
728
|
|
@@ -768,7 +780,7 @@ class VpcResource:
|
|
768
780
|
)
|
769
781
|
)["AvailabilityZones"]
|
770
782
|
zones = [az["ZoneName"] for az in azs]
|
771
|
-
subnets = []
|
783
|
+
subnets: list[Any] = []
|
772
784
|
for i, subnet_cidr in enumerate(subnet_cidrs[0:3]):
|
773
785
|
subnets.append(
|
774
786
|
await anyio.to_thread.run_sync(
|
@@ -828,7 +840,7 @@ class VpcResource:
|
|
828
840
|
)
|
829
841
|
|
830
842
|
@property
|
831
|
-
def next_steps(self):
|
843
|
+
def next_steps(self) -> list[str]:
|
832
844
|
return []
|
833
845
|
|
834
846
|
|
@@ -838,9 +850,9 @@ class ContainerRepositoryResource:
|
|
838
850
|
self._repository_name = repository_name
|
839
851
|
self._requires_provisioning = None
|
840
852
|
self._work_pool_name = work_pool_name
|
841
|
-
self._next_steps = []
|
853
|
+
self._next_steps: list[str | Panel] = []
|
842
854
|
|
843
|
-
async def get_task_count(self):
|
855
|
+
async def get_task_count(self) -> int:
|
844
856
|
"""
|
845
857
|
Returns the number of tasks that will be executed to provision this resource.
|
846
858
|
|
@@ -895,9 +907,9 @@ class ContainerRepositoryResource:
|
|
895
907
|
|
896
908
|
async def provision(
|
897
909
|
self,
|
898
|
-
base_job_template:
|
910
|
+
base_job_template: dict[str, Any],
|
899
911
|
advance: Callable[[], None],
|
900
|
-
):
|
912
|
+
) -> None:
|
901
913
|
"""
|
902
914
|
Provisions an ECR repository.
|
903
915
|
|
@@ -978,7 +990,7 @@ class ContainerRepositoryResource:
|
|
978
990
|
)
|
979
991
|
|
980
992
|
@property
|
981
|
-
def next_steps(self):
|
993
|
+
def next_steps(self) -> list[str | Panel]:
|
982
994
|
return self._next_steps
|
983
995
|
|
984
996
|
|
@@ -1000,7 +1012,7 @@ class ExecutionRoleResource:
|
|
1000
1012
|
)
|
1001
1013
|
self._requires_provisioning = None
|
1002
1014
|
|
1003
|
-
async def get_task_count(self):
|
1015
|
+
async def get_task_count(self) -> int:
|
1004
1016
|
"""
|
1005
1017
|
Returns the number of tasks that will be executed to provision this resource.
|
1006
1018
|
|
@@ -1046,9 +1058,9 @@ class ExecutionRoleResource:
|
|
1046
1058
|
|
1047
1059
|
async def provision(
|
1048
1060
|
self,
|
1049
|
-
base_job_template:
|
1061
|
+
base_job_template: dict[str, Any],
|
1050
1062
|
advance: Callable[[], None],
|
1051
|
-
):
|
1063
|
+
) -> str:
|
1052
1064
|
"""
|
1053
1065
|
Provisions an IAM role.
|
1054
1066
|
|
@@ -1087,7 +1099,7 @@ class ExecutionRoleResource:
|
|
1087
1099
|
return response["Role"]["Arn"]
|
1088
1100
|
|
1089
1101
|
@property
|
1090
|
-
def next_steps(self):
|
1102
|
+
def next_steps(self) -> list[str]:
|
1091
1103
|
return []
|
1092
1104
|
|
1093
1105
|
|
@@ -1100,11 +1112,11 @@ class ElasticContainerServicePushProvisioner:
|
|
1100
1112
|
self._console = Console()
|
1101
1113
|
|
1102
1114
|
@property
|
1103
|
-
def console(self):
|
1115
|
+
def console(self) -> Console:
|
1104
1116
|
return self._console
|
1105
1117
|
|
1106
1118
|
@console.setter
|
1107
|
-
def console(self, value):
|
1119
|
+
def console(self, value: Console) -> None:
|
1108
1120
|
self._console = value
|
1109
1121
|
|
1110
1122
|
async def _prompt_boto3_installation(self):
|
@@ -1115,7 +1127,7 @@ class ElasticContainerServicePushProvisioner:
|
|
1115
1127
|
boto3 = importlib.import_module("boto3")
|
1116
1128
|
|
1117
1129
|
@staticmethod
|
1118
|
-
def is_boto3_installed():
|
1130
|
+
def is_boto3_installed() -> bool:
|
1119
1131
|
"""
|
1120
1132
|
Check if boto3 is installed.
|
1121
1133
|
"""
|
@@ -1157,8 +1169,8 @@ class ElasticContainerServicePushProvisioner:
|
|
1157
1169
|
async def provision(
|
1158
1170
|
self,
|
1159
1171
|
work_pool_name: str,
|
1160
|
-
base_job_template: dict,
|
1161
|
-
) ->
|
1172
|
+
base_job_template: dict[str, Any],
|
1173
|
+
) -> dict[str, Any]:
|
1162
1174
|
"""
|
1163
1175
|
Provisions the infrastructure for an ECS push work pool.
|
1164
1176
|
|
@@ -1310,7 +1322,7 @@ class ElasticContainerServicePushProvisioner:
|
|
1310
1322
|
# provision calls will be no-ops, but update the base job template
|
1311
1323
|
|
1312
1324
|
base_job_template_copy = deepcopy(base_job_template)
|
1313
|
-
next_steps = []
|
1325
|
+
next_steps: list[str | Panel] = []
|
1314
1326
|
with Progress(console=self._console, disable=num_tasks == 0) as progress:
|
1315
1327
|
task = progress.add_task(
|
1316
1328
|
"Provisioning Infrastructure",
|
@@ -2,6 +2,7 @@ import importlib
|
|
2
2
|
import shlex
|
3
3
|
import sys
|
4
4
|
from copy import deepcopy
|
5
|
+
from types import ModuleType
|
5
6
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
|
6
7
|
|
7
8
|
from anyio import run_process
|
@@ -19,7 +20,7 @@ if TYPE_CHECKING:
|
|
19
20
|
from prefect.client.orchestration import PrefectClient
|
20
21
|
|
21
22
|
|
22
|
-
modal = lazy_import("modal")
|
23
|
+
modal: ModuleType = lazy_import("modal")
|
23
24
|
|
24
25
|
|
25
26
|
class ModalPushProvisioner:
|
@@ -28,14 +29,14 @@ class ModalPushProvisioner:
|
|
28
29
|
"""
|
29
30
|
|
30
31
|
def __init__(self, client: Optional["PrefectClient"] = None):
|
31
|
-
self._console = Console()
|
32
|
+
self._console: Console = Console()
|
32
33
|
|
33
34
|
@property
|
34
|
-
def console(self):
|
35
|
+
def console(self) -> Console:
|
35
36
|
return self._console
|
36
37
|
|
37
38
|
@console.setter
|
38
|
-
def console(self, value):
|
39
|
+
def console(self, value: Console) -> None:
|
39
40
|
self._console = value
|
40
41
|
|
41
42
|
@staticmethod
|
prefect/input/actions.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import inspect
|
1
2
|
from typing import TYPE_CHECKING, Any, Optional, Set
|
2
3
|
from uuid import UUID
|
3
4
|
|
@@ -44,9 +45,12 @@ async def create_flow_run_input_from_model(
|
|
44
45
|
else:
|
45
46
|
json_safe = orjson.loads(model_instance.json())
|
46
47
|
|
47
|
-
|
48
|
+
coro = create_flow_run_input(
|
48
49
|
key=key, value=json_safe, flow_run_id=flow_run_id, sender=sender
|
49
50
|
)
|
51
|
+
if TYPE_CHECKING:
|
52
|
+
assert inspect.iscoroutine(coro)
|
53
|
+
await coro
|
50
54
|
|
51
55
|
|
52
56
|
@sync_compatible
|