This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new 06f088d93 chore(deps): bump the pyo3 group in /bindings/python with 2
updates (#7120)
06f088d93 is described below
commit 06f088d93e38e1150a3f1558f327ec0252446302
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
AuthorDate: Mon Jan 19 15:41:36 2026 +0800
chore(deps): bump the pyo3 group in /bindings/python with 2 updates (#7120)
* chore(deps): bump the pyo3 group in /bindings/python with 2 updates
Updates the requirements on [pyo3](https://github.com/pyo3/pyo3) and
[pyo3-async-runtimes](https://github.com/PyO3/pyo3-async-runtimes) to permit
the latest version.
Updates `pyo3` to 0.26.0
- [Release notes](https://github.com/pyo3/pyo3/releases)
- [Changelog](https://github.com/PyO3/pyo3/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pyo3/pyo3/compare/v0.26.0...v0.26.0)
Updates `pyo3-async-runtimes` to 0.27.0
- [Release notes](https://github.com/PyO3/pyo3-async-runtimes/releases)
-
[Changelog](https://github.com/PyO3/pyo3-async-runtimes/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/PyO3/pyo3-async-runtimes/compare/v0.26.0...v0.27.0)
---
updated-dependencies:
- dependency-name: pyo3
dependency-version: 0.26.0
dependency-type: direct:production
dependency-group: pyo3
- dependency-name: pyo3-async-runtimes
dependency-version: 0.27.0
dependency-type: direct:production
dependency-group: pyo3
...
Signed-off-by: dependabot[bot] <[email protected]>
* Fix build
---------
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot]
<49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Xuanwo <[email protected]>
---
bindings/python/Cargo.toml | 5 +-
bindings/python/src/options.rs | 125 +++++++++++++++++++++++++++++++++++++++--
2 files changed, 122 insertions(+), 8 deletions(-)
diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml
index b4fe5553b..cabe0b503 100644
--- a/bindings/python/Cargo.toml
+++ b/bindings/python/Cargo.toml
@@ -198,7 +198,6 @@ name = "_opendal"
[dependencies]
bytes = "1.5.0"
-dict_derive = "0.6.0"
futures = "0.3.28"
jiff = { version = "0.2.15" }
mea = "0.6"
@@ -207,8 +206,8 @@ opendal = { version = ">=0", path = "../../core", features
= [
"blocking",
"layers-mime-guess",
] }
-pyo3 = { version = "0.26.0", features = ["generate-import-lib", "jiff-02"] }
-pyo3-async-runtimes = { version = "0.26.0", features = ["tokio-runtime"] }
+pyo3 = { version = "0.27.2", features = ["generate-import-lib", "jiff-02"] }
+pyo3-async-runtimes = { version = "0.27.0", features = ["tokio-runtime"] }
pyo3-stub-gen = { version = "0.17" }
tokio = "1"
diff --git a/bindings/python/src/options.rs b/bindings/python/src/options.rs
index fa5f74061..f623be266 100644
--- a/bindings/python/src/options.rs
+++ b/bindings/python/src/options.rs
@@ -15,13 +15,22 @@
// specific language governing permissions and limitations
// under the License.
-use dict_derive::FromPyObject;
use opendal::{self as ocore, raw::BytesRange};
+use pyo3::Borrowed;
+use pyo3::FromPyObject;
+use pyo3::PyAny;
+use pyo3::PyErr;
+use pyo3::PyResult;
+use pyo3::conversion::FromPyObjectOwned;
+use pyo3::exceptions::PyTypeError;
use pyo3::pyclass;
+use pyo3::types::PyAnyMethods;
+use pyo3::types::PyDict;
+use pyo3::types::PyDictMethods;
use std::collections::HashMap;
#[pyclass(module = "opendal")]
-#[derive(FromPyObject, Default)]
+#[derive(Default)]
pub struct ReadOptions {
pub version: Option<String>,
pub concurrent: Option<usize>,
@@ -39,6 +48,55 @@ pub struct ReadOptions {
pub content_disposition: Option<String>,
}
+fn map_exception(name: &str, err: PyErr) -> PyErr {
+ PyErr::new::<PyTypeError, _>(format!("Unable to convert key: {name}.
Error: {err}"))
+}
+
+fn extract_optional<'py, T>(dict: &pyo3::Bound<'py, PyDict>, name: &str) ->
PyResult<Option<T>>
+where
+ T: FromPyObjectOwned<'py>,
+{
+ match dict.get_item(name)? {
+ Some(v) => v
+ .extract::<T>()
+ .map(Some)
+ .map_err(|err| map_exception(name, err.into())),
+ None => Ok(None),
+ }
+}
+
+fn downcast_kwargs<'a, 'py>(obj: Borrowed<'a, 'py, PyAny>) ->
PyResult<pyo3::Bound<'py, PyDict>> {
+ let obj: &pyo3::Bound<'_, PyAny> = &obj;
+ obj.cast::<PyDict>()
+ .cloned()
+ .map_err(|_| PyErr::new::<PyTypeError, _>("Invalid type to convert,
expected dict"))
+}
+
+impl<'a, 'py> FromPyObject<'a, 'py> for ReadOptions {
+ type Error = PyErr;
+
+ fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
+ let dict = downcast_kwargs(obj)?;
+
+ Ok(Self {
+ version: extract_optional(&dict, "version")?,
+ concurrent: extract_optional(&dict, "concurrent")?,
+ chunk: extract_optional(&dict, "chunk")?,
+ gap: extract_optional(&dict, "gap")?,
+ offset: extract_optional(&dict, "offset")?,
+ prefetch: extract_optional(&dict, "prefetch")?,
+ size: extract_optional(&dict, "size")?,
+ if_match: extract_optional(&dict, "if_match")?,
+ if_none_match: extract_optional(&dict, "if_none_match")?,
+ if_modified_since: extract_optional(&dict, "if_modified_since")?,
+ if_unmodified_since: extract_optional(&dict,
"if_unmodified_since")?,
+ content_type: extract_optional(&dict, "content_type")?,
+ cache_control: extract_optional(&dict, "cache_control")?,
+ content_disposition: extract_optional(&dict,
"content_disposition")?,
+ })
+ }
+}
+
impl ReadOptions {
pub fn make_range(&self) -> BytesRange {
let offset = self.offset.unwrap_or_default() as u64;
@@ -49,7 +107,7 @@ impl ReadOptions {
}
#[pyclass(module = "opendal")]
-#[derive(FromPyObject, Default)]
+#[derive(Default)]
pub struct WriteOptions {
pub append: Option<bool>,
pub chunk: Option<usize>,
@@ -64,6 +122,28 @@ pub struct WriteOptions {
pub user_metadata: Option<HashMap<String, String>>,
}
+impl<'a, 'py> FromPyObject<'a, 'py> for WriteOptions {
+ type Error = PyErr;
+
+ fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
+ let dict = downcast_kwargs(obj)?;
+
+ Ok(Self {
+ append: extract_optional(&dict, "append")?,
+ chunk: extract_optional(&dict, "chunk")?,
+ concurrent: extract_optional(&dict, "concurrent")?,
+ cache_control: extract_optional(&dict, "cache_control")?,
+ content_type: extract_optional(&dict, "content_type")?,
+ content_disposition: extract_optional(&dict,
"content_disposition")?,
+ content_encoding: extract_optional(&dict, "content_encoding")?,
+ if_match: extract_optional(&dict, "if_match")?,
+ if_none_match: extract_optional(&dict, "if_none_match")?,
+ if_not_exists: extract_optional(&dict, "if_not_exists")?,
+ user_metadata: extract_optional(&dict, "user_metadata")?,
+ })
+ }
+}
+
impl From<ReadOptions> for ocore::options::ReadOptions {
fn from(opts: ReadOptions) -> Self {
Self {
@@ -118,7 +198,7 @@ impl From<WriteOptions> for ocore::options::WriteOptions {
}
#[pyclass(module = "opendal")]
-#[derive(FromPyObject, Default, Debug)]
+#[derive(Default, Debug)]
pub struct ListOptions {
pub limit: Option<usize>,
pub start_after: Option<String>,
@@ -127,6 +207,22 @@ pub struct ListOptions {
pub deleted: Option<bool>,
}
+impl<'a, 'py> FromPyObject<'a, 'py> for ListOptions {
+ type Error = PyErr;
+
+ fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
+ let dict = downcast_kwargs(obj)?;
+
+ Ok(Self {
+ limit: extract_optional(&dict, "limit")?,
+ start_after: extract_optional(&dict, "start_after")?,
+ recursive: extract_optional(&dict, "recursive")?,
+ versions: extract_optional(&dict, "versions")?,
+ deleted: extract_optional(&dict, "deleted")?,
+ })
+ }
+}
+
impl From<ListOptions> for ocore::options::ListOptions {
fn from(opts: ListOptions) -> Self {
Self {
@@ -140,7 +236,7 @@ impl From<ListOptions> for ocore::options::ListOptions {
}
#[pyclass(module = "opendal")]
-#[derive(FromPyObject, Default, Debug)]
+#[derive(Default, Debug)]
pub struct StatOptions {
pub version: Option<String>,
pub if_match: Option<String>,
@@ -152,6 +248,25 @@ pub struct StatOptions {
pub content_disposition: Option<String>,
}
+impl<'a, 'py> FromPyObject<'a, 'py> for StatOptions {
+ type Error = PyErr;
+
+ fn extract(obj: Borrowed<'a, 'py, PyAny>) -> Result<Self, Self::Error> {
+ let dict = downcast_kwargs(obj)?;
+
+ Ok(Self {
+ version: extract_optional(&dict, "version")?,
+ if_match: extract_optional(&dict, "if_match")?,
+ if_none_match: extract_optional(&dict, "if_none_match")?,
+ if_modified_since: extract_optional(&dict, "if_modified_since")?,
+ if_unmodified_since: extract_optional(&dict,
"if_unmodified_since")?,
+ content_type: extract_optional(&dict, "content_type")?,
+ cache_control: extract_optional(&dict, "cache_control")?,
+ content_disposition: extract_optional(&dict,
"content_disposition")?,
+ })
+ }
+}
+
impl From<StatOptions> for ocore::options::StatOptions {
fn from(opts: StatOptions) -> Self {
Self {