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(bindings/python): implement delete and export more metadata fields #1539

Merged
merged 2 commits into from
Mar 9, 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
17 changes: 17 additions & 0 deletions bindings/python/opendal.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -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): ...

Expand All @@ -20,14 +21,30 @@ 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): ...
async def read(self, path: str) -> bytes: ...
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: ...
60 changes: 60 additions & 0 deletions bindings/python/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -176,17 +185,68 @@ 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]
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 {
Expand Down
4 changes: 4 additions & 0 deletions bindings/python/tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/")

Expand All @@ -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/")

Expand Down