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

Add support for extracting PathBuf from pathlib.Path #1654

Merged
merged 1 commit into from
Jun 5, 2021
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add `#[pyo3(name = "...")]` syntax for setting Python names. [#1567](https://github.com/PyO3/pyo3/pull/1567)
- Add FFI definition `PyDateTime_TimeZone_UTC`. [#1572](https://github.com/PyO3/pyo3/pull/1572)
- Add support for `#[pyclass(extends=Exception)]`. [#1591](https://github.com/PyO3/pyo3/pull/1591)
- Add support for extracting `PathBuf` from `pathlib.Path`. [#1654](https://github.com/PyO3/pyo3/pull/1654)

### Changed
- Allow only one `#[pymethods]` block per `#[pyclass]` by default, to simplify the proc macro implementations. Add `multiple-pymethods` feature to opt-in to the more complex full behavior. [#1457](https://github.com/PyO3/pyo3/pull/1457)
Expand Down
4 changes: 4 additions & 0 deletions examples/pyo3-pytests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod dict_iter;
pub mod misc;
pub mod objstore;
pub mod othermod;
pub mod path;
pub mod pyclass_iter;
pub mod subclassing;

Expand All @@ -17,6 +18,7 @@ use dict_iter::*;
use misc::*;
use objstore::*;
use othermod::*;
use path::*;
use pyclass_iter::*;
use subclassing::*;

Expand All @@ -28,6 +30,7 @@ fn pyo3_pytests(py: Python, m: &PyModule) -> PyResult<()> {
m.add_wrapped(wrap_pymodule!(misc))?;
m.add_wrapped(wrap_pymodule!(objstore))?;
m.add_wrapped(wrap_pymodule!(othermod))?;
m.add_wrapped(wrap_pymodule!(path))?;
m.add_wrapped(wrap_pymodule!(pyclass_iter))?;
m.add_wrapped(wrap_pymodule!(subclassing))?;

Expand All @@ -42,6 +45,7 @@ fn pyo3_pytests(py: Python, m: &PyModule) -> PyResult<()> {
sys_modules.set_item("pyo3_pytests.misc", m.getattr("misc")?)?;
sys_modules.set_item("pyo3_pytests.objstore", m.getattr("objstore")?)?;
sys_modules.set_item("pyo3_pytests.othermod", m.getattr("othermod")?)?;
sys_modules.set_item("pyo3_pytests.path", m.getattr("path")?)?;
sys_modules.set_item("pyo3_pytests.pyclass_iter", m.getattr("pyclass_iter")?)?;
sys_modules.set_item("pyo3_pytests.subclassing", m.getattr("subclassing")?)?;

Expand Down
21 changes: 21 additions & 0 deletions examples/pyo3-pytests/src/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use pyo3::prelude::*;
use pyo3::wrap_pyfunction;
use std::path::{Path, PathBuf};

#[pyfunction]
fn make_path() -> PathBuf {
Path::new("/root").to_owned()
}

#[pyfunction]
fn take_pathbuf(path: PathBuf) -> PathBuf {
path
}

#[pymodule]
fn path(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(make_path, m)?)?;
m.add_function(wrap_pyfunction!(take_pathbuf, m)?)?;

Ok(())
}
18 changes: 18 additions & 0 deletions examples/pyo3-pytests/tests/test_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pathlib

import pyo3_pytests.path as rpath


def test_make_path():
p = rpath.make_path()
assert p == "/root"


def test_take_pathbuf():
p = "/root"
assert rpath.take_pathbuf(p) == p


def test_take_pathlib():
p = pathlib.Path("/root")
assert rpath.take_pathbuf(p) == str(p)
19 changes: 17 additions & 2 deletions src/conversions/path.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
use crate::types::PyType;
use crate::{FromPyObject, IntoPy, PyAny, PyNativeType, PyObject, PyResult, Python, ToPyObject};
use std::borrow::Cow;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
Expand All @@ -13,7 +14,21 @@ impl ToPyObject for Path {

impl FromPyObject<'_> for PathBuf {
fn extract(ob: &PyAny) -> PyResult<Self> {
Ok(PathBuf::from(OsString::extract(ob)?))
let os_str = match OsString::extract(ob) {
Ok(s) => s,
Err(err) => {
let py = ob.py();
let pathlib = py.import("pathlib")?;
let pathlib_path: &PyType = pathlib.getattr("Path")?.downcast()?;
if pathlib_path.is_instance(ob)? {
let path_str = ob.call_method0("__str__")?;
OsString::extract(path_str)?
} else {
return Err(err);
}
}
};
Ok(PathBuf::from(os_str))
}
}

Expand Down