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