Skip to main content

pyo3/conversions/std/
string.rs

1#[cfg(feature = "experimental-inspect")]
2use crate::inspect::PyStaticExpr;
3use crate::platform::prelude::*;
4#[cfg(feature = "experimental-inspect")]
5use crate::type_object::PyTypeInfo;
6use crate::{
7    conversion::IntoPyObject, instance::Bound, types::PyString, Borrowed, FromPyObject, PyAny,
8    PyErr, Python,
9};
10use alloc::borrow::Cow;
11use core::convert::Infallible;
12
13impl<'py> IntoPyObject<'py> for &str {
14    type Target = PyString;
15    type Output = Bound<'py, Self::Target>;
16    type Error = Infallible;
17
18    #[cfg(feature = "experimental-inspect")]
19    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
20
21    #[inline]
22    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
23        Ok(PyString::new(py, self))
24    }
25}
26
27impl<'py> IntoPyObject<'py> for &&str {
28    type Target = PyString;
29    type Output = Bound<'py, Self::Target>;
30    type Error = Infallible;
31
32    #[cfg(feature = "experimental-inspect")]
33    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
34
35    #[inline]
36    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
37        (*self).into_pyobject(py)
38    }
39}
40
41impl<'py> IntoPyObject<'py> for Cow<'_, str> {
42    type Target = PyString;
43    type Output = Bound<'py, Self::Target>;
44    type Error = Infallible;
45
46    #[cfg(feature = "experimental-inspect")]
47    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
48
49    #[inline]
50    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
51        (*self).into_pyobject(py)
52    }
53}
54
55impl<'py> IntoPyObject<'py> for &Cow<'_, str> {
56    type Target = PyString;
57    type Output = Bound<'py, Self::Target>;
58    type Error = Infallible;
59
60    #[cfg(feature = "experimental-inspect")]
61    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
62
63    #[inline]
64    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
65        (&**self).into_pyobject(py)
66    }
67}
68
69impl<'py> IntoPyObject<'py> for char {
70    type Target = PyString;
71    type Output = Bound<'py, Self::Target>;
72    type Error = Infallible;
73
74    #[cfg(feature = "experimental-inspect")]
75    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
76
77    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
78        let mut bytes = [0u8; 4];
79        Ok(PyString::new(py, self.encode_utf8(&mut bytes)))
80    }
81}
82
83impl<'py> IntoPyObject<'py> for &char {
84    type Target = PyString;
85    type Output = Bound<'py, Self::Target>;
86    type Error = Infallible;
87
88    #[cfg(feature = "experimental-inspect")]
89    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
90
91    #[inline]
92    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
93        (*self).into_pyobject(py)
94    }
95}
96
97impl<'py> IntoPyObject<'py> for String {
98    type Target = PyString;
99    type Output = Bound<'py, Self::Target>;
100    type Error = Infallible;
101
102    #[cfg(feature = "experimental-inspect")]
103    const OUTPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
104
105    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
106        Ok(PyString::new(py, &self))
107    }
108}
109
110impl<'py> IntoPyObject<'py> for &String {
111    type Target = PyString;
112    type Output = Bound<'py, Self::Target>;
113    type Error = Infallible;
114
115    #[cfg(feature = "experimental-inspect")]
116    const OUTPUT_TYPE: PyStaticExpr = String::OUTPUT_TYPE;
117
118    #[inline]
119    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
120        Ok(PyString::new(py, self))
121    }
122}
123
124#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
125impl<'a> FromPyObject<'a, '_> for &'a str {
126    type Error = PyErr;
127
128    #[cfg(feature = "experimental-inspect")]
129    const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
130
131    fn extract(ob: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
132        ob.cast::<PyString>()?.to_str()
133    }
134}
135
136impl<'a> FromPyObject<'a, '_> for Cow<'a, str> {
137    type Error = PyErr;
138
139    #[cfg(feature = "experimental-inspect")]
140    const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
141
142    fn extract(ob: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
143        ob.cast::<PyString>()?.to_cow()
144    }
145}
146
147/// Allows extracting strings from Python objects.
148/// Accepts Python `str` and `unicode` objects.
149impl FromPyObject<'_, '_> for String {
150    type Error = PyErr;
151
152    #[cfg(feature = "experimental-inspect")]
153    const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
154
155    fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
156        obj.cast::<PyString>()?.to_cow().map(Cow::into_owned)
157    }
158}
159
160impl FromPyObject<'_, '_> for char {
161    type Error = PyErr;
162
163    #[cfg(feature = "experimental-inspect")]
164    const INPUT_TYPE: PyStaticExpr = PyString::TYPE_HINT;
165
166    fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
167        let s = obj.cast::<PyString>()?.to_cow()?;
168        let mut iter = s.chars();
169        if let (Some(ch), None) = (iter.next(), iter.next()) {
170            Ok(ch)
171        } else {
172            Err(crate::exceptions::PyValueError::new_err(
173                "expected a string of length 1",
174            ))
175        }
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use crate::platform::prelude::*;
182    use crate::types::any::PyAnyMethods;
183    use crate::{IntoPyObject, Python};
184    use alloc::borrow::Cow;
185
186    #[test]
187    fn test_cow_into_pyobject() {
188        Python::attach(|py| {
189            let s = "Hello Python";
190            let py_string = Cow::Borrowed(s).into_pyobject(py).unwrap();
191            assert_eq!(s, py_string.extract::<Cow<'_, str>>().unwrap());
192            let py_string = Cow::<str>::Owned(s.into()).into_pyobject(py).unwrap();
193            assert_eq!(s, py_string.extract::<Cow<'_, str>>().unwrap());
194        })
195    }
196
197    #[test]
198    fn test_non_bmp() {
199        Python::attach(|py| {
200            let s = "\u{1F30F}";
201            let py_string = s.into_pyobject(py).unwrap();
202            assert_eq!(s, py_string.extract::<String>().unwrap());
203        })
204    }
205
206    #[test]
207    fn test_extract_str() {
208        Python::attach(|py| {
209            let s = "Hello Python";
210            let py_string = s.into_pyobject(py).unwrap();
211
212            let s2: Cow<'_, str> = py_string.extract().unwrap();
213            assert_eq!(s, s2);
214        })
215    }
216
217    #[test]
218    fn test_extract_char() {
219        Python::attach(|py| {
220            let ch = '😃';
221            let py_string = ch.into_pyobject(py).unwrap();
222            let ch2: char = py_string.extract().unwrap();
223            assert_eq!(ch, ch2);
224        })
225    }
226
227    #[test]
228    fn test_extract_char_err() {
229        Python::attach(|py| {
230            let s = "Hello Python";
231            let py_string = s.into_pyobject(py).unwrap();
232            let err: crate::PyResult<char> = py_string.extract();
233            assert!(err
234                .unwrap_err()
235                .to_string()
236                .contains("expected a string of length 1"));
237        })
238    }
239
240    #[test]
241    fn test_string_into_pyobject() {
242        Python::attach(|py| {
243            let s = "Hello Python";
244            let s2 = s.to_owned();
245            let s3 = &s2;
246            assert_eq!(
247                s,
248                s3.into_pyobject(py)
249                    .unwrap()
250                    .extract::<Cow<'_, str>>()
251                    .unwrap()
252            );
253            assert_eq!(
254                s,
255                s2.into_pyobject(py)
256                    .unwrap()
257                    .extract::<Cow<'_, str>>()
258                    .unwrap()
259            );
260            assert_eq!(
261                s,
262                s.into_pyobject(py)
263                    .unwrap()
264                    .extract::<Cow<'_, str>>()
265                    .unwrap()
266            );
267        })
268    }
269}