Skip to content

Commit 23a1299

Browse files
fix(rest): refine S3-compatible scheme fallback
1 parent a9cbf87 commit 23a1299

2 files changed

Lines changed: 16 additions & 8 deletions

File tree

pyiceberg/catalog/rest/__init__.py

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

398398

399-
def _is_hadoop_only_config(config: Properties) -> bool:
399+
def _is_hadoop_only_config(config: dict[str, str]) -> bool:
400400
"""Return True if every key is a Hadoop ``fs.*`` key — pyiceberg has no HadoopFileIO to consume them."""
401401
return bool(config) and all(k.startswith("fs.") for k in config)
402402

@@ -487,10 +487,9 @@ def _resolve_storage_credentials(storage_credentials: list[StorageCredential], l
487487
if best_match is None or len(cred.prefix) > len(best_match.prefix):
488488
best_match = cred
489489

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://")):
490+
# Java S3FileIO ROOT_PREFIX fallback. Only oss:// needs it — s3/s3a/s3n
491+
# already match the "s3" credential via startswith in the loop above.
492+
if best_match is None and location.startswith("oss://"):
494493
best_match = next((c for c in consumable if c.prefix == "s3"), None)
495494

496495
return best_match.config if best_match else {}

tests/catalog/test_rest.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3112,7 +3112,6 @@ def test_resolve_storage_credentials_empty() -> None:
31123112
def test_resolve_storage_credentials_skips_hadoop_only() -> None:
31133113
from pyiceberg.catalog.rest.scan_planning import StorageCredential
31143114

3115-
# The longer fs.* prefix would win a blind longest-match; the filter drops it.
31163115
credentials = [
31173116
StorageCredential(prefix="s3://warehouse/jindo", config={"fs.s3.access-key": "hadoop-k"}),
31183117
StorageCredential(prefix="s3://warehouse", config={"s3.access-key-id": "native-k"}),
@@ -3141,10 +3140,20 @@ def test_resolve_storage_credentials_all_hadoop_only_returns_empty() -> None:
31413140
assert RestCatalog._resolve_storage_credentials(credentials, "custom://bucket/path") == {}
31423141

31433142

3144-
def test_resolve_storage_credentials_root_prefix_fallback_for_s3_compatible_scheme() -> None:
3143+
def test_resolve_storage_credentials_s3a_s3n_match_root_prefix_directly() -> None:
3144+
from pyiceberg.catalog.rest.scan_planning import StorageCredential
3145+
3146+
credentials = [
3147+
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
3148+
]
3149+
for scheme in ("s3a", "s3n"):
3150+
result = RestCatalog._resolve_storage_credentials(credentials, f"{scheme}://bucket/path")
3151+
assert result == {"s3.access-key-id": "native-k"}, f"{scheme}:// should match prefix='s3' directly"
3152+
3153+
3154+
def test_resolve_storage_credentials_root_prefix_fallback_for_oss() -> None:
31453155
from pyiceberg.catalog.rest.scan_planning import StorageCredential
31463156

3147-
# oss:// is routed through pyarrow's S3FileSystem, so ROOT_PREFIX "s3" applies.
31483157
credentials = [
31493158
StorageCredential(prefix="s3", config={"s3.access-key-id": "native-k"}),
31503159
]

0 commit comments

Comments
 (0)