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