pyo3/conversions/std/
path.rs1use crate::conversion::IntoPyObject;
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::instance::Bound;
4use crate::sync::GILOnceCell;
5use crate::types::any::PyAnyMethods;
6use crate::{ffi, FromPyObject, IntoPyObjectExt, PyAny, PyErr, PyObject, PyResult, Python};
7#[allow(deprecated)]
8use crate::{IntoPy, ToPyObject};
9use std::borrow::Cow;
10use std::ffi::OsString;
11use std::path::{Path, PathBuf};
12
13#[allow(deprecated)]
14impl ToPyObject for Path {
15 #[inline]
16 fn to_object(&self, py: Python<'_>) -> PyObject {
17 self.as_os_str().into_py_any(py).unwrap()
18 }
19}
20
21impl FromPyObject<'_> for PathBuf {
24 fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
25 let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? };
27 Ok(path.extract::<OsString>()?.into())
28 }
29}
30
31#[allow(deprecated)]
32impl IntoPy<PyObject> for &Path {
33 #[inline]
34 fn into_py(self, py: Python<'_>) -> PyObject {
35 self.to_object(py)
36 }
37}
38
39impl<'py> IntoPyObject<'py> for &Path {
40 type Target = PyAny;
41 type Output = Bound<'py, Self::Target>;
42 type Error = PyErr;
43
44 #[inline]
45 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
46 static PY_PATH: GILOnceCell<PyObject> = GILOnceCell::new();
47 PY_PATH
48 .import(py, "pathlib", "Path")?
49 .call((self.as_os_str(),), None)
50 }
51}
52
53impl<'py> IntoPyObject<'py> for &&Path {
54 type Target = PyAny;
55 type Output = Bound<'py, Self::Target>;
56 type Error = PyErr;
57
58 #[inline]
59 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
60 (*self).into_pyobject(py)
61 }
62}
63
64#[allow(deprecated)]
65impl ToPyObject for Cow<'_, Path> {
66 #[inline]
67 fn to_object(&self, py: Python<'_>) -> PyObject {
68 (**self).to_object(py)
69 }
70}
71
72#[allow(deprecated)]
73impl IntoPy<PyObject> for Cow<'_, Path> {
74 #[inline]
75 fn into_py(self, py: Python<'_>) -> PyObject {
76 (*self).to_object(py)
77 }
78}
79
80impl<'py> IntoPyObject<'py> for Cow<'_, Path> {
81 type Target = PyAny;
82 type Output = Bound<'py, Self::Target>;
83 type Error = PyErr;
84
85 #[inline]
86 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
87 (*self).into_pyobject(py)
88 }
89}
90
91impl<'py> IntoPyObject<'py> for &Cow<'_, Path> {
92 type Target = PyAny;
93 type Output = Bound<'py, Self::Target>;
94 type Error = PyErr;
95
96 #[inline]
97 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
98 (&**self).into_pyobject(py)
99 }
100}
101
102#[allow(deprecated)]
103impl ToPyObject for PathBuf {
104 #[inline]
105 fn to_object(&self, py: Python<'_>) -> PyObject {
106 (**self).to_object(py)
107 }
108}
109
110#[allow(deprecated)]
111impl IntoPy<PyObject> for PathBuf {
112 #[inline]
113 fn into_py(self, py: Python<'_>) -> PyObject {
114 (*self).to_object(py)
115 }
116}
117
118impl<'py> IntoPyObject<'py> for PathBuf {
119 type Target = PyAny;
120 type Output = Bound<'py, Self::Target>;
121 type Error = PyErr;
122
123 #[inline]
124 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
125 (&self).into_pyobject(py)
126 }
127}
128
129#[allow(deprecated)]
130impl IntoPy<PyObject> for &PathBuf {
131 #[inline]
132 fn into_py(self, py: Python<'_>) -> PyObject {
133 (**self).to_object(py)
134 }
135}
136
137impl<'py> IntoPyObject<'py> for &PathBuf {
138 type Target = PyAny;
139 type Output = Bound<'py, Self::Target>;
140 type Error = PyErr;
141
142 #[inline]
143 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
144 (&**self).into_pyobject(py)
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use crate::types::{PyAnyMethods, PyString, PyStringMethods};
151 use crate::{IntoPyObject, IntoPyObjectExt, PyObject, Python};
152 use std::borrow::Cow;
153 use std::fmt::Debug;
154 use std::path::{Path, PathBuf};
155
156 #[test]
157 #[cfg(not(windows))]
158 fn test_non_utf8_conversion() {
159 Python::with_gil(|py| {
160 use std::ffi::OsStr;
161 #[cfg(not(target_os = "wasi"))]
162 use std::os::unix::ffi::OsStrExt;
163 #[cfg(target_os = "wasi")]
164 use std::os::wasi::ffi::OsStrExt;
165
166 let payload = &[250, 251, 252, 253, 254, 255, 0, 255];
168 let path = Path::new(OsStr::from_bytes(payload));
169
170 let py_str = path.into_pyobject(py).unwrap();
172 let path_2: PathBuf = py_str.extract().unwrap();
173 assert_eq!(path, path_2);
174 });
175 }
176
177 #[test]
178 fn test_intopyobject_roundtrip() {
179 Python::with_gil(|py| {
180 fn test_roundtrip<'py, T>(py: Python<'py>, obj: T)
181 where
182 T: IntoPyObject<'py> + AsRef<Path> + Debug + Clone,
183 T::Error: Debug,
184 {
185 let pyobject = obj.clone().into_bound_py_any(py).unwrap();
186 let roundtripped_obj: PathBuf = pyobject.extract().unwrap();
187 assert_eq!(obj.as_ref(), roundtripped_obj.as_path());
188 }
189 let path = Path::new("Hello\0\nš");
190 test_roundtrip::<&Path>(py, path);
191 test_roundtrip::<Cow<'_, Path>>(py, Cow::Borrowed(path));
192 test_roundtrip::<Cow<'_, Path>>(py, Cow::Owned(path.to_path_buf()));
193 test_roundtrip::<PathBuf>(py, path.to_path_buf());
194 });
195 }
196
197 #[test]
198 fn test_from_pystring() {
199 Python::with_gil(|py| {
200 let path = "Hello\0\nš";
201 let pystring = PyString::new(py, path);
202 let roundtrip: PathBuf = pystring.extract().unwrap();
203 assert_eq!(roundtrip, Path::new(path));
204 });
205 }
206
207 #[test]
208 #[allow(deprecated)]
209 fn test_intopy_string() {
210 use crate::IntoPy;
211
212 Python::with_gil(|py| {
213 fn test_roundtrip<T>(py: Python<'_>, obj: T)
214 where
215 T: IntoPy<PyObject> + AsRef<Path> + Debug + Clone,
216 {
217 let pyobject = obj.clone().into_py(py).into_bound(py);
218 let pystring = pyobject.downcast_exact::<PyString>().unwrap();
219 assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
220 let roundtripped_obj: PathBuf = pyobject.extract().unwrap();
221 assert_eq!(obj.as_ref(), roundtripped_obj.as_path());
222 }
223 let path = Path::new("Hello\0\nš");
224 test_roundtrip::<&Path>(py, path);
225 test_roundtrip::<Cow<'_, Path>>(py, Cow::Borrowed(path));
226 test_roundtrip::<Cow<'_, Path>>(py, Cow::Owned(path.to_path_buf()));
227 test_roundtrip::<PathBuf>(py, path.to_path_buf());
228 });
229 }
230}