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