1 | use std::{borrow::Cow, convert::Infallible}; |
2 | |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use 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)] |
12 | use 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)] |
17 | impl 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)] |
25 | impl 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)] |
33 | impl 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 | |
40 | impl<'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 | |
56 | impl<'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)] |
75 | impl 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)] |
83 | impl 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 | |
90 | impl<'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 | |
106 | impl<'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)] |
125 | impl 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)] |
133 | impl 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)] |
141 | impl 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 | |
148 | impl<'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: [u8; 4] = [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 | |
164 | impl<'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)] |
181 | impl 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 | |
188 | impl<'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)] |
204 | impl 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 | |
211 | impl<'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)))] |
228 | impl<'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 | |
239 | impl<'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. |
252 | impl FromPyObject<'_> for String { |
253 | fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> { |
254 | obj.downcast::<PyString>()?.to_cow().map(op:Cow::into_owned) |
255 | } |
256 | |
257 | #[cfg (feature = "experimental-inspect" )] |
258 | fn type_input() -> TypeInfo { |
259 | Self::type_output() |
260 | } |
261 | } |
262 | |
263 | impl FromPyObject<'_> for char { |
264 | fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> { |
265 | let s: Cow<'_, str> = obj.downcast::<PyString>()?.to_cow()?; |
266 | let mut iter: Chars<'_> = s.chars(); |
267 | if let (Some(ch: char), None) = (iter.next(), iter.next()) { |
268 | Ok(ch) |
269 | } else { |
270 | Err(crate::exceptions::PyValueError::new_err( |
271 | args:"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)] |
283 | mod 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 | } |
385 | |