Skip to content

Commit 75b74cf

Browse files
jackylee-chclaude
andauthored
fix: treat a .pyiceberg.yaml without a mapping as no config (#3915)
`_load_yaml` passed `strictyaml.load(...).data` straight to `_lowercase_dictionary_keys`. For an empty or comment-only document that value is a `str`, not a mapping, so the call raised `AttributeError: 'str' object has no attribute 'items'`. `Config()` runs at import time, so commenting out the file made `import pyiceberg.catalog` fail with an error naming neither YAML nor the file. Return `None` instead, which the annotated return type already allows and which the caller already handles as "keep looking". Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 7a7a99b commit 75b74cf

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

pyiceberg/utils/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def _load_yaml(directory: str | None) -> RecursiveDict | None:
7979
with open(path, encoding=UTF8) as f:
8080
yml_str = f.read()
8181
file_config = strictyaml.load(yml_str).data
82+
if not isinstance(file_config, dict):
83+
# An empty or comment-only document parses as a string
84+
return None
8285
file_config_lowercase = _lowercase_dictionary_keys(file_config)
8386
return file_config_lowercase
8487
return None

tests/utils/test_config.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,20 @@ def create_config_file(path: str, uri: str | None) -> None:
183183
assert (
184184
result["catalog"]["default"]["uri"] if result else None # type: ignore
185185
) == expected_result, f"Unexpected configuration result. Expected: {expected_result}, Actual: {result}"
186+
187+
188+
@pytest.mark.parametrize("content", ["", "\n", "# only a comment\n"])
189+
def test_from_configuration_files_without_a_mapping(
190+
monkeypatch: pytest.MonkeyPatch, tmp_path_factory: pytest.TempPathFactory, content: str
191+
) -> None:
192+
"""A file that holds no mapping should be treated as absent."""
193+
pyiceberg_home = str(tmp_path_factory.mktemp("pyiceberg_home"))
194+
empty_dir = str(tmp_path_factory.mktemp("empty"))
195+
with open(os.path.join(pyiceberg_home, ".pyiceberg.yaml"), "w", encoding=UTF8) as file:
196+
file.write(content)
197+
198+
monkeypatch.setenv("PYICEBERG_HOME", pyiceberg_home)
199+
monkeypatch.setattr(os.path, "expanduser", lambda _: empty_dir)
200+
monkeypatch.chdir(empty_dir)
201+
202+
assert Config()._from_configuration_files() is None

0 commit comments

Comments
 (0)