Skip to content

Commit 0d085b0

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

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

31433143

3144-
def test_resolve_storage_credentials_root_prefix_fallback_for_s3_compatible_scheme() -> None:
3144+
def test_resolve_storage_credentials_s3a_s3n_match_root_prefix_directly() -> None:
31453145
from pyiceberg.catalog.rest.scan_planning import StorageCredential
31463146

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

0 commit comments

Comments
 (0)