pyo3/conversions/std/
string.rs

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