From d95d51854d570a1eb5b189e98f703bd491941b6e Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 6 Nov 2023 21:00:57 +0800 Subject: [PATCH 1/7] refactor(binding/python): Add multiple custom exception for each of error code in Rust Core Signed-off-by: Manjusaka --- 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(-) create mode 100644 bindings/python/python/opendal/exceptions.pyi create mode 100644 bindings/python/src/errors.rs diff --git a/bindings/python/python/opendal/__init__.pyi b/bindings/python/python/opendal/__init__.pyi index 7713e2a3c797..2a28f523f3eb 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 000000000000..9ca922335362 --- /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 000000000000..5d7f98e49001 --- /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 a9c41fc007b9..bada6d204a1e 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::()?; m.add_class::()?; m.add_class::()?; - m.add("Error", py.get_type::())?; // 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::())?; + exception_module.add("UnexpectedError", py.get_type::())?; + exception_module.add("UnsupportedError", py.get_type::())?; + exception_module.add("ConfigInvalidError", py.get_type::())?; + exception_module.add("NotFoundError", py.get_type::())?; + exception_module.add( + "PermissionDeniedError", + py.get_type::(), + )?; + exception_module.add("IsADirectoryError", py.get_type::())?; + exception_module.add("NotADirectoryError", py.get_type::())?; + exception_module.add("AlreadyExistsError", py.get_type::())?; + exception_module.add("IsSameFileError", py.get_type::())?; + exception_module.add( + "ConditionNotMatchError", + py.get_type::(), + )?; + exception_module.add( + "ContentTruncatedError", + py.get_type::(), + )?; + exception_module.add( + "ContentIncompleteError", + py.get_type::(), + )?; + exception_module.add("InvalidInputError", py.get_type::())?; + 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 24cbca9da767..df3a41aaa349 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 b5fea80d1734..551709e1bce1 100644 --- a/bindings/python/tests/test_async_copy.py +++ b/bindings/python/tests/test_async_copy.py @@ -20,6 +20,7 @@ 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 07e94f85e45a..ab1051b580ef 100644 --- a/bindings/python/tests/test_async_delete.py +++ b/bindings/python/tests/test_async_delete.py @@ -20,6 +20,7 @@ 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 77ed1fc32256..33737600df57 100644 --- a/bindings/python/tests/test_async_rename.py +++ b/bindings/python/tests/test_async_rename.py @@ -20,6 +20,7 @@ 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 62a424104d40..9e6b40460a7e 100644 --- a/bindings/python/tests/test_read.py +++ b/bindings/python/tests/test_read.py @@ -20,6 +20,7 @@ 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 3db7bb6f16bf..de2ad35c90ed 100644 --- a/bindings/python/tests/test_sync_copy.py +++ b/bindings/python/tests/test_sync_copy.py @@ -20,6 +20,7 @@ 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 9d06d34cd330..d969e55879d3 100644 --- a/bindings/python/tests/test_sync_delete.py +++ b/bindings/python/tests/test_sync_delete.py @@ -20,6 +20,7 @@ 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 02def280570f..12e0c73e4d3f 100644 --- a/bindings/python/tests/test_sync_rename.py +++ b/bindings/python/tests/test_sync_rename.py @@ -20,6 +20,7 @@ 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 987f0ce3c54f..8f7d1a0ed750 100644 --- a/bindings/python/tests/test_write.py +++ b/bindings/python/tests/test_write.py @@ -20,6 +20,7 @@ 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) From d0fa9a22ed2642be3ca7c2f57ce25bf53b904d34 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 6 Nov 2023 22:35:12 +0800 Subject: [PATCH 2/7] update code Signed-off-by: Manjusaka --- bindings/python/python/opendal/exceptions.pyi | 30 +++++++------- bindings/python/src/errors.rs | 9 ++++- bindings/python/src/lib.rs | 40 +++++++------------ bindings/python/src/operator.rs | 10 +++-- bindings/python/tests/test_async_copy.py | 10 ++--- bindings/python/tests/test_async_delete.py | 4 +- bindings/python/tests/test_async_rename.py | 16 ++++---- bindings/python/tests/test_read.py | 6 +-- bindings/python/tests/test_sync_copy.py | 10 ++--- bindings/python/tests/test_sync_delete.py | 4 +- bindings/python/tests/test_sync_rename.py | 16 ++++---- bindings/python/tests/test_write.py | 6 +-- bindings/python/upgrade.md | 19 +++++++++ 13 files changed, 97 insertions(+), 83 deletions(-) diff --git a/bindings/python/python/opendal/exceptions.pyi b/bindings/python/python/opendal/exceptions.pyi index 9ca922335362..af37d7b6da05 100644 --- a/bindings/python/python/opendal/exceptions.pyi +++ b/bindings/python/python/opendal/exceptions.pyi @@ -15,72 +15,72 @@ # specific language governing permissions and limitations # under the License. -class Error(Exception): - """OpenDAL unrelated errors""" +class Unknown(Exception): + """Unknown error""" pass -class UnexpectedError(Exception): +class Unexpected(Exception): """Unexpected errors""" pass -class UnsupportedError(Exception): +class Unsupported(Exception): """Unsupported operation""" pass -class ConfigInvalidError(Exception): +class ConfigInvalid(Exception): """Config is invalid""" pass -class NotFoundError(Exception): +class NotFound(Exception): """Not found""" pass -class PermissionDeniedError(Exception): +class PermissionDenied(Exception): """Permission denied""" pass -class IsADirectoryError(Exception): +class IsADirectory(Exception): """Is a directory""" pass -class NotADirectoryError(Exception): +class NotADirectory(Exception): """Not a directory""" pass -class AlreadyExistsError(Exception): +class AlreadyExists(Exception): """Already exists""" pass -class IsSameFileError(Exception): +class IsSameFile(Exception): """Is same file""" pass -class ConditionNotMatchError(Exception): +class ConditionNotMatch(Exception): """Condition not match""" pass -class ContentTruncatedError(Exception): +class ContentTruncated(Exception): """Content truncated""" pass -class ContentIncompleteError(Exception): +class ContentIncomplete(Exception): """Content incomplete""" pass -class InvalidInputError(Exception): +class InvalidInput(Exception): """Invalid input""" pass diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs index 5d7f98e49001..af9a10e806f1 100644 --- a/bindings/python/src/errors.rs +++ b/bindings/python/src/errors.rs @@ -63,7 +63,12 @@ create_exception!( "Content incomplete" ); create_exception!(opendal, InvalidInputError, PyException, "Invalid input"); -create_exception!(opendal, Error, PyException, "OpenDAL unrelated errors"); +create_exception!( + opendal, + Unknown, + PyException, + "OpenDAL Python binding unknown error" +); pub fn format_pyerr(err: ocore::Error) -> PyErr { use ocore::ErrorKind::*; @@ -81,6 +86,6 @@ pub fn format_pyerr(err: ocore::Error) -> PyErr { 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()), + _ => Unknown::new_err(err.to_string()), } } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index bada6d204a1e..f52e861888ba 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -95,32 +95,20 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> { .set_item("opendal.layers", layers_module)?; let exception_module = PyModule::new(py, "exceptions")?; - exception_module.add("Error", py.get_type::())?; - exception_module.add("UnexpectedError", py.get_type::())?; - exception_module.add("UnsupportedError", py.get_type::())?; - exception_module.add("ConfigInvalidError", py.get_type::())?; - exception_module.add("NotFoundError", py.get_type::())?; - exception_module.add( - "PermissionDeniedError", - py.get_type::(), - )?; - exception_module.add("IsADirectoryError", py.get_type::())?; - exception_module.add("NotADirectoryError", py.get_type::())?; - exception_module.add("AlreadyExistsError", py.get_type::())?; - exception_module.add("IsSameFileError", py.get_type::())?; - exception_module.add( - "ConditionNotMatchError", - py.get_type::(), - )?; - exception_module.add( - "ContentTruncatedError", - py.get_type::(), - )?; - exception_module.add( - "ContentIncompleteError", - py.get_type::(), - )?; - exception_module.add("InvalidInputError", py.get_type::())?; + exception_module.add("Unknown", py.get_type::())?; + exception_module.add("Unexpected", py.get_type::())?; + exception_module.add("Unsupported", py.get_type::())?; + exception_module.add("ConfigInvalid", py.get_type::())?; + exception_module.add("NotFound", py.get_type::())?; + exception_module.add("PermissionDenied", py.get_type::())?; + exception_module.add("IsADirectory", py.get_type::())?; + exception_module.add("NotADirectory", py.get_type::())?; + exception_module.add("AlreadyExists", py.get_type::())?; + exception_module.add("IsSameFile", py.get_type::())?; + exception_module.add("ConditionNotMatch", py.get_type::())?; + exception_module.add("ContentTruncated", py.get_type::())?; + exception_module.add("ContentIncomplete", py.get_type::())?; + exception_module.add("InvalidInput", py.get_type::())?; m.add_submodule(exception_module)?; py.import("sys")? .getattr("modules")? diff --git a/bindings/python/src/operator.rs b/bindings/python/src/operator.rs index 5bc95a2ccffa..ac2326323a6f 100644 --- a/bindings/python/src/operator.rs +++ b/bindings/python/src/operator.rs @@ -86,7 +86,7 @@ impl Operator { let w = this.writer(&path).map_err(format_pyerr)?; Ok(File::new_writer(w)) } else { - Err(Error::new_err(format!( + Err(UnsupportedError::new_err(format!( "OpenDAL doesn't support mode: {mode}" ))) } @@ -245,7 +245,7 @@ impl AsyncOperator { let w = this.writer(&path).await.map_err(format_pyerr)?; Ok(AsyncFile::new_writer(w)) } else { - Err(Error::new_err(format!( + Err(UnsupportedError::new_err(format!( "OpenDAL doesn't support mode: {mode}" ))) } @@ -547,9 +547,11 @@ impl PresignedRequest { let mut headers = HashMap::new(); for (k, v) in self.0.header().iter() { let k = k.as_str(); - let v = v.to_str().map_err(|err| Error::new_err(err.to_string()))?; + let v = v + .to_str() + .map_err(|err| Unknown::new_err(err.to_string()))?; if headers.insert(k, v).is_some() { - return Err(Error::new_err("duplicate header")); + return Err(Unknown::new_err("duplicate header")); } } Ok(headers) diff --git a/bindings/python/tests/test_async_copy.py b/bindings/python/tests/test_async_copy.py index 551709e1bce1..424e3427c8fb 100644 --- a/bindings/python/tests/test_async_copy.py +++ b/bindings/python/tests/test_async_copy.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectoryError, IsSameFileError, NotFoundError +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -43,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.copy(source_path, target_path) @@ -53,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.copy(source_path, target_path) @@ -65,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.copy(source_path, target_path) await async_operator.delete(source_path) await async_operator.delete(target_path) @@ -77,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(IsSameFileError) as e_info: + with pytest.raises(IsSameFile) 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 ab1051b580ef..ff3d41d0c3f5 100644 --- a/bindings/python/tests/test_async_delete.py +++ b/bindings/python/tests/test_async_delete.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import NotFoundError +from opendal.exceptions import NotFound @pytest.mark.asyncio @@ -44,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(NotFoundError) as e_info: + with pytest.raises(NotFound) 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 33737600df57..8c718fc52592 100644 --- a/bindings/python/tests/test_async_rename.py +++ b/bindings/python/tests/test_async_rename.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectoryError, IsSameFileError, NotFoundError +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -31,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) @@ -43,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.rename(source_path, target_path) @@ -53,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.rename(source_path, target_path) @@ -64,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.rename(source_path, target_path) await async_operator.delete(source_path) @@ -75,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(IsSameFileError) as e_info: + with pytest.raises(IsSameFile) as e_info: await async_operator.rename(source_path, source_path) await async_operator.delete(source_path) @@ -88,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) @@ -106,7 +106,7 @@ 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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) assert await async_operator.read(target_path) == source_content await async_operator.delete(target_path) diff --git a/bindings/python/tests/test_read.py b/bindings/python/tests/test_read.py index 9e6b40460a7e..82eb1e9fd664 100644 --- a/bindings/python/tests/test_read.py +++ b/bindings/python/tests/test_read.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectoryError, IsSameFileError, NotFoundError +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "delete") @@ -135,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(NotFoundError): + with pytest.raises(NotFound): 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(NotFoundError): + with pytest.raises(NotFound): 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 de2ad35c90ed..0c4f2bb54055 100644 --- a/bindings/python/tests/test_sync_copy.py +++ b/bindings/python/tests/test_sync_copy.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectoryError, IsSameFileError, NotFoundError +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "copy") @@ -41,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: operator.copy(source_path, target_path) @@ -50,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.copy(source_path, target_path) @@ -61,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.copy(source_path, target_path) operator.delete(source_path) operator.delete(target_path) @@ -72,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(IsSameFileError) as e_info: + with pytest.raises(IsSameFile) 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 d969e55879d3..7deb5c5f2dee 100644 --- a/bindings/python/tests/test_sync_delete.py +++ b/bindings/python/tests/test_sync_delete.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import NotFoundError +from opendal.exceptions import NotFound @pytest.mark.need_capability("read", "write", "delete", "list", "blocking") @@ -43,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(NotFoundError) as e_info: + with pytest.raises(NotFound) 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 12e0c73e4d3f..3caced0594dc 100644 --- a/bindings/python/tests/test_sync_rename.py +++ b/bindings/python/tests/test_sync_rename.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectoryError, IsSameFileError, NotFoundError +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "rename") @@ -30,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) assert operator.read(target_path) == content operator.delete(target_path) @@ -41,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: operator.rename(source_path, target_path) @@ -50,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.rename(source_path, target_path) @@ -60,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(IsADirectoryError) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.rename(source_path, target_path) operator.delete(source_path) @@ -70,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(IsSameFileError) as e_info: + with pytest.raises(IsSameFile) as e_info: operator.rename(source_path, source_path) operator.delete(source_path) @@ -82,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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) assert operator.read(target_path) == content operator.delete(target_path) @@ -99,7 +99,7 @@ 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(NotFoundError) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) assert operator.read(target_path) == source_content operator.delete(target_path) diff --git a/bindings/python/tests/test_write.py b/bindings/python/tests/test_write.py index 8f7d1a0ed750..e98c6e959be9 100644 --- a/bindings/python/tests/test_write.py +++ b/bindings/python/tests/test_write.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import NotFoundError +from opendal.exceptions import NotFound @pytest.mark.need_capability("write", "delete", "stat") @@ -85,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(NotFoundError): + with pytest.raises(NotFound): operator.stat(filename) @@ -98,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(NotFoundError): + with pytest.raises(NotFound): await operator.stat(filename) diff --git a/bindings/python/upgrade.md b/bindings/python/upgrade.md index de9041e611b2..58f9b3c80612 100644 --- a/bindings/python/upgrade.md +++ b/bindings/python/upgrade.md @@ -27,3 +27,22 @@ Open a file for reading in async way: async with await op.open(filename, "rb") as r: content = await r.read() ``` + +## Breaking change for Errors + +We remove the old error classes and provide a couple of Exception based class for the error handling. + +1. `opendal.Error` is removed, use `opendal.exceptions.Unknown` instead. +2. `opendal.exceptions.Unexpected` is added. +3. `opendal.exceptions.Unsupported` is added. +4. `opendal.exceptions.ConfigInvalid` is added. +5. `opendal.exceptions.NotFound` is added. +6. `opendal.exceptions.PermissionDenied` is added. +7. `opendal.exceptions.IsADirectory` is added. +8. `opendal.exceptions.NotADirectory` is added. +9. `opendal.exceptions.AlreadyExists` is added. +10. `opendal.exceptions.IsSameFile` is added. +11. `opendal.exceptions.ConditionNotMatch` is added. +12. `opendal.exceptions.ContentTruncated` is added. +13. `opendal.exceptions.ContentIncomplete` is added. +14. `opendal.exceptions.InvalidInput` is added. From 244db6b84b8aa03ab46776ac8604eeff3abd2ee0 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Mon, 6 Nov 2023 22:41:52 +0800 Subject: [PATCH 3/7] Update code Signed-off-by: Manjusaka --- bindings/python/python/opendal/exceptions.pyi | 5 ----- bindings/python/src/errors.rs | 8 +------- bindings/python/src/lib.rs | 1 - bindings/python/src/operator.rs | 4 ++-- bindings/python/upgrade.md | 2 +- 5 files changed, 4 insertions(+), 16 deletions(-) diff --git a/bindings/python/python/opendal/exceptions.pyi b/bindings/python/python/opendal/exceptions.pyi index af37d7b6da05..60c0ddaff84f 100644 --- a/bindings/python/python/opendal/exceptions.pyi +++ b/bindings/python/python/opendal/exceptions.pyi @@ -15,11 +15,6 @@ # specific language governing permissions and limitations # under the License. -class Unknown(Exception): - """Unknown error""" - - pass - class Unexpected(Exception): """Unexpected errors""" diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs index af9a10e806f1..93a8acf4e9ff 100644 --- a/bindings/python/src/errors.rs +++ b/bindings/python/src/errors.rs @@ -63,12 +63,6 @@ create_exception!( "Content incomplete" ); create_exception!(opendal, InvalidInputError, PyException, "Invalid input"); -create_exception!( - opendal, - Unknown, - PyException, - "OpenDAL Python binding unknown error" -); pub fn format_pyerr(err: ocore::Error) -> PyErr { use ocore::ErrorKind::*; @@ -86,6 +80,6 @@ pub fn format_pyerr(err: ocore::Error) -> PyErr { ContentTruncated => ContentTruncatedError::new_err(err.to_string()), ContentIncomplete => ContentIncompleteError::new_err(err.to_string()), InvalidInput => InvalidInputError::new_err(err.to_string()), - _ => Unknown::new_err(err.to_string()), + _ => UnexpectedError::new_err(err.to_string()), } } diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index f52e861888ba..8952e87663c5 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -95,7 +95,6 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> { .set_item("opendal.layers", layers_module)?; let exception_module = PyModule::new(py, "exceptions")?; - exception_module.add("Unknown", py.get_type::())?; exception_module.add("Unexpected", py.get_type::())?; exception_module.add("Unsupported", py.get_type::())?; exception_module.add("ConfigInvalid", py.get_type::())?; diff --git a/bindings/python/src/operator.rs b/bindings/python/src/operator.rs index ac2326323a6f..f6e8c3f864b3 100644 --- a/bindings/python/src/operator.rs +++ b/bindings/python/src/operator.rs @@ -549,9 +549,9 @@ impl PresignedRequest { let k = k.as_str(); let v = v .to_str() - .map_err(|err| Unknown::new_err(err.to_string()))?; + .map_err(|err| UnexpectedError::new_err(err.to_string()))?; if headers.insert(k, v).is_some() { - return Err(Unknown::new_err("duplicate header")); + return Err(UnexpectedError::new_err("duplicate header")); } } Ok(headers) diff --git a/bindings/python/upgrade.md b/bindings/python/upgrade.md index 58f9b3c80612..6cb92c0c1113 100644 --- a/bindings/python/upgrade.md +++ b/bindings/python/upgrade.md @@ -32,7 +32,7 @@ async with await op.open(filename, "rb") as r: We remove the old error classes and provide a couple of Exception based class for the error handling. -1. `opendal.Error` is removed, use `opendal.exceptions.Unknown` instead. +1. `opendal.Error` is removed, use `opendal.exceptions.Unexpected` instead. 2. `opendal.exceptions.Unexpected` is added. 3. `opendal.exceptions.Unsupported` is added. 4. `opendal.exceptions.ConfigInvalid` is added. From 720122b0c9d07e86e26a64a21ae5a04895761168 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Tue, 7 Nov 2023 12:40:31 +0800 Subject: [PATCH 4/7] Update code Signed-off-by: Manjusaka --- bindings/python/python/opendal/exceptions.pyi | 31 ++++++----- bindings/python/src/errors.rs | 52 +++++-------------- bindings/python/src/lib.rs | 1 + 3 files changed, 33 insertions(+), 51 deletions(-) diff --git a/bindings/python/python/opendal/exceptions.pyi b/bindings/python/python/opendal/exceptions.pyi index 60c0ddaff84f..a14f25c26ff0 100644 --- a/bindings/python/python/opendal/exceptions.pyi +++ b/bindings/python/python/opendal/exceptions.pyi @@ -15,67 +15,72 @@ # specific language governing permissions and limitations # under the License. -class Unexpected(Exception): +class Error(Exception): + """Base class for exceptions in this module.""" + + pass + +class Unexpected(Error): """Unexpected errors""" pass -class Unsupported(Exception): +class Unsupported(Error): """Unsupported operation""" pass -class ConfigInvalid(Exception): +class ConfigInvalid(Error): """Config is invalid""" pass -class NotFound(Exception): +class NotFound(Error): """Not found""" pass -class PermissionDenied(Exception): +class PermissionDenied(Error): """Permission denied""" pass -class IsADirectory(Exception): +class IsADirectory(Error): """Is a directory""" pass -class NotADirectory(Exception): +class NotADirectory(Error): """Not a directory""" pass -class AlreadyExists(Exception): +class AlreadyExists(Error): """Already exists""" pass -class IsSameFile(Exception): +class IsSameFile(Error): """Is same file""" pass -class ConditionNotMatch(Exception): +class ConditionNotMatch(Error): """Condition not match""" pass -class ContentTruncated(Exception): +class ContentTruncated(Error): """Content truncated""" pass -class ContentIncomplete(Exception): +class ContentIncomplete(Error): """Content incomplete""" pass -class InvalidInput(Exception): +class InvalidInput(Error): """Invalid input""" pass diff --git a/bindings/python/src/errors.rs b/bindings/python/src/errors.rs index 93a8acf4e9ff..45bff4060e56 100644 --- a/bindings/python/src/errors.rs +++ b/bindings/python/src/errors.rs @@ -20,49 +20,25 @@ 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, Error, PyException, "OpenDAL Base Exception"); +create_exception!(opendal, UnexpectedError, Error, "Unexpected errors"); +create_exception!(opendal, UnsupportedError, Error, "Unsupported operation"); +create_exception!(opendal, ConfigInvalidError, Error, "Config is invalid"); +create_exception!(opendal, NotFoundError, Error, "Not found"); +create_exception!(opendal, PermissionDeniedError, Error, "Permission denied"); +create_exception!(opendal, IsADirectoryError, Error, "Is a directory"); +create_exception!(opendal, NotADirectoryError, Error, "Not a directory"); +create_exception!(opendal, AlreadyExistsError, Error, "Already exists"); +create_exception!(opendal, IsSameFileError, Error, "Is same file"); create_exception!( opendal, ConditionNotMatchError, - PyException, + Error, "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, ContentTruncatedError, Error, "Content truncated"); +create_exception!(opendal, ContentIncompleteError, Error, "Content incomplete"); +create_exception!(opendal, InvalidInputError, Error, "Invalid input"); pub fn format_pyerr(err: ocore::Error) -> PyErr { use ocore::ErrorKind::*; diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 8952e87663c5..076c756964c6 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -95,6 +95,7 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> { .set_item("opendal.layers", layers_module)?; let exception_module = PyModule::new(py, "exceptions")?; + exception_module.add("Error", py.get_type::())?; exception_module.add("Unexpected", py.get_type::())?; exception_module.add("Unsupported", py.get_type::())?; exception_module.add("ConfigInvalid", py.get_type::())?; From 1d7d5a797c9c4236df78ba780b117451001dd02d Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Tue, 7 Nov 2023 14:04:00 +0800 Subject: [PATCH 5/7] Update code Signed-off-by: Manjusaka --- bindings/python/tests/test_async_copy.py | 15 ++-- bindings/python/tests/test_async_delete.py | 6 +- bindings/python/tests/test_async_rename.py | 24 +++--- bindings/python/tests/test_capability.py | 2 +- bindings/python/tests/test_read.py | 8 +- bindings/python/tests/test_sync_copy.py | 88 +++++++++++----------- bindings/python/tests/test_sync_delete.py | 8 +- bindings/python/tests/test_sync_rename.py | 24 +++--- bindings/python/upgrade.md | 2 +- 9 files changed, 99 insertions(+), 78 deletions(-) diff --git a/bindings/python/tests/test_async_copy.py b/bindings/python/tests/test_async_copy.py index 424e3427c8fb..c902d42d7c45 100644 --- a/bindings/python/tests/test_async_copy.py +++ b/bindings/python/tests/test_async_copy.py @@ -16,11 +16,10 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -43,8 +42,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.copy(source_path, target_path) + assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @@ -53,8 +53,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: await async_operator.copy(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) @pytest.mark.asyncio @@ -65,8 +66,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: await async_operator.copy(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) await async_operator.delete(source_path) await async_operator.delete(target_path) @@ -77,8 +79,9 @@ 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(IsSameFile) as e_info: + with pytest.raises(Error) as e_info: await async_operator.copy(source_path, source_path) + assert isinstance(e_info.value, IsSameFile) 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 ff3d41d0c3f5..04e3fc3fdc54 100644 --- a/bindings/python/tests/test_async_delete.py +++ b/bindings/python/tests/test_async_delete.py @@ -16,11 +16,10 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import NotFound +from opendal.exceptions import Error, NotFound @pytest.mark.asyncio @@ -44,6 +43,7 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.read(f"{parent}/{path}") + assert isinstance(e_info.value, NotFound) 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 8c718fc52592..7db808c9f860 100644 --- a/bindings/python/tests/test_async_rename.py +++ b/bindings/python/tests/test_async_rename.py @@ -16,11 +16,10 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -31,8 +30,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.read(source_path) + assert isinstance(e_info.value, NotFound) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) await async_operator.delete(source_path) @@ -43,8 +43,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.rename(source_path, target_path) + assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @@ -53,8 +54,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: await async_operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) @pytest.mark.asyncio @@ -64,8 +66,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: await async_operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) await async_operator.delete(source_path) @@ -75,8 +78,9 @@ 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(IsSameFile) as e_info: + with pytest.raises(Error) as e_info: await async_operator.rename(source_path, source_path) + assert isinstance(e_info.value, IsSameFile) await async_operator.delete(source_path) @@ -88,8 +92,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.read(source_path) + assert isinstance(e_info.value, NotFound) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) await async_operator.delete(source_path) @@ -106,8 +111,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: await async_operator.read(source_path) + assert isinstance(e_info.value, NotFound) 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_capability.py b/bindings/python/tests/test_capability.py index da2e9c14f854..1b47af4f86a0 100644 --- a/bindings/python/tests/test_capability.py +++ b/bindings/python/tests/test_capability.py @@ -27,5 +27,5 @@ def test_capability(service_name, operator): def test_capability_exception(service_name, operator): cap = operator.capability() assert cap is not None - with pytest.raises(AttributeError) as e_info: + with pytest.raises(AttributeError): cap.read_demo diff --git a/bindings/python/tests/test_read.py b/bindings/python/tests/test_read.py index 82eb1e9fd664..ce9eed9084ea 100644 --- a/bindings/python/tests/test_read.py +++ b/bindings/python/tests/test_read.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import Error, NotFound @pytest.mark.need_capability("read", "write", "delete") @@ -135,12 +135,14 @@ 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(NotFound): + with pytest.raises(Error) as e_info: operator.read(str(uuid4())) + assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @pytest.mark.need_capability("read") async def test_async_read_not_exists(service_name, operator, async_operator): - with pytest.raises(NotFound): + with pytest.raises(Error) as e_info: await async_operator.read(str(uuid4())) + assert isinstance(e_info.value, NotFound) diff --git a/bindings/python/tests/test_sync_copy.py b/bindings/python/tests/test_sync_copy.py index 0c4f2bb54055..56f8d9c29131 100644 --- a/bindings/python/tests/test_sync_copy.py +++ b/bindings/python/tests/test_sync_copy.py @@ -16,83 +16,86 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +def test_sync_rename_file(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" content = os.urandom(1024) operator.write(source_path, content) target_path = f"random_file_{str(uuid4())}" - operator.copy(source_path, target_path) - read_content = operator.read(target_path) - assert read_content is not None - assert read_content == content - operator.delete(source_path) + operator.rename(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.read(source_path) + assert isinstance(e_info.value, NotFound) + assert operator.read(target_path) == content operator.delete(target_path) + operator.delete(source_path) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_non_exist(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +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(NotFound) as e_info: - operator.copy(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.rename(source_path, target_path) + assert isinstance(e_info.value, NotFound) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_source_directory(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +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(IsADirectory) as e_info: - operator.copy(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_target_directory(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +def test_sync_rename_file_to_directory(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" content = os.urandom(1024) operator.write(source_path, content) target_path = f"random_file_{str(uuid4())}/" - operator.create_dir(target_path) - with pytest.raises(IsADirectory) as e_info: - operator.copy(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) operator.delete(source_path) - operator.delete(target_path) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_self(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +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(IsSameFile) as e_info: - operator.copy(source_path, source_path) + with pytest.raises(Error) as e_info: + operator.rename(source_path, source_path) + assert isinstance(e_info.value, IsSameFile) operator.delete(source_path) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_nested(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +def test_sync_rename_nested(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" - target_path = f"random_file_{str(uuid4())}/{str(uuid4())}/{str(uuid4())}" content = os.urandom(1024) operator.write(source_path, content) - operator.copy(source_path, target_path) - target_content = operator.read(target_path) - assert target_content is not None - assert target_content == content - operator.delete(source_path) + target_path = f"random_file_{str(uuid4())}/{str(uuid4())}/{str(uuid4())}" + operator.rename(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.read(source_path) + assert isinstance(e_info.value, NotFound) + assert operator.read(target_path) == content operator.delete(target_path) + operator.delete(source_path) -@pytest.mark.need_capability("read", "write", "copy") -def test_sync_copy_overwrite(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "rename") +def test_sync_rename_overwrite(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" target_path = f"random_file_{str(uuid4())}" source_content = os.urandom(1024) @@ -100,9 +103,10 @@ def test_sync_copy_overwrite(service_name, operator, async_operator): assert source_content != target_content operator.write(source_path, source_content) operator.write(target_path, target_content) - operator.copy(source_path, target_path) - target_content = operator.read(target_path) - assert target_content is not None - assert target_content == source_content - operator.delete(source_path) + operator.rename(source_path, target_path) + with pytest.raises(Error) as e_info: + operator.read(source_path) + assert isinstance(e_info.value, NotFound) + assert operator.read(target_path) == source_content operator.delete(target_path) + operator.delete(source_path) diff --git a/bindings/python/tests/test_sync_delete.py b/bindings/python/tests/test_sync_delete.py index 7deb5c5f2dee..f81b9bfbe8e3 100644 --- a/bindings/python/tests/test_sync_delete.py +++ b/bindings/python/tests/test_sync_delete.py @@ -16,14 +16,13 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import NotFound +from opendal.exceptions import Error, NotFound -@pytest.mark.need_capability("read", "write", "delete", "list", "blocking") +@pytest.mark.need_capability("read", "write", "delete", "list") def test_sync_remove_all(service_name, operator, async_operator): parent = f"random_dir_{str(uuid4())}" excepted = [ @@ -43,6 +42,7 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: operator.read(f"{parent}/{path}") + assert isinstance(e_info.value, NotFound) operator.remove_all(f"{parent}/") diff --git a/bindings/python/tests/test_sync_rename.py b/bindings/python/tests/test_sync_rename.py index 3caced0594dc..56f8d9c29131 100644 --- a/bindings/python/tests/test_sync_rename.py +++ b/bindings/python/tests/test_sync_rename.py @@ -16,11 +16,10 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "rename") @@ -30,8 +29,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: operator.read(source_path) + assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == content operator.delete(target_path) operator.delete(source_path) @@ -41,8 +41,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: operator.rename(source_path, target_path) + assert isinstance(e_info.value, NotFound) @pytest.mark.need_capability("read", "write", "rename") @@ -50,8 +51,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) @pytest.mark.need_capability("read", "write", "rename") @@ -60,8 +62,9 @@ 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(IsADirectory) as e_info: + with pytest.raises(Error) as e_info: operator.rename(source_path, target_path) + assert isinstance(e_info.value, IsADirectory) operator.delete(source_path) @@ -70,8 +73,9 @@ 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(IsSameFile) as e_info: + with pytest.raises(Error) as e_info: operator.rename(source_path, source_path) + assert isinstance(e_info.value, IsSameFile) operator.delete(source_path) @@ -82,8 +86,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: operator.read(source_path) + assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == content operator.delete(target_path) operator.delete(source_path) @@ -99,8 +104,9 @@ 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(NotFound) as e_info: + with pytest.raises(Error) as e_info: operator.read(source_path) + assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == source_content operator.delete(target_path) operator.delete(source_path) diff --git a/bindings/python/upgrade.md b/bindings/python/upgrade.md index 6cb92c0c1113..72ff6efe232c 100644 --- a/bindings/python/upgrade.md +++ b/bindings/python/upgrade.md @@ -32,7 +32,7 @@ async with await op.open(filename, "rb") as r: We remove the old error classes and provide a couple of Exception based class for the error handling. -1. `opendal.Error` is removed, use `opendal.exceptions.Unexpected` instead. +1. `opendal.Error` is based class for all the exceptions now. 2. `opendal.exceptions.Unexpected` is added. 3. `opendal.exceptions.Unsupported` is added. 4. `opendal.exceptions.ConfigInvalid` is added. From f80337163a2ae28ffb3b18333457a9d247744026 Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Tue, 7 Nov 2023 14:15:28 +0800 Subject: [PATCH 6/7] Revert "Update code" This reverts commit 1d7d5a797c9c4236df78ba780b117451001dd02d. --- bindings/python/tests/test_async_copy.py | 15 ++-- bindings/python/tests/test_async_delete.py | 6 +- bindings/python/tests/test_async_rename.py | 24 +++--- bindings/python/tests/test_capability.py | 2 +- bindings/python/tests/test_read.py | 8 +- bindings/python/tests/test_sync_copy.py | 88 +++++++++++----------- bindings/python/tests/test_sync_delete.py | 8 +- bindings/python/tests/test_sync_rename.py | 24 +++--- bindings/python/upgrade.md | 2 +- 9 files changed, 78 insertions(+), 99 deletions(-) diff --git a/bindings/python/tests/test_async_copy.py b/bindings/python/tests/test_async_copy.py index c902d42d7c45..424e3427c8fb 100644 --- a/bindings/python/tests/test_async_copy.py +++ b/bindings/python/tests/test_async_copy.py @@ -16,10 +16,11 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -42,9 +43,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.copy(source_path, target_path) - assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @@ -53,9 +53,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.copy(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) @pytest.mark.asyncio @@ -66,9 +65,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.copy(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) await async_operator.delete(source_path) await async_operator.delete(target_path) @@ -79,9 +77,8 @@ 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(Error) as e_info: + with pytest.raises(IsSameFile) as e_info: await async_operator.copy(source_path, source_path) - assert isinstance(e_info.value, IsSameFile) 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 04e3fc3fdc54..ff3d41d0c3f5 100644 --- a/bindings/python/tests/test_async_delete.py +++ b/bindings/python/tests/test_async_delete.py @@ -16,10 +16,11 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, NotFound +from opendal.exceptions import NotFound @pytest.mark.asyncio @@ -43,7 +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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(f"{parent}/{path}") - assert isinstance(e_info.value, NotFound) 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 7db808c9f860..8c718fc52592 100644 --- a/bindings/python/tests/test_async_rename.py +++ b/bindings/python/tests/test_async_rename.py @@ -16,10 +16,11 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.asyncio @@ -30,9 +31,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) - assert isinstance(e_info.value, NotFound) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) await async_operator.delete(source_path) @@ -43,9 +43,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.rename(source_path, target_path) - assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @@ -54,9 +53,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) @pytest.mark.asyncio @@ -66,9 +64,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: await async_operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) await async_operator.delete(source_path) @@ -78,9 +75,8 @@ 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(Error) as e_info: + with pytest.raises(IsSameFile) as e_info: await async_operator.rename(source_path, source_path) - assert isinstance(e_info.value, IsSameFile) await async_operator.delete(source_path) @@ -92,9 +88,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) - assert isinstance(e_info.value, NotFound) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) await async_operator.delete(source_path) @@ -111,9 +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(Error) as e_info: + with pytest.raises(NotFound) as e_info: await async_operator.read(source_path) - assert isinstance(e_info.value, NotFound) 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_capability.py b/bindings/python/tests/test_capability.py index 1b47af4f86a0..da2e9c14f854 100644 --- a/bindings/python/tests/test_capability.py +++ b/bindings/python/tests/test_capability.py @@ -27,5 +27,5 @@ def test_capability(service_name, operator): def test_capability_exception(service_name, operator): cap = operator.capability() assert cap is not None - with pytest.raises(AttributeError): + with pytest.raises(AttributeError) as e_info: cap.read_demo diff --git a/bindings/python/tests/test_read.py b/bindings/python/tests/test_read.py index ce9eed9084ea..82eb1e9fd664 100644 --- a/bindings/python/tests/test_read.py +++ b/bindings/python/tests/test_read.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import Error, NotFound +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "delete") @@ -135,14 +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(Error) as e_info: + with pytest.raises(NotFound): operator.read(str(uuid4())) - assert isinstance(e_info.value, NotFound) @pytest.mark.asyncio @pytest.mark.need_capability("read") async def test_async_read_not_exists(service_name, operator, async_operator): - with pytest.raises(Error) as e_info: + with pytest.raises(NotFound): await async_operator.read(str(uuid4())) - assert isinstance(e_info.value, NotFound) diff --git a/bindings/python/tests/test_sync_copy.py b/bindings/python/tests/test_sync_copy.py index 56f8d9c29131..0c4f2bb54055 100644 --- a/bindings/python/tests/test_sync_copy.py +++ b/bindings/python/tests/test_sync_copy.py @@ -16,86 +16,83 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound +from opendal.exceptions import IsADirectory, IsSameFile, NotFound -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_file(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +def test_sync_copy(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" content = os.urandom(1024) operator.write(source_path, content) target_path = f"random_file_{str(uuid4())}" - operator.rename(source_path, target_path) - with pytest.raises(Error) as e_info: - operator.read(source_path) - assert isinstance(e_info.value, NotFound) - assert operator.read(target_path) == content - operator.delete(target_path) + operator.copy(source_path, target_path) + read_content = operator.read(target_path) + assert read_content is not None + assert read_content == content operator.delete(source_path) + operator.delete(target_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_non_exists_file(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +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(Error) as e_info: - operator.rename(source_path, target_path) - assert isinstance(e_info.value, NotFound) + with pytest.raises(NotFound) as e_info: + operator.copy(source_path, target_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_directory(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +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(Error) as e_info: - operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) + with pytest.raises(IsADirectory) as e_info: + operator.copy(source_path, target_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_file_to_directory(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +def test_sync_copy_target_directory(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" content = os.urandom(1024) operator.write(source_path, content) target_path = f"random_file_{str(uuid4())}/" - with pytest.raises(Error) as e_info: - operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) + operator.create_dir(target_path) + with pytest.raises(IsADirectory) as e_info: + operator.copy(source_path, target_path) operator.delete(source_path) + operator.delete(target_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_self(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +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(Error) as e_info: - operator.rename(source_path, source_path) - assert isinstance(e_info.value, IsSameFile) + with pytest.raises(IsSameFile) as e_info: + operator.copy(source_path, source_path) operator.delete(source_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_nested(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +def test_sync_copy_nested(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" + target_path = f"random_file_{str(uuid4())}/{str(uuid4())}/{str(uuid4())}" content = os.urandom(1024) 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(Error) as e_info: - operator.read(source_path) - assert isinstance(e_info.value, NotFound) - assert operator.read(target_path) == content - operator.delete(target_path) + operator.copy(source_path, target_path) + target_content = operator.read(target_path) + assert target_content is not None + assert target_content == content operator.delete(source_path) + operator.delete(target_path) -@pytest.mark.need_capability("read", "write", "rename") -def test_sync_rename_overwrite(service_name, operator, async_operator): +@pytest.mark.need_capability("read", "write", "copy") +def test_sync_copy_overwrite(service_name, operator, async_operator): source_path = f"random_file_{str(uuid4())}" target_path = f"random_file_{str(uuid4())}" source_content = os.urandom(1024) @@ -103,10 +100,9 @@ def test_sync_rename_overwrite(service_name, operator, async_operator): assert source_content != target_content operator.write(source_path, source_content) operator.write(target_path, target_content) - operator.rename(source_path, target_path) - with pytest.raises(Error) as e_info: - operator.read(source_path) - assert isinstance(e_info.value, NotFound) - assert operator.read(target_path) == source_content - operator.delete(target_path) + operator.copy(source_path, target_path) + target_content = operator.read(target_path) + assert target_content is not None + assert target_content == source_content operator.delete(source_path) + operator.delete(target_path) diff --git a/bindings/python/tests/test_sync_delete.py b/bindings/python/tests/test_sync_delete.py index f81b9bfbe8e3..7deb5c5f2dee 100644 --- a/bindings/python/tests/test_sync_delete.py +++ b/bindings/python/tests/test_sync_delete.py @@ -16,13 +16,14 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, NotFound +from opendal.exceptions import NotFound -@pytest.mark.need_capability("read", "write", "delete", "list") +@pytest.mark.need_capability("read", "write", "delete", "list", "blocking") def test_sync_remove_all(service_name, operator, async_operator): parent = f"random_dir_{str(uuid4())}" excepted = [ @@ -42,7 +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(Error) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(f"{parent}/{path}") - assert isinstance(e_info.value, NotFound) operator.remove_all(f"{parent}/") diff --git a/bindings/python/tests/test_sync_rename.py b/bindings/python/tests/test_sync_rename.py index 56f8d9c29131..3caced0594dc 100644 --- a/bindings/python/tests/test_sync_rename.py +++ b/bindings/python/tests/test_sync_rename.py @@ -16,10 +16,11 @@ # under the License. import os +from random import randint from uuid import uuid4 import pytest -from opendal.exceptions import Error, IsADirectory, IsSameFile, NotFound +from opendal.exceptions import IsADirectory, IsSameFile, NotFound @pytest.mark.need_capability("read", "write", "rename") @@ -29,9 +30,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) - assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == content operator.delete(target_path) operator.delete(source_path) @@ -41,9 +41,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: operator.rename(source_path, target_path) - assert isinstance(e_info.value, NotFound) @pytest.mark.need_capability("read", "write", "rename") @@ -51,9 +50,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) @pytest.mark.need_capability("read", "write", "rename") @@ -62,9 +60,8 @@ 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(Error) as e_info: + with pytest.raises(IsADirectory) as e_info: operator.rename(source_path, target_path) - assert isinstance(e_info.value, IsADirectory) operator.delete(source_path) @@ -73,9 +70,8 @@ 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(Error) as e_info: + with pytest.raises(IsSameFile) as e_info: operator.rename(source_path, source_path) - assert isinstance(e_info.value, IsSameFile) operator.delete(source_path) @@ -86,9 +82,8 @@ 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(Error) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) - assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == content operator.delete(target_path) operator.delete(source_path) @@ -104,9 +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(Error) as e_info: + with pytest.raises(NotFound) as e_info: operator.read(source_path) - assert isinstance(e_info.value, NotFound) assert operator.read(target_path) == source_content operator.delete(target_path) operator.delete(source_path) diff --git a/bindings/python/upgrade.md b/bindings/python/upgrade.md index 72ff6efe232c..6cb92c0c1113 100644 --- a/bindings/python/upgrade.md +++ b/bindings/python/upgrade.md @@ -32,7 +32,7 @@ async with await op.open(filename, "rb") as r: We remove the old error classes and provide a couple of Exception based class for the error handling. -1. `opendal.Error` is based class for all the exceptions now. +1. `opendal.Error` is removed, use `opendal.exceptions.Unexpected` instead. 2. `opendal.exceptions.Unexpected` is added. 3. `opendal.exceptions.Unsupported` is added. 4. `opendal.exceptions.ConfigInvalid` is added. From 6e89ab23986db19ddbc151ec99dbab749ed8b9ec Mon Sep 17 00:00:00 2001 From: Manjusaka Date: Tue, 7 Nov 2023 14:21:13 +0800 Subject: [PATCH 7/7] Update code Signed-off-by: Manjusaka --- bindings/python/tests/test_async_copy.py | 9 ++++---- bindings/python/tests/test_async_delete.py | 3 +-- bindings/python/tests/test_async_rename.py | 15 ++++++------ bindings/python/tests/test_capability.py | 2 +- bindings/python/tests/test_exceptions.py | 27 ++++++++++++++++++++++ bindings/python/tests/test_read.py | 2 +- bindings/python/tests/test_sync_copy.py | 9 ++++---- bindings/python/tests/test_sync_delete.py | 3 +-- bindings/python/tests/test_sync_rename.py | 15 ++++++------ bindings/python/upgrade.md | 4 ++-- 10 files changed, 55 insertions(+), 34 deletions(-) create mode 100644 bindings/python/tests/test_exceptions.py diff --git a/bindings/python/tests/test_async_copy.py b/bindings/python/tests/test_async_copy.py index 424e3427c8fb..e2965a65c47c 100644 --- a/bindings/python/tests/test_async_copy.py +++ b/bindings/python/tests/test_async_copy.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -43,7 +42,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(NotFound) as e_info: + with pytest.raises(NotFound) : await async_operator.copy(source_path, target_path) @@ -53,7 +52,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : await async_operator.copy(source_path, target_path) @@ -65,7 +64,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : await async_operator.copy(source_path, target_path) await async_operator.delete(source_path) await async_operator.delete(target_path) @@ -77,7 +76,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(IsSameFile) as e_info: + with pytest.raises(IsSameFile) : 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 ff3d41d0c3f5..78f1c962f9a2 100644 --- a/bindings/python/tests/test_async_delete.py +++ b/bindings/python/tests/test_async_delete.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -44,6 +43,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(NotFound) as e_info: + with pytest.raises(NotFound) : 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 8c718fc52592..181e9d184475 100644 --- a/bindings/python/tests/test_async_rename.py +++ b/bindings/python/tests/test_async_rename.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -31,7 +30,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(NotFound) as e_info: + with pytest.raises(NotFound) : await async_operator.read(source_path) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) @@ -43,7 +42,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(NotFound) as e_info: + with pytest.raises(NotFound) : await async_operator.rename(source_path, target_path) @@ -53,7 +52,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : await async_operator.rename(source_path, target_path) @@ -64,7 +63,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : await async_operator.rename(source_path, target_path) await async_operator.delete(source_path) @@ -75,7 +74,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(IsSameFile) as e_info: + with pytest.raises(IsSameFile) : await async_operator.rename(source_path, source_path) await async_operator.delete(source_path) @@ -88,7 +87,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(NotFound) as e_info: + with pytest.raises(NotFound) : await async_operator.read(source_path) assert await async_operator.read(target_path) == content await async_operator.delete(target_path) @@ -106,7 +105,7 @@ 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(NotFound) as e_info: + with pytest.raises(NotFound) : await async_operator.read(source_path) assert await async_operator.read(target_path) == source_content await async_operator.delete(target_path) diff --git a/bindings/python/tests/test_capability.py b/bindings/python/tests/test_capability.py index da2e9c14f854..ed0ea6493104 100644 --- a/bindings/python/tests/test_capability.py +++ b/bindings/python/tests/test_capability.py @@ -27,5 +27,5 @@ def test_capability(service_name, operator): def test_capability_exception(service_name, operator): cap = operator.capability() assert cap is not None - with pytest.raises(AttributeError) as e_info: + with pytest.raises(AttributeError) : cap.read_demo diff --git a/bindings/python/tests/test_exceptions.py b/bindings/python/tests/test_exceptions.py new file mode 100644 index 000000000000..0d872e5ef097 --- /dev/null +++ b/bindings/python/tests/test_exceptions.py @@ -0,0 +1,27 @@ +# 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. + +import inspect + +from opendal import exceptions +from opendal.exceptions import Error + + +def test_exceptions(): + for name, obj in inspect.getmembers(exceptions): + if inspect.isclass(obj): + assert issubclass(obj, Error) diff --git a/bindings/python/tests/test_read.py b/bindings/python/tests/test_read.py index 82eb1e9fd664..4e88f2a9a4ae 100644 --- a/bindings/python/tests/test_read.py +++ b/bindings/python/tests/test_read.py @@ -20,7 +20,7 @@ from uuid import uuid4 import pytest -from opendal.exceptions import IsADirectory, IsSameFile, NotFound +from opendal.exceptions import NotFound @pytest.mark.need_capability("read", "write", "delete") diff --git a/bindings/python/tests/test_sync_copy.py b/bindings/python/tests/test_sync_copy.py index 0c4f2bb54055..f55ec92fcc75 100644 --- a/bindings/python/tests/test_sync_copy.py +++ b/bindings/python/tests/test_sync_copy.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -41,7 +40,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(NotFound) as e_info: + with pytest.raises(NotFound) : operator.copy(source_path, target_path) @@ -50,7 +49,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : operator.copy(source_path, target_path) @@ -61,7 +60,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : operator.copy(source_path, target_path) operator.delete(source_path) operator.delete(target_path) @@ -72,7 +71,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(IsSameFile) as e_info: + with pytest.raises(IsSameFile) : 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 7deb5c5f2dee..0ce3d32b8968 100644 --- a/bindings/python/tests/test_sync_delete.py +++ b/bindings/python/tests/test_sync_delete.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -43,6 +42,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(NotFound) as e_info: + with pytest.raises(NotFound) : 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 3caced0594dc..ca6adca01a8d 100644 --- a/bindings/python/tests/test_sync_rename.py +++ b/bindings/python/tests/test_sync_rename.py @@ -16,7 +16,6 @@ # under the License. import os -from random import randint from uuid import uuid4 import pytest @@ -30,7 +29,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(NotFound) as e_info: + with pytest.raises(NotFound) : operator.read(source_path) assert operator.read(target_path) == content operator.delete(target_path) @@ -41,7 +40,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(NotFound) as e_info: + with pytest.raises(NotFound) : operator.rename(source_path, target_path) @@ -50,7 +49,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : operator.rename(source_path, target_path) @@ -60,7 +59,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(IsADirectory) as e_info: + with pytest.raises(IsADirectory) : operator.rename(source_path, target_path) operator.delete(source_path) @@ -70,7 +69,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(IsSameFile) as e_info: + with pytest.raises(IsSameFile) : operator.rename(source_path, source_path) operator.delete(source_path) @@ -82,7 +81,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(NotFound) as e_info: + with pytest.raises(NotFound) : operator.read(source_path) assert operator.read(target_path) == content operator.delete(target_path) @@ -99,7 +98,7 @@ 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(NotFound) as e_info: + with pytest.raises(NotFound) : operator.read(source_path) assert operator.read(target_path) == source_content operator.delete(target_path) diff --git a/bindings/python/upgrade.md b/bindings/python/upgrade.md index 6cb92c0c1113..cbb43b85a5f7 100644 --- a/bindings/python/upgrade.md +++ b/bindings/python/upgrade.md @@ -32,7 +32,7 @@ async with await op.open(filename, "rb") as r: We remove the old error classes and provide a couple of Exception based class for the error handling. -1. `opendal.Error` is removed, use `opendal.exceptions.Unexpected` instead. +1. `opendal.Error` is based class for all the exceptions now. 2. `opendal.exceptions.Unexpected` is added. 3. `opendal.exceptions.Unsupported` is added. 4. `opendal.exceptions.ConfigInvalid` is added. @@ -45,4 +45,4 @@ We remove the old error classes and provide a couple of Exception based class fo 11. `opendal.exceptions.ConditionNotMatch` is added. 12. `opendal.exceptions.ContentTruncated` is added. 13. `opendal.exceptions.ContentIncomplete` is added. -14. `opendal.exceptions.InvalidInput` is added. +14. `opendal.exceptions.InvalidInput` is added. \ No newline at end of file