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