arraylake 0.32.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.32.0 → arraylake-0.33.0}/.gitignore +1 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/PKG-INFO +2 -2
- arraylake-0.33.0/arraylake/_version.py +24 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/compute.py +2 -2
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/config.py +2 -2
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/repo.py +6 -8
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/client.py +70 -60
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/compute/types.py +4 -4
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/metastore/http_metastore.py +47 -7
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/repos/icechunk/types.py +23 -3
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/repos/icechunk/virtual.py +22 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/types.py +60 -37
- {arraylake-0.32.0 → arraylake-0.33.0}/pyproject.toml +2 -2
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_cli.py +1 -1
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_client.py +6 -11
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_subscriptions.py +11 -11
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_virtual_chunks.py +100 -99
- {arraylake-0.32.0 → arraylake-0.33.0}/uv.lock +48 -365
- arraylake-0.32.0/arraylake/_version.py +0 -34
- {arraylake-0.32.0 → arraylake-0.33.0}/README.md +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/__main__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/api_utils.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/asyn.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/auth.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/main.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/cli/utils.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/compute/doctor.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/compute/http_client.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/compute/services.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/config.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/config.yaml +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/credentials.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/diagnostics.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/display/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/display/repo.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/display/repolist.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/exceptions.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/log_util.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/metastore/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/metastore/abc.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/py.typed +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/repos/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/repos/icechunk/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/repos/icechunk/storage.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/token.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/arraylake/tuning.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/compute/test_client.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/compute/test_doctor.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/config.yaml +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/conftest.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/icechunk/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/icechunk/conftest.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/icechunk/test_storage.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/icechunk/test_virtual.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_api_utils.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_asyn.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_config.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_credentials.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_diagnostics.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_performance.py +0 -0
- {arraylake-0.32.0 → arraylake-0.33.0}/tests/test_token_handler.py +0 -0
- {arraylake-0.32.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,
|
|
@@ -558,6 +559,16 @@ class AsyncClient:
|
|
|
558
559
|
if response.virtual_chunk_credentials:
|
|
559
560
|
credentials_map: dict[str, icechunk.AnyS3Credential | icechunk.AnyGcsCredential | icechunk.AnyAzureCredential | None] = {}
|
|
560
561
|
for vcc_prefix, vcc_creds in response.virtual_chunk_credentials.items():
|
|
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,
|
|
568
|
+
)
|
|
569
|
+
container.name = vcc_creds.runtime_vcc_name
|
|
570
|
+
config.set_virtual_chunk_container(container)
|
|
571
|
+
|
|
561
572
|
# TODO: build refresh closures here using vcc_creds.org, vcc_creds.bucket_nickname,
|
|
562
573
|
# and vcc_creds.platform so that credentials can be refreshed when they expire. (See https://github.com/earth-mover/arraylake/issues/5122)
|
|
563
574
|
# TODO: support Azure virtual chunk credentials
|
|
@@ -644,7 +655,6 @@ class AsyncClient:
|
|
|
644
655
|
*,
|
|
645
656
|
bucket_config_nickname: BucketNickname | None = None,
|
|
646
657
|
prefix: str | None = None,
|
|
647
|
-
import_existing: bool = False,
|
|
648
658
|
description: str | None = None,
|
|
649
659
|
metadata: RepoMetadataT | None = None,
|
|
650
660
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -658,7 +668,6 @@ class AsyncClient:
|
|
|
658
668
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo will be created.
|
|
659
669
|
If the repo exists, bucket_config_nickname is ignored.
|
|
660
670
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
661
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
662
671
|
description: Optional description for the repo.
|
|
663
672
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
664
673
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -707,7 +716,6 @@ class AsyncClient:
|
|
|
707
716
|
name,
|
|
708
717
|
bucket_config_nickname=bucket_config_nickname,
|
|
709
718
|
prefix=prefix,
|
|
710
|
-
import_existing=import_existing,
|
|
711
719
|
description=description,
|
|
712
720
|
metadata=metadata,
|
|
713
721
|
config=config,
|
|
@@ -721,7 +729,6 @@ class AsyncClient:
|
|
|
721
729
|
*,
|
|
722
730
|
bucket_config_nickname: BucketNickname | None = None,
|
|
723
731
|
prefix: str | None = None,
|
|
724
|
-
import_existing: bool = False,
|
|
725
732
|
description: str | None = None,
|
|
726
733
|
metadata: RepoMetadataT | None = None,
|
|
727
734
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -734,7 +741,6 @@ class AsyncClient:
|
|
|
734
741
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
735
742
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo will be created.
|
|
736
743
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
737
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
738
744
|
description: Optional description for the repo.
|
|
739
745
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
740
746
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -761,13 +767,12 @@ class AsyncClient:
|
|
|
761
767
|
bucket_config_nickname,
|
|
762
768
|
kind=RepoKind.Icechunk,
|
|
763
769
|
prefix=prefix,
|
|
764
|
-
import_existing=import_existing,
|
|
765
770
|
description=description,
|
|
766
771
|
metadata=metadata,
|
|
767
772
|
)
|
|
768
773
|
|
|
769
774
|
# Raise a warning about token scoping for Azure delegated credentials
|
|
770
|
-
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":
|
|
771
776
|
warnings.warn(
|
|
772
777
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
773
778
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -776,6 +781,13 @@ class AsyncClient:
|
|
|
776
781
|
)
|
|
777
782
|
|
|
778
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
|
+
|
|
779
791
|
if not isinstance(config, RepositoryConfig) and config is not None:
|
|
780
792
|
raise ValueError(f"config must be an icechunk.RepositoryConfig object or None: {config}.")
|
|
781
793
|
|
|
@@ -796,41 +808,11 @@ class AsyncClient:
|
|
|
796
808
|
# TODO: make icechunk accept an empty dict instead of None
|
|
797
809
|
prefixes_credential_mapping = None
|
|
798
810
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
)
|
|
805
|
-
|
|
806
|
-
if import_existing:
|
|
807
|
-
warnings.warn(
|
|
808
|
-
"The `import_existing` keyword argument to `create_repo` is deprecated. Please use the dedicated `import_repo` method instead of using `import_existing=True.",
|
|
809
|
-
DeprecationWarning,
|
|
810
|
-
)
|
|
811
|
-
|
|
812
|
-
ic_repo = IcechunkRepository.open(
|
|
813
|
-
icechunk_storage,
|
|
814
|
-
config=config,
|
|
815
|
-
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
816
|
-
)
|
|
817
|
-
|
|
818
|
-
# Note: We decided this was better than silently attempting to modify the IC repo config upon import by saving the config
|
|
819
|
-
if authorize_virtual_chunk_access:
|
|
820
|
-
warnings.warn(
|
|
821
|
-
"New virtual chunk containers will not be persisted to the icechunk repo config. "
|
|
822
|
-
"If you want future `client.get_repo` calls to be authorized to access the same virtual chunks, please now modify the config explicitly. "
|
|
823
|
-
"You can do this using `client.set_virtual_chunk_containers()` with the same `authorize_virtual_chunk_access`, i.e: "
|
|
824
|
-
f"`client.set_virtual_chunk_containers({name}, authorize_virtual_chunk_access={authorize_virtual_chunk_access})`. "
|
|
825
|
-
"Note that this requires write access to the location of the icechunk repository, so will not work for anonymous access buckets, for example.",
|
|
826
|
-
UserWarning,
|
|
827
|
-
)
|
|
828
|
-
else:
|
|
829
|
-
ic_repo = IcechunkRepository.create(
|
|
830
|
-
icechunk_storage,
|
|
831
|
-
config=config,
|
|
832
|
-
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
833
|
-
)
|
|
811
|
+
ic_repo = IcechunkRepository.create(
|
|
812
|
+
icechunk_storage,
|
|
813
|
+
config=config,
|
|
814
|
+
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
815
|
+
)
|
|
834
816
|
|
|
835
817
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
836
818
|
|
|
@@ -845,10 +827,11 @@ class AsyncClient:
|
|
|
845
827
|
self,
|
|
846
828
|
name: OrgAndRepoName,
|
|
847
829
|
bucket_config_nickname: BucketNickname,
|
|
830
|
+
prefix: str,
|
|
848
831
|
*,
|
|
849
|
-
prefix: str | None = None,
|
|
850
832
|
description: str | None = None,
|
|
851
833
|
metadata: RepoMetadataT | None = None,
|
|
834
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
852
835
|
storage_options: StorageOptions | None = None,
|
|
853
836
|
) -> IcechunkRepository:
|
|
854
837
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -856,35 +839,36 @@ class AsyncClient:
|
|
|
856
839
|
Args:
|
|
857
840
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
858
841
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
859
|
-
prefix:
|
|
842
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
860
843
|
description: Optional description for the repo.
|
|
861
844
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
862
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.
|
|
863
849
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
864
850
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
865
851
|
|
|
866
852
|
Returns:
|
|
867
853
|
icechunk.Repository object for the repo.
|
|
868
854
|
"""
|
|
869
|
-
|
|
870
855
|
org, repo_name = _parse_org_and_repo(name)
|
|
871
856
|
mstore = self._metastore_for_org(org)
|
|
872
857
|
|
|
873
858
|
user = await mstore.get_user()
|
|
874
859
|
author: Author = user.as_author()
|
|
875
860
|
|
|
876
|
-
repo_model = await mstore.
|
|
861
|
+
repo_model = await mstore.import_database(
|
|
877
862
|
repo_name,
|
|
878
863
|
bucket_config_nickname,
|
|
879
|
-
kind=RepoKind.Icechunk,
|
|
880
864
|
prefix=prefix,
|
|
881
|
-
|
|
865
|
+
kind=RepoKind.Icechunk,
|
|
882
866
|
description=description,
|
|
883
867
|
metadata=metadata,
|
|
884
868
|
)
|
|
885
869
|
|
|
886
870
|
# Raise a warning about token scoping for Azure delegated credentials
|
|
887
|
-
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":
|
|
888
872
|
warnings.warn(
|
|
889
873
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
890
874
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -897,7 +881,34 @@ class AsyncClient:
|
|
|
897
881
|
repo_model, user_id=user.id, storage_options=storage_options
|
|
898
882
|
)
|
|
899
883
|
|
|
900
|
-
|
|
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
|
+
)
|
|
901
912
|
|
|
902
913
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
903
914
|
|
|
@@ -1600,7 +1611,6 @@ class Client:
|
|
|
1600
1611
|
*,
|
|
1601
1612
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1602
1613
|
prefix: str | None = None,
|
|
1603
|
-
import_existing: bool = False,
|
|
1604
1614
|
description: str | None = None,
|
|
1605
1615
|
metadata: RepoMetadataT | None = None,
|
|
1606
1616
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -1614,7 +1624,6 @@ class Client:
|
|
|
1614
1624
|
bucket_config_nickname: The created repo will use this bucket for its chunks.
|
|
1615
1625
|
If the repo exists, bucket_config_nickname is ignored.
|
|
1616
1626
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
1617
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
1618
1627
|
description: Optional description for the repo.
|
|
1619
1628
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1620
1629
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1640,7 +1649,6 @@ class Client:
|
|
|
1640
1649
|
name,
|
|
1641
1650
|
bucket_config_nickname=bucket_config_nickname,
|
|
1642
1651
|
prefix=prefix,
|
|
1643
|
-
import_existing=import_existing,
|
|
1644
1652
|
description=description,
|
|
1645
1653
|
metadata=metadata,
|
|
1646
1654
|
config=config,
|
|
@@ -1654,7 +1662,6 @@ class Client:
|
|
|
1654
1662
|
*,
|
|
1655
1663
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1656
1664
|
prefix: str | None = None,
|
|
1657
|
-
import_existing: bool = False,
|
|
1658
1665
|
description: str | None = None,
|
|
1659
1666
|
metadata: RepoMetadataT | None = None,
|
|
1660
1667
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -1667,7 +1674,6 @@ class Client:
|
|
|
1667
1674
|
name: Full name of the repo to create (of the form [ORG]/[REPO])
|
|
1668
1675
|
bucket_config_nickname: An optional bucket to use for the chunkstore
|
|
1669
1676
|
prefix: Optional prefix for Icechunk store. If not provided, a random ID + the repo name will be used.
|
|
1670
|
-
import_existing: If True, the Icechunk repo will be imported if it already exists.
|
|
1671
1677
|
description: Optional description for the repo.
|
|
1672
1678
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1673
1679
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1688,7 +1694,6 @@ class Client:
|
|
|
1688
1694
|
name,
|
|
1689
1695
|
bucket_config_nickname=bucket_config_nickname,
|
|
1690
1696
|
prefix=prefix,
|
|
1691
|
-
import_existing=import_existing,
|
|
1692
1697
|
description=description,
|
|
1693
1698
|
metadata=metadata,
|
|
1694
1699
|
config=config,
|
|
@@ -1700,10 +1705,11 @@ class Client:
|
|
|
1700
1705
|
self,
|
|
1701
1706
|
name: OrgAndRepoName,
|
|
1702
1707
|
bucket_config_nickname: BucketNickname,
|
|
1708
|
+
prefix: str,
|
|
1703
1709
|
*,
|
|
1704
|
-
prefix: str | None = None,
|
|
1705
1710
|
description: str | None = None,
|
|
1706
1711
|
metadata: RepoMetadataT | None = None,
|
|
1712
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
1707
1713
|
storage_options: StorageOptions | None = None,
|
|
1708
1714
|
) -> IcechunkRepository:
|
|
1709
1715
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -1711,10 +1717,13 @@ class Client:
|
|
|
1711
1717
|
Args:
|
|
1712
1718
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
1713
1719
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
1714
|
-
prefix:
|
|
1720
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
1715
1721
|
description: Optional description for the repo.
|
|
1716
1722
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1717
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.
|
|
1718
1727
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
1719
1728
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
1720
1729
|
|
|
@@ -1724,10 +1733,11 @@ class Client:
|
|
|
1724
1733
|
return sync(
|
|
1725
1734
|
self.aclient.import_repo,
|
|
1726
1735
|
name,
|
|
1727
|
-
bucket_config_nickname
|
|
1728
|
-
prefix
|
|
1736
|
+
bucket_config_nickname,
|
|
1737
|
+
prefix,
|
|
1729
1738
|
description=description,
|
|
1730
1739
|
metadata=metadata,
|
|
1740
|
+
authorize_virtual_chunk_access=authorize_virtual_chunk_access,
|
|
1731
1741
|
storage_options=storage_options,
|
|
1732
1742
|
)
|
|
1733
1743
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from enum import
|
|
3
|
+
from enum import StrEnum
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, field_validator
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class ServiceType(
|
|
8
|
+
class ServiceType(StrEnum):
|
|
9
9
|
dap = "dap2"
|
|
10
10
|
edr = "edr"
|
|
11
11
|
wms = "wms"
|
|
@@ -13,14 +13,14 @@ class ServiceType(str, Enum):
|
|
|
13
13
|
zarr = "zarr"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
class ServiceStatus(
|
|
16
|
+
class ServiceStatus(StrEnum):
|
|
17
17
|
available = "available"
|
|
18
18
|
progressing = "progressing"
|
|
19
19
|
unknown = "unknown"
|
|
20
20
|
error = "error"
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class LogLevel(
|
|
23
|
+
class LogLevel(StrEnum):
|
|
24
24
|
debug = "DEBUG"
|
|
25
25
|
info = "INFO"
|
|
26
26
|
warning = "WARNING"
|
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
4
|
from dataclasses import dataclass, field
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
8
|
from pydantic import TypeAdapter
|
|
@@ -26,6 +26,7 @@ from arraylake.types import (
|
|
|
26
26
|
Repo,
|
|
27
27
|
RepoActions,
|
|
28
28
|
RepoCreateBody,
|
|
29
|
+
RepoImportBody,
|
|
29
30
|
RepoKind,
|
|
30
31
|
RepoMetadataT,
|
|
31
32
|
RepoModifyRequest,
|
|
@@ -132,10 +133,9 @@ class HttpMetastore(ArraylakeHttpClient, Metastore):
|
|
|
132
133
|
bucket_nickname: str | None = None,
|
|
133
134
|
kind: RepoKind = RepoKind.Icechunk,
|
|
134
135
|
prefix: str | None = None,
|
|
135
|
-
import_existing: bool = False,
|
|
136
136
|
description: str | None = None,
|
|
137
137
|
metadata: RepoMetadataT | None = None,
|
|
138
|
-
):
|
|
138
|
+
) -> Repo:
|
|
139
139
|
"""
|
|
140
140
|
Creates a repo database entry in the metastore.
|
|
141
141
|
|
|
@@ -144,17 +144,18 @@ class HttpMetastore(ArraylakeHttpClient, Metastore):
|
|
|
144
144
|
bucket_nickname: Optional nickname of a bucket already existing in the org.
|
|
145
145
|
kind: Kind of repo to create
|
|
146
146
|
prefix: Optional prefix for the icechunk repo
|
|
147
|
-
import_existing: Whether to import an existing icechunk repo
|
|
148
147
|
description: Optional description for the repo
|
|
149
148
|
metadata: Optional metadata for the repo
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
The created Repo object
|
|
150
152
|
"""
|
|
151
|
-
create_mode: Literal["create", "register", "import"] = "import" if import_existing else "register"
|
|
152
153
|
body = RepoCreateBody(
|
|
153
154
|
name=name,
|
|
154
155
|
bucket_nickname=bucket_nickname,
|
|
155
156
|
kind=kind,
|
|
156
157
|
prefix=prefix,
|
|
157
|
-
create_mode=
|
|
158
|
+
create_mode="register",
|
|
158
159
|
description=description,
|
|
159
160
|
metadata=metadata,
|
|
160
161
|
)
|
|
@@ -163,7 +164,46 @@ class HttpMetastore(ArraylakeHttpClient, Metastore):
|
|
|
163
164
|
repo = Repo.model_validate_json(response.content)
|
|
164
165
|
|
|
165
166
|
if repo.kind == RepoKind.Icechunk:
|
|
166
|
-
|
|
167
|
+
return repo
|
|
168
|
+
|
|
169
|
+
raise ValueError(f"Unknown repo kind: {repo.kind}")
|
|
170
|
+
|
|
171
|
+
async def import_database(
|
|
172
|
+
self,
|
|
173
|
+
name: str,
|
|
174
|
+
bucket_nickname: str,
|
|
175
|
+
prefix: str,
|
|
176
|
+
kind: RepoKind = RepoKind.Icechunk,
|
|
177
|
+
description: str | None = None,
|
|
178
|
+
metadata: RepoMetadataT | None = None,
|
|
179
|
+
) -> Repo:
|
|
180
|
+
"""
|
|
181
|
+
Imports an existing icechunk repo from storage.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
name: Name of the repo to create
|
|
185
|
+
bucket_nickname: Nickname of the bucket containing the existing icechunk repo.
|
|
186
|
+
prefix: Prefix where the icechunk repo exists in the bucket.
|
|
187
|
+
kind: Kind of repo to create
|
|
188
|
+
description: Optional description for the repo
|
|
189
|
+
metadata: Optional metadata for the repo
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
The imported Repo object
|
|
193
|
+
"""
|
|
194
|
+
body = RepoImportBody(
|
|
195
|
+
name=name,
|
|
196
|
+
bucket_nickname=bucket_nickname,
|
|
197
|
+
kind=kind,
|
|
198
|
+
prefix=prefix,
|
|
199
|
+
description=description,
|
|
200
|
+
metadata=metadata,
|
|
201
|
+
)
|
|
202
|
+
response = await self._request("POST", f"/orgs/{self._config.org}/repos/import", content=body.model_dump_json())
|
|
203
|
+
handle_response(response)
|
|
204
|
+
repo = Repo.model_validate_json(response.content)
|
|
205
|
+
|
|
206
|
+
if repo.kind == RepoKind.Icechunk:
|
|
167
207
|
return repo
|
|
168
208
|
|
|
169
209
|
raise ValueError(f"Unknown repo kind: {repo.kind}")
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from enum import
|
|
3
|
+
from enum import StrEnum
|
|
4
4
|
from typing import Any, NewType
|
|
5
5
|
|
|
6
6
|
from icechunk import Diff, SnapshotInfo
|
|
7
|
-
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_serializer
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_serializer, field_validator
|
|
8
8
|
from zarr import AsyncArray, AsyncGroup
|
|
9
9
|
from zarr.abc.metadata import Metadata
|
|
10
10
|
|
|
@@ -153,7 +153,7 @@ class ExpirationResult(BaseModel):
|
|
|
153
153
|
# Dataset Tree types for xarray-style representation
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
class ArrayClassification(
|
|
156
|
+
class ArrayClassification(StrEnum):
|
|
157
157
|
"""Classification of an array as coordinate or data variable (xarray convention)."""
|
|
158
158
|
|
|
159
159
|
COORDINATE = "coordinate"
|
|
@@ -198,4 +198,24 @@ class DatasetNode(BaseModel):
|
|
|
198
198
|
child_groups: list[str] # Names of direct child groups (for navigation)
|
|
199
199
|
is_xarray_compatible: bool = True # False if dimensions conflict or arrays lack dimension_names
|
|
200
200
|
nbytes: int = 0 # Total bytes consumed by all arrays (uncompressed)
|
|
201
|
+
# Returning spec_version in this response to avoid another roundtrip in the web app for IC spec version
|
|
202
|
+
spec_version: int | None = None # Icechunk spec version (1 or 2)
|
|
201
203
|
model_config = {"arbitrary_types_allowed": True}
|
|
204
|
+
|
|
205
|
+
@field_validator("spec_version", mode="before")
|
|
206
|
+
@classmethod
|
|
207
|
+
def validate_spec_version(cls, value: Any) -> int | None:
|
|
208
|
+
"""Convert SpecVersion enum to int during validation."""
|
|
209
|
+
if value is None:
|
|
210
|
+
return None
|
|
211
|
+
# Handle icechunk.SpecVersion which has __int__ method
|
|
212
|
+
return int(value)
|
|
213
|
+
|
|
214
|
+
@field_serializer("spec_version")
|
|
215
|
+
@classmethod
|
|
216
|
+
def serialize_spec_version(cls, value: Any) -> int | None:
|
|
217
|
+
"""Serialize SpecVersion to int for API responses."""
|
|
218
|
+
if value is None:
|
|
219
|
+
return None
|
|
220
|
+
# Handle icechunk.SpecVersion which has __int__ method
|
|
221
|
+
return int(value)
|
|
@@ -19,6 +19,28 @@ from arraylake.types import (
|
|
|
19
19
|
FORBIDDEN_VIRTUAL_CHUNK_PREFIXES = {"file://", "memory://", "http://"}
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def create_vcc_store_config_for_platform(platform: str) -> icechunk.AnyObjectStoreConfig:
|
|
23
|
+
"""Create an Icechunk ObjectStoreConfig for a virtual chunk container based on platform.
|
|
24
|
+
|
|
25
|
+
This is used for runtime VCCs (e.g., filtered subscriptions) where we need to create
|
|
26
|
+
a VCC dynamically based on the server-provided platform type.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
platform: The storage platform (s3, s3-compatible, minio, gs, azure)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
An icechunk ObjectStoreConfig for the given platform
|
|
33
|
+
"""
|
|
34
|
+
if platform in ("s3", "s3-compatible", "minio"):
|
|
35
|
+
return icechunk.ObjectStoreConfig.S3(icechunk.S3Options())
|
|
36
|
+
elif platform == "gs":
|
|
37
|
+
return icechunk.ObjectStoreConfig.Gcs({})
|
|
38
|
+
elif platform == "azure":
|
|
39
|
+
return icechunk.ObjectStoreConfig.Azure({})
|
|
40
|
+
else:
|
|
41
|
+
raise ValueError(f"Unsupported platform for VCC store config: {platform}")
|
|
42
|
+
|
|
43
|
+
|
|
22
44
|
def get_icechunk_container_credentials(
|
|
23
45
|
bucket_platform: str,
|
|
24
46
|
credentials: S3Credentials | GSCredentials | None,
|