Skip to content

Commit 7b73de3

Browse files
fix(rest): refine S3-compatible scheme fallback
1 parent 3088136 commit 7b73de3

2 files changed

Lines changed: 22 additions & 7 deletions

File tree

pyiceberg/catalog/rest/__init__.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,10 @@ class ListViewsResponse(IcebergBaseModel):
396396
_PLANNING_RESPONSE_ADAPTER = TypeAdapter(PlanningResponse)
397397

398398

399-
def _is_hadoop_only_config(config: Properties) -> bool:
399+
_S3_COMPATIBLE_SCHEMES = ("s3://", "s3a://", "s3n://", "oss://")
400+
401+
402+
def _is_hadoop_only_config(config: dict[str, str]) -> bool:
400403
"""Return True if every key is a Hadoop ``fs.*`` key — pyiceberg has no HadoopFileIO to consume them."""
401404
return bool(config) and all(k.startswith("fs.") for k in config)
402405

@@ -487,10 +490,8 @@ def _resolve_storage_credentials(storage_credentials: list[StorageCredential], l
487490
if best_match is None or len(cred.prefix) > len(best_match.prefix):
488491
best_match = cred
489492

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

496497
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
@@ -3145,10 +3145,24 @@ def test_resolve_storage_credentials_all_hadoop_only_returns_empty() -> None:
31453145
assert RestCatalog._resolve_storage_credentials(credentials, "custom://bucket/path") == {}
31463146

31473147

3148-
def test_resolve_storage_credentials_root_prefix_fallback_for_s3_compatible_scheme() -> None:
3148+
def test_resolve_storage_credentials_s3a_s3n_match_root_prefix_directly() -> None:
31493149
from pyiceberg.catalog.rest.scan_planning import StorageCredential
31503150

3151-
# oss:// is routed through pyarrow's S3FileSystem, so ROOT_PREFIX "s3" applies.
3151+
# s3a:// and s3n:// start with "s3", so they match prefix="s3" via
3152+
# startswith at the normal matching stage — no fallback needed.
3153+
credentials = [
3154+
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
3155+
]
3156+
for scheme in ("s3a", "s3n"):
3157+
result = RestCatalog._resolve_storage_credentials(credentials, f"{scheme}://bucket/path")
3158+
assert result == {"s3.access-key-id": "native-k"}, f"{scheme}:// should match prefix='s3' directly"
3159+
3160+
3161+
def test_resolve_storage_credentials_root_prefix_fallback_for_oss() -> None:
3162+
from pyiceberg.catalog.rest.scan_planning import StorageCredential
3163+
3164+
# oss:// is S3-API-compatible; pyiceberg routes it through pyarrow's
3165+
# S3FileSystem, so the ROOT_PREFIX "s3" credential applies.
31523166
credentials = [
31533167
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
31543168
]

0 commit comments

Comments
 (0)