This is an automated email from the ASF dual-hosted git repository.
fokko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git
The following commit(s) were added to refs/heads/main by this push:
new 7b84d108 Clean up logging: move exception tracebacks to debug level
(#2867)
7b84d108 is described below
commit 7b84d10826e6f231991e2e9bb6f944b21dc98632
Author: Kevin Liu <[email protected]>
AuthorDate: Tue Jan 6 03:49:57 2026 -0500
Clean up logging: move exception tracebacks to debug level (#2867)
<!--
Thanks for opening a pull request!
-->
<!-- In the case this PR will resolve an issue, please replace
${GITHUB_ISSUE_ID} below with the actual Github issue id. -->
<!-- Closes #${GITHUB_ISSUE_ID} -->
# Rationale for this change
I noticed we dump the entire error traceback in log warn messages. This
causes a wall of text when using the CLI.
This PR conditionally dump traceback only for `debug` log level. This
provides cleaner user-facing output by default while still making
detailed exception info available for troubleshooting via debug logging.
- Users now see clean one-line warnings instead of full tracebacks.
- Developers can enable debug logging to see detailed exception info.
Using the shorthand, `exc_info` in logging.warning's kwargs:
https://docs.python.org/3/library/logging.html#:~:text=If%20exc_info%20does%20not%20evaluate%20as%20false%2C%20it%20causes%20exception%20information%20to%20be%20added%20to%20the%20logging%20message.%20If%20an%20exception%20tuple%20(in%20the%20format%20returned%20by%20sys.exc_info())%20or%20an%20exception%20instance%20is%20provided%2C%20it%20is%20used%3B%20otherwise%2C%20sys.exc_info()%20is%20called%20to%20get%20the%20exception%20information.
## Are these changes tested?
## Are there any user-facing changes?
<!-- In the case of user-facing changes, please add the changelog label.
-->
#### Before
```
Could not initialize FileIO: pyiceberg.io.pyarrow.PyArrowFileIO
Traceback (most recent call last):
File "/Users/kevinliu/repos/iceberg-python/pyiceberg/io/__init__.py",
line 321, in _import_file_io
module = importlib.import_module(module_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File
"/Users/kevinliu/.pyenv/versions/3.12.11/lib/python3.12/importlib/__init__.py",
line 90, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<frozen importlib._bootstrap>", line 1387, in _gcd_import
File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
File "<frozen importlib._bootstrap>", line 1331, in
_find_and_load_unlocked
File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
File "<frozen importlib._bootstrap_external>", line 999, in exec_module
File "<frozen importlib._bootstrap>", line 488, in
_call_with_frames_removed
File "/Users/kevinliu/repos/iceberg-python/pyiceberg/io/pyarrow.py", line
54, in <module>
import pyarrow as pa
ModuleNotFoundError: No module named 'pyarrow'
```
#### After
```
Could not initialize FileIO: pyiceberg.io.pyarrow.PyArrowFileIO
```
---
pyiceberg/catalog/__init__.py | 12 ++++++------
pyiceberg/io/__init__.py | 4 ++--
pyiceberg/table/locations.py | 7 +++++--
tests/io/test_io.py | 4 ++++
tests/table/test_locations.py | 4 ++++
5 files changed, 21 insertions(+), 10 deletions(-)
diff --git a/pyiceberg/catalog/__init__.py b/pyiceberg/catalog/__init__.py
index a4f1d47b..d1d83c6d 100644
--- a/pyiceberg/catalog/__init__.py
+++ b/pyiceberg/catalog/__init__.py
@@ -285,8 +285,8 @@ def delete_files(io: FileIO, files_to_delete: set[str],
file_type: str) -> None:
for file in files_to_delete:
try:
io.delete(file)
- except OSError as exc:
- logger.warning(msg=f"Failed to delete {file_type} file {file}",
exc_info=exc)
+ except OSError:
+ logger.warning(f"Failed to delete {file_type} file {file}",
exc_info=logger.isEnabledFor(logging.DEBUG))
def delete_data_files(io: FileIO, manifests_to_delete: list[ManifestFile]) ->
None:
@@ -305,8 +305,8 @@ def delete_data_files(io: FileIO, manifests_to_delete:
list[ManifestFile]) -> No
if not deleted_files.get(path, False):
try:
io.delete(path)
- except OSError as exc:
- logger.warning(msg=f"Failed to delete data file {path}",
exc_info=exc)
+ except OSError:
+ logger.warning(f"Failed to delete data file {path}",
exc_info=logger.isEnabledFor(logging.DEBUG))
deleted_files[path] = True
@@ -319,8 +319,8 @@ def _import_catalog(name: str, catalog_impl: str,
properties: Properties) -> Cat
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
return class_(name, **properties)
- except ModuleNotFoundError as exc:
- logger.warning(f"Could not initialize Catalog: {catalog_impl}",
exc_info=exc)
+ except ModuleNotFoundError:
+ logger.warning(f"Could not initialize Catalog: {catalog_impl}",
exc_info=logger.isEnabledFor(logging.DEBUG))
return None
diff --git a/pyiceberg/io/__init__.py b/pyiceberg/io/__init__.py
index c7109993..87d155a0 100644
--- a/pyiceberg/io/__init__.py
+++ b/pyiceberg/io/__init__.py
@@ -321,8 +321,8 @@ def _import_file_io(io_impl: str, properties: Properties)
-> FileIO | None:
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
return class_(properties)
- except ModuleNotFoundError as exc:
- logger.warning(f"Could not initialize FileIO: {io_impl}", exc_info=exc)
+ except ModuleNotFoundError:
+ logger.warning(f"Could not initialize FileIO: {io_impl}",
exc_info=logger.isEnabledFor(logging.DEBUG))
return None
diff --git a/pyiceberg/table/locations.py b/pyiceberg/table/locations.py
index 25da7e7f..3aeaaf22 100644
--- a/pyiceberg/table/locations.py
+++ b/pyiceberg/table/locations.py
@@ -178,8 +178,11 @@ def _import_location_provider(
module = importlib.import_module(module_name)
class_ = getattr(module, class_name)
return class_(table_location, table_properties)
- except ModuleNotFoundError as exc:
- logger.warning(f"Could not initialize LocationProvider:
{location_provider_impl}", exc_info=exc)
+ except ModuleNotFoundError:
+ logger.warning(
+ f"Could not initialize LocationProvider: {location_provider_impl}",
+ exc_info=logger.isEnabledFor(logging.DEBUG),
+ )
return None
diff --git a/tests/io/test_io.py b/tests/io/test_io.py
index ac1d7b4f..d9bee33f 100644
--- a/tests/io/test_io.py
+++ b/tests/io/test_io.py
@@ -279,7 +279,11 @@ def test_import_file_io() -> None:
def test_import_file_io_does_not_exist(caplog: Any) -> None:
+ import logging
+
+ caplog.set_level(logging.DEBUG)
assert _import_file_io("pyiceberg.does.not.exist.FileIO", {}) is None
+ assert "Could not initialize FileIO: pyiceberg.does.not.exist.FileIO" in
caplog.text
assert "ModuleNotFoundError: No module named 'pyiceberg.does'" in
caplog.text
diff --git a/tests/table/test_locations.py b/tests/table/test_locations.py
index 3634151d..6dd7520f 100644
--- a/tests/table/test_locations.py
+++ b/tests/table/test_locations.py
@@ -66,10 +66,14 @@ def test_custom_location_provider_single_path() -> None:
def test_custom_location_provider_not_found(caplog: Any) -> None:
+ import logging
+
+ caplog.set_level(logging.DEBUG)
with pytest.raises(ValueError, match=r"Could not initialize
LocationProvider"):
load_location_provider(
table_location="table_location",
table_properties={"write.py-location-provider.impl": "module.not_found"}
)
+ assert "Could not initialize LocationProvider: module.not_found" in
caplog.text
assert "ModuleNotFoundError: No module named 'module'" in caplog.text