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

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

commit 12259b1d971edf215149ef165504ceb266c21cda
Author: Manjusaka <[email protected]>
AuthorDate: Thu Nov 2 23:12:38 2023 +0800

    feat(binding/python): Support remove_all API for Python binding
    
    Signed-off-by: Manjusaka <[email protected]>
---
 bindings/python/python/opendal/__init__.pyi |  2 ++
 bindings/python/src/asyncio.rs              |  8 +++++
 bindings/python/src/lib.rs                  |  6 ++++
 bindings/python/tests/test_async_delete.py  | 46 +++++++++++++++++++++++++++++
 bindings/python/tests/test_sync_delete.py   | 45 ++++++++++++++++++++++++++++
 5 files changed, 107 insertions(+)

diff --git a/bindings/python/python/opendal/__init__.pyi 
b/bindings/python/python/opendal/__init__.pyi
index dcb7faf8d..2c33057e3 100644
--- a/bindings/python/python/opendal/__init__.pyi
+++ b/bindings/python/python/opendal/__init__.pyi
@@ -42,6 +42,7 @@ class Operator:
     def capability(self) -> Capability: ...
     def copy(self, source: str, target: str): ...
     def rename(self, source: str, target: str): ...
+    def remove_all(self, path: str): ...
 
 class AsyncOperator:
     def __init__(self, scheme: str, **kwargs): ...
@@ -71,6 +72,7 @@ class AsyncOperator:
     def capability(self) -> Capability: ...
     async def copy(self, source: str, target: str): ...
     async def rename(self, source: str, target: str): ...
+    async def remove_all(self, path: str): ...
 
 class Reader:
     def read(self, size: Optional[int] = None) -> memoryview: ...
diff --git a/bindings/python/src/asyncio.rs b/bindings/python/src/asyncio.rs
index 027a73f98..2ef6253d6 100644
--- a/bindings/python/src/asyncio.rs
+++ b/bindings/python/src/asyncio.rs
@@ -171,6 +171,14 @@ impl AsyncOperator {
         })
     }
 
+    /// Remove all file
+    pub fn remove_all<'p>(&'p self, py: Python<'p>, path: String) -> 
PyResult<&'p PyAny> {
+        let this = self.0.clone();
+        future_into_py(py, async move {
+            this.remove_all(&path).await.map_err(format_pyerr)
+        })
+    }
+
     /// Create a dir at given path.
     ///
     /// # Notes
diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs
index 65dc8fdb6..cacbadc9c 100644
--- a/bindings/python/src/lib.rs
+++ b/bindings/python/src/lib.rs
@@ -185,6 +185,12 @@ impl Operator {
     pub fn rename(&self, source: &str, target: &str) -> PyResult<()> {
         self.0.rename(source, target).map_err(format_pyerr)
     }
+
+    /// Remove all file
+    pub fn remove_all(&self, path: &str) -> PyResult<()> {
+        self.0.remove_all(path).map_err(format_pyerr)
+    }
+
     /// Create a dir at given path.
     ///
     /// # Notes
diff --git a/bindings/python/tests/test_async_delete.py 
b/bindings/python/tests/test_async_delete.py
new file mode 100644
index 000000000..e06ba4218
--- /dev/null
+++ b/bindings/python/tests/test_async_delete.py
@@ -0,0 +1,46 @@
+# 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 os
+from random import randint
+from uuid import uuid4
+
+import pytest
+
+
[email protected]
[email protected]_capability("read", "write", "delete")
+async def test_async_remove_all(service_name, operator, async_operator):
+    parent = f"random_dir_{str(uuid4())}"
+    excepted = [
+        "x/",
+        "x/y",
+        "x/y/z",
+        "x/y/z/",
+        "x/y/z/a",
+        "x/y/z/a/",
+    ]
+    for path in excepted:
+        if path.endswith("/"):
+            await async_operator.create_dir(f"{parent}/{path}")
+        else:
+            await async_operator.write(f"{parent}/{path}", os.urandom(1024))
+    await async_operator.remove_all(f"{parent}/x/")
+    for path in excepted:
+        if not path.endswith("/"):
+            with pytest.raises(FileNotFoundError) as e_info:
+                await async_operator.read(f"{parent}/{path}")
diff --git a/bindings/python/tests/test_sync_delete.py 
b/bindings/python/tests/test_sync_delete.py
new file mode 100644
index 000000000..318d1407b
--- /dev/null
+++ b/bindings/python/tests/test_sync_delete.py
@@ -0,0 +1,45 @@
+# 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 os
+from random import randint
+from uuid import uuid4
+
+import pytest
+
+
[email protected]_capability("read", "write", "delete")
+def test_sync_remove_all(service_name, operator, async_operator):
+    parent = f"random_dir_{str(uuid4())}"
+    excepted = [
+        "x/",
+        "x/y",
+        "x/y/z",
+        "x/y/z/",
+        "x/y/z/a",
+        "x/y/z/a/",
+    ]
+    for path in excepted:
+        if path.endswith("/"):
+            operator.create_dir(f"{parent}/{path}")
+        else:
+            operator.write(f"{parent}/{path}", os.urandom(1024))
+    operator.remove_all(f"{parent}/x/")
+    for path in excepted:
+        if not path.endswith("/"):
+            with pytest.raises(FileNotFoundError) as e_info:
+                operator.read(f"{parent}/{path}")

Reply via email to