This is an automated email from the ASF dual-hosted git repository. xuanwo pushed a commit to branch improve-python-errors in repository https://gitbox.apache.org/repos/asf/opendal.git
commit d7707edb93d3151028793d5d1e3d71177a83dad9 Author: Xuanwo <git...@xuanwo.io> AuthorDate: Thu Jun 19 15:02:52 2025 +0800 fix(bindings/python): Fix Writer doesn't throw correct error code Signed-off-by: Xuanwo <git...@xuanwo.io> --- bindings/python/src/errors.rs | 19 ++++++++++++++++++- bindings/python/src/file.rs | 7 ++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs index 575962cbf..336c36a52 100644 --- a/bindings/python/src/errors.rs +++ b/bindings/python/src/errors.rs @@ -17,6 +17,7 @@ use pyo3::create_exception; use pyo3::exceptions::PyException; +use pyo3::exceptions::PyIOError; use crate::*; @@ -57,7 +58,7 @@ create_exception!( "Condition not match" ); -pub fn format_pyerr(err: ocore::Error) -> PyErr { +fn format_pyerr_impl(err: &ocore::Error) -> PyErr { let e = format!("{err:?}"); match err.kind() { ocore::ErrorKind::Unexpected => Unexpected::new_err(e), @@ -73,3 +74,19 @@ pub fn format_pyerr(err: ocore::Error) -> PyErr { _ => Unexpected::new_err(e), } } + +pub fn format_pyerr(err: ocore::Error) -> PyErr { + format_pyerr_impl(&err) +} + +/// Format a std::io::Error into a PyErr, checking if it wraps an OpenDAL error +pub fn format_pyerr_from_io_error(err: std::io::Error) -> PyErr { + // Check if this io::Error wraps an OpenDAL error + if let Some(opendal_err) = err.get_ref().and_then(|e| e.downcast_ref::<ocore::Error>()) { + // If it does, format it as an OpenDAL error to preserve the error type + format_pyerr_impl(opendal_err) + } else { + // Otherwise, format it as a regular IO error + PyIOError::new_err(err.to_string()) + } +} diff --git a/bindings/python/src/file.rs b/bindings/python/src/file.rs index a84fa8ade..10aa24d80 100644 --- a/bindings/python/src/file.rs +++ b/bindings/python/src/file.rs @@ -268,8 +268,7 @@ impl File { fn close(&mut self) -> PyResult<()> { if let FileState::Writer(w) = &mut self.0 { - w.close() - .map_err(|err| PyIOError::new_err(err.to_string()))?; + w.close().map_err(format_pyerr_from_io_error)?; }; self.0 = FileState::Closed; Ok(()) @@ -514,9 +513,7 @@ impl AsyncFile { future_into_py(py, async move { let mut state = state.lock().await; if let AsyncFileState::Writer(w) = &mut *state { - w.close() - .await - .map_err(|err| PyIOError::new_err(err.to_string()))?; + w.close().await.map_err(format_pyerr_from_io_error)?; } *state = AsyncFileState::Closed; Ok(())