coiled 1.128.3.dev21__tar.gz → 1.130.1.dev14__tar.gz
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.
Potentially problematic release.
This version of coiled might be problematic. Click here for more details.
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/PKG-INFO +1 -1
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/capture_environment.py +12 -5
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/azure.py +50 -1
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/core.py +21 -2
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/filestore.py +1 -1
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/pypi_conda_map.py +22 -96
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/software.py +18 -3
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/software_utils.py +33 -11
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/types.py +2 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/core.py +11 -1
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/widgets/rich.py +2 -1
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/.gitignore +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/LICENSE +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/README.md +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/__main__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/analytics.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/auth.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/batch.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/list.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/logs.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/run.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/status.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/util.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/batch/wait.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/azure_logs.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/better_logs.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/crud.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/get_address.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/list.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/logs.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/metrics.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/ssh.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/cluster/utils.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/config.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/core.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/curl.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/diagnostics.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/env.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/file.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/exit.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/hello_world.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/nyc_parquet.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/pytorch.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/examples/xarray_nwm.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/hello.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/scripts/fill_ipython.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/scripts/nyc_parquet.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/scripts/pytorch.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/scripts/xarray_nwm.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/hello/utils.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/login.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/mpi.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/notebook/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/notebook/notebook.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/package_sync.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/prefect.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/prefect_serve.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/run.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/amp.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/aws.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/entry.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/gcp.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/prometheus.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/setup/util.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/sync.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cli/utils.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/cluster.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/coiled.yaml +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/compatibility.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/config.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/context.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/credentials/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/credentials/aws.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/credentials/google.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/errors.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/exceptions.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/extensions/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/extensions/prefect/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/extensions/prefect/runners.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/extensions/prefect/workers.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/function.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/plugins.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/prefect.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/scan.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/spans.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/spark.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/utils.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/cluster.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/cluster_comms.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/cwi_log_link.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/states.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/widgets/__init__.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/widgets/interface.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/v2/widgets/util.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/coiled/websockets.py +0 -0
- {coiled-1.128.3.dev21 → coiled-1.130.1.dev14}/pyproject.toml +0 -0
|
@@ -14,9 +14,12 @@ from typing_extensions import Literal
|
|
|
14
14
|
from coiled.context import track_context
|
|
15
15
|
from coiled.scan import scan_prefix
|
|
16
16
|
from coiled.software_utils import (
|
|
17
|
+
ANY_AVAILABLE,
|
|
18
|
+
PYTHON_VERSION,
|
|
17
19
|
check_pip_happy,
|
|
18
20
|
create_wheels_for_local_python,
|
|
19
21
|
create_wheels_for_packages,
|
|
22
|
+
get_lockfile,
|
|
20
23
|
partition_ignored_packages,
|
|
21
24
|
partition_local_packages,
|
|
22
25
|
partition_local_python_code_packages,
|
|
@@ -34,10 +37,6 @@ from coiled.v2.core import CloudV2
|
|
|
34
37
|
from coiled.v2.widgets.rich import CONSOLE_WIDTH, print_rich_package_table
|
|
35
38
|
from coiled.v2.widgets.util import simple_progress, use_rich_widget
|
|
36
39
|
|
|
37
|
-
PYTHON_VERSION = platform.python_version_tuple()
|
|
38
|
-
ANY_AVAILABLE = "ANY-AVAILABLE"
|
|
39
|
-
|
|
40
|
-
|
|
41
40
|
logger = getLogger("coiled.package_sync")
|
|
42
41
|
|
|
43
42
|
|
|
@@ -68,6 +67,7 @@ async def approximate_packages(
|
|
|
68
67
|
pip_check_errors: Optional[Dict[str, List[str]]] = None,
|
|
69
68
|
gpu_enabled: bool = False,
|
|
70
69
|
use_uv_installer: bool = True,
|
|
70
|
+
lockfile_path: Optional[Path] = None,
|
|
71
71
|
) -> typing.List[ResolvedPackageInfo]:
|
|
72
72
|
user_conda_installed_python = next((p for p in packages if p["name"] == "python"), None)
|
|
73
73
|
# Only add pip if we need it
|
|
@@ -163,6 +163,8 @@ async def approximate_packages(
|
|
|
163
163
|
architecture=architecture,
|
|
164
164
|
pip_check_errors=pip_check_errors,
|
|
165
165
|
gpu_enabled=gpu_enabled,
|
|
166
|
+
lockfile_name=lockfile_path.name if lockfile_path else None,
|
|
167
|
+
lockfile_content=lockfile_path.read_text() if lockfile_path else None,
|
|
166
168
|
)
|
|
167
169
|
finalized_packages: typing.List[ResolvedPackageInfo] = []
|
|
168
170
|
finalized_packages.extend(await create_wheels_for_local_python(local_python_code, progress=progress))
|
|
@@ -240,6 +242,7 @@ async def create_environment_approximation(
|
|
|
240
242
|
pip_check_errors=pip_check_errors,
|
|
241
243
|
gpu_enabled=gpu_enabled,
|
|
242
244
|
use_uv_installer=use_uv_installer,
|
|
245
|
+
lockfile_path=get_lockfile(),
|
|
243
246
|
)
|
|
244
247
|
return result
|
|
245
248
|
|
|
@@ -262,7 +265,7 @@ async def scan_and_create(
|
|
|
262
265
|
):
|
|
263
266
|
use_widget = force_rich_widget or (show_widget and use_rich_widget())
|
|
264
267
|
|
|
265
|
-
local_env_name = Path(sys.prefix).name
|
|
268
|
+
local_env_name = str(get_lockfile() or Path(sys.prefix).name)
|
|
266
269
|
if use_widget:
|
|
267
270
|
progress = Progress(TextColumn("[progress.description]{task.description}"), BarColumn(), TimeElapsedColumn())
|
|
268
271
|
live = Live(Panel(progress, title=f"[green]Package Sync for {local_env_name}", width=CONSOLE_WIDTH))
|
|
@@ -271,6 +274,9 @@ async def scan_and_create(
|
|
|
271
274
|
progress = None
|
|
272
275
|
|
|
273
276
|
with live:
|
|
277
|
+
# We do this even with lockfiles because some early checks happen
|
|
278
|
+
# on this endpoint to prevent people getting delayed quota errors
|
|
279
|
+
# TODO: Add a lighter weight endpoint that does just these checks
|
|
274
280
|
with simple_progress("Fetching latest package priorities", progress):
|
|
275
281
|
logger.info(f"Resolving your local {local_env_name} Python environment...")
|
|
276
282
|
async with (
|
|
@@ -371,6 +377,7 @@ async def scan_and_create(
|
|
|
371
377
|
# default region in declarative service create_software_environment
|
|
372
378
|
region_name=region_name,
|
|
373
379
|
use_uv_installer=use_uv_installer,
|
|
380
|
+
lockfile_path=get_lockfile(),
|
|
374
381
|
)
|
|
375
382
|
if use_widget:
|
|
376
383
|
print_rich_package_table(packages_with_notes, packages_with_errors)
|
|
@@ -175,8 +175,21 @@ coiled curl -X POST "${SETUP_ENDPOINT}" --json --data "{\\"credentials\\": {\\"t
|
|
|
175
175
|
"service principal for Coiled to use."
|
|
176
176
|
),
|
|
177
177
|
)
|
|
178
|
+
@click.option(
|
|
179
|
+
"--refresh-for-app-id", default=None, help="Refresh the secret key used by Coiled for specified Application ID."
|
|
180
|
+
)
|
|
178
181
|
@click.command(context_settings=CONTEXT_SETTINGS)
|
|
179
|
-
def azure_setup(
|
|
182
|
+
def azure_setup(
|
|
183
|
+
subscription,
|
|
184
|
+
resource_group,
|
|
185
|
+
region,
|
|
186
|
+
account,
|
|
187
|
+
iam_user,
|
|
188
|
+
keep_existing_access,
|
|
189
|
+
save_script,
|
|
190
|
+
ship_token,
|
|
191
|
+
refresh_for_app_id,
|
|
192
|
+
):
|
|
180
193
|
print(
|
|
181
194
|
"Coiled on Azure is currently in [bold]public beta[/bold], "
|
|
182
195
|
"please contact [link]support@coiled.io[/link] if you have any questions or problems."
|
|
@@ -264,6 +277,17 @@ def azure_setup(subscription, resource_group, region, account, iam_user, keep_ex
|
|
|
264
277
|
f"with [green]{region}[/green] as the default region\n"
|
|
265
278
|
)
|
|
266
279
|
|
|
280
|
+
if refresh_for_app_id:
|
|
281
|
+
refresh_app_creds(
|
|
282
|
+
app_id=refresh_for_app_id,
|
|
283
|
+
coiled_account=coiled_account,
|
|
284
|
+
sub_id=sub_id,
|
|
285
|
+
rg_name=rg_name,
|
|
286
|
+
region=region,
|
|
287
|
+
keep_existing_keys=True,
|
|
288
|
+
)
|
|
289
|
+
return
|
|
290
|
+
|
|
267
291
|
if ship_token:
|
|
268
292
|
enable_providers(creds, sub_id)
|
|
269
293
|
ship_token_creds(
|
|
@@ -378,6 +402,31 @@ def setup_with_service_principal(
|
|
|
378
402
|
return True
|
|
379
403
|
|
|
380
404
|
|
|
405
|
+
def refresh_app_creds(app_id, keep_existing_keys, coiled_account, sub_id, rg_name, region):
|
|
406
|
+
print(f" [bright_black]Resetting/retrieving credentials for {app_id}...")
|
|
407
|
+
cred_reset_opts = "--append" if keep_existing_keys else ""
|
|
408
|
+
app_creds_json = az_cli_wrapper(f"az ad app credential reset --id {app_id} {cred_reset_opts}")
|
|
409
|
+
app_creds = json.loads(app_creds_json)
|
|
410
|
+
|
|
411
|
+
creds_to_submit = {
|
|
412
|
+
"tenant_id": app_creds["tenant"],
|
|
413
|
+
"client_id": app_creds["appId"],
|
|
414
|
+
"client_secret": app_creds["password"],
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
print("Sending Azure credentials to Coiled... ", end="")
|
|
418
|
+
submit_azure_credentials(
|
|
419
|
+
coiled_account=coiled_account,
|
|
420
|
+
sub_id=sub_id,
|
|
421
|
+
rg_name=rg_name,
|
|
422
|
+
region=region,
|
|
423
|
+
creds_to_submit=creds_to_submit,
|
|
424
|
+
)
|
|
425
|
+
print(f"Azure credentials have been updated for {coiled_account} using Azure app {app_id} and {rg_name}!")
|
|
426
|
+
|
|
427
|
+
coiled.add_interaction(action="RefreshCloudCredentials", success=True)
|
|
428
|
+
|
|
429
|
+
|
|
381
430
|
def submit_azure_credentials(coiled_account, sub_id, rg_name, region, creds_to_submit, check_after: bool = False):
|
|
382
431
|
with coiled.Cloud(account=coiled_account) as cloud:
|
|
383
432
|
setup_endpoint = f"/api/v2/cloud-credentials/{coiled_account}/azure"
|
|
@@ -924,6 +924,7 @@ class Cloud(Generic[IsAsynchronous]):
|
|
|
924
924
|
include_local_code: bool = False,
|
|
925
925
|
ignore_local_packages: List[str] | None = None,
|
|
926
926
|
use_uv_installer: bool = True,
|
|
927
|
+
lockfile_path: Union[str, pathlib.Path, None] = None,
|
|
927
928
|
) -> SoftwareEnvironmentAlias | None:
|
|
928
929
|
if name is None and conda is not None and isinstance(conda, dict) and "name" in conda:
|
|
929
930
|
name = conda["name"]
|
|
@@ -943,8 +944,20 @@ class Cloud(Generic[IsAsynchronous]):
|
|
|
943
944
|
raise TypeError("The build backend does not support specifying both packages and a container")
|
|
944
945
|
if container and include_local_code:
|
|
945
946
|
raise TypeError("The build backend does not support including local code when using a container")
|
|
946
|
-
if
|
|
947
|
-
|
|
947
|
+
if lockfile_path:
|
|
948
|
+
if pip or conda:
|
|
949
|
+
raise TypeError("The build backend does not support specifying both a lockfile and packages")
|
|
950
|
+
lockfile_path = pathlib.Path(lockfile_path)
|
|
951
|
+
if not lockfile_path.exists():
|
|
952
|
+
raise FileNotFoundError(f"Lockfile not found: {lockfile_path}")
|
|
953
|
+
if not lockfile_path.name.endswith(("uv.lock", "pylock.toml", "conda-lock.yml")):
|
|
954
|
+
logger.warning(
|
|
955
|
+
"The specified lockfile does not appear to be generated by a supported tool "
|
|
956
|
+
"(uv, pip, conda-lock). Proceeding anyway."
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
if conda or pip or lockfile_path:
|
|
960
|
+
senv = await create_env_spec(conda=conda, pip=pip, lockfile_path=lockfile_path)
|
|
948
961
|
if include_local_code:
|
|
949
962
|
prefix = await scan_prefix()
|
|
950
963
|
packages, _ = partition_ignored_packages(
|
|
@@ -1030,6 +1043,8 @@ class Cloud(Generic[IsAsynchronous]):
|
|
|
1030
1043
|
architecture: ArchitectureTypesEnum,
|
|
1031
1044
|
pip_check_errors: Dict[str, List[str]] | None = None,
|
|
1032
1045
|
gpu_enabled: bool = False,
|
|
1046
|
+
lockfile_name: str | None = None,
|
|
1047
|
+
lockfile_content: str | None = None,
|
|
1033
1048
|
) -> List[ApproximatePackageResult]:
|
|
1034
1049
|
response = await self._do_request(
|
|
1035
1050
|
"POST",
|
|
@@ -1046,6 +1061,8 @@ class Cloud(Generic[IsAsynchronous]):
|
|
|
1046
1061
|
"index_urls": get_index_urls(),
|
|
1047
1062
|
"pip_check_errors": pip_check_errors,
|
|
1048
1063
|
"gpu_enabled": gpu_enabled,
|
|
1064
|
+
"lockfile_name": lockfile_name,
|
|
1065
|
+
"lockfile_content": lockfile_content,
|
|
1049
1066
|
},
|
|
1050
1067
|
)
|
|
1051
1068
|
if response.status >= 400:
|
|
@@ -1133,6 +1150,8 @@ class Cloud(Generic[IsAsynchronous]):
|
|
|
1133
1150
|
"architecture": architecture,
|
|
1134
1151
|
"region_name": region_name,
|
|
1135
1152
|
"enable_experimental_installer": use_uv_installer,
|
|
1153
|
+
"lockfile_content": senv.get("lockfile_content") if senv else None,
|
|
1154
|
+
"lockfile_name": senv.get("lockfile_name") if senv else None,
|
|
1136
1155
|
}
|
|
1137
1156
|
if senv:
|
|
1138
1157
|
payload["packages"] = senv["packages"]
|
|
@@ -422,7 +422,7 @@ class FilestoreManagerWithoutHttp:
|
|
|
422
422
|
for chunk in response.iter_bytes(chunk_size=8192):
|
|
423
423
|
f.write(chunk)
|
|
424
424
|
return # Success, exit function
|
|
425
|
-
except (httpx.RemoteProtocolError, httpx.ReadTimeout, httpx.ConnectError) as e:
|
|
425
|
+
except (httpx.RemoteProtocolError, httpx.ReadTimeout, httpx.ConnectError, httpx.HTTPStatusError) as e:
|
|
426
426
|
if attempt < max_retries - 1:
|
|
427
427
|
wait_time = 2**attempt # Exponential backoff: 1s, 2s, 4s
|
|
428
428
|
if verbose:
|
|
@@ -1,58 +1,41 @@
|
|
|
1
1
|
# This file was auto-generated by scripts/update_pypi_conda_mapping.py
|
|
2
2
|
# DO NOT MANUALLY EDIT
|
|
3
3
|
PYPI_TO_CONDA = {
|
|
4
|
-
"Boruta": "boruta_py",
|
|
5
|
-
"Flask-Cors": "flask_cors",
|
|
6
|
-
"Flask-JSON": "flask_json",
|
|
7
|
-
"SoundFile": "pysoundfile",
|
|
8
|
-
"aiohttp_cors": "aiohttp-cors",
|
|
9
4
|
"annoy": "python-annoy",
|
|
10
5
|
"arrakis": "arrakis-python",
|
|
11
6
|
"art": "ascii-art",
|
|
12
7
|
"asana": "python-asana",
|
|
13
8
|
"backtrace": "python-backtrace",
|
|
14
|
-
"
|
|
9
|
+
"bash-completion": "py-bash-completion",
|
|
15
10
|
"bilby-pipe": "bilby_pipe",
|
|
16
11
|
"blis": "cython-blis",
|
|
12
|
+
"boruta": "boruta_py",
|
|
17
13
|
"build": "python-build",
|
|
18
14
|
"captest": "pvcaptest",
|
|
19
15
|
"cdo": "python-cdo",
|
|
20
|
-
"click_config_file": "click-config-file",
|
|
21
16
|
"cockroachdb": "cockroachdb-python",
|
|
22
|
-
"conda_lock": "conda-lock",
|
|
23
|
-
"conda_mirror": "conda-mirror",
|
|
24
17
|
"confluent-kafka": "python-confluent-kafka",
|
|
25
18
|
"coreapi": "python-coreapi",
|
|
26
19
|
"coreschema": "python-coreschema",
|
|
27
|
-
"cryptography_vectors": "cryptography-vectors",
|
|
28
20
|
"cufflinks": "python-cufflinks",
|
|
29
21
|
"dashing": "python-dashing",
|
|
30
22
|
"dask": "dask-core",
|
|
31
|
-
"data-morph-ai": "data-morph-ai",
|
|
32
|
-
"dataclasses": "dataclasses",
|
|
33
23
|
"datadotworld": "datadotworld-py",
|
|
34
|
-
"
|
|
35
|
-
"delegator.py": "delegator",
|
|
24
|
+
"delegator-py": "delegator",
|
|
36
25
|
"dftd3": "dftd3-python",
|
|
37
26
|
"dftd4": "dftd4-python",
|
|
38
|
-
"dials_data": "dials-data",
|
|
39
27
|
"docker": "docker-py",
|
|
40
28
|
"duckdb": "python-duckdb",
|
|
41
|
-
"dye_score": "dye-score",
|
|
42
29
|
"eccodes": "python-eccodes",
|
|
43
|
-
"empyrical_dist": "empyrical-dist",
|
|
44
|
-
"enum34": "enum34",
|
|
45
30
|
"esprima": "esprima-python",
|
|
46
|
-
"et_xmlfile": "et-xmlfile",
|
|
47
31
|
"evalml": "evaml-core",
|
|
48
|
-
"exceptiongroup": "exceptiongroup",
|
|
49
32
|
"extract-msg": "msg-extractor",
|
|
50
33
|
"fastjsonschema": "python-fastjsonschema",
|
|
51
34
|
"flair": "python-flair",
|
|
52
|
-
"
|
|
35
|
+
"flask-cors": "flask_cors",
|
|
36
|
+
"flask-json": "flask_json",
|
|
53
37
|
"flatbuffers": "python-flatbuffers",
|
|
54
38
|
"flex": "flex-swagger",
|
|
55
|
-
"flit_core": "flit-core",
|
|
56
39
|
"gnupg": "gnupg-py",
|
|
57
40
|
"google": "googlesearch",
|
|
58
41
|
"google-cloud-bigquery": "google-cloud-bigquery-core",
|
|
@@ -63,47 +46,32 @@ PYPI_TO_CONDA = {
|
|
|
63
46
|
"hdfs": "python-hdfs",
|
|
64
47
|
"hjson": "hjson-py",
|
|
65
48
|
"htcondor": "python-htcondor",
|
|
66
|
-
"
|
|
67
|
-
"ib_insync": "ib-insync",
|
|
68
|
-
"import_metadata": "import_metadata",
|
|
69
|
-
"importlib_metadata": "importlib-metadata",
|
|
49
|
+
"import-metadata": "import_metadata",
|
|
70
50
|
"installer": "python-installer",
|
|
71
51
|
"intake-astro": "intake-accumulo",
|
|
72
|
-
"intake_geopandas": "intake-geopandas",
|
|
73
|
-
"ioos_tools": "ioos-tools",
|
|
74
52
|
"jsii": "python-jsii",
|
|
75
53
|
"jupyter-client": "jupyter_client",
|
|
76
|
-
"jupyter_jaeger": "jupyter-jaeger",
|
|
77
|
-
"jupyterlab_git": "jupyterlab-git",
|
|
78
|
-
"jupyterlab_latex": "jupyterlab-latex",
|
|
79
54
|
"kaleido": "python-kaleido",
|
|
80
55
|
"kubernetes": "python-kubernetes",
|
|
81
56
|
"libaio": "python-libaio",
|
|
82
57
|
"libnacl": "libnacl-python-bindings",
|
|
83
58
|
"lmdb": "python-lmdb",
|
|
84
59
|
"matplotlib": "matplotlib-base",
|
|
85
|
-
"md_toc": "md-toc",
|
|
86
|
-
"message_ix": "message-ix",
|
|
87
60
|
"msgpack": "msgpack-python",
|
|
88
61
|
"mss": "python-mss",
|
|
89
|
-
"nbconvert_utils": "nbconvert-utils",
|
|
90
|
-
"ndarray_listener": "ndarray-listener",
|
|
91
62
|
"neo": "python-neo",
|
|
92
63
|
"neo4j": "neo4j-python-driver",
|
|
93
|
-
"nest_asyncio": "nest-asyncio",
|
|
94
64
|
"node-semver": "python-node-semver",
|
|
95
65
|
"nvidia-ml-py3": "nvidia-ml",
|
|
96
66
|
"opencv-python": "opencv",
|
|
97
67
|
"opencv-python-headless": "opencv",
|
|
98
68
|
"ortools": "ortools-python",
|
|
99
69
|
"p-astro": "p_astro",
|
|
100
|
-
"pandas_flavor": "pandas-flavor",
|
|
101
70
|
"paragraph": "python-paragraph",
|
|
102
71
|
"prince": "prince-factor-analysis",
|
|
103
72
|
"pvlib": "pvlib-python",
|
|
104
73
|
"pyqt4": "pyqt",
|
|
105
74
|
"pyqt5": "pyqt",
|
|
106
|
-
"pytest_check_links": "pytest-check-links",
|
|
107
75
|
"python-datamatrix": "datamatrix",
|
|
108
76
|
"python-fileinspector": "fileinspector",
|
|
109
77
|
"python-opencv": "opencv",
|
|
@@ -113,16 +81,12 @@ PYPI_TO_CONDA = {
|
|
|
113
81
|
"python-qnotifications": "qnotifications",
|
|
114
82
|
"pywin32": "pywin32-on-windows",
|
|
115
83
|
"quantum-grove": "grove",
|
|
116
|
-
"radio_beam": "radio-beam",
|
|
117
84
|
"redis": "redis-py",
|
|
118
85
|
"rocketpyalpha": "rocketpy",
|
|
119
|
-
"
|
|
120
|
-
"
|
|
121
|
-
"sagemaker_mxnet_training": "sagemaker_mxnet_container",
|
|
122
|
-
"scikit-build": "scikit-build",
|
|
123
|
-
"scikit-learn": "scikit-learn",
|
|
124
|
-
"setuptools_scm": "setuptools-scm",
|
|
86
|
+
"sagemaker-inference": "sagemaker-inference-toolkit",
|
|
87
|
+
"sagemaker-mxnet-training": "sagemaker_mxnet_container",
|
|
125
88
|
"sounddevice": "python-sounddevice",
|
|
89
|
+
"soundfile": "pysoundfile",
|
|
126
90
|
"spherical-functions": "spherical_functions",
|
|
127
91
|
"sphinx-rtd-theme": "sphinx_rtd_theme",
|
|
128
92
|
"stjudecloud-oliver": "oliver",
|
|
@@ -133,54 +97,37 @@ PYPI_TO_CONDA = {
|
|
|
133
97
|
"termstyle": "python-termstyle",
|
|
134
98
|
"torch": "pytorch",
|
|
135
99
|
"torch-cluster": "pytorch_cluster",
|
|
100
|
+
"torch-geometric": "pytorch_geometric",
|
|
136
101
|
"torch-sparse": "pytorch_sparse",
|
|
137
|
-
"torch_geometric": "pytorch_geometric",
|
|
138
102
|
"trino": "trino-python-client",
|
|
139
|
-
"typing": "typing",
|
|
140
103
|
"typing-extensions": "typing_extensions",
|
|
141
|
-
"
|
|
104
|
+
"usedave": "dave",
|
|
142
105
|
"vsts": "vsts-python-api",
|
|
143
106
|
"xtb": "xtb-python",
|
|
144
107
|
"xxhash": "python-xxhash",
|
|
145
108
|
}
|
|
146
109
|
|
|
147
110
|
CONDA_TO_PYPI = {
|
|
148
|
-
"aiohttp-cors": "aiohttp_cors",
|
|
149
111
|
"arrakis-python": "arrakis",
|
|
150
112
|
"ascii-art": "art",
|
|
151
113
|
"bilby_pipe": "bilby-pipe",
|
|
152
|
-
"boruta_py": "
|
|
153
|
-
"click-config-file": "click_config_file",
|
|
114
|
+
"boruta_py": "boruta",
|
|
154
115
|
"cockroachdb-python": "cockroachdb",
|
|
155
|
-
"conda-lock": "conda_lock",
|
|
156
|
-
"conda-mirror": "conda_mirror",
|
|
157
|
-
"cryptography-vectors": "cryptography_vectors",
|
|
158
116
|
"cython-blis": "blis",
|
|
159
117
|
"dask-core": "dask",
|
|
160
|
-
"data-morph-ai": "data-morph-ai",
|
|
161
|
-
"dataclasses": "dataclasses",
|
|
162
118
|
"datadotworld-py": "datadotworld",
|
|
163
|
-
"datalad-container": "datalad_container",
|
|
164
119
|
"datamatrix": "python-datamatrix",
|
|
165
|
-
"dave": "
|
|
166
|
-
"delegator": "delegator
|
|
120
|
+
"dave": "usedave",
|
|
121
|
+
"delegator": "delegator-py",
|
|
167
122
|
"dftd3-python": "dftd3",
|
|
168
123
|
"dftd4-python": "dftd4",
|
|
169
|
-
"dials-data": "dials_data",
|
|
170
124
|
"docker-py": "docker",
|
|
171
|
-
"dye-score": "dye_score",
|
|
172
|
-
"empyrical-dist": "empyrical_dist",
|
|
173
|
-
"enum34": "enum34",
|
|
174
125
|
"esprima-python": "esprima",
|
|
175
|
-
"et-xmlfile": "et_xmlfile",
|
|
176
126
|
"evaml-core": "evalml",
|
|
177
|
-
"exceptiongroup": "exceptiongroup",
|
|
178
127
|
"fileinspector": "python-fileinspector",
|
|
179
|
-
"
|
|
180
|
-
"
|
|
181
|
-
"flask_json": "Flask-JSON",
|
|
128
|
+
"flask_cors": "flask-cors",
|
|
129
|
+
"flask_json": "flask-json",
|
|
182
130
|
"flex-swagger": "flex",
|
|
183
|
-
"flit-core": "flit_core",
|
|
184
131
|
"gnupg-py": "gnupg",
|
|
185
132
|
"google-cloud-bigquery-core": "google-cloud-bigquery",
|
|
186
133
|
"googlesearch": "google",
|
|
@@ -188,43 +135,28 @@ CONDA_TO_PYPI = {
|
|
|
188
135
|
"h2o-py": "h2o",
|
|
189
136
|
"h3-py": "h3",
|
|
190
137
|
"hjson-py": "hjson",
|
|
191
|
-
"
|
|
192
|
-
"ib-insync": "ib_insync",
|
|
193
|
-
"import_metadata": "import_metadata",
|
|
194
|
-
"importlib-metadata": "importlib_metadata",
|
|
138
|
+
"import_metadata": "import-metadata",
|
|
195
139
|
"intake-accumulo": "intake-astro",
|
|
196
|
-
"intake-geopandas": "intake_geopandas",
|
|
197
|
-
"ioos-tools": "ioos_tools",
|
|
198
|
-
"jupyter-jaeger": "jupyter_jaeger",
|
|
199
140
|
"jupyter_client": "jupyter-client",
|
|
200
|
-
"jupyterlab-git": "jupyterlab_git",
|
|
201
|
-
"jupyterlab-latex": "jupyterlab_latex",
|
|
202
141
|
"libnacl-python-bindings": "libnacl",
|
|
203
142
|
"matplotlib-base": "matplotlib",
|
|
204
|
-
"md-toc": "md_toc",
|
|
205
|
-
"message-ix": "message_ix",
|
|
206
143
|
"msg-extractor": "extract-msg",
|
|
207
144
|
"msgpack-python": "msgpack",
|
|
208
|
-
"nbconvert-utils": "nbconvert_utils",
|
|
209
|
-
"ndarray-listener": "ndarray_listener",
|
|
210
145
|
"neo4j-python-driver": "neo4j",
|
|
211
|
-
"nest-asyncio": "nest_asyncio",
|
|
212
146
|
"nvidia-ml": "nvidia-ml-py3",
|
|
213
147
|
"oliver": "stjudecloud-oliver",
|
|
214
148
|
"opencv": "python-opencv",
|
|
215
149
|
"ortools-python": "ortools",
|
|
216
150
|
"p_astro": "p-astro",
|
|
217
|
-
"pandas-flavor": "pandas_flavor",
|
|
218
151
|
"prince-factor-analysis": "prince",
|
|
219
152
|
"pseudorandom": "python-pseudorandom",
|
|
220
153
|
"pvcaptest": "captest",
|
|
221
154
|
"pvlib-python": "pvlib",
|
|
222
|
-
"py-bash-completion": "
|
|
155
|
+
"py-bash-completion": "bash-completion",
|
|
223
156
|
"pygaze": "python-pygaze",
|
|
224
157
|
"pyqt": "pyqt5",
|
|
225
|
-
"pysoundfile": "
|
|
158
|
+
"pysoundfile": "soundfile",
|
|
226
159
|
"pytables": "tables",
|
|
227
|
-
"pytest-check-links": "pytest_check_links",
|
|
228
160
|
"python-annoy": "annoy",
|
|
229
161
|
"python-asana": "asana",
|
|
230
162
|
"python-backtrace": "backtrace",
|
|
@@ -261,25 +193,19 @@ CONDA_TO_PYPI = {
|
|
|
261
193
|
"python-xxhash": "xxhash",
|
|
262
194
|
"pytorch": "torch",
|
|
263
195
|
"pytorch_cluster": "torch-cluster",
|
|
264
|
-
"pytorch_geometric": "
|
|
196
|
+
"pytorch_geometric": "torch-geometric",
|
|
265
197
|
"pytorch_sparse": "torch-sparse",
|
|
266
198
|
"pywin32-on-windows": "pywin32",
|
|
267
199
|
"qdatamatrix": "python-qdatamatrix",
|
|
268
200
|
"qnotifications": "python-qnotifications",
|
|
269
|
-
"radio-beam": "radio_beam",
|
|
270
201
|
"redis-py": "redis",
|
|
271
202
|
"rocketpy": "rocketpyalpha",
|
|
272
|
-
"
|
|
273
|
-
"
|
|
274
|
-
"sagemaker_mxnet_container": "sagemaker_mxnet_training",
|
|
275
|
-
"scikit-build": "scikit-build",
|
|
276
|
-
"scikit-learn": "scikit-learn",
|
|
277
|
-
"setuptools-scm": "setuptools_scm",
|
|
203
|
+
"sagemaker-inference-toolkit": "sagemaker-inference",
|
|
204
|
+
"sagemaker_mxnet_container": "sagemaker-mxnet-training",
|
|
278
205
|
"spherical_functions": "spherical-functions",
|
|
279
206
|
"sphinx_rtd_theme": "sphinx-rtd-theme",
|
|
280
207
|
"tblite-python": "tblite",
|
|
281
208
|
"trino-python-client": "trino",
|
|
282
|
-
"typing": "typing",
|
|
283
209
|
"typing_extensions": "typing-extensions",
|
|
284
210
|
"vsts-python-api": "vsts",
|
|
285
211
|
"xtb-python": "xtb",
|
|
@@ -118,6 +118,9 @@ def parse_pip(pip: Union[List[str], str, Path]) -> Tuple[List[PackageSchema], Li
|
|
|
118
118
|
if req["is_editable"]:
|
|
119
119
|
logger.warning(f"Editable requirement {raw_line!r} is not supported and will be ignored")
|
|
120
120
|
continue
|
|
121
|
+
if req.get("is_local_path", False):
|
|
122
|
+
logger.warning(f"Local path requirement {raw_line!r} is not supported and will be ignored")
|
|
123
|
+
continue
|
|
121
124
|
if req["is_vcs_url"]:
|
|
122
125
|
raw_pip.append(raw_line)
|
|
123
126
|
continue
|
|
@@ -139,10 +142,22 @@ def parse_pip(pip: Union[List[str], str, Path]) -> Tuple[List[PackageSchema], Li
|
|
|
139
142
|
async def create_env_spec(
|
|
140
143
|
conda: Union[CondaEnvSchema, str, Path, list, None] = None,
|
|
141
144
|
pip: Union[List[str], str, Path, None] = None,
|
|
145
|
+
lockfile_path: Union[str, Path, None] = None,
|
|
142
146
|
) -> SoftwareEnvSpec:
|
|
143
|
-
if not conda and not pip:
|
|
144
|
-
raise TypeError("
|
|
145
|
-
spec: SoftwareEnvSpec = {
|
|
147
|
+
if not conda and not pip and not lockfile_path:
|
|
148
|
+
raise TypeError("At least one of the conda, pip, and lockfile_path kwargs must be specified")
|
|
149
|
+
spec: SoftwareEnvSpec = {
|
|
150
|
+
"packages": [],
|
|
151
|
+
"raw_conda": None,
|
|
152
|
+
"raw_pip": None,
|
|
153
|
+
"lockfile_name": None,
|
|
154
|
+
"lockfile_content": None,
|
|
155
|
+
}
|
|
156
|
+
if lockfile_path:
|
|
157
|
+
lockfile_path = Path(lockfile_path)
|
|
158
|
+
lockfile_content = lockfile_path.read_text()
|
|
159
|
+
spec["lockfile_name"] = lockfile_path.name
|
|
160
|
+
spec["lockfile_content"] = lockfile_content
|
|
146
161
|
if conda:
|
|
147
162
|
packages, raw_conda, raw_pip = parse_conda(conda)
|
|
148
163
|
spec["raw_conda"] = raw_conda
|
|
@@ -477,11 +477,10 @@ async def check_pip_happy(progress: Progress | None = None) -> Dict[str, List[st
|
|
|
477
477
|
return faulty_packages
|
|
478
478
|
|
|
479
479
|
|
|
480
|
-
def
|
|
480
|
+
def _find_file_in_hierarchy(path: Union[Path, str]) -> Path | None:
|
|
481
481
|
path = Path(path)
|
|
482
482
|
filename = path.name
|
|
483
483
|
dir_path = safe_path_resolve(path.parent)
|
|
484
|
-
toml_dict = {}
|
|
485
484
|
# Walk up the directory tree to find the first toml file (up to 10 levels above)
|
|
486
485
|
for _ in range(10):
|
|
487
486
|
path = dir_path / filename
|
|
@@ -490,13 +489,21 @@ def _load_toml(path: Union[Path, str]) -> Dict:
|
|
|
490
489
|
break
|
|
491
490
|
dir_path = dir_path.parent
|
|
492
491
|
continue
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
492
|
+
return path
|
|
493
|
+
return None
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _load_toml(path: Union[Path, str]) -> Dict:
|
|
497
|
+
resolved_path = _find_file_in_hierarchy(path)
|
|
498
|
+
if not resolved_path:
|
|
499
|
+
logger.debug(f"Failed to find {path}")
|
|
500
|
+
return {}
|
|
501
|
+
try:
|
|
502
|
+
with resolved_path.open() as f:
|
|
503
|
+
return toml.load(f)
|
|
504
|
+
except Exception as e:
|
|
505
|
+
logger.debug(f"Failed to load {path}: {e}", exc_info=True)
|
|
506
|
+
return {}
|
|
500
507
|
|
|
501
508
|
|
|
502
509
|
def _get_pip_index_urls() -> List[str]:
|
|
@@ -572,6 +579,9 @@ def get_mamba_auth_dict(home_dir: Path | None = None) -> dict[str, tuple[str, st
|
|
|
572
579
|
if auth_file.exists():
|
|
573
580
|
with auth_file.open("r") as f:
|
|
574
581
|
auth_data = json.load(f)
|
|
582
|
+
if not isinstance(auth_data, dict):
|
|
583
|
+
logger.debug(f"Mamba auth file {auth_file} does not contain a dictionary at top level")
|
|
584
|
+
return domain_auth
|
|
575
585
|
for domain, auth in auth_data.items():
|
|
576
586
|
auth_type = auth.get("type")
|
|
577
587
|
if auth_type == "CondaToken":
|
|
@@ -634,6 +644,9 @@ def get_rattler_auth_dict(home_dir: Path | None = None) -> dict[str, tuple[str,
|
|
|
634
644
|
if auth_file.exists():
|
|
635
645
|
with auth_file.open("r") as f:
|
|
636
646
|
auth_data = json.load(f)
|
|
647
|
+
if not isinstance(auth_data, dict):
|
|
648
|
+
logger.debug(f"Rattler auth file {auth_file} does not contain a dictionary at top level")
|
|
649
|
+
return domain_auth
|
|
637
650
|
for domain, auth in auth_data.items():
|
|
638
651
|
parsed_auth = _parse_rattler_auth_data(auth)
|
|
639
652
|
if parsed_auth:
|
|
@@ -880,8 +893,8 @@ def set_auth_for_url(url: Url | str) -> str:
|
|
|
880
893
|
or (get_conda_auth(no_auth_url) if use_conda_auth else None)
|
|
881
894
|
# mamba could have URL stored by netloc/path or netloc
|
|
882
895
|
or ((get_mamba_auth(f"{netloc}{path}") or get_mamba_auth(netloc)) if use_mamba_auth else None)
|
|
883
|
-
# rattler/pixi
|
|
884
|
-
or (get_rattler_auth(netloc) if use_rattler_auth else None)
|
|
896
|
+
# rattler/pixi could store netloc or *.netloc in keyring or a fallback file
|
|
897
|
+
or ((get_rattler_auth(netloc) or get_rattler_auth(f"*.{netloc}")) if use_rattler_auth else None)
|
|
885
898
|
)
|
|
886
899
|
if auth_parts is not None:
|
|
887
900
|
username, password = auth_parts
|
|
@@ -1035,3 +1048,12 @@ def make_coiled_local_name(dirname: str):
|
|
|
1035
1048
|
if not cleaned_name:
|
|
1036
1049
|
cleaned_name = "rootdir"
|
|
1037
1050
|
return COILED_LOCAL_PACKAGE_PREFIX + cleaned_name
|
|
1051
|
+
|
|
1052
|
+
|
|
1053
|
+
def get_lockfile() -> Path | None:
|
|
1054
|
+
"""Search for a supported lockfile and return its path"""
|
|
1055
|
+
# TODO: Add support for other lockfiles
|
|
1056
|
+
lockfile = None
|
|
1057
|
+
if dask.config.get("coiled.package_sync.use_lockfile.uv", True):
|
|
1058
|
+
lockfile = _find_file_in_hierarchy("uv.lock")
|
|
1059
|
+
return lockfile
|
|
@@ -154,6 +154,8 @@ class SoftwareEnvSpec(TypedDict):
|
|
|
154
154
|
packages: List[PackageSchema]
|
|
155
155
|
raw_pip: Optional[List[str]]
|
|
156
156
|
raw_conda: Optional[CondaEnvSchema]
|
|
157
|
+
lockfile_name: Optional[str]
|
|
158
|
+
lockfile_content: Optional[str]
|
|
157
159
|
|
|
158
160
|
|
|
159
161
|
# KNOWN_SUBDIRS is copied from conda's known subdirs
|
|
@@ -7,6 +7,7 @@ import logging
|
|
|
7
7
|
import time
|
|
8
8
|
import weakref
|
|
9
9
|
from collections import namedtuple
|
|
10
|
+
from pathlib import Path
|
|
10
11
|
from typing import (
|
|
11
12
|
Awaitable,
|
|
12
13
|
Callable,
|
|
@@ -523,6 +524,7 @@ class CloudV2(OldCloud, Generic[IsAsynchronous]):
|
|
|
523
524
|
architecture: ArchitectureTypesEnum = ArchitectureTypesEnum.X86_64,
|
|
524
525
|
region_name: str | None = None,
|
|
525
526
|
use_uv_installer: bool = True,
|
|
527
|
+
lockfile_path: str | Path | None = None,
|
|
526
528
|
) -> SoftwareEnvironmentAlias:
|
|
527
529
|
workspace = workspace or self.default_workspace
|
|
528
530
|
prepared_packages: List[PackageSchema] = []
|
|
@@ -547,12 +549,20 @@ class CloudV2(OldCloud, Generic[IsAsynchronous]):
|
|
|
547
549
|
"client_version": pkg["client_version"],
|
|
548
550
|
"file": file_id,
|
|
549
551
|
})
|
|
550
|
-
|
|
552
|
+
lockfile_content = None
|
|
553
|
+
if lockfile_path:
|
|
554
|
+
lockfile_path = Path(lockfile_path)
|
|
555
|
+
lockfile_content = lockfile_path.read_text()
|
|
556
|
+
with simple_progress(
|
|
557
|
+
"Requesting package sync build" if not lockfile_path else "Creating software environment", progress=progress
|
|
558
|
+
):
|
|
551
559
|
result = await self._create_software_environment_v2(
|
|
552
560
|
senv={
|
|
553
561
|
"packages": prepared_packages,
|
|
554
562
|
"raw_pip": None,
|
|
555
563
|
"raw_conda": None,
|
|
564
|
+
"lockfile_name": str(lockfile_path.name) if lockfile_path else None,
|
|
565
|
+
"lockfile_content": lockfile_content,
|
|
556
566
|
},
|
|
557
567
|
workspace=workspace,
|
|
558
568
|
architecture=architecture,
|
|
@@ -22,6 +22,7 @@ import rich.progress
|
|
|
22
22
|
import rich.table
|
|
23
23
|
from rich.console import RenderableType
|
|
24
24
|
|
|
25
|
+
from coiled.software_utils import get_lockfile
|
|
25
26
|
from coiled.types import PackageLevelEnum
|
|
26
27
|
|
|
27
28
|
from ...capture_environment import ResolvedPackageInfo
|
|
@@ -289,7 +290,7 @@ class LightRichClusterWidget(ClusterWidget):
|
|
|
289
290
|
env_name = self._cluster_details["senv_alias"]["name"]
|
|
290
291
|
if env_name.startswith("package-sync-"):
|
|
291
292
|
env_name = ""
|
|
292
|
-
local_env_name = Path(sys.prefix).name
|
|
293
|
+
local_env_name = str(get_lockfile() or Path(sys.prefix).name)
|
|
293
294
|
env_line = f"[bold green]Synced local Python environment:[/bold green] {local_env_name}"
|
|
294
295
|
else:
|
|
295
296
|
local_env_name = ""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|