From 140328c925c5d98927ae516fb067e563cad36f70 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 8 Mar 2023 20:15:16 +0800 Subject: [PATCH 1/3] refactor(bindings/python): raise RuntimeError instead of BaseException --- bindings/python/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 98d249cd9c8a..337e6bb0a0c7 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -16,8 +16,7 @@ use std::collections::HashMap; use std::str::FromStr; use ::opendal as od; -use pyo3::exceptions::PyBaseException; -use pyo3::exceptions::PyFileNotFoundError; +use pyo3::exceptions::{PyFileNotFoundError, PyRuntimeError}; use pyo3::prelude::*; use pyo3::types::PyDict; use pyo3_asyncio::tokio::future_into_py; @@ -139,7 +138,7 @@ fn format_pyerr(err: od::Error) -> PyErr { use od::ErrorKind::*; match err.kind() { NotFound => PyFileNotFoundError::new_err(err.to_string()), - _ => PyBaseException::new_err(err.to_string()), + _ => PyRuntimeError::new_err(err.to_string()), } } From 93d6c48ae9f1aa484ccc59840b27d09b091c8a6d Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 8 Mar 2023 20:30:40 +0800 Subject: [PATCH 2/3] refactor(bindings/python): return bytes from `read` --- bindings/python/src/lib.rs | 13 ++++++++----- bindings/python/tests/main.py | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index 337e6bb0a0c7..4efae46b5996 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -18,7 +18,7 @@ use std::str::FromStr; use ::opendal as od; use pyo3::exceptions::{PyFileNotFoundError, PyRuntimeError}; use pyo3::prelude::*; -use pyo3::types::PyDict; +use pyo3::types::{PyBytes, PyDict}; use pyo3_asyncio::tokio::future_into_py; fn build_operator(scheme: od::Scheme, map: HashMap) -> PyResult { @@ -64,8 +64,9 @@ impl AsyncOperator { let this = self.0.clone(); let path = path.to_string(); future_into_py(py, async move { - let res: Vec = this.read(&path).await.map_err(format_pyerr)?; - Ok(res) + let res = this.read(&path).await.map_err(format_pyerr)?; + let bytes = Python::with_gil(|py| PyBytes::new(py, &res).to_object(py)); + Ok(bytes) }) } @@ -111,8 +112,10 @@ impl Operator { Ok(Operator(build_operator(scheme, map)?.blocking())) } - pub fn read(&self, path: &str) -> PyResult> { - self.0.read(path).map_err(format_pyerr) + pub fn read<'p>(&'p self, py: Python<'p>, path: &str) -> PyResult<&'p PyBytes> { + let res = self.0.read(path).map_err(format_pyerr)?; + let bytes = PyBytes::new(py, &res); + Ok(bytes) } pub fn write(&self, path: &str, bs: Vec) -> PyResult<()> { diff --git a/bindings/python/tests/main.py b/bindings/python/tests/main.py index 6b1c341284cf..9859068d12d9 100644 --- a/bindings/python/tests/main.py +++ b/bindings/python/tests/main.py @@ -21,7 +21,7 @@ def test_blocking(): op = opendal.Operator("memory") op.write("test", b"Hello, World!") bs = op.read("test") - print(bytes(bs).decode("utf-8")) + print(bs.decode("utf-8")) meta = op.stat("test") print(f"content_length: {meta.content_length()}") @@ -31,7 +31,7 @@ async def test_async(): op = opendal.AsyncOperator("memory") await op.write("test", b"Hello, World!") bs = await op.read("test") - print(bytes(bs).decode("utf-8")) + print(bs.decode("utf-8")) meta = await op.stat("test") print(f"content_length: {meta.content_length()}") From d7ff5d9205add5fa8ae72c2bb910f3c98dfd2625 Mon Sep 17 00:00:00 2001 From: messense Date: Wed, 8 Mar 2023 20:31:22 +0800 Subject: [PATCH 3/3] feat(bindings/python): add type stub file --- bindings/python/opendal.pyi | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 bindings/python/opendal.pyi diff --git a/bindings/python/opendal.pyi b/bindings/python/opendal.pyi new file mode 100644 index 000000000000..f61b4fb8ff0b --- /dev/null +++ b/bindings/python/opendal.pyi @@ -0,0 +1,14 @@ +class Operator: + def __init__(scheme: str, **kwargs): ... + def read(self, path: str) -> bytes: ... + def write(self, path: str, bs: bytes): ... + def stat(self, path: str) -> Metadata: ... + +class AsyncOperator: + def __init__(scheme: str, **kwargs): ... + async def read(self, path: str) -> bytes: ... + async def write(self, path: str, bs: bytes): ... + async def stat(self, path: str) -> Metadata: ... + +class Metadata: + def content_length(self) -> int: ...