pyo3/conversions/std/
string.rs1use 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
168impl 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}