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