pyo3/conversions/std/
cstring.rs1use crate::exceptions::PyUnicodeDecodeError;
2#[cfg(feature = "experimental-inspect")]
3use crate::inspect::PyStaticExpr;
4#[allow(unused_imports, reason = "conditionally used")]
5use crate::platform::prelude::*;
6#[cfg(feature = "experimental-inspect")]
7use crate::type_object::PyTypeInfo;
8use crate::types::PyString;
9use crate::{Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python};
10use alloc::borrow::Cow;
11use alloc::ffi::CString;
12use core::ffi::CStr;
13#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
14use {
15 crate::{exceptions::PyValueError, ffi},
16 core::slice,
17};
18
19impl<'py> IntoPyObject<'py> for &CStr {
20 type Target = PyString;
21 type Output = Bound<'py, Self::Target>;
22 type Error = PyErr;
23
24 #[cfg(feature = "experimental-inspect")]
25 const OUTPUT_TYPE: PyStaticExpr = <&str>::OUTPUT_TYPE;
26
27 #[inline]
28 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
29 self.to_str()
30 .map_err(|e| PyUnicodeDecodeError::new_err_from_utf8(py, self.to_bytes(), e))?
31 .into_pyobject(py)
32 .map_err(|err| match err {})
33 }
34}
35
36impl<'py> IntoPyObject<'py> for CString {
37 type Target = PyString;
38 type Output = Bound<'py, Self::Target>;
39 type Error = PyErr;
40
41 #[cfg(feature = "experimental-inspect")]
42 const OUTPUT_TYPE: PyStaticExpr = <&CStr>::OUTPUT_TYPE;
43
44 #[inline]
45 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
46 (&*self).into_pyobject(py)
47 }
48}
49
50impl<'py> IntoPyObject<'py> for &CString {
51 type Target = PyString;
52 type Output = Bound<'py, Self::Target>;
53 type Error = PyErr;
54
55 #[cfg(feature = "experimental-inspect")]
56 const OUTPUT_TYPE: PyStaticExpr = <&CStr>::OUTPUT_TYPE;
57
58 #[inline]
59 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
60 (&**self).into_pyobject(py)
61 }
62}
63
64impl<'py> IntoPyObject<'py> for Cow<'_, CStr> {
65 type Target = PyString;
66 type Output = Bound<'py, Self::Target>;
67 type Error = PyErr;
68
69 #[cfg(feature = "experimental-inspect")]
70 const OUTPUT_TYPE: PyStaticExpr = <&CStr>::OUTPUT_TYPE;
71
72 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
73 (*self).into_pyobject(py)
74 }
75}
76
77impl<'py> IntoPyObject<'py> for &Cow<'_, CStr> {
78 type Target = PyString;
79 type Output = Bound<'py, Self::Target>;
80 type Error = PyErr;
81
82 #[cfg(feature = "experimental-inspect")]
83 const OUTPUT_TYPE: PyStaticExpr = <&CStr>::OUTPUT_TYPE;
84
85 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
86 (&**self).into_pyobject(py)
87 }
88}
89
90#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
91impl<'a> FromPyObject<'a, '_> for &'a CStr {
92 type Error = PyErr;
93
94 #[cfg(feature = "experimental-inspect")]
95 const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
96
97 fn extract(obj: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
98 let obj = obj.cast::<PyString>()?;
99 let mut size = 0;
100 let ptr = unsafe { ffi::PyUnicode_AsUTF8AndSize(obj.as_ptr(), &mut size) };
102
103 if ptr.is_null() {
104 return Err(PyErr::fetch(obj.py()));
105 }
106
107 let slice = unsafe { slice::from_raw_parts(ptr.cast(), size as usize + 1) };
110
111 CStr::from_bytes_with_nul(slice).map_err(|err| PyValueError::new_err(err.to_string()))
112 }
113}
114
115impl<'a> FromPyObject<'a, '_> for Cow<'a, CStr> {
116 type Error = PyErr;
117
118 #[cfg(feature = "experimental-inspect")]
119 const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
120
121 fn extract(obj: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
122 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
123 {
124 Ok(Cow::Borrowed(obj.extract::<&CStr>()?))
125 }
126
127 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
128 {
129 Ok(Cow::Owned(obj.extract::<CString>()?))
130 }
131 }
132}
133impl FromPyObject<'_, '_> for CString {
134 type Error = PyErr;
135
136 #[cfg(feature = "experimental-inspect")]
137 const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
138
139 fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
140 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
141 {
142 Ok(obj.extract::<&CStr>()?.to_owned())
143 }
144
145 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
146 {
147 CString::new(&*obj.cast::<PyString>()?.to_cow()?).map_err(Into::into)
148 }
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::types::string::PyStringMethods;
156 use crate::types::PyAnyMethods;
157 use crate::Python;
158
159 #[test]
160 fn test_into_pyobject() {
161 Python::attach(|py| {
162 let s = "Hello, Python!";
163 let cstr = CString::new(s).unwrap();
164
165 let py_string = cstr.as_c_str().into_pyobject(py).unwrap();
166 assert_eq!(py_string.to_cow().unwrap(), s);
167
168 let py_string = cstr.into_pyobject(py).unwrap();
169 assert_eq!(py_string.to_cow().unwrap(), s);
170 })
171 }
172
173 #[test]
174 fn test_extract_with_nul_error() {
175 Python::attach(|py| {
176 let s = "Hello\0Python";
177 let py_string = s.into_pyobject(py).unwrap();
178
179 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
180 {
181 let err = py_string.extract::<&CStr>();
182 assert!(err.is_err());
183 }
184
185 let err = py_string.extract::<CString>();
186 assert!(err.is_err());
187 })
188 }
189
190 #[test]
191 fn test_extract_cstr_and_cstring() {
192 Python::attach(|py| {
193 let s = "Hello, world!";
194 let cstr = CString::new(s).unwrap();
195 let py_string = cstr.as_c_str().into_pyobject(py).unwrap();
196
197 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
198 {
199 let extracted_cstr: &CStr = py_string.extract().unwrap();
200 assert_eq!(extracted_cstr.to_str().unwrap(), s);
201 }
202
203 let extracted_cstring: CString = py_string.extract().unwrap();
204 assert_eq!(extracted_cstring.to_str().unwrap(), s);
205 })
206 }
207
208 #[test]
209 fn test_cow_roundtrip() {
210 Python::attach(|py| {
211 let s = "Hello, world!";
212 let cstr = CString::new(s).unwrap();
213 let cow: Cow<'_, CStr> = Cow::Borrowed(cstr.as_c_str());
214
215 let py_string = cow.into_pyobject(py).unwrap();
216 assert_eq!(py_string.to_cow().unwrap(), s);
217
218 let roundtripped: Cow<'_, CStr> = py_string.extract().unwrap();
219 assert_eq!(roundtripped.as_ref(), cstr.as_c_str());
220 })
221 }
222}