Skip to content

Commit d21fe8f

Browse files
fix(rest): refine S3-compatible scheme fallback
1 parent 4a4f18c commit d21fe8f

2 files changed

Lines changed: 21 additions & 6 deletions

File tree

pyiceberg/catalog/rest/__init__.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ class ListViewsResponse(IcebergBaseModel):
387387
_PLANNING_RESPONSE_ADAPTER = TypeAdapter(PlanningResponse)
388388

389389

390+
_S3_COMPATIBLE_SCHEMES = ("s3://", "s3a://", "s3n://", "oss://")
391+
392+
390393
def _is_hadoop_only_config(config: Properties) -> bool:
391394
"""Return True if every key is a Hadoop ``fs.*`` key — pyiceberg has no HadoopFileIO to consume them."""
392395
return bool(config) and all(k.startswith("fs.") for k in config)
@@ -478,10 +481,8 @@ def _resolve_storage_credentials(storage_credentials: list[StorageCredential], l
478481
if best_match is None or len(cred.prefix) > len(best_match.prefix):
479482
best_match = cred
480483

481-
# Java S3FileIO falls back to the "s3" ROOT_PREFIX credential; scope it to
482-
# schemes pyarrow's S3FileSystem handles so non-S3 schemes (gs://, abfs://,
483-
# etc.) don't get handed s3.* keys.
484-
if best_match is None and location.startswith(("s3://", "s3a://", "s3n://", "oss://")):
484+
# Mirrors Java S3FileIO.clientForStoragePath() ROOT_PREFIX fallback.
485+
if best_match is None and location.startswith(_S3_COMPATIBLE_SCHEMES):
485486
best_match = next((c for c in consumable if c.prefix == "s3"), None)
486487

487488
return best_match.config if best_match else {}

tests/catalog/test_rest.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,10 +2876,24 @@ def test_resolve_storage_credentials_all_hadoop_only_returns_empty() -> None:
28762876
assert RestCatalog._resolve_storage_credentials(credentials, "custom://bucket/path") == {}
28772877

28782878

2879-
def test_resolve_storage_credentials_root_prefix_fallback_for_s3_compatible_scheme() -> None:
2879+
def test_resolve_storage_credentials_s3a_s3n_match_root_prefix_directly() -> None:
28802880
from pyiceberg.catalog.rest.scan_planning import StorageCredential
28812881

2882-
# oss:// is routed through pyarrow's S3FileSystem, so ROOT_PREFIX "s3" applies.
2882+
# s3a:// and s3n:// start with "s3", so they match prefix="s3" via
2883+
# startswith at the normal matching stage — no fallback needed.
2884+
credentials = [
2885+
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
2886+
]
2887+
for scheme in ("s3a", "s3n"):
2888+
result = RestCatalog._resolve_storage_credentials(credentials, f"{scheme}://bucket/path")
2889+
assert result == {"s3.access-key-id": "native-k"}, f"{scheme}:// should match prefix='s3' directly"
2890+
2891+
2892+
def test_resolve_storage_credentials_root_prefix_fallback_for_oss() -> None:
2893+
from pyiceberg.catalog.rest.scan_planning import StorageCredential
2894+
2895+
# oss:// is S3-API-compatible; pyiceberg routes it through pyarrow's
2896+
# S3FileSystem, so the ROOT_PREFIX "s3" credential applies.
28832897
credentials = [
28842898
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
28852899
]

0 commit comments

Comments
 (0)