diff --git a/bindings/python/opendal.pyi b/bindings/python/opendal.pyi index 7bb1075dcc49..c0695fac623f 100644 --- a/bindings/python/opendal.pyi +++ b/bindings/python/opendal.pyi @@ -11,6 +11,7 @@ # 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. +from typing import Optional class Error(Exception): ... @@ -20,6 +21,7 @@ class Operator: def write(self, path: str, bs: bytes): ... def stat(self, path: str) -> Metadata: ... def create_dir(self, path: str): ... + def delete(self, path: str): ... class AsyncOperator: def __init__(self, scheme: str, **kwargs): ... @@ -27,7 +29,22 @@ class AsyncOperator: async def write(self, path: str, bs: bytes): ... async def stat(self, path: str) -> Metadata: ... async def create_dir(self, path: str): ... + async def delete(self, path: str): ... class Metadata: + @property + def content_disposition(self) -> Optional[str]: ... @property def content_length(self) -> int: ... + @property + def content_md5(self) -> Optional[str]: ... + @property + def content_type(self) -> Optional[str]: ... + @property + def etag(self) -> Optional[str]: ... + @property + def mode(self) -> EntryMode: ... + +class EntryMode: + def is_file(self) -> bool: ... + def is_dir(self) -> bool: ... diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 60b0954c1a29..11ec633e66c8 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -133,6 +133,15 @@ impl AsyncOperator { this.create_dir(&path).await.map_err(format_pyerr) }) } + + pub fn delete<'p>(&'p self, py: Python<'p>, path: &'p str) -> PyResult<&'p PyAny> { + let this = self.0.clone(); + let path = path.to_string(); + future_into_py( + py, + async move { this.delete(&path).await.map_err(format_pyerr) }, + ) + } } #[pyclass] @@ -176,6 +185,10 @@ impl Operator { pub fn create_dir(&self, path: &str) -> PyResult<()> { self.0.create_dir(path).map_err(format_pyerr) } + + pub fn delete(&self, path: &str) -> PyResult<()> { + self.0.delete(path).map_err(format_pyerr) + } } #[pyclass] @@ -183,10 +196,57 @@ struct Metadata(od::Metadata); #[pymethods] impl Metadata { + #[getter] + pub fn content_disposition(&self) -> Option<&str> { + self.0.content_disposition() + } + #[getter] pub fn content_length(&self) -> u64 { self.0.content_length() } + + #[getter] + pub fn content_md5(&self) -> Option<&str> { + self.0.content_md5() + } + + #[getter] + pub fn content_type(&self) -> Option<&str> { + self.0.content_type() + } + + #[getter] + pub fn etag(&self) -> Option<&str> { + self.0.etag() + } + + #[getter] + pub fn mode(&self) -> EntryMode { + EntryMode(self.0.mode()) + } +} + +#[pyclass] +struct EntryMode(od::EntryMode); + +#[pymethods] +impl EntryMode { + pub fn is_file(&self) -> bool { + self.0.is_file() + } + + pub fn is_dir(&self) -> bool { + self.0.is_dir() + } + + pub fn __repr__(&self) -> &'static str { + match self.0 { + od::EntryMode::FILE => "EntryMode.FILE", + od::EntryMode::DIR => "EntryMode.DIR", + od::EntryMode::Unknown => "EntryMode.UNKNOWN", + } + } } fn format_pyerr(err: od::Error) -> PyErr { diff --git a/bindings/python/tests/test_core.py b/bindings/python/tests/test_core.py index 5a8367404104..5a0f303c5566 100644 --- a/bindings/python/tests/test_core.py +++ b/bindings/python/tests/test_core.py @@ -24,6 +24,8 @@ def test_blocking(): assert bs == b"Hello, World!", bs meta = op.stat("test") assert meta.content_length == 13, meta.content_length + assert meta.mode.is_file() + op.delete("test") op.create_dir("test/") @@ -36,6 +38,8 @@ async def test_async(): assert bs == b"Hello, World!", bs meta = await op.stat("test") assert meta.content_length == 13, meta.content_length + assert meta.mode.is_file() + await op.delete("test") await op.create_dir("test/")