Skip to main content

pyo3/conversions/std/
string.rs

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