This is an automated email from the ASF dual-hosted git repository.

manjusaka pushed a commit to branch manjusaka/polish-exception
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git

commit 5242d1c1ff2ed1a9a77f81fa2f553b0309e129fd
Author: Manjusaka <[email protected]>
AuthorDate: Mon Nov 6 21:00:57 2023 +0800

    reactor(binding/python): Add multiple custom exception for each of error 
code in Rust Core
    
    Signed-off-by: Manjusaka <[email protected]>
---
 bindings/python/python/opendal/__init__.pyi   |  2 -
 bindings/python/python/opendal/exceptions.pyi | 86 +++++++++++++++++++++++++++
 bindings/python/src/errors.rs                 | 86 +++++++++++++++++++++++++++
 bindings/python/src/lib.rs                    | 34 ++++++++++-
 bindings/python/src/utils.rs                  | 21 -------
 bindings/python/tests/test_async_copy.py      |  9 +--
 bindings/python/tests/test_async_delete.py    |  3 +-
 bindings/python/tests/test_async_rename.py    | 17 +++---
 bindings/python/tests/test_read.py            |  5 +-
 bindings/python/tests/test_sync_copy.py       |  9 +--
 bindings/python/tests/test_sync_delete.py     |  3 +-
 bindings/python/tests/test_sync_rename.py     | 17 +++---
 bindings/python/tests/test_write.py           |  5 +-
 13 files changed, 243 insertions(+), 54 deletions(-)

diff --git a/bindings/python/python/opendal/__init__.pyi 
b/bindings/python/python/opendal/__init__.pyi
index 7713e2a3c..2a28f523f 100644
--- a/bindings/python/python/opendal/__init__.pyi
+++ b/bindings/python/python/opendal/__init__.pyi
@@ -19,8 +19,6 @@ from typing import AsyncIterable, Iterable, Optional
 
 from opendal.layers import Layer
 
-class Error(Exception): ...
-
 class Operator:
     def __init__(self, scheme: str, **kwargs): ...
     def layer(self, layer: Layer): ...
diff --git a/bindings/python/python/opendal/exceptions.pyi 
b/bindings/python/python/opendal/exceptions.pyi
new file mode 100644
index 000000000..9ca922335
--- /dev/null
+++ b/bindings/python/python/opendal/exceptions.pyi
@@ -0,0 +1,86 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+class Error(Exception):
+    """OpenDAL unrelated errors"""
+
+    pass
+
+class UnexpectedError(Exception):
+    """Unexpected errors"""
+
+    pass
+
+class UnsupportedError(Exception):
+    """Unsupported operation"""
+
+    pass
+
+class ConfigInvalidError(Exception):
+    """Config is invalid"""
+
+    pass
+
+class NotFoundError(Exception):
+    """Not found"""
+
+    pass
+
+class PermissionDeniedError(Exception):
+    """Permission denied"""
+
+    pass
+
+class IsADirectoryError(Exception):
+    """Is a directory"""
+
+    pass
+
+class NotADirectoryError(Exception):
+    """Not a directory"""
+
+    pass
+
+class AlreadyExistsError(Exception):
+    """Already exists"""
+
+    pass
+
+class IsSameFileError(Exception):
+    """Is same file"""
+
+    pass
+
+class ConditionNotMatchError(Exception):
+    """Condition not match"""
+
+    pass
+
+class ContentTruncatedError(Exception):
+    """Content truncated"""
+
+    pass
+
+class ContentIncompleteError(Exception):
+    """Content incomplete"""
+
+    pass
+
+class InvalidInputError(Exception):
+    """Invalid input"""
+
+    pass
diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs
new file mode 100644
index 000000000..5d7f98e49
--- /dev/null
+++ b/bindings/python/src/errors.rs
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use pyo3::create_exception;
+use pyo3::exceptions::PyException;
+
+use crate::*;
+
+create_exception!(opendal, UnexpectedError, PyException, "Unexpected errors");
+create_exception!(
+    opendal,
+    UnsupportedError,
+    PyException,
+    "Unsupported operation"
+);
+create_exception!(
+    opendal,
+    ConfigInvalidError,
+    PyException,
+    "Config is invalid"
+);
+create_exception!(opendal, NotFoundError, PyException, "Not found");
+create_exception!(
+    opendal,
+    PermissionDeniedError,
+    PyException,
+    "Permission denied"
+);
+create_exception!(opendal, IsADirectoryError, PyException, "Is a directory");
+create_exception!(opendal, NotADirectoryError, PyException, "Not a directory");
+create_exception!(opendal, AlreadyExistsError, PyException, "Already exists");
+create_exception!(opendal, IsSameFileError, PyException, "Is same file");
+create_exception!(
+    opendal,
+    ConditionNotMatchError,
+    PyException,
+    "Condition not match"
+);
+create_exception!(
+    opendal,
+    ContentTruncatedError,
+    PyException,
+    "Content truncated"
+);
+create_exception!(
+    opendal,
+    ContentIncompleteError,
+    PyException,
+    "Content incomplete"
+);
+create_exception!(opendal, InvalidInputError, PyException, "Invalid input");
+create_exception!(opendal, Error, PyException, "OpenDAL unrelated errors");
+
+pub fn format_pyerr(err: ocore::Error) -> PyErr {
+    use ocore::ErrorKind::*;
+    match err.kind() {
+        Unexpected => UnexpectedError::new_err(err.to_string()),
+        Unsupported => UnsupportedError::new_err(err.to_string()),
+        ConfigInvalid => ConfigInvalidError::new_err(err.to_string()),
+        NotFound => NotFoundError::new_err(err.to_string()),
+        PermissionDenied => PermissionDeniedError::new_err(err.to_string()),
+        IsADirectory => IsADirectoryError::new_err(err.to_string()),
+        NotADirectory => NotADirectoryError::new_err(err.to_string()),
+        AlreadyExists => AlreadyExistsError::new_err(err.to_string()),
+        IsSameFile => IsSameFileError::new_err(err.to_string()),
+        ConditionNotMatch => ConditionNotMatchError::new_err(err.to_string()),
+        ContentTruncated => ContentTruncatedError::new_err(err.to_string()),
+        ContentIncomplete => ContentIncompleteError::new_err(err.to_string()),
+        InvalidInput => InvalidInputError::new_err(err.to_string()),
+        _ => Error::new_err(err.to_string()),
+    }
+}
diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs
index a9c41fc00..bada6d204 100644
--- a/bindings/python/src/lib.rs
+++ b/bindings/python/src/lib.rs
@@ -37,6 +37,8 @@ mod file;
 pub use file::*;
 mod utils;
 pub use utils::*;
+mod errors;
+pub use errors::*;
 
 /// OpenDAL Python binding
 ///
@@ -82,7 +84,6 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> {
     m.add_class::<Metadata>()?;
     m.add_class::<PresignedRequest>()?;
     m.add_class::<Capability>()?;
-    m.add("Error", py.get_type::<Error>())?;
 
     // Layer module
     let layers_module = PyModule::new(py, "layers")?;
@@ -93,5 +94,36 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> {
         .getattr("modules")?
         .set_item("opendal.layers", layers_module)?;
 
+    let exception_module = PyModule::new(py, "exceptions")?;
+    exception_module.add("Error", py.get_type::<Error>())?;
+    exception_module.add("UnexpectedError", py.get_type::<UnexpectedError>())?;
+    exception_module.add("UnsupportedError", 
py.get_type::<UnsupportedError>())?;
+    exception_module.add("ConfigInvalidError", 
py.get_type::<ConfigInvalidError>())?;
+    exception_module.add("NotFoundError", py.get_type::<NotFoundError>())?;
+    exception_module.add(
+        "PermissionDeniedError",
+        py.get_type::<PermissionDeniedError>(),
+    )?;
+    exception_module.add("IsADirectoryError", 
py.get_type::<IsADirectoryError>())?;
+    exception_module.add("NotADirectoryError", 
py.get_type::<NotADirectoryError>())?;
+    exception_module.add("AlreadyExistsError", 
py.get_type::<AlreadyExistsError>())?;
+    exception_module.add("IsSameFileError", py.get_type::<IsSameFileError>())?;
+    exception_module.add(
+        "ConditionNotMatchError",
+        py.get_type::<ConditionNotMatchError>(),
+    )?;
+    exception_module.add(
+        "ContentTruncatedError",
+        py.get_type::<ContentTruncatedError>(),
+    )?;
+    exception_module.add(
+        "ContentIncompleteError",
+        py.get_type::<ContentIncompleteError>(),
+    )?;
+    exception_module.add("InvalidInputError", 
py.get_type::<InvalidInputError>())?;
+    m.add_submodule(exception_module)?;
+    py.import("sys")?
+        .getattr("modules")?
+        .set_item("opendal.exceptions", exception_module)?;
     Ok(())
 }
diff --git a/bindings/python/src/utils.rs b/bindings/python/src/utils.rs
index 24cbca9da..df3a41aaa 100644
--- a/bindings/python/src/utils.rs
+++ b/bindings/python/src/utils.rs
@@ -17,20 +17,10 @@
 
 use std::os::raw::c_int;
 
-use pyo3::create_exception;
-use pyo3::exceptions::PyException;
-use pyo3::exceptions::PyFileExistsError;
-use pyo3::exceptions::PyFileNotFoundError;
-use pyo3::exceptions::PyNotImplementedError;
-use pyo3::exceptions::PyPermissionError;
 use pyo3::ffi;
 use pyo3::prelude::*;
 use pyo3::AsPyPointer;
 
-use crate::*;
-
-create_exception!(opendal, Error, PyException, "OpenDAL related errors");
-
 /// A bytes-like object that implements buffer protocol.
 #[pyclass(module = "opendal")]
 pub struct Buffer {
@@ -83,14 +73,3 @@ impl Buffer {
         Ok(())
     }
 }
-
-pub fn format_pyerr(err: ocore::Error) -> PyErr {
-    use ocore::ErrorKind::*;
-    match err.kind() {
-        NotFound => PyFileNotFoundError::new_err(err.to_string()),
-        AlreadyExists => PyFileExistsError::new_err(err.to_string()),
-        PermissionDenied => PyPermissionError::new_err(err.to_string()),
-        Unsupported => PyNotImplementedError::new_err(err.to_string()),
-        _ => Error::new_err(err.to_string()),
-    }
-}
diff --git a/bindings/python/tests/test_async_copy.py 
b/bindings/python/tests/test_async_copy.py
index b5fea80d1..551709e1b 100644
--- a/bindings/python/tests/test_async_copy.py
+++ b/bindings/python/tests/test_async_copy.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import IsADirectoryError, IsSameFileError, 
NotFoundError
 
 
 @pytest.mark.asyncio
@@ -42,7 +43,7 @@ async def test_async_copy(service_name, operator, 
async_operator):
 async def test_async_copy_non_exist(service_name, operator, async_operator):
     source_path = f"random_file_{str(uuid4())}"
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         await async_operator.copy(source_path, target_path)
 
 
@@ -52,7 +53,7 @@ async def test_async_copy_source_directory(service_name, 
operator, async_operato
     source_path = f"random_file_{str(uuid4())}/"
     await async_operator.create_dir(source_path)
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         await async_operator.copy(source_path, target_path)
 
 
@@ -64,7 +65,7 @@ async def test_async_copy_target_directory(service_name, 
operator, async_operato
     await async_operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/"
     await async_operator.create_dir(target_path)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         await async_operator.copy(source_path, target_path)
     await async_operator.delete(source_path)
     await async_operator.delete(target_path)
@@ -76,7 +77,7 @@ async def test_async_copy_self(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}"
     content = os.urandom(1024)
     await async_operator.write(source_path, content)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsSameFileError) as e_info:
         await async_operator.copy(source_path, source_path)
     await async_operator.delete(source_path)
 
diff --git a/bindings/python/tests/test_async_delete.py 
b/bindings/python/tests/test_async_delete.py
index 07e94f85e..ab1051b58 100644
--- a/bindings/python/tests/test_async_delete.py
+++ b/bindings/python/tests/test_async_delete.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import NotFoundError
 
 
 @pytest.mark.asyncio
@@ -43,6 +44,6 @@ async def test_async_remove_all(service_name, operator, 
async_operator):
     await async_operator.remove_all(f"{parent}/x/")
     for path in excepted:
         if not path.endswith("/"):
-            with pytest.raises(FileNotFoundError) as e_info:
+            with pytest.raises(NotFoundError) as e_info:
                 await async_operator.read(f"{parent}/{path}")
     await async_operator.remove_all(f"{parent}/")
diff --git a/bindings/python/tests/test_async_rename.py 
b/bindings/python/tests/test_async_rename.py
index 77ed1fc32..33737600d 100644
--- a/bindings/python/tests/test_async_rename.py
+++ b/bindings/python/tests/test_async_rename.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import IsADirectoryError, IsSameFileError, 
NotFoundError
 
 
 @pytest.mark.asyncio
@@ -30,7 +31,7 @@ async def test_async_rename_file(service_name, operator, 
async_operator):
     await async_operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}"
     await async_operator.rename(source_path, target_path)
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         await async_operator.read(source_path)
     assert await async_operator.read(target_path) == content
     await async_operator.delete(target_path)
@@ -42,7 +43,7 @@ async def test_async_rename_file(service_name, operator, 
async_operator):
 async def test_async_rename_non_exists_file(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}"
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         await async_operator.rename(source_path, target_path)
 
 
@@ -52,7 +53,7 @@ async def test_async_rename_directory(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}/"
     await async_operator.create_dir(source_path)
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         await async_operator.rename(source_path, target_path)
 
 
@@ -63,7 +64,7 @@ async def test_async_rename_file_to_directory(service_name, 
operator, async_oper
     content = os.urandom(1024)
     await async_operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         await async_operator.rename(source_path, target_path)
     await async_operator.delete(source_path)
 
@@ -74,7 +75,7 @@ async def test_async_rename_self(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}"
     content = os.urandom(1024)
     await async_operator.write(source_path, content)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsSameFileError) as e_info:
         await async_operator.rename(source_path, source_path)
     await async_operator.delete(source_path)
 
@@ -87,7 +88,7 @@ async def test_async_rename_nested(service_name, operator, 
async_operator):
     await async_operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/{str(uuid4())}/{str(uuid4())}"
     await async_operator.rename(source_path, target_path)
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         await async_operator.read(source_path)
     assert await async_operator.read(target_path) == content
     await async_operator.delete(target_path)
@@ -105,8 +106,8 @@ async def test_async_rename_overwrite(service_name, 
operator, async_operator):
     await async_operator.write(source_path, source_content)
     await async_operator.write(target_path, target_content)
     await async_operator.rename(source_path, target_path)
-    with pytest.raises(Exception) as e_info:
-        await async_operator.read(source_content)
+    with pytest.raises(NotFoundError) as e_info:
+        await async_operator.read(source_path)
     assert await async_operator.read(target_path) == source_content
     await async_operator.delete(target_path)
     await async_operator.delete(source_path)
diff --git a/bindings/python/tests/test_read.py 
b/bindings/python/tests/test_read.py
index 62a424104..9e6b40460 100644
--- a/bindings/python/tests/test_read.py
+++ b/bindings/python/tests/test_read.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import IsADirectoryError, IsSameFileError, 
NotFoundError
 
 
 @pytest.mark.need_capability("read", "write", "delete")
@@ -134,12 +135,12 @@ async def test_async_read_stat(service_name, operator, 
async_operator):
 
 @pytest.mark.need_capability("read")
 def test_sync_read_not_exists(service_name, operator, async_operator):
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(NotFoundError):
         operator.read(str(uuid4()))
 
 
 @pytest.mark.asyncio
 @pytest.mark.need_capability("read")
 async def test_async_read_not_exists(service_name, operator, async_operator):
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(NotFoundError):
         await async_operator.read(str(uuid4()))
diff --git a/bindings/python/tests/test_sync_copy.py 
b/bindings/python/tests/test_sync_copy.py
index 3db7bb6f1..de2ad35c9 100644
--- a/bindings/python/tests/test_sync_copy.py
+++ b/bindings/python/tests/test_sync_copy.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import IsADirectoryError, IsSameFileError, 
NotFoundError
 
 
 @pytest.mark.need_capability("read", "write", "copy")
@@ -40,7 +41,7 @@ def test_sync_copy(service_name, operator, async_operator):
 def test_sync_copy_non_exist(service_name, operator, async_operator):
     source_path = f"random_file_{str(uuid4())}"
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         operator.copy(source_path, target_path)
 
 
@@ -49,7 +50,7 @@ def test_sync_copy_source_directory(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}/"
     operator.create_dir(source_path)
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         operator.copy(source_path, target_path)
 
 
@@ -60,7 +61,7 @@ def test_sync_copy_target_directory(service_name, operator, 
async_operator):
     operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/"
     operator.create_dir(target_path)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         operator.copy(source_path, target_path)
     operator.delete(source_path)
     operator.delete(target_path)
@@ -71,7 +72,7 @@ def test_sync_copy_self(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}"
     content = os.urandom(1024)
     operator.write(source_path, content)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsSameFileError) as e_info:
         operator.copy(source_path, source_path)
     operator.delete(source_path)
 
diff --git a/bindings/python/tests/test_sync_delete.py 
b/bindings/python/tests/test_sync_delete.py
index 9d06d34cd..d969e5587 100644
--- a/bindings/python/tests/test_sync_delete.py
+++ b/bindings/python/tests/test_sync_delete.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import NotFoundError
 
 
 @pytest.mark.need_capability("read", "write", "delete", "list", "blocking")
@@ -42,6 +43,6 @@ def test_sync_remove_all(service_name, operator, 
async_operator):
     operator.remove_all(f"{parent}/x/")
     for path in excepted:
         if not path.endswith("/"):
-            with pytest.raises(FileNotFoundError) as e_info:
+            with pytest.raises(NotFoundError) as e_info:
                 operator.read(f"{parent}/{path}")
     operator.remove_all(f"{parent}/")
diff --git a/bindings/python/tests/test_sync_rename.py 
b/bindings/python/tests/test_sync_rename.py
index 02def2805..12e0c73e4 100644
--- a/bindings/python/tests/test_sync_rename.py
+++ b/bindings/python/tests/test_sync_rename.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import IsADirectoryError, IsSameFileError, 
NotFoundError
 
 
 @pytest.mark.need_capability("read", "write", "rename")
@@ -29,7 +30,7 @@ def test_sync_rename_file(service_name, operator, 
async_operator):
     operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}"
     operator.rename(source_path, target_path)
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         operator.read(source_path)
     assert operator.read(target_path) == content
     operator.delete(target_path)
@@ -40,7 +41,7 @@ def test_sync_rename_file(service_name, operator, 
async_operator):
 def test_sync_rename_non_exists_file(service_name, operator, async_operator):
     source_path = f"random_file_{str(uuid4())}"
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         operator.rename(source_path, target_path)
 
 
@@ -49,7 +50,7 @@ def test_sync_rename_directory(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}/"
     operator.create_dir(source_path)
     target_path = f"random_file_{str(uuid4())}"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         operator.rename(source_path, target_path)
 
 
@@ -59,7 +60,7 @@ def test_sync_rename_file_to_directory(service_name, 
operator, async_operator):
     content = os.urandom(1024)
     operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/"
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsADirectoryError) as e_info:
         operator.rename(source_path, target_path)
     operator.delete(source_path)
 
@@ -69,7 +70,7 @@ def test_sync_rename_self(service_name, operator, 
async_operator):
     source_path = f"random_file_{str(uuid4())}"
     content = os.urandom(1024)
     operator.write(source_path, content)
-    with pytest.raises(Exception) as e_info:
+    with pytest.raises(IsSameFileError) as e_info:
         operator.rename(source_path, source_path)
     operator.delete(source_path)
 
@@ -81,7 +82,7 @@ def test_sync_rename_nested(service_name, operator, 
async_operator):
     operator.write(source_path, content)
     target_path = f"random_file_{str(uuid4())}/{str(uuid4())}/{str(uuid4())}"
     operator.rename(source_path, target_path)
-    with pytest.raises(FileNotFoundError) as e_info:
+    with pytest.raises(NotFoundError) as e_info:
         operator.read(source_path)
     assert operator.read(target_path) == content
     operator.delete(target_path)
@@ -98,8 +99,8 @@ def test_sync_rename_overwrite(service_name, operator, 
async_operator):
     operator.write(source_path, source_content)
     operator.write(target_path, target_content)
     operator.rename(source_path, target_path)
-    with pytest.raises(Exception) as e_info:
-        operator.read(source_content)
+    with pytest.raises(NotFoundError) as e_info:
+        operator.read(source_path)
     assert operator.read(target_path) == source_content
     operator.delete(target_path)
     operator.delete(source_path)
diff --git a/bindings/python/tests/test_write.py 
b/bindings/python/tests/test_write.py
index 987f0ce3c..8f7d1a0ed 100644
--- a/bindings/python/tests/test_write.py
+++ b/bindings/python/tests/test_write.py
@@ -20,6 +20,7 @@ from random import randint
 from uuid import uuid4
 
 import pytest
+from opendal.exceptions import NotFoundError
 
 
 @pytest.mark.need_capability("write", "delete", "stat")
@@ -84,7 +85,7 @@ def test_sync_delete(service_name, operator, async_operator):
     size = len(content)
     operator.write(filename, content)
     operator.delete(filename)
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(NotFoundError):
         operator.stat(filename)
 
 
@@ -97,5 +98,5 @@ async def test_async_delete(service_name, operator, 
async_operator):
     size = len(content)
     await async_operator.write(filename, content)
     await async_operator.delete(filename)
-    with pytest.raises(FileNotFoundError):
+    with pytest.raises(NotFoundError):
         await operator.stat(filename)

Reply via email to