Skip to content

Commit

Permalink
Makes PathBuf FromPyObject implementation work on all os.PathLike
Browse files Browse the repository at this point in the history
PyOS_FSPath is in abi3-py36
  • Loading branch information
Tpt committed Aug 8, 2023
1 parent 28b8623 commit 1e5a495
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 17 deletions.
1 change: 1 addition & 0 deletions guide/src/conversions/tables.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ The table below contains the Python type and the corresponding function argument
| `decimal.Decimal` | `rust_decimal::Decimal`[^5] | - |
| `ipaddress.IPv4Address` | `std::net::IpAddr`, `std::net::IpV4Addr` | - |
| `ipaddress.IPv6Address` | `std::net::IpAddr`, `std::net::IpV6Addr` | - |
| `os.PathLike ` | `PathBuf`, `Path` | `&PyString`, `&PyUnicode` |
| `pathlib.Path` | `PathBuf`, `Path` | `&PyString`, `&PyUnicode` |
| `typing.Optional[T]` | `Option<T>` | - |
| `typing.Sequence[T]` | `Vec<T>` | `&PySequence` |
Expand Down
1 change: 1 addition & 0 deletions newsfragments/3374.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
`PathBuf` `FromPyObject` implementation now works on all `os.PathLike` values.
24 changes: 24 additions & 0 deletions pytests/tests/test_path.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pathlib

import pytest

import pyo3_pytests.path as rpath


Expand All @@ -16,3 +18,25 @@ def test_take_pathbuf():
def test_take_pathlib():
p = pathlib.Path("/root")
assert rpath.take_pathbuf(p) == str(p)


def test_take_pathlike():
assert rpath.take_pathbuf(PathLike("/root")) == "/root"


def test_take_invalid_pathlike():
with pytest.raises(TypeError):
assert rpath.take_pathbuf(PathLike(1))


def test_take_invalid():
with pytest.raises(TypeError):
assert rpath.take_pathbuf(3)


class PathLike:
def __init__(self, path):
self._path = path

def __fspath__(self):
return self._path
24 changes: 7 additions & 17 deletions src/conversions/std/path.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use crate::intern;
use crate::{FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
use crate::{
ffi, AsPyPointer, FromPyObject, FromPyPointer, IntoPy, PyAny, PyObject, PyResult, Python,
ToPyObject,
};
use std::borrow::Cow;
use std::ffi::OsString;
use std::path::{Path, PathBuf};
Expand All @@ -14,21 +16,9 @@ impl ToPyObject for Path {

impl FromPyObject<'_> for PathBuf {
fn extract(ob: &PyAny) -> PyResult<Self> {
let os_str = match OsString::extract(ob) {
Ok(s) => s,
Err(err) => {
let py = ob.py();
let pathlib = py.import(intern!(py, "pathlib"))?;
let pathlib_path = pathlib.getattr(intern!(py, "Path"))?;
if ob.is_instance(pathlib_path)? {
let path_str = ob.call_method0(intern!(py, "__str__"))?;
OsString::extract(path_str)?
} else {
return Err(err);
}
}
};
Ok(PathBuf::from(os_str))
// We use os.fspath to get the underlying path as bytes or str
let path = unsafe { PyAny::from_owned_ptr_or_err(ob.py(), ffi::PyOS_FSPath(ob.as_ptr())) }?;
Ok(OsString::extract(path)?.into())
}
}

Expand Down

0 comments on commit 1e5a495

Please sign in to comment.