1 | use std::borrow::Cow; |
2 | |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use crate::{ |
6 | types::PyString, FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject, |
7 | }; |
8 | |
9 | /// Converts a Rust `str` to a Python object. |
10 | /// See `PyString::new` for details on the conversion. |
11 | impl ToPyObject for str { |
12 | #[inline ] |
13 | fn to_object(&self, py: Python<'_>) -> PyObject { |
14 | PyString::new(py, self).into() |
15 | } |
16 | } |
17 | |
18 | impl<'a> IntoPy<PyObject> for &'a str { |
19 | #[inline ] |
20 | fn into_py(self, py: Python<'_>) -> PyObject { |
21 | PyString::new(py, self).into() |
22 | } |
23 | |
24 | #[cfg (feature = "experimental-inspect" )] |
25 | fn type_output() -> TypeInfo { |
26 | <String>::type_output() |
27 | } |
28 | } |
29 | |
30 | impl<'a> IntoPy<Py<PyString>> for &'a str { |
31 | #[inline ] |
32 | fn into_py(self, py: Python<'_>) -> Py<PyString> { |
33 | PyString::new(py, self).into() |
34 | } |
35 | |
36 | #[cfg (feature = "experimental-inspect" )] |
37 | fn type_output() -> TypeInfo { |
38 | <String>::type_output() |
39 | } |
40 | } |
41 | |
42 | /// Converts a Rust `Cow<'_, str>` to a Python object. |
43 | /// See `PyString::new` for details on the conversion. |
44 | impl ToPyObject for Cow<'_, str> { |
45 | #[inline ] |
46 | fn to_object(&self, py: Python<'_>) -> PyObject { |
47 | PyString::new(py, self).into() |
48 | } |
49 | } |
50 | |
51 | impl IntoPy<PyObject> for Cow<'_, str> { |
52 | #[inline ] |
53 | fn into_py(self, py: Python<'_>) -> PyObject { |
54 | self.to_object(py) |
55 | } |
56 | |
57 | #[cfg (feature = "experimental-inspect" )] |
58 | fn type_output() -> TypeInfo { |
59 | <String>::type_output() |
60 | } |
61 | } |
62 | |
63 | /// Converts a Rust `String` to a Python object. |
64 | /// See `PyString::new` for details on the conversion. |
65 | impl ToPyObject for String { |
66 | #[inline ] |
67 | fn to_object(&self, py: Python<'_>) -> PyObject { |
68 | PyString::new(py, self).into() |
69 | } |
70 | } |
71 | |
72 | impl ToPyObject for char { |
73 | fn to_object(&self, py: Python<'_>) -> PyObject { |
74 | self.into_py(py) |
75 | } |
76 | } |
77 | |
78 | impl IntoPy<PyObject> for char { |
79 | fn into_py(self, py: Python<'_>) -> PyObject { |
80 | let mut bytes: [u8; 4] = [0u8; 4]; |
81 | PyString::new(py, self.encode_utf8(&mut bytes)).into() |
82 | } |
83 | |
84 | #[cfg (feature = "experimental-inspect" )] |
85 | fn type_output() -> TypeInfo { |
86 | <String>::type_output() |
87 | } |
88 | } |
89 | |
90 | impl IntoPy<PyObject> for String { |
91 | fn into_py(self, py: Python<'_>) -> PyObject { |
92 | PyString::new(py, &self).into() |
93 | } |
94 | |
95 | #[cfg (feature = "experimental-inspect" )] |
96 | fn type_output() -> TypeInfo { |
97 | TypeInfo::builtin("str" ) |
98 | } |
99 | } |
100 | |
101 | impl<'a> IntoPy<PyObject> for &'a String { |
102 | #[inline ] |
103 | fn into_py(self, py: Python<'_>) -> PyObject { |
104 | PyString::new(py, self).into() |
105 | } |
106 | |
107 | #[cfg (feature = "experimental-inspect" )] |
108 | fn type_output() -> TypeInfo { |
109 | <String>::type_output() |
110 | } |
111 | } |
112 | |
113 | /// Allows extracting strings from Python objects. |
114 | /// Accepts Python `str` and `unicode` objects. |
115 | impl<'source> FromPyObject<'source> for &'source str { |
116 | fn extract(ob: &'source PyAny) -> PyResult<Self> { |
117 | ob.downcast::<PyString>()?.to_str() |
118 | } |
119 | |
120 | #[cfg (feature = "experimental-inspect" )] |
121 | fn type_input() -> TypeInfo { |
122 | <String>::type_input() |
123 | } |
124 | } |
125 | |
126 | /// Allows extracting strings from Python objects. |
127 | /// Accepts Python `str` and `unicode` objects. |
128 | impl FromPyObject<'_> for String { |
129 | fn extract(obj: &PyAny) -> PyResult<Self> { |
130 | obj.downcast::<PyString>()?.to_str().map(op:ToOwned::to_owned) |
131 | } |
132 | |
133 | #[cfg (feature = "experimental-inspect" )] |
134 | fn type_input() -> TypeInfo { |
135 | Self::type_output() |
136 | } |
137 | } |
138 | |
139 | impl FromPyObject<'_> for char { |
140 | fn extract(obj: &PyAny) -> PyResult<Self> { |
141 | let s: &str = obj.downcast::<PyString>()?.to_str()?; |
142 | let mut iter: Chars<'_> = s.chars(); |
143 | if let (Some(ch: char), None) = (iter.next(), iter.next()) { |
144 | Ok(ch) |
145 | } else { |
146 | Err(crate::exceptions::PyValueError::new_err( |
147 | args:"expected a string of length 1" , |
148 | )) |
149 | } |
150 | } |
151 | |
152 | #[cfg (feature = "experimental-inspect" )] |
153 | fn type_input() -> TypeInfo { |
154 | <String>::type_input() |
155 | } |
156 | } |
157 | |
158 | #[cfg (test)] |
159 | mod tests { |
160 | use crate::Python; |
161 | use crate::{FromPyObject, IntoPy, PyObject, ToPyObject}; |
162 | use std::borrow::Cow; |
163 | |
164 | #[test ] |
165 | fn test_cow_into_py() { |
166 | Python::with_gil(|py| { |
167 | let s = "Hello Python" ; |
168 | let py_string: PyObject = Cow::Borrowed(s).into_py(py); |
169 | assert_eq!(s, py_string.extract::<&str>(py).unwrap()); |
170 | let py_string: PyObject = Cow::<str>::Owned(s.into()).into_py(py); |
171 | assert_eq!(s, py_string.extract::<&str>(py).unwrap()); |
172 | }) |
173 | } |
174 | |
175 | #[test ] |
176 | fn test_cow_to_object() { |
177 | Python::with_gil(|py| { |
178 | let s = "Hello Python" ; |
179 | let py_string = Cow::Borrowed(s).to_object(py); |
180 | assert_eq!(s, py_string.extract::<&str>(py).unwrap()); |
181 | let py_string = Cow::<str>::Owned(s.into()).to_object(py); |
182 | assert_eq!(s, py_string.extract::<&str>(py).unwrap()); |
183 | }) |
184 | } |
185 | |
186 | #[test ] |
187 | fn test_non_bmp() { |
188 | Python::with_gil(|py| { |
189 | let s = " \u{1F30F}" ; |
190 | let py_string = s.to_object(py); |
191 | assert_eq!(s, py_string.extract::<String>(py).unwrap()); |
192 | }) |
193 | } |
194 | |
195 | #[test ] |
196 | fn test_extract_str() { |
197 | Python::with_gil(|py| { |
198 | let s = "Hello Python" ; |
199 | let py_string = s.to_object(py); |
200 | |
201 | let s2: &str = FromPyObject::extract(py_string.as_ref(py)).unwrap(); |
202 | assert_eq!(s, s2); |
203 | }) |
204 | } |
205 | |
206 | #[test ] |
207 | fn test_extract_char() { |
208 | Python::with_gil(|py| { |
209 | let ch = '😃' ; |
210 | let py_string = ch.to_object(py); |
211 | let ch2: char = FromPyObject::extract(py_string.as_ref(py)).unwrap(); |
212 | assert_eq!(ch, ch2); |
213 | }) |
214 | } |
215 | |
216 | #[test ] |
217 | fn test_extract_char_err() { |
218 | Python::with_gil(|py| { |
219 | let s = "Hello Python" ; |
220 | let py_string = s.to_object(py); |
221 | let err: crate::PyResult<char> = FromPyObject::extract(py_string.as_ref(py)); |
222 | assert!(err |
223 | .unwrap_err() |
224 | .to_string() |
225 | .contains("expected a string of length 1" )); |
226 | }) |
227 | } |
228 | |
229 | #[test ] |
230 | fn test_string_into_py() { |
231 | Python::with_gil(|py| { |
232 | let s = "Hello Python" ; |
233 | let s2 = s.to_owned(); |
234 | let s3 = &s2; |
235 | assert_eq!( |
236 | s, |
237 | IntoPy::<PyObject>::into_py(s3, py) |
238 | .extract::<&str>(py) |
239 | .unwrap() |
240 | ); |
241 | assert_eq!( |
242 | s, |
243 | IntoPy::<PyObject>::into_py(s2, py) |
244 | .extract::<&str>(py) |
245 | .unwrap() |
246 | ); |
247 | assert_eq!( |
248 | s, |
249 | IntoPy::<PyObject>::into_py(s, py) |
250 | .extract::<&str>(py) |
251 | .unwrap() |
252 | ); |
253 | }) |
254 | } |
255 | } |
256 | |