arraylake 0.32.0__tar.gz → 1.0.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-1.0.0}/.gitignore +1 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/PKG-INFO +6 -8
- arraylake-1.0.0/arraylake/_version.py +24 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/api_utils.py +8 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/compute.py +2 -2
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/config.py +2 -2
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/repo.py +6 -8
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/client.py +79 -59
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/compute/types.py +4 -4
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/metastore/http_metastore.py +47 -7
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/repos/icechunk/types.py +23 -3
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/types.py +135 -37
- {arraylake-0.32.0 → arraylake-1.0.0}/pyproject.toml +7 -9
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_api_utils.py +30 -3
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_cli.py +1 -1
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_client.py +47 -11
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_subscriptions.py +11 -11
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_virtual_chunks.py +119 -103
- {arraylake-0.32.0 → arraylake-1.0.0}/uv.lock +456 -767
- arraylake-0.32.0/arraylake/_version.py +0 -34
- {arraylake-0.32.0 → arraylake-1.0.0}/README.md +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/__main__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/asyn.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/auth.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/main.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/cli/utils.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/compute/doctor.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/compute/http_client.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/compute/services.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/config.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/config.yaml +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/credentials.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/diagnostics.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/display/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/display/repo.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/display/repolist.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/exceptions.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/log_util.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/metastore/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/metastore/abc.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/py.typed +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/repos/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/repos/icechunk/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/repos/icechunk/storage.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/repos/icechunk/virtual.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/token.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/arraylake/tuning.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/compute/test_client.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/compute/test_doctor.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/config.yaml +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/conftest.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/icechunk/__init__.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/icechunk/conftest.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/icechunk/test_storage.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/icechunk/test_virtual.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_asyn.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_config.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_credentials.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_diagnostics.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_performance.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_token_handler.py +0 -0
- {arraylake-0.32.0 → arraylake-1.0.0}/tests/test_types.py +0 -0
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: arraylake
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.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
|
|
7
7
|
Author-email: Joseph Hamman <joe@earthmover.io>
|
|
8
8
|
License-Expression: MIT
|
|
9
9
|
Classifier: Programming Language :: Python
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
|
-
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Python: <3.15,>=3.12
|
|
14
14
|
Requires-Dist: click<9.0,>=8.1.3
|
|
15
15
|
Requires-Dist: donfig<1.0,>=0.7
|
|
16
|
-
Requires-Dist: httpx<0.
|
|
17
|
-
Requires-Dist: icechunk<
|
|
16
|
+
Requires-Dist: httpx<0.29,>=0.23
|
|
17
|
+
Requires-Dist: icechunk<3.0.0,>=2.0.1
|
|
18
18
|
Requires-Dist: numpy<3.0,>=1.23
|
|
19
19
|
Requires-Dist: packaging<26.0,>=23.0
|
|
20
20
|
Requires-Dist: pydantic[email]<3.0,>=2.9
|
|
@@ -26,9 +26,7 @@ Requires-Dist: typer<1.0,>=0.12.0
|
|
|
26
26
|
Requires-Dist: zarr<4.0,>=3.1.0
|
|
27
27
|
Provides-Extra: maximal
|
|
28
28
|
Requires-Dist: cf-xarray>=0.10.4; extra == 'maximal'
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: pyarrow<22; extra == 'maximal'
|
|
31
|
-
Requires-Dist: xarray[complete]>=2024.10.0; extra == 'maximal'
|
|
29
|
+
Requires-Dist: xarray[accel,etc,io,parallel]>=2024.10.0; extra == 'maximal'
|
|
32
30
|
Provides-Extra: xarray
|
|
33
31
|
Requires-Dist: cf-xarray>=0.10.4; extra == 'xarray'
|
|
34
32
|
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 = '1.0.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 0, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -4,6 +4,7 @@ import asyncio
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
6
|
import random
|
|
7
|
+
import warnings
|
|
7
8
|
import weakref
|
|
8
9
|
from collections.abc import AsyncGenerator, Callable, Generator, Mapping
|
|
9
10
|
from contextlib import _AsyncGeneratorContextManager
|
|
@@ -430,8 +431,15 @@ def _exception_log_debug(request: httpx.Request, response: httpx.Response):
|
|
|
430
431
|
)
|
|
431
432
|
|
|
432
433
|
|
|
434
|
+
_emitted_deprecation_warnings: set[str] = set()
|
|
435
|
+
|
|
436
|
+
|
|
433
437
|
def handle_response(response: httpx.Response):
|
|
434
438
|
"""Convenience function to handle response status codes"""
|
|
439
|
+
deprecation_msg = response.headers.get("X-Deprecation-Warning")
|
|
440
|
+
if deprecation_msg and deprecation_msg not in _emitted_deprecation_warnings:
|
|
441
|
+
_emitted_deprecation_warnings.add(deprecation_msg)
|
|
442
|
+
warnings.warn(deprecation_msg, DeprecationWarning, stacklevel=2)
|
|
435
443
|
try:
|
|
436
444
|
response.raise_for_status()
|
|
437
445
|
except httpx.RequestError as exc:
|
|
@@ -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
|
)
|
|
@@ -558,6 +558,17 @@ class AsyncClient:
|
|
|
558
558
|
if response.virtual_chunk_credentials:
|
|
559
559
|
credentials_map: dict[str, icechunk.AnyS3Credential | icechunk.AnyGcsCredential | icechunk.AnyAzureCredential | None] = {}
|
|
560
560
|
for vcc_prefix, vcc_creds in response.virtual_chunk_credentials.items():
|
|
561
|
+
# Handle runtime VCCs (e.g., filtered subscriptions) - create VCC on the fly
|
|
562
|
+
if vcc_creds.runtime_vcc_name is not None:
|
|
563
|
+
bucket_config = await self.get_bucket_config(org=vcc_creds.org, nickname=vcc_creds.bucket_nickname)
|
|
564
|
+
store_config = bucket_config.to_vcc_store_config(user_id=UUID(response.principal_id))
|
|
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,12 +729,12 @@ 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,
|
|
728
735
|
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
729
736
|
storage_options: StorageOptions | None = None,
|
|
737
|
+
spec_version: int = 2,
|
|
730
738
|
) -> IcechunkRepository:
|
|
731
739
|
"""Create a new repo.
|
|
732
740
|
|
|
@@ -734,7 +742,6 @@ class AsyncClient:
|
|
|
734
742
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
735
743
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo will be created.
|
|
736
744
|
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
745
|
description: Optional description for the repo.
|
|
739
746
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
740
747
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -745,6 +752,7 @@ class AsyncClient:
|
|
|
745
752
|
and used for the containers. Must be supplied as complete urls.
|
|
746
753
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
747
754
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
755
|
+
spec_version: The Icechunk spec version to use for the repo. Defaults to 2.
|
|
748
756
|
|
|
749
757
|
Returns:
|
|
750
758
|
icechunk.Repository object for the repo.
|
|
@@ -761,13 +769,12 @@ class AsyncClient:
|
|
|
761
769
|
bucket_config_nickname,
|
|
762
770
|
kind=RepoKind.Icechunk,
|
|
763
771
|
prefix=prefix,
|
|
764
|
-
import_existing=import_existing,
|
|
765
772
|
description=description,
|
|
766
773
|
metadata=metadata,
|
|
767
774
|
)
|
|
768
775
|
|
|
769
776
|
# 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":
|
|
777
|
+
if repo_model.bucket and repo_model.bucket.auth_config and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
771
778
|
warnings.warn(
|
|
772
779
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
773
780
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -776,6 +783,13 @@ class AsyncClient:
|
|
|
776
783
|
)
|
|
777
784
|
|
|
778
785
|
try:
|
|
786
|
+
# Throw error if the bucket is configured for anonymous access, this is only supported for imported repo
|
|
787
|
+
if _use_anonymous_credentials(repo_model.bucket):
|
|
788
|
+
bucket_name = f"{repo_model.bucket.nickname}" if repo_model.bucket else ""
|
|
789
|
+
raise ValueError(
|
|
790
|
+
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."
|
|
791
|
+
)
|
|
792
|
+
|
|
779
793
|
if not isinstance(config, RepositoryConfig) and config is not None:
|
|
780
794
|
raise ValueError(f"config must be an icechunk.RepositoryConfig object or None: {config}.")
|
|
781
795
|
|
|
@@ -796,41 +810,16 @@ class AsyncClient:
|
|
|
796
810
|
# TODO: make icechunk accept an empty dict instead of None
|
|
797
811
|
prefixes_credential_mapping = None
|
|
798
812
|
|
|
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
|
-
)
|
|
813
|
+
create_kwargs: dict[str, object] = {
|
|
814
|
+
"config": config,
|
|
815
|
+
"authorize_virtual_chunk_access": prefixes_credential_mapping,
|
|
816
|
+
"spec_version": spec_version,
|
|
817
|
+
}
|
|
817
818
|
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
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
|
-
)
|
|
819
|
+
ic_repo = IcechunkRepository.create(
|
|
820
|
+
icechunk_storage,
|
|
821
|
+
**create_kwargs, # type: ignore[arg-type]
|
|
822
|
+
)
|
|
834
823
|
|
|
835
824
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
836
825
|
|
|
@@ -845,10 +834,11 @@ class AsyncClient:
|
|
|
845
834
|
self,
|
|
846
835
|
name: OrgAndRepoName,
|
|
847
836
|
bucket_config_nickname: BucketNickname,
|
|
837
|
+
prefix: str,
|
|
848
838
|
*,
|
|
849
|
-
prefix: str | None = None,
|
|
850
839
|
description: str | None = None,
|
|
851
840
|
metadata: RepoMetadataT | None = None,
|
|
841
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
852
842
|
storage_options: StorageOptions | None = None,
|
|
853
843
|
) -> IcechunkRepository:
|
|
854
844
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -856,35 +846,36 @@ class AsyncClient:
|
|
|
856
846
|
Args:
|
|
857
847
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
858
848
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
859
|
-
prefix:
|
|
849
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
860
850
|
description: Optional description for the repo.
|
|
861
851
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
862
852
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
853
|
+
authorize_virtual_chunk_access: A mapping from virtual chunk container prefixes to bucket nicknames.
|
|
854
|
+
Credentials will be fetched for these buckets based on the auth configuration
|
|
855
|
+
and used for the containers. Must be supplied as complete urls.
|
|
863
856
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
864
857
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
865
858
|
|
|
866
859
|
Returns:
|
|
867
860
|
icechunk.Repository object for the repo.
|
|
868
861
|
"""
|
|
869
|
-
|
|
870
862
|
org, repo_name = _parse_org_and_repo(name)
|
|
871
863
|
mstore = self._metastore_for_org(org)
|
|
872
864
|
|
|
873
865
|
user = await mstore.get_user()
|
|
874
866
|
author: Author = user.as_author()
|
|
875
867
|
|
|
876
|
-
repo_model = await mstore.
|
|
868
|
+
repo_model = await mstore.import_database(
|
|
877
869
|
repo_name,
|
|
878
870
|
bucket_config_nickname,
|
|
879
|
-
kind=RepoKind.Icechunk,
|
|
880
871
|
prefix=prefix,
|
|
881
|
-
|
|
872
|
+
kind=RepoKind.Icechunk,
|
|
882
873
|
description=description,
|
|
883
874
|
metadata=metadata,
|
|
884
875
|
)
|
|
885
876
|
|
|
886
877
|
# 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":
|
|
878
|
+
if repo_model.bucket and repo_model.bucket.auth_config and repo_model.bucket.auth_config.method == "azure_credential_delegation":
|
|
888
879
|
warnings.warn(
|
|
889
880
|
"The bucket is configured to use Azure Customer Managed Role authentication. "
|
|
890
881
|
"Credentials will be scoped to the container level only for this repo. "
|
|
@@ -897,7 +888,34 @@ class AsyncClient:
|
|
|
897
888
|
repo_model, user_id=user.id, storage_options=storage_options
|
|
898
889
|
)
|
|
899
890
|
|
|
900
|
-
|
|
891
|
+
if authorize_virtual_chunk_access is not None:
|
|
892
|
+
config = await self._add_virtual_chunk_containers(
|
|
893
|
+
org=org, user=user, config=None, authorize_virtual_chunk_access=authorize_virtual_chunk_access
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
# fetch credentials to access buckets containing virtual chunks
|
|
897
|
+
prefixes_credential_mapping = await self.containers_credentials_for_buckets(
|
|
898
|
+
org, containers_to_buckets_map=dict(authorize_virtual_chunk_access)
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
# Warn that VCCs won't be persisted when importing
|
|
902
|
+
warnings.warn(
|
|
903
|
+
"New virtual chunk containers will not be persisted to the icechunk repo config. "
|
|
904
|
+
"If you want future `client.get_repo` calls to be authorized to access the same virtual chunks, please now modify the config explicitly. "
|
|
905
|
+
"You can do this using `client.set_virtual_chunk_containers()` with the same `authorize_virtual_chunk_access`, i.e: "
|
|
906
|
+
f"`client.set_virtual_chunk_containers({name}, authorize_virtual_chunk_access={authorize_virtual_chunk_access})`. "
|
|
907
|
+
"Note that this requires write access to the location of the icechunk repository, so will not work for anonymous access buckets, for example.",
|
|
908
|
+
UserWarning,
|
|
909
|
+
)
|
|
910
|
+
else:
|
|
911
|
+
config = None
|
|
912
|
+
prefixes_credential_mapping = None
|
|
913
|
+
|
|
914
|
+
ic_repo = IcechunkRepository.open(
|
|
915
|
+
icechunk_storage,
|
|
916
|
+
config=config,
|
|
917
|
+
authorize_virtual_chunk_access=prefixes_credential_mapping,
|
|
918
|
+
)
|
|
901
919
|
|
|
902
920
|
ic_repo.set_default_commit_metadata({"author_name": author.name, "author_email": author.email})
|
|
903
921
|
|
|
@@ -1600,7 +1618,6 @@ class Client:
|
|
|
1600
1618
|
*,
|
|
1601
1619
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1602
1620
|
prefix: str | None = None,
|
|
1603
|
-
import_existing: bool = False,
|
|
1604
1621
|
description: str | None = None,
|
|
1605
1622
|
metadata: RepoMetadataT | None = None,
|
|
1606
1623
|
config: icechunk.RepositoryConfig | None = None,
|
|
@@ -1614,7 +1631,6 @@ class Client:
|
|
|
1614
1631
|
bucket_config_nickname: The created repo will use this bucket for its chunks.
|
|
1615
1632
|
If the repo exists, bucket_config_nickname is ignored.
|
|
1616
1633
|
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
1634
|
description: Optional description for the repo.
|
|
1619
1635
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1620
1636
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1640,7 +1656,6 @@ class Client:
|
|
|
1640
1656
|
name,
|
|
1641
1657
|
bucket_config_nickname=bucket_config_nickname,
|
|
1642
1658
|
prefix=prefix,
|
|
1643
|
-
import_existing=import_existing,
|
|
1644
1659
|
description=description,
|
|
1645
1660
|
metadata=metadata,
|
|
1646
1661
|
config=config,
|
|
@@ -1654,12 +1669,12 @@ class Client:
|
|
|
1654
1669
|
*,
|
|
1655
1670
|
bucket_config_nickname: BucketNickname | None = None,
|
|
1656
1671
|
prefix: str | None = None,
|
|
1657
|
-
import_existing: bool = False,
|
|
1658
1672
|
description: str | None = None,
|
|
1659
1673
|
metadata: RepoMetadataT | None = None,
|
|
1660
1674
|
config: icechunk.RepositoryConfig | None = None,
|
|
1661
1675
|
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
1662
1676
|
storage_options: StorageOptions | None = None,
|
|
1677
|
+
spec_version: int = 2,
|
|
1663
1678
|
) -> IcechunkRepository:
|
|
1664
1679
|
"""Create a new repo
|
|
1665
1680
|
|
|
@@ -1667,7 +1682,6 @@ class Client:
|
|
|
1667
1682
|
name: Full name of the repo to create (of the form [ORG]/[REPO])
|
|
1668
1683
|
bucket_config_nickname: An optional bucket to use for the chunkstore
|
|
1669
1684
|
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
1685
|
description: Optional description for the repo.
|
|
1672
1686
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1673
1687
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
@@ -1679,6 +1693,7 @@ class Client:
|
|
|
1679
1693
|
and used for the containers. Used for Icechunk repos only.
|
|
1680
1694
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
1681
1695
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
1696
|
+
spec_version: The Icechunk spec version to use for the repo. Defaults to 2.
|
|
1682
1697
|
|
|
1683
1698
|
Returns:
|
|
1684
1699
|
icechunk.Repository: The created Icechunk repository.
|
|
@@ -1688,22 +1703,23 @@ class Client:
|
|
|
1688
1703
|
name,
|
|
1689
1704
|
bucket_config_nickname=bucket_config_nickname,
|
|
1690
1705
|
prefix=prefix,
|
|
1691
|
-
import_existing=import_existing,
|
|
1692
1706
|
description=description,
|
|
1693
1707
|
metadata=metadata,
|
|
1694
1708
|
config=config,
|
|
1695
1709
|
authorize_virtual_chunk_access=authorize_virtual_chunk_access,
|
|
1696
1710
|
storage_options=storage_options,
|
|
1711
|
+
spec_version=spec_version,
|
|
1697
1712
|
)
|
|
1698
1713
|
|
|
1699
1714
|
def import_repo(
|
|
1700
1715
|
self,
|
|
1701
1716
|
name: OrgAndRepoName,
|
|
1702
1717
|
bucket_config_nickname: BucketNickname,
|
|
1718
|
+
prefix: str,
|
|
1703
1719
|
*,
|
|
1704
|
-
prefix: str | None = None,
|
|
1705
1720
|
description: str | None = None,
|
|
1706
1721
|
metadata: RepoMetadataT | None = None,
|
|
1722
|
+
authorize_virtual_chunk_access: Mapping[BucketPrefix, BucketNickname] | None = None,
|
|
1707
1723
|
storage_options: StorageOptions | None = None,
|
|
1708
1724
|
) -> IcechunkRepository:
|
|
1709
1725
|
"""Create a new Arraylake Repo by importing an existing Icechunk Repository.
|
|
@@ -1711,10 +1727,13 @@ class Client:
|
|
|
1711
1727
|
Args:
|
|
1712
1728
|
name: Full name of the repo to create (of the form [ORG]/[REPO]).
|
|
1713
1729
|
bucket_config_nickname: Bucket in which the underlying Icechunk repo exists.
|
|
1714
|
-
prefix:
|
|
1730
|
+
prefix: Prefix in which the Icechunk repo exists in the bucket.
|
|
1715
1731
|
description: Optional description for the repo.
|
|
1716
1732
|
metadata: Optional dictionary of metadata to tag the repo with.
|
|
1717
1733
|
Dictionary values can be a scalar (string, int, float, bool, or None) or a list of scalars.
|
|
1734
|
+
authorize_virtual_chunk_access: A mapping from virtual chunk container prefixes to bucket nicknames.
|
|
1735
|
+
Credentials will be fetched for these buckets based on the auth configuration
|
|
1736
|
+
and used for the containers. Must be supplied as complete urls.
|
|
1718
1737
|
storage_options: Optional storage options for the underlying Icechunk storage.
|
|
1719
1738
|
Currently supports `network_stream_timeout_seconds` for S3, Tigris, and R2 storage.
|
|
1720
1739
|
|
|
@@ -1724,10 +1743,11 @@ class Client:
|
|
|
1724
1743
|
return sync(
|
|
1725
1744
|
self.aclient.import_repo,
|
|
1726
1745
|
name,
|
|
1727
|
-
bucket_config_nickname
|
|
1728
|
-
prefix
|
|
1746
|
+
bucket_config_nickname,
|
|
1747
|
+
prefix,
|
|
1729
1748
|
description=description,
|
|
1730
1749
|
metadata=metadata,
|
|
1750
|
+
authorize_virtual_chunk_access=authorize_virtual_chunk_access,
|
|
1731
1751
|
storage_options=storage_options,
|
|
1732
1752
|
)
|
|
1733
1753
|
|
|
@@ -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}")
|