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.
Files changed (66) hide show
  1. {arraylake-0.31.0 → arraylake-0.33.0}/.gitignore +1 -0
  2. {arraylake-0.31.0 → arraylake-0.33.0}/PKG-INFO +2 -2
  3. arraylake-0.33.0/arraylake/_version.py +24 -0
  4. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/compute.py +2 -2
  5. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/config.py +2 -2
  6. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/repo.py +6 -8
  7. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/client.py +126 -102
  8. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/types.py +4 -4
  9. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/http_metastore.py +67 -27
  10. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/types.py +23 -3
  11. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/virtual.py +23 -12
  12. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/types.py +66 -41
  13. {arraylake-0.31.0 → arraylake-0.33.0}/pyproject.toml +2 -2
  14. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_cli.py +10 -6
  15. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_client.py +43 -11
  16. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_performance.py +24 -12
  17. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_subscriptions.py +36 -4
  18. arraylake-0.33.0/tests/test_virtual_chunks.py +1202 -0
  19. {arraylake-0.31.0 → arraylake-0.33.0}/uv.lock +117 -436
  20. arraylake-0.31.0/arraylake/_version.py +0 -34
  21. arraylake-0.31.0/tests/test_virtual_chunks.py +0 -803
  22. {arraylake-0.31.0 → arraylake-0.33.0}/README.md +0 -0
  23. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/__init__.py +0 -0
  24. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/__main__.py +0 -0
  25. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/api_utils.py +0 -0
  26. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/asyn.py +0 -0
  27. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/__init__.py +0 -0
  28. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/auth.py +0 -0
  29. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/main.py +0 -0
  30. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/cli/utils.py +0 -0
  31. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/doctor.py +0 -0
  32. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/http_client.py +0 -0
  33. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/compute/services.py +0 -0
  34. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/config.py +0 -0
  35. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/config.yaml +0 -0
  36. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/credentials.py +0 -0
  37. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/diagnostics.py +0 -0
  38. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/__init__.py +0 -0
  39. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/repo.py +0 -0
  40. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/display/repolist.py +0 -0
  41. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/exceptions.py +0 -0
  42. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/log_util.py +0 -0
  43. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/__init__.py +0 -0
  44. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/metastore/abc.py +0 -0
  45. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/py.typed +0 -0
  46. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/__init__.py +0 -0
  47. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/__init__.py +0 -0
  48. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/repos/icechunk/storage.py +0 -0
  49. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/token.py +0 -0
  50. {arraylake-0.31.0 → arraylake-0.33.0}/arraylake/tuning.py +0 -0
  51. {arraylake-0.31.0 → arraylake-0.33.0}/tests/__init__.py +0 -0
  52. {arraylake-0.31.0 → arraylake-0.33.0}/tests/compute/test_client.py +0 -0
  53. {arraylake-0.31.0 → arraylake-0.33.0}/tests/compute/test_doctor.py +0 -0
  54. {arraylake-0.31.0 → arraylake-0.33.0}/tests/config.yaml +0 -0
  55. {arraylake-0.31.0 → arraylake-0.33.0}/tests/conftest.py +0 -0
  56. {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/__init__.py +0 -0
  57. {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/conftest.py +0 -0
  58. {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/test_storage.py +0 -0
  59. {arraylake-0.31.0 → arraylake-0.33.0}/tests/icechunk/test_virtual.py +0 -0
  60. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_api_utils.py +0 -0
  61. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_asyn.py +0 -0
  62. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_config.py +0 -0
  63. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_credentials.py +0 -0
  64. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_diagnostics.py +0 -0
  65. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_token_handler.py +0 -0
  66. {arraylake-0.31.0 → arraylake-0.33.0}/tests/test_types.py +0 -0
@@ -104,6 +104,7 @@ celerybeat.pid
104
104
 
105
105
  # Environments
106
106
  .env
107
+ !jobs/subsync/.env
107
108
  .venv
108
109
  env/
109
110
  venv/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arraylake
3
- Version: 0.31.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[complete]>=2024.10.0; extra == 'maximal'
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 Enum
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(str, Enum):
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 Enum
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(str, Enum):
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(str, Enum):
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.Option(None, help="Sub-prefix in which the Icechunk repo exists"),
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 --bucket-config-nickname icechunk-bucket --prefix my-icechunk-prefix --metadata '{"key": "value"}'
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=bucket_config_nickname,
195
- prefix=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
- ExplicitVirtualChunkLocationResponse,
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 (currently only anonymous-access buckets)
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
- # TODO: once non-anonymous VCC buckets are supported, build refresh closures here
555
- # using vcc_creds.org, vcc_creds.bucket_nickname, and vcc_creds.platform
556
- if vcc_creds.credentials is not None:
557
- raise NotImplementedError(
558
- f"Non-anonymous credentials for buckets are not yet supported for virtual chunks. (prefix: {vcc_prefix})."
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
- credentials_map[vcc_prefix] = None
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
- # Throws a specific error if the bucket is configured for anonymous access and the repo is not being imported
788
- if _use_anonymous_credentials(repo_model.bucket) and not import_existing:
789
- bucket_name = f"{repo_model.bucket.nickname}" if repo_model.bucket else ""
790
- raise ValueError(
791
- f"The bucket {bucket_name} is configured for anonymous access and cannot be written to. Import existing repositories from this bucket with the `import_existing` parameter."
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: Sub-prefix in which the Icechunk repo exists. If not provided, repo is assumed to be located at the root of the bucket.
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.create_database(
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
- import_existing=True,
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
- ic_repo = IcechunkRepository.open(icechunk_storage)
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 set_virtual_chunk_location(
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
- ) -> ExplicitVirtualChunkLocationResponse:
1380
- """Set an explicit virtual chunk location on this org.
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 VCL.
1385
- subprefix: Subprefix within the bucket for this VCL.
1386
- public: Whether this VCL allows public access.
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
- vcl = await mstore.set_virtual_chunk_location(
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 ExplicitVirtualChunkLocationResponse.model_validate(vcl.model_dump())
1418
+ return ExplicitVirtualChunkAccessPolicyResponse.model_validate(vcap.model_dump())
1396
1419
 
1397
- async def list_virtual_chunk_locations(self, org: OrgName) -> list[ExplicitVirtualChunkLocationResponse]:
1398
- """List explicit virtual chunk locations on this org.
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
- vcls = await mstore.list_virtual_chunk_locations()
1405
- return [ExplicitVirtualChunkLocationResponse.model_validate(v.model_dump()) for v in vcls if v.use_case == "explicit"]
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 delete_virtual_chunk_location(
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 location from this org (idempotent).
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 VCL.
1420
- subprefix: Subprefix of the VCL to delete.
1421
- public: Whether this VCL allows public access.
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.delete_virtual_chunk_location(
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: Sub-prefix in which the Icechunk repo exists. If not provided, repo is assumed to be located at the root of the bucket.
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=bucket_config_nickname,
1716
- prefix=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 set_virtual_chunk_location(
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
- ) -> ExplicitVirtualChunkLocationResponse:
1951
- """Set an explicit virtual chunk location on this org.
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 VCL.
1956
- subprefix: Subprefix within the bucket for this VCL.
1957
- public: Whether this VCL allows public access.
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.set_virtual_chunk_location, org, bucket_nickname=bucket_nickname, subprefix=subprefix, public=public)
1981
+ return sync(self.aclient.set_virtual_chunk_access_policy, org, bucket_nickname=bucket_nickname, subprefix=subprefix, public=public)
1960
1982
 
1961
- def list_virtual_chunk_locations(self, org: OrgName) -> list[ExplicitVirtualChunkLocationResponse]:
1962
- """List explicit virtual chunk locations on this org.
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.list_virtual_chunk_locations, org)
1989
+ return sync(self.aclient.list_virtual_chunk_access_policies, org)
1968
1990
 
1969
- def delete_virtual_chunk_location(
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 location from this org.
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 VCL.
1982
- subprefix: Subprefix of the VCL to delete.
1983
- public: Whether this VCL allows public access.
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(self.aclient.delete_virtual_chunk_location, org, bucket_nickname=bucket_nickname, subprefix=subprefix, public=public)
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.