Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 2 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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