pyo3/conversions/std/
path.rs
1use crate::conversion::IntoPyObject;
2use crate::ffi_ptr_ext::FfiPtrExt;
3use crate::instance::Bound;
4use crate::types::any::PyAnyMethods;
5use crate::types::PyString;
6use crate::{ffi, FromPyObject, PyAny, PyObject, PyResult, Python};
7#[allow(deprecated)]
8use crate::{IntoPy, ToPyObject};
9use std::borrow::Cow;
10use std::convert::Infallible;
11use std::ffi::OsString;
12use std::path::{Path, PathBuf};
13
14#[allow(deprecated)]
15impl ToPyObject for Path {
16 #[inline]
17 fn to_object(&self, py: Python<'_>) -> PyObject {
18 self.into_pyobject(py).unwrap().into_any().unbind()
19 }
20}
21
22impl FromPyObject<'_> for PathBuf {
25 fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
26 let path = unsafe { ffi::PyOS_FSPath(ob.as_ptr()).assume_owned_or_err(ob.py())? };
28 Ok(path.extract::<OsString>()?.into())
29 }
30}
31
32#[allow(deprecated)]
33impl IntoPy<PyObject> for &Path {
34 #[inline]
35 fn into_py(self, py: Python<'_>) -> PyObject {
36 self.into_pyobject(py).unwrap().into_any().unbind()
37 }
38}
39
40impl<'py> IntoPyObject<'py> for &Path {
41 type Target = PyString;
42 type Output = Bound<'py, Self::Target>;
43 type Error = Infallible;
44
45 #[inline]
46 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
47 self.as_os_str().into_pyobject(py)
48 }
49}
50
51impl<'py> IntoPyObject<'py> for &&Path {
52 type Target = PyString;
53 type Output = Bound<'py, Self::Target>;
54 type Error = Infallible;
55
56 #[inline]
57 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
58 (*self).into_pyobject(py)
59 }
60}
61
62#[allow(deprecated)]
63impl ToPyObject for Cow<'_, Path> {
64 #[inline]
65 fn to_object(&self, py: Python<'_>) -> PyObject {
66 self.into_pyobject(py).unwrap().into_any().unbind()
67 }
68}
69
70#[allow(deprecated)]
71impl IntoPy<PyObject> for Cow<'_, Path> {
72 #[inline]
73 fn into_py(self, py: Python<'_>) -> PyObject {
74 self.into_pyobject(py).unwrap().into_any().unbind()
75 }
76}
77
78impl<'py> IntoPyObject<'py> for Cow<'_, Path> {
79 type Target = PyString;
80 type Output = Bound<'py, Self::Target>;
81 type Error = Infallible;
82
83 #[inline]
84 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
85 self.as_os_str().into_pyobject(py)
86 }
87}
88
89impl<'py> IntoPyObject<'py> for &Cow<'_, Path> {
90 type Target = PyString;
91 type Output = Bound<'py, Self::Target>;
92 type Error = Infallible;
93
94 #[inline]
95 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
96 self.as_os_str().into_pyobject(py)
97 }
98}
99
100#[allow(deprecated)]
101impl ToPyObject for PathBuf {
102 #[inline]
103 fn to_object(&self, py: Python<'_>) -> PyObject {
104 self.into_pyobject(py).unwrap().into_any().unbind()
105 }
106}
107
108#[allow(deprecated)]
109impl IntoPy<PyObject> for PathBuf {
110 #[inline]
111 fn into_py(self, py: Python<'_>) -> PyObject {
112 self.into_pyobject(py).unwrap().into_any().unbind()
113 }
114}
115
116impl<'py> IntoPyObject<'py> for PathBuf {
117 type Target = PyString;
118 type Output = Bound<'py, Self::Target>;
119 type Error = Infallible;
120
121 #[inline]
122 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
123 self.as_os_str().into_pyobject(py)
124 }
125}
126
127#[allow(deprecated)]
128impl IntoPy<PyObject> for &PathBuf {
129 #[inline]
130 fn into_py(self, py: Python<'_>) -> PyObject {
131 self.into_pyobject(py).unwrap().into_any().unbind()
132 }
133}
134
135impl<'py> IntoPyObject<'py> for &PathBuf {
136 type Target = PyString;
137 type Output = Bound<'py, Self::Target>;
138 type Error = Infallible;
139
140 #[inline]
141 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
142 self.as_os_str().into_pyobject(py)
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use crate::types::{PyAnyMethods, PyString, PyStringMethods};
149 use crate::{BoundObject, IntoPyObject, Python};
150 use std::borrow::Cow;
151 use std::fmt::Debug;
152 use std::path::{Path, PathBuf};
153
154 #[test]
155 #[cfg(not(windows))]
156 fn test_non_utf8_conversion() {
157 Python::with_gil(|py| {
158 use std::ffi::OsStr;
159 #[cfg(not(target_os = "wasi"))]
160 use std::os::unix::ffi::OsStrExt;
161 #[cfg(target_os = "wasi")]
162 use std::os::wasi::ffi::OsStrExt;
163
164 let payload = &[250, 251, 252, 253, 254, 255, 0, 255];
166 let path = Path::new(OsStr::from_bytes(payload));
167
168 let py_str = path.into_pyobject(py).unwrap();
170 let path_2: PathBuf = py_str.extract().unwrap();
171 assert_eq!(path, path_2);
172 });
173 }
174
175 #[test]
176 fn test_intopyobject_roundtrip() {
177 Python::with_gil(|py| {
178 fn test_roundtrip<'py, T>(py: Python<'py>, obj: T)
179 where
180 T: IntoPyObject<'py> + AsRef<Path> + Debug + Clone,
181 T::Error: Debug,
182 {
183 let pyobject = obj.clone().into_pyobject(py).unwrap().into_any();
184 let pystring = pyobject.as_borrowed().downcast::<PyString>().unwrap();
185 assert_eq!(pystring.to_string_lossy(), obj.as_ref().to_string_lossy());
186 let roundtripped_obj: PathBuf = pystring.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}