Skip to content

Commit

Permalink
feat(binding/python): Export full_capability API for Python binding (#…
Browse files Browse the repository at this point in the history
…3402)

* feat(binding/python): Export full_capability API for Python binding

Signed-off-by: Manjusaka <[email protected]>

* Update test_services.py

---------

Signed-off-by: Manjusaka <[email protected]>
  • Loading branch information
Zheaoli authored Oct 30, 2023
1 parent 7fe95b4 commit 6b57b42
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 2 deletions.
2 changes: 1 addition & 1 deletion bindings/python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@ futures = "0.3.28"
opendal.workspace = true
pyo3 = "0.19"
pyo3-asyncio = { version = "0.19", features = ["tokio-runtime"] }
tokio = "1"
tokio = "1"
51 changes: 51 additions & 0 deletions bindings/python/python/opendal/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class Operator:
def delete(self, path: str): ...
def list(self, path: str) -> Iterable[Entry]: ...
def scan(self, path: str) -> Iterable[Entry]: ...
def capability(self) -> Capability: ...

class AsyncOperator:
def __init__(self, scheme: str, **kwargs): ...
Expand All @@ -63,6 +64,7 @@ class AsyncOperator:
async def presign_write(
self, path: str, expire_second: int
) -> PresignedRequest: ...
def capability(self) -> Capability: ...

class Reader:
def read(self, size: Optional[int] = None) -> memoryview: ...
Expand Down Expand Up @@ -107,3 +109,52 @@ class PresignedRequest:
def method(self) -> str: ...
@property
def headers(self) -> dict[str, str]: ...

class Capability:
stat: bool
stat_with_if_match: bool
stat_with_if_none_match: bool

read: bool
read_can_seek: bool
read_can_next: bool
read_with_range: bool
read_with_if_match: bool
read_with_if_none_match: bool
read_with_override_cache_control: bool
read_with_override_content_disposition: bool
read_with_override_content_type: bool

write: bool
write_can_multi: bool
write_can_empty: bool
write_can_append: bool
write_with_content_type: bool
write_with_content_disposition: bool
write_with_cache_control: bool
write_multi_max_size: Optional[int]
write_multi_min_size: Optional[int]
write_multi_align_size: Optional[int]
write_total_max_size: Optional[int]

create_dir: bool
delete: bool
copy: bool
rename: bool

list: bool
list_with_limit: bool
list_with_start_after: bool
list_with_delimiter_slash: bool
list_without_delimiter: bool

presign: bool
presign_read: bool
presign_stat: bool
presign_write: bool

batch: bool
batch_delete: bool
batch_max_operations: Optional[int]

blocking: bool
6 changes: 6 additions & 0 deletions bindings/python/src/asyncio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ use crate::Entry;
use crate::Metadata;
use crate::PresignedRequest;

use crate::capability;

/// `AsyncOperator` is the entry for all public async APIs
///
/// Create a new `AsyncOperator` with the given `scheme` and options(`**kwargs`).
Expand Down Expand Up @@ -250,6 +252,10 @@ impl AsyncOperator {
})
}

pub fn capability(&self) -> PyResult<capability::Capability> {
Ok(capability::Capability::new(self.0.info().full_capability()))
}

fn __repr__(&self) -> String {
let info = self.0.info();
let name = info.name();
Expand Down
110 changes: 110 additions & 0 deletions bindings/python/src/capability.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// 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::prelude::*;

#[pyclass(get_all, module = "opendal")]
pub struct Capability {
pub stat: bool,
pub stat_with_if_match: bool,
pub stat_with_if_none_match: bool,
pub read: bool,
pub read_can_seek: bool,
pub read_can_next: bool,
pub read_with_range: bool,
pub read_with_if_match: bool,
pub read_with_if_none_match: bool,
pub read_with_override_cache_control: bool,
pub read_with_override_content_disposition: bool,
pub read_with_override_content_type: bool,
pub write: bool,
pub write_can_multi: bool,
pub write_can_empty: bool,
pub write_can_append: bool,
pub write_with_content_type: bool,
pub write_with_content_disposition: bool,
pub write_with_cache_control: bool,
pub write_multi_max_size: Option<usize>,
pub write_multi_min_size: Option<usize>,
pub write_multi_align_size: Option<usize>,
pub write_total_max_size: Option<usize>,
pub create_dir: bool,
pub delete: bool,
pub copy: bool,
pub rename: bool,
pub list: bool,
pub list_with_limit: bool,
pub list_with_start_after: bool,
pub list_with_delimiter_slash: bool,
pub list_without_delimiter: bool,
pub presign: bool,
pub presign_read: bool,
pub presign_stat: bool,
pub presign_write: bool,
pub batch: bool,
pub batch_delete: bool,
pub batch_max_operations: Option<usize>,
pub blocking: bool,
}

impl Capability {
pub fn new(capability: opendal::Capability) -> Self {
Self {
stat: capability.stat,
stat_with_if_match: capability.stat_with_if_match,
stat_with_if_none_match: capability.stat_with_if_none_match,
read: capability.read,
read_can_seek: capability.read_can_seek,
read_can_next: capability.read_can_next,
read_with_range: capability.read_with_range,
read_with_if_match: capability.read_with_if_match,
read_with_if_none_match: capability.read_with_if_none_match,
read_with_override_cache_control: capability.read_with_override_cache_control,
read_with_override_content_disposition: capability
.read_with_override_content_disposition,
read_with_override_content_type: capability.read_with_override_content_type,
write: capability.write,
write_can_multi: capability.write_can_multi,
write_can_empty: capability.write_can_empty,
write_can_append: capability.write_can_append,
write_with_content_type: capability.write_with_content_type,
write_with_content_disposition: capability.write_with_content_disposition,
write_with_cache_control: capability.write_with_cache_control,
write_multi_max_size: capability.write_multi_max_size,
write_multi_min_size: capability.write_multi_min_size,
write_multi_align_size: capability.write_multi_align_size,
write_total_max_size: capability.write_total_max_size,
create_dir: capability.create_dir,
delete: capability.delete,
copy: capability.copy,
rename: capability.rename,
list: capability.list,
list_with_limit: capability.list_with_limit,
list_with_start_after: capability.list_with_start_after,
list_with_delimiter_slash: capability.list_with_delimiter_slash,
list_without_delimiter: capability.list_without_delimiter,
presign: capability.presign,
presign_read: capability.presign_read,
presign_stat: capability.presign_stat,
presign_write: capability.presign_write,
batch: capability.batch,
batch_delete: capability.batch_delete,
batch_max_operations: capability.batch_max_operations,
blocking: capability.blocking,
}
}
}
6 changes: 6 additions & 0 deletions bindings/python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use pyo3::types::PyDict;
use pyo3::AsPyPointer;

mod asyncio;
mod capability;
mod layers;

use crate::asyncio::*;
Expand Down Expand Up @@ -228,6 +229,10 @@ impl Operator {
))
}

pub fn capability(&self) -> PyResult<capability::Capability> {
Ok(capability::Capability::new(self.0.info().full_capability()))
}

fn __repr__(&self) -> String {
let info = self.0.info();
let name = info.name();
Expand Down Expand Up @@ -572,6 +577,7 @@ fn _opendal(py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<EntryMode>()?;
m.add_class::<Metadata>()?;
m.add_class::<PresignedRequest>()?;
m.add_class::<capability::Capability>()?;
m.add("Error", py.get_type::<Error>())?;

let layers = layers::create_submodule(py)?;
Expand Down
11 changes: 11 additions & 0 deletions bindings/python/tests/test_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,17 @@ async def test_async_delete(self):
await self.async_operator.delete(filename)
with pytest.raises(FileNotFoundError):
await self.operator.stat(filename)

def test_capability(self):
cap = self.operator.capability()
assert cap is not None
assert cap.read is not None

def test_capability_exception(self):
cap = self.operator.capability()
assert cap is not None
with pytest.raises(AttributeError) as e_info:
cap.read_demo


class TestS3(AbstractTestSuite):
Expand Down
1 change: 0 additions & 1 deletion core/src/types/capability.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
// under the License.

use std::fmt::Debug;

/// Capability is used to describe what operations are supported
/// by current Operator.
///
Expand Down

0 comments on commit 6b57b42

Please sign in to comment.