arraylake 0.31.0__tar.gz → 0.33.0__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.
- {arraylake-0.31.0 → arraylake-0.33.0}/.gitignore +1 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/PKG-INFO +2 -2
- arraylake-0.33.0/arraylake/_version.py +24 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/compute.py +2 -2
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/config.py +2 -2
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/repo.py +6 -8
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/client.py +126 -102
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/types.py +4 -4
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/http_metastore.py +67 -27
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/types.py +23 -3
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/virtual.py +23 -12
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/types.py +66 -41
- {arraylake-0.31.0 → arraylake-0.33.0}/pyproject.toml +2 -2
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_cli.py +10 -6
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_client.py +43 -11
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_performance.py +24 -12
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_subscriptions.py +36 -4
- arraylake-0.33.0/tests/test_virtual_chunks.py +1202 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/uv.lock +117 -436
- arraylake-0.31.0/arraylake/_version.py +0 -34
- arraylake-0.31.0/tests/test_virtual_chunks.py +0 -803
- {arraylake-0.31.0 → arraylake-0.33.0}/README.md +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/__main__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/api_utils.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/asyn.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/auth.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/main.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/utils.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/doctor.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/http_client.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/services.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/config.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/config.yaml +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/credentials.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/diagnostics.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/repo.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/repolist.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/exceptions.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/log_util.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/abc.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/py.typed +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/storage.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/token.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/tuning.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/compute/test_client.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/compute/test_doctor.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/config.yaml +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/conftest.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/__init__.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/conftest.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/test_storage.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/test_virtual.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_api_utils.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_asyn.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_config.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_credentials.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_diagnostics.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_token_handler.py +0 -0
- {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arraylake
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.33.0
|
|
4
4
|
Summary: Python client for ArrayLake
|
|
5
5
|
Project-URL: Documentation, https://docs.earthmover.io/
|
|
6
6
|
Project-URL: Changelog, https://docs.earthmover.io/changelog
|
|
@@ -28,7 +28,7 @@ Provides-Extra: maximal
|
|
|
28
28
|
Requires-Dist: cf-xarray>=0.10.4; extra == 'maximal'
|
|
29
29
|
Requires-Dist: numpy<2.3; extra == 'maximal'
|
|
30
30
|
Requires-Dist: pyarrow<22; extra == 'maximal'
|
|
31
|
-
Requires-Dist: xarray[
|
|
31
|
+
Requires-Dist: xarray[accel,etc,io,parallel]>=2024.10.0; extra == 'maximal'
|
|
32
32
|
Provides-Extra: xarray
|
|
33
33
|
Requires-Dist: cf-xarray>=0.10.4; extra == 'xarray'
|
|
34
34
|
Requires-Dist: xarray>=2024.10.0; extra == 'xarray'
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.33.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 33, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from enum import
|
|
2
|
+
from enum import StrEnum
|
|
3
3
|
|
|
4
4
|
import typer
|
|
5
5
|
from rich import print_json
|
|
@@ -14,7 +14,7 @@ app = typer.Typer(help="Manage Arraylake compute services", no_args_is_help=True
|
|
|
14
14
|
logger = get_logger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class ListOutputType(
|
|
17
|
+
class ListOutputType(StrEnum):
|
|
18
18
|
rich = "rich"
|
|
19
19
|
json = "json"
|
|
20
20
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import io
|
|
2
|
-
from enum import
|
|
2
|
+
from enum import StrEnum
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import typer
|
|
@@ -17,7 +17,7 @@ yaml = YAML(typ="safe", pure=True)
|
|
|
17
17
|
yaml.default_flow_style = False
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class ListOutputType(
|
|
20
|
+
class ListOutputType(StrEnum):
|
|
21
21
|
json = "json"
|
|
22
22
|
yaml = "yaml"
|
|
23
23
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import warnings
|
|
3
|
-
from enum import Enum
|
|
3
|
+
from enum import Enum, StrEnum
|
|
4
4
|
from typing import TYPE_CHECKING, Optional
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
@@ -30,7 +30,7 @@ app = typer.Typer(help="Manage Arraylake repositories", no_args_is_help=True)
|
|
|
30
30
|
logger = get_logger(__name__)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
class ListOutputType(
|
|
33
|
+
class ListOutputType(StrEnum):
|
|
34
34
|
rich = "rich"
|
|
35
35
|
json = "json"
|
|
36
36
|
|
|
@@ -142,7 +142,6 @@ async def create(
|
|
|
142
142
|
repo_name: str = typer.Argument(..., help="Name of repository {ORG}/{REPO_NAME}"),
|
|
143
143
|
bucket_config_nickname: str | None = typer.Option(None, help="Bucket config nickname"),
|
|
144
144
|
prefix: str = typer.Option(None, help="Optional prefix for Icechunk repo. If not provided, a random ID + the repo name will be used."),
|
|
145
|
-
import_existing: bool = typer.Option(False, help="Import existing Icechunk data into the new repo"),
|
|
146
145
|
description: str | None = typer.Option(None, help="Description of the repo"),
|
|
147
146
|
metadata: str | None = typer.Option(None, help="Optional metadata for the repo"),
|
|
148
147
|
):
|
|
@@ -162,7 +161,6 @@ async def create(
|
|
|
162
161
|
repo_name,
|
|
163
162
|
bucket_config_nickname=bucket_config_nickname,
|
|
164
163
|
prefix=prefix,
|
|
165
|
-
import_existing=import_existing,
|
|
166
164
|
description=description,
|
|
167
165
|
metadata=metadata_dict,
|
|
168
166
|
)
|
|
@@ -173,7 +171,7 @@ async def create(
|
|
|
173
171
|
async def import_repo(
|
|
174
172
|
repo_name: str = typer.Argument(..., help="Name of repository {ORG}/{REPO_NAME}"),
|
|
175
173
|
bucket_config_nickname: str = typer.Argument(..., help="Bucket config nickname"),
|
|
176
|
-
prefix: str = typer.
|
|
174
|
+
prefix: str = typer.Argument(..., help="Prefix in which the Icechunk repo exists in the bucket"),
|
|
177
175
|
description: str | None = typer.Option(None, help="Description of the repo"),
|
|
178
176
|
metadata: str | None = typer.Option(None, help="Optional metadata for the repo"),
|
|
179
177
|
):
|
|
@@ -184,15 +182,15 @@ async def import_repo(
|
|
|
184
182
|
- Import existing Icechunk repository
|
|
185
183
|
|
|
186
184
|
```
|
|
187
|
-
$ arraylake repo import my-org/example-repo
|
|
185
|
+
$ arraylake repo import my-org/example-repo icechunk-bucket my-icechunk-prefix --metadata '{"key": "value"}'
|
|
188
186
|
```
|
|
189
187
|
"""
|
|
190
188
|
metadata_dict = json.loads(metadata) if metadata else None
|
|
191
189
|
with simple_progress(f"Importing repo [bold]{repo_name}[/bold]..."):
|
|
192
190
|
await AsyncClient().import_repo(
|
|
193
191
|
repo_name,
|
|
194
|
-
bucket_config_nickname
|
|
195
|
-
prefix
|
|
192
|
+
bucket_config_nickname,
|
|
193
|
+
prefix,
|
|
196
194
|
description=description,
|
|
197
195
|
metadata=metadata_dict,
|
|
198
196
|
)
|
|
@@ -46,6 +46,7 @@ from arraylake.repos.icechunk.storage import (
|
|
|
46
46
|
_get_icechunk_storage_obj,
|
|
47
47
|
)
|
|
48
48
|
from arraylake.repos.icechunk.virtual import (
|
|
49
|
+
create_vcc_store_config_for_platform,
|
|
49
50
|
create_virtual_chunk_container,
|
|
50
51
|
forbid_unsafe_virtual_bucket_configs,
|
|
51
52
|
forbid_unsafe_virtual_chunk_containers,
|
|
@@ -62,7 +63,7 @@ from arraylake.types import (
|
|
|
62
63
|
BucketNickname,
|
|
63
64
|
BucketPrefix,
|
|
64
65
|
BucketResponse,
|
|
65
|
-
|
|
66
|
+
ExplicitVirtualChunkAccessPolicyResponse,
|
|
66
67
|
GSCredentials,
|
|
67
68
|
NewBucket,
|
|
68
69
|
OptimizationConfig,
|
|
@@ -506,6 +507,13 @@ class AsyncClient:
|
|
|
506
507
|
# Single API call to get everything needed to open the repo
|
|
507
508
|
response = await mstore.open_repo(repo_name)
|
|
508
509
|
|
|
510
|
+
if response.status.mode == RepoOperationMode.OFFLINE:
|
|
511
|
+
raise ValueError(f"Repository is offline, check back after {response.status.estimated_end_time}")
|
|
512
|
+
elif response.status.mode == RepoOperationMode.MAINTENANCE:
|
|
513
|
+
warnings.warn(
|
|
514
|
+
f"Repository is in maintenance mode until {response.status.estimated_end_time}, do not attempt to write to the store"
|
|
515
|
+
)
|
|
516
|
+
|
|
509
517
|
# Build icechunk storage from the response
|
|
510
518
|
credential_refresh_func = self._maybe_get_credential_refresh_func_for_icechunk(
|
|
511
519
|
bucket=response.repo_bucket, org=org, repo_name=repo_name
|
|
@@ -546,18 +554,33 @@ class AsyncClient:
|
|
|
546
554
|
forbid_configs_unsafe_for_virtual_chunks=True,
|
|
547
555
|
)
|
|
548
556
|
else:
|
|
549
|
-
# Use server-provided VCC credentials
|
|
557
|
+
# Use server-provided VCC credentials
|
|
550
558
|
vcc_credentials = {}
|
|
551
559
|
if response.virtual_chunk_credentials:
|
|
552
560
|
credentials_map: dict[str, icechunk.AnyS3Credential | icechunk.AnyGcsCredential | icechunk.AnyAzureCredential | None] = {}
|
|
553
561
|
for vcc_prefix, vcc_creds in response.virtual_chunk_credentials.items():
|
|
554
|
-
#
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
562
|
+
# Handle runtime VCCs (e.g., filtered subscriptions) - create VCC on the fly
|
|
563
|
+
if vcc_creds.runtime_vcc_name is not None:
|
|
564
|
+
store_config = create_vcc_store_config_for_platform(vcc_creds.platform)
|
|
565
|
+
container = icechunk.VirtualChunkContainer(
|
|
566
|
+
url_prefix=vcc_prefix,
|
|
567
|
+
store=store_config,
|
|
559
568
|
)
|
|
560
|
-
|
|
569
|
+
container.name = vcc_creds.runtime_vcc_name
|
|
570
|
+
config.set_virtual_chunk_container(container)
|
|
571
|
+
|
|
572
|
+
# TODO: build refresh closures here using vcc_creds.org, vcc_creds.bucket_nickname,
|
|
573
|
+
# and vcc_creds.platform so that credentials can be refreshed when they expire. (See https://github.com/earth-mover/arraylake/issues/5122)
|
|
574
|
+
# TODO: support Azure virtual chunk credentials
|
|
575
|
+
if isinstance(vcc_creds.credentials, AzureCredentials):
|
|
576
|
+
raise NotImplementedError(f"Azure virtual chunk credentials are not yet supported. (prefix: {vcc_prefix}).")
|
|
577
|
+
# TODO: fix return type of get_icechunk_container_credentials (returns GcsBearerCredential
|
|
578
|
+
# which is not in icechunk's AnyGcsCredential union — icechunk typing gap)
|
|
579
|
+
credentials_map[vcc_prefix] = get_icechunk_container_credentials( # type: ignore[assignment]
|
|
580
|
+
bucket_platform=vcc_creds.platform,
|
|
581
|
+
credentials=vcc_creds.credentials,
|
|
582
|
+
credential_refresh_func=None,
|
|
583
|
+
)
|
|
561
584
|
vcc_credentials = icechunk.containers_credentials(credentials_map)
|
|
562
585
|
|
|
563
586
|
ic_repo: IcechunkRepository = await IcechunkRepository.open_async(
|
|
@@ -632,7 +655,6 @@ class AsyncClient:
|
|
|
632
655
|
*,
|
|
633
656
|
bucket_config_nickname: BucketNickname | None = None,
|
|
634
657
|
prefix: str | None = None,
|
|
635
|
-
import_existing: bool = False,
|
|
636
658
|
description: str | None = None,
|
|
637
659
|
metadata: RepoMetadataT | None = None,
|
|
638
660
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -646,7 +668,6 @@ class AsyncClient:
|
|
|
646
668
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo will be created.
|
|
647
669
|
If the repo exists, bucket_config_nickname is ignored.
|
|
648
670
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
649
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
650
671
|
description: Optional description for the repo.
|
|
651
672
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
652
673
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -695,7 +716,6 @@ class AsyncClient:
|
|
|
695
716
|
name,
|
|
696
717
|
bucket_config_nickname=bucket_config_nickname,
|
|
697
718
|
prefix=prefix,
|
|
698
|
-
import_existing=import_existing,
|
|
699
719
|
description=description,
|
|
700
720
|
metadata=metadata,
|
|
701
721
|
config=config,
|
|
@@ -709,7 +729,6 @@ class AsyncClient:
|
|
|
709
729
|
*,
|
|
710
730
|
bucket_config_nickname: BucketNickname | None = None,
|
|
711
731
|
prefix: str | None = None,
|
|
712
|
-
import_existing: bool = False,
|
|
713
732
|
description: str | None = None,
|
|
714
733
|
metadata: RepoMetadataT | None = None,
|
|
715
734
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -722,7 +741,6 @@ class AsyncClient:
|
|
|
722
741
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
723
742
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo will be created.
|
|
724
743
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
725
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
726
744
|
description: Optional description for the repo.
|
|
727
745
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
728
746
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -749,13 +767,12 @@ class AsyncClient:
|
|
|
749
767
|
bucket_config_nickname,
|
|
750
768
|
kind=RepoKind.Icechunk,
|
|
751
769
|
prefix=prefix,
|
|
752
|
-
import_existing=import_existing,
|
|
753
770
|
description=description,
|
|
754
771
|
metadata=metadata,
|
|
755
772
|
)
|
|
756
773
|
|
|
757
774
|
# Raise a warning about token scoping for Azure delegated credentials
|
|
758
|
-
if repo_model.bucket and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
775
|
+
if repo_model.bucket and repo_model.bucket.auth_config and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
759
776
|
warnings.warn(
|
|
760
777
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
761
778
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -764,6 +781,13 @@ class AsyncClient:
|
|
|
764
781
|
)
|
|
765
782
|
|
|
766
783
|
try:
|
|
784
|
+
# Throw error if the bucket is configured for anonymous access, this is only supported for imported repo
|
|
785
|
+
if _use_anonymous_credentials(repo_model.bucket):
|
|
786
|
+
bucket_name = f"{repo_model.bucket.nickname}" if repo_model.bucket else ""
|
|
787
|
+
raise ValueError(
|
|
788
|
+
f"The bucket {bucket_name} is configured for anonymous access and cannot be written to. Use `import_repo` to import existing repositories from this bucket."
|
|
789
|
+
)
|
|
790
|
+
|
|
767
791
|
if not isinstance(config, RepositoryConfig) and config is not None:
|
|
768
792
|
raise ValueError(f"config must be an icechunk.RepositoryConfig object or None: {config}.")
|
|
769
793
|
|
|
@@ -784,41 +808,11 @@ class AsyncClient:
|
|
|
784
808
|
# TODO: make icechunk accept an empty dict instead of None
|
|
785
809
|
prefixes_credential_mapping = None
|
|
786
810
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
)
|
|
793
|
-
|
|
794
|
-
if import_existing:
|
|
795
|
-
warnings.warn(
|
|
796
|
-
"The `import_existing` keyword argument to `create_repo` is deprecated. Please use the dedicated `import_repo` method instead of using `import_existing=True.",
|
|
797
|
-
DeprecationWarning,
|
|
798
|
-
)
|
|
799
|
-
|
|
800
|
-
ic_repo = IcechunkRepository.open(
|
|
801
|
-
icechunk_storage,
|
|
802
|
-
config=config,
|
|
803
|
-
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
804
|
-
)
|
|
805
|
-
|
|
806
|
-
# Note: We decided this was better than silently attempting to modify the IC repo config upon import by saving the config
|
|
807
|
-
if authorize_virtual_chunk_access:
|
|
808
|
-
warnings.warn(
|
|
809
|
-
"New virtual chunk containers will not be persisted to the icechunk repo config. "
|
|
810
|
-
"If you want future `client.get_repo` calls to be authorized to access the same virtual chunks, please now modify the config explicitly. "
|
|
811
|
-
"You can do this using `client.set_virtual_chunk_containers()` with the same `authorize_virtual_chunk_access`, i.e: "
|
|
812
|
-
f"`client.set_virtual_chunk_containers({name}, authorize_virtual_chunk_access={authorize_virtual_chunk_access})`. "
|
|
813
|
-
"Note that this requires write access to the location of the icechunk repository, so will not work for anonymous access buckets, for example.",
|
|
814
|
-
UserWarning,
|
|
815
|
-
)
|
|
816
|
-
else:
|
|
817
|
-
ic_repo = IcechunkRepository.create(
|
|
818
|
-
icechunk_storage,
|
|
819
|
-
config=config,
|
|
820
|
-
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
821
|
-
)
|
|
811
|
+
ic_repo = IcechunkRepository.create(
|
|
812
|
+
icechunk_storage,
|
|
813
|
+
config=config,
|
|
814
|
+
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
815
|
+
)
|
|
822
816
|
|
|
823
817
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
824
818
|
|
|
@@ -833,10 +827,11 @@ class AsyncClient:
|
|
|
833
827
|
self,
|
|
834
828
|
name: OrgAndRepoName,
|
|
835
829
|
bucket_config_nickname: BucketNickname,
|
|
830
|
+
prefix: str,
|
|
836
831
|
*,
|
|
837
|
-
prefix: str | None = None,
|
|
838
832
|
description: str | None = None,
|
|
839
833
|
metadata: RepoMetadataT | None = None,
|
|
834
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
840
835
|
storage_options: StorageOptions | None = None,
|
|
841
836
|
) -> IcechunkRepository:
|
|
842
837
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -844,35 +839,36 @@ class AsyncClient:
|
|
|
844
839
|
Args:
|
|
845
840
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
846
841
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
847
|
-
prefix:
|
|
842
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
848
843
|
description: Optional description for the repo.
|
|
849
844
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
850
845
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
846
|
+
authorize_virtual_chunk_access: A mapping from virtual chunk container prefixes to bucket nicknames.
|
|
847
|
+
Credentials will be fetched for these buckets based on the auth configuration
|
|
848
|
+
and used for the containers. Must be supplied as complete urls.
|
|
851
849
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
852
850
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
853
851
|
|
|
854
852
|
Returns:
|
|
855
853
|
icechunk.Repository object for the repo.
|
|
856
854
|
"""
|
|
857
|
-
|
|
858
855
|
org, repo_name = _parse_org_and_repo(name)
|
|
859
856
|
mstore = self._metastore_for_org(org)
|
|
860
857
|
|
|
861
858
|
user = await mstore.get_user()
|
|
862
859
|
author: Author = user.as_author()
|
|
863
860
|
|
|
864
|
-
repo_model = await mstore.
|
|
861
|
+
repo_model = await mstore.import_database(
|
|
865
862
|
repo_name,
|
|
866
863
|
bucket_config_nickname,
|
|
867
|
-
kind=RepoKind.Icechunk,
|
|
868
864
|
prefix=prefix,
|
|
869
|
-
|
|
865
|
+
kind=RepoKind.Icechunk,
|
|
870
866
|
description=description,
|
|
871
867
|
metadata=metadata,
|
|
872
868
|
)
|
|
873
869
|
|
|
874
870
|
# Raise a warning about token scoping for Azure delegated credentials
|
|
875
|
-
if repo_model.bucket and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
871
|
+
if repo_model.bucket and repo_model.bucket.auth_config and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
876
872
|
warnings.warn(
|
|
877
873
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
878
874
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -885,7 +881,34 @@ class AsyncClient:
|
|
|
885
881
|
repo_model, user_id=user.id, storage_options=storage_options
|
|
886
882
|
)
|
|
887
883
|
|
|
888
|
-
|
|
884
|
+
if authorize_virtual_chunk_access is not None:
|
|
885
|
+
config = await self._add_virtual_chunk_containers(
|
|
886
|
+
org=org, user=user, config=None, authorize_virtual_chunk_access=authorize_virtual_chunk_access
|
|
887
|
+
)
|
|
888
|
+
|
|
889
|
+
# fetch credentials to access buckets containing virtual chunks
|
|
890
|
+
prefixes_credential_mapping = await self.containers_credentials_for_buckets(
|
|
891
|
+
org, containers_to_buckets_map=dict(authorize_virtual_chunk_access)
|
|
892
|
+
)
|
|
893
|
+
|
|
894
|
+
# Warn that VCCs won't be persisted when importing
|
|
895
|
+
warnings.warn(
|
|
896
|
+
"New virtual chunk containers will not be persisted to the icechunk repo config. "
|
|
897
|
+
"If you want future `client.get_repo` calls to be authorized to access the same virtual chunks, please now modify the config explicitly. "
|
|
898
|
+
"You can do this using `client.set_virtual_chunk_containers()` with the same `authorize_virtual_chunk_access`, i.e: "
|
|
899
|
+
f"`client.set_virtual_chunk_containers({name}, authorize_virtual_chunk_access={authorize_virtual_chunk_access})`. "
|
|
900
|
+
"Note that this requires write access to the location of the icechunk repository, so will not work for anonymous access buckets, for example.",
|
|
901
|
+
UserWarning,
|
|
902
|
+
)
|
|
903
|
+
else:
|
|
904
|
+
config = None
|
|
905
|
+
prefixes_credential_mapping = None
|
|
906
|
+
|
|
907
|
+
ic_repo = IcechunkRepository.open(
|
|
908
|
+
icechunk_storage,
|
|
909
|
+
config=config,
|
|
910
|
+
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
911
|
+
)
|
|
889
912
|
|
|
890
913
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
891
914
|
|
|
@@ -1369,42 +1392,42 @@ class AsyncClient:
|
|
|
1369
1392
|
bucket_id = await self._bucket_id_for_nickname(mstore, nickname)
|
|
1370
1393
|
await mstore.delete_bucket_config(bucket_id)
|
|
1371
1394
|
|
|
1372
|
-
async def
|
|
1395
|
+
async def set_virtual_chunk_access_policy(
|
|
1373
1396
|
self,
|
|
1374
1397
|
org: OrgName,
|
|
1375
1398
|
*,
|
|
1376
1399
|
bucket_nickname: BucketNickname,
|
|
1377
1400
|
subprefix: str = "",
|
|
1378
1401
|
public: bool = False,
|
|
1379
|
-
) ->
|
|
1380
|
-
"""Set an explicit virtual chunk
|
|
1402
|
+
) -> ExplicitVirtualChunkAccessPolicyResponse:
|
|
1403
|
+
"""Set an explicit virtual chunk access policy on this org.
|
|
1381
1404
|
|
|
1382
1405
|
Args:
|
|
1383
1406
|
org: Name of the organization.
|
|
1384
|
-
bucket_nickname: Nickname of the bucket config to associate with this
|
|
1385
|
-
subprefix: Subprefix within the bucket for this
|
|
1386
|
-
public: Whether this
|
|
1407
|
+
bucket_nickname: Nickname of the bucket config to associate with this VCAP.
|
|
1408
|
+
subprefix: Subprefix within the bucket for this VCAP.
|
|
1409
|
+
public: Whether this VCAP allows public access.
|
|
1387
1410
|
"""
|
|
1388
1411
|
mstore = self._metastore_for_org(org)
|
|
1389
1412
|
bucket_id = await self._bucket_id_for_nickname(mstore, bucket_nickname)
|
|
1390
|
-
|
|
1413
|
+
vcap = await mstore.set_virtual_chunk_access_policy(
|
|
1391
1414
|
bucket_id=str(bucket_id),
|
|
1392
1415
|
subprefix=subprefix.strip("/"),
|
|
1393
1416
|
public=public,
|
|
1394
1417
|
)
|
|
1395
|
-
return
|
|
1418
|
+
return ExplicitVirtualChunkAccessPolicyResponse.model_validate(vcap.model_dump())
|
|
1396
1419
|
|
|
1397
|
-
async def
|
|
1398
|
-
"""List explicit virtual chunk
|
|
1420
|
+
async def list_virtual_chunk_access_policies(self, org: OrgName) -> list[ExplicitVirtualChunkAccessPolicyResponse]:
|
|
1421
|
+
"""List explicit virtual chunk access policies on this org.
|
|
1399
1422
|
|
|
1400
1423
|
Args:
|
|
1401
1424
|
org: Name of the organization.
|
|
1402
1425
|
"""
|
|
1403
1426
|
mstore = self._metastore_for_org(org)
|
|
1404
|
-
|
|
1405
|
-
return [
|
|
1427
|
+
vcaps = await mstore.list_virtual_chunk_access_policies()
|
|
1428
|
+
return [ExplicitVirtualChunkAccessPolicyResponse.model_validate(v.model_dump()) for v in vcaps if v.use_case == "explicit"]
|
|
1406
1429
|
|
|
1407
|
-
async def
|
|
1430
|
+
async def delete_virtual_chunk_access_policy(
|
|
1408
1431
|
self,
|
|
1409
1432
|
org: OrgName,
|
|
1410
1433
|
*,
|
|
@@ -1412,17 +1435,17 @@ class AsyncClient:
|
|
|
1412
1435
|
subprefix: str,
|
|
1413
1436
|
public: bool,
|
|
1414
1437
|
) -> None:
|
|
1415
|
-
"""Delete a virtual chunk
|
|
1438
|
+
"""Delete a virtual chunk access policy from this org (idempotent).
|
|
1416
1439
|
|
|
1417
1440
|
Args:
|
|
1418
1441
|
org: Name of the organization.
|
|
1419
|
-
bucket_nickname: Nickname of the bucket config for the
|
|
1420
|
-
subprefix: Subprefix of the
|
|
1421
|
-
public: Whether this
|
|
1442
|
+
bucket_nickname: Nickname of the bucket config for the VCAP.
|
|
1443
|
+
subprefix: Subprefix of the VCAP to delete.
|
|
1444
|
+
public: Whether this VCAP allows public access.
|
|
1422
1445
|
"""
|
|
1423
1446
|
mstore = self._metastore_for_org(org)
|
|
1424
1447
|
bucket_id = await self._bucket_id_for_nickname(mstore, bucket_nickname)
|
|
1425
|
-
await mstore.
|
|
1448
|
+
await mstore.delete_virtual_chunk_access_policy(
|
|
1426
1449
|
bucket_id=str(bucket_id),
|
|
1427
1450
|
subprefix=subprefix.strip("/"),
|
|
1428
1451
|
public=public,
|
|
@@ -1588,7 +1611,6 @@ class Client:
|
|
|
1588
1611
|
*,
|
|
1589
1612
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1590
1613
|
prefix: str | None = None,
|
|
1591
|
-
import_existing: bool = False,
|
|
1592
1614
|
description: str | None = None,
|
|
1593
1615
|
metadata: RepoMetadataT | None = None,
|
|
1594
1616
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -1602,7 +1624,6 @@ class Client:
|
|
|
1602
1624
|
bucket_config_nickname: The created repo will use this bucket for its chunks.
|
|
1603
1625
|
If the repo exists, bucket_config_nickname is ignored.
|
|
1604
1626
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
1605
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
1606
1627
|
description: Optional description for the repo.
|
|
1607
1628
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1608
1629
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1628,7 +1649,6 @@ class Client:
|
|
|
1628
1649
|
name,
|
|
1629
1650
|
bucket_config_nickname=bucket_config_nickname,
|
|
1630
1651
|
prefix=prefix,
|
|
1631
|
-
import_existing=import_existing,
|
|
1632
1652
|
description=description,
|
|
1633
1653
|
metadata=metadata,
|
|
1634
1654
|
config=config,
|
|
@@ -1642,7 +1662,6 @@ class Client:
|
|
|
1642
1662
|
*,
|
|
1643
1663
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1644
1664
|
prefix: str | None = None,
|
|
1645
|
-
import_existing: bool = False,
|
|
1646
1665
|
description: str | None = None,
|
|
1647
1666
|
metadata: RepoMetadataT | None = None,
|
|
1648
1667
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -1655,7 +1674,6 @@ class Client:
|
|
|
1655
1674
|
name: Full name of the repo to create (of the form [ORG]/[REPO])
|
|
1656
1675
|
bucket_config_nickname: An optional bucket to use for the chunkstore
|
|
1657
1676
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
1658
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
1659
1677
|
description: Optional description for the repo.
|
|
1660
1678
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1661
1679
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1676,7 +1694,6 @@ class Client:
|
|
|
1676
1694
|
name,
|
|
1677
1695
|
bucket_config_nickname=bucket_config_nickname,
|
|
1678
1696
|
prefix=prefix,
|
|
1679
|
-
import_existing=import_existing,
|
|
1680
1697
|
description=description,
|
|
1681
1698
|
metadata=metadata,
|
|
1682
1699
|
config=config,
|
|
@@ -1688,10 +1705,11 @@ class Client:
|
|
|
1688
1705
|
self,
|
|
1689
1706
|
name: OrgAndRepoName,
|
|
1690
1707
|
bucket_config_nickname: BucketNickname,
|
|
1708
|
+
prefix: str,
|
|
1691
1709
|
*,
|
|
1692
|
-
prefix: str | None = None,
|
|
1693
1710
|
description: str | None = None,
|
|
1694
1711
|
metadata: RepoMetadataT | None = None,
|
|
1712
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
1695
1713
|
storage_options: StorageOptions | None = None,
|
|
1696
1714
|
) -> IcechunkRepository:
|
|
1697
1715
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -1699,10 +1717,13 @@ class Client:
|
|
|
1699
1717
|
Args:
|
|
1700
1718
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
1701
1719
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
1702
|
-
prefix:
|
|
1720
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
1703
1721
|
description: Optional description for the repo.
|
|
1704
1722
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1705
1723
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
1724
|
+
authorize_virtual_chunk_access: A mapping from virtual chunk container prefixes to bucket nicknames.
|
|
1725
|
+
Credentials will be fetched for these buckets based on the auth configuration
|
|
1726
|
+
and used for the containers. Must be supplied as complete urls.
|
|
1706
1727
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
1707
1728
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
1708
1729
|
|
|
@@ -1712,10 +1733,11 @@ class Client:
|
|
|
1712
1733
|
return sync(
|
|
1713
1734
|
self.aclient.import_repo,
|
|
1714
1735
|
name,
|
|
1715
|
-
bucket_config_nickname
|
|
1716
|
-
prefix
|
|
1736
|
+
bucket_config_nickname,
|
|
1737
|
+
prefix,
|
|
1717
1738
|
description=description,
|
|
1718
1739
|
metadata=metadata,
|
|
1740
|
+
authorize_virtual_chunk_access=authorize_virtual_chunk_access,
|
|
1719
1741
|
storage_options=storage_options,
|
|
1720
1742
|
)
|
|
1721
1743
|
|
|
@@ -1940,33 +1962,33 @@ class Client:
|
|
|
1940
1962
|
"""
|
|
1941
1963
|
return sync(self.aclient.delete_bucket_config, org=org, nickname=nickname, imsure=imsure, imreallysure=imreallysure)
|
|
1942
1964
|
|
|
1943
|
-
def
|
|
1965
|
+
def set_virtual_chunk_access_policy(
|
|
1944
1966
|
self,
|
|
1945
1967
|
org: OrgName,
|
|
1946
1968
|
*,
|
|
1947
1969
|
bucket_nickname: BucketNickname,
|
|
1948
1970
|
subprefix: str,
|
|
1949
1971
|
public: bool,
|
|
1950
|
-
) ->
|
|
1951
|
-
"""Set an explicit virtual chunk
|
|
1972
|
+
) -> ExplicitVirtualChunkAccessPolicyResponse:
|
|
1973
|
+
"""Set an explicit virtual chunk access policy on this org.
|
|
1952
1974
|
|
|
1953
1975
|
Args:
|
|
1954
1976
|
org: Name of the organization.
|
|
1955
|
-
bucket_nickname: Nickname of the bucket config to associate with this
|
|
1956
|
-
subprefix: Subprefix within the bucket for this
|
|
1957
|
-
public: Whether this
|
|
1977
|
+
bucket_nickname: Nickname of the bucket config to associate with this VCAP.
|
|
1978
|
+
subprefix: Subprefix within the bucket for this VCAP.
|
|
1979
|
+
public: Whether this VCAP allows public access.
|
|
1958
1980
|
"""
|
|
1959
|
-
return sync(self.aclient.
|
|
1981
|
+
return sync(self.aclient.set_virtual_chunk_access_policy, org, bucket_nickname=bucket_nickname, subprefix=subprefix, public=public)
|
|
1960
1982
|
|
|
1961
|
-
def
|
|
1962
|
-
"""List explicit virtual chunk
|
|
1983
|
+
def list_virtual_chunk_access_policies(self, org: OrgName) -> list[ExplicitVirtualChunkAccessPolicyResponse]:
|
|
1984
|
+
"""List explicit virtual chunk access policies on this org.
|
|
1963
1985
|
|
|
1964
1986
|
Args:
|
|
1965
1987
|
org: Name of the organization.
|
|
1966
1988
|
"""
|
|
1967
|
-
return sync(self.aclient.
|
|
1989
|
+
return sync(self.aclient.list_virtual_chunk_access_policies, org)
|
|
1968
1990
|
|
|
1969
|
-
def
|
|
1991
|
+
def delete_virtual_chunk_access_policy(
|
|
1970
1992
|
self,
|
|
1971
1993
|
org: OrgName,
|
|
1972
1994
|
*,
|
|
@@ -1974,15 +1996,17 @@ class Client:
|
|
|
1974
1996
|
subprefix: str,
|
|
1975
1997
|
public: bool,
|
|
1976
1998
|
) -> None:
|
|
1977
|
-
"""Delete a virtual chunk
|
|
1999
|
+
"""Delete a virtual chunk access policy from this org.
|
|
1978
2000
|
|
|
1979
2001
|
Args:
|
|
1980
2002
|
org: Name of the organization.
|
|
1981
|
-
bucket_nickname: Nickname of the bucket config for the
|
|
1982
|
-
subprefix: Subprefix of the
|
|
1983
|
-
public: Whether this
|
|
2003
|
+
bucket_nickname: Nickname of the bucket config for the VCAP.
|
|
2004
|
+
subprefix: Subprefix of the VCAP to delete.
|
|
2005
|
+
public: Whether this VCAP allows public access.
|
|
1984
2006
|
"""
|
|
1985
|
-
return sync(
|
|
2007
|
+
return sync(
|
|
2008
|
+
self.aclient.delete_virtual_chunk_access_policy, org, bucket_nickname=bucket_nickname, subprefix=subprefix, public=public
|
|
2009
|
+
)
|
|
1986
2010
|
|
|
1987
2011
|
def login(self, *, browser: bool = False) -> None:
|
|
1988
2012
|
"""Login to ArrayLake.
|