pyo3/conversions/std/
string.rs1use std::borrow::Cow;
2
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6 instance::Bound,
7 types::{any::PyAnyMethods, string::PyStringMethods, PyString},
8 FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject,
9};
10
11impl ToPyObject for str {
14 #[inline]
15 fn to_object(&self, py: Python<'_>) -> PyObject {
16 PyString::new_bound(py, self).into()
17 }
18}
19
20impl<'a> IntoPy<PyObject> for &'a str {
21 #[inline]
22 fn into_py(self, py: Python<'_>) -> PyObject {
23 PyString::new_bound(py, self).into()
24 }
25
26 #[cfg(feature = "experimental-inspect")]
27 fn type_output() -> TypeInfo {
28 <String>::type_output()
29 }
30}
31
32impl<'a> IntoPy<Py<PyString>> for &'a str {
33 #[inline]
34 fn into_py(self, py: Python<'_>) -> Py<PyString> {
35 PyString::new_bound(py, self).into()
36 }
37
38 #[cfg(feature = "experimental-inspect")]
39 fn type_output() -> TypeInfo {
40 <String>::type_output()
41 }
42}
43
44impl ToPyObject for Cow<'_, str> {
47 #[inline]
48 fn to_object(&self, py: Python<'_>) -> PyObject {
49 PyString::new_bound(py, self).into()
50 }
51}
52
53impl IntoPy<PyObject> for Cow<'_, str> {
54 #[inline]
55 fn into_py(self, py: Python<'_>) -> PyObject {
56 self.to_object(py)
57 }
58
59 #[cfg(feature = "experimental-inspect")]
60 fn type_output() -> TypeInfo {
61 <String>::type_output()
62 }
63}
64
65impl ToPyObject for String {
68 #[inline]
69 fn to_object(&self, py: Python<'_>) -> PyObject {
70 PyString::new_bound(py, self).into()
71 }
72}
73
74impl ToPyObject for char {
75 fn to_object(&self, py: Python<'_>) -> PyObject {
76 self.into_py(py)
77 }
78}
79
80impl IntoPy<PyObject> for char {
81 fn into_py(self, py: Python<'_>) -> PyObject {
82 let mut bytes = [0u8; 4];
83 PyString::new_bound(py, self.encode_utf8(&mut bytes)).into()
84 }
85
86 #[cfg(feature = "experimental-inspect")]
87 fn type_output() -> TypeInfo {
88 <String>::type_output()
89 }
90}
91
92impl IntoPy<PyObject> for String {
93 fn into_py(self, py: Python<'_>) -> PyObject {
94 PyString::new_bound(py, &self).into()
95 }
96
97 #[cfg(feature = "experimental-inspect")]
98 fn type_output() -> TypeInfo {
99 TypeInfo::builtin("str")
100 }
101}
102
103impl<'a> IntoPy<PyObject> for &'a String {
104 #[inline]
105 fn into_py(self, py: Python<'_>) -> PyObject {
106 PyString::new_bound(py, self).into()
107 }
108
109 #[cfg(feature = "experimental-inspect")]
110 fn type_output() -> TypeInfo {
111 <String>::type_output()
112 }
113}
114
115#[cfg(feature = "gil-refs")]
118impl<'py> FromPyObject<'py> for &'py str {
119 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
120 ob.clone().into_gil_ref().downcast::<PyString>()?.to_str()
121 }
122
123 #[cfg(feature = "experimental-inspect")]
124 fn type_input() -> TypeInfo {
125 <String as crate::FromPyObject>::type_input()
126 }
127}
128
129#[cfg(all(not(feature = "gil-refs"), any(Py_3_10, not(Py_LIMITED_API))))]
130impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for &'a str {
131 fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
132 ob.downcast::<PyString>()?.to_str()
133 }
134
135 #[cfg(feature = "experimental-inspect")]
136 fn type_input() -> TypeInfo {
137 <String as crate::FromPyObject>::type_input()
138 }
139}
140
141#[cfg(feature = "gil-refs")]
142impl<'py> FromPyObject<'py> for Cow<'py, str> {
143 fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
144 ob.extract().map(Cow::Owned)
145 }
146
147 #[cfg(feature = "experimental-inspect")]
148 fn type_input() -> TypeInfo {
149 <String as crate::FromPyObject>::type_input()
150 }
151}
152
153#[cfg(not(feature = "gil-refs"))]
154impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> {
155 fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
156 ob.downcast::<PyString>()?.to_cow()
157 }
158
159 #[cfg(feature = "experimental-inspect")]
160 fn type_input() -> TypeInfo {
161 <String as crate::FromPyObject>::type_input()
162 }
163}
164
165impl FromPyObject<'_> for String {
168 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
169 obj.downcast::<PyString>()?.to_cow().map(Cow::into_owned)
170 }
171
172 #[cfg(feature = "experimental-inspect")]
173 fn type_input() -> TypeInfo {
174 Self::type_output()
175 }
176}
177
178impl FromPyObject<'_> for char {
179 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
180 let s = obj.downcast::<PyString>()?.to_cow()?;
181 let mut iter = s.chars();
182 if let (Some(ch), None) = (iter.next(), iter.next()) {
183 Ok(ch)
184 } else {
185 Err(crate::exceptions::PyValueError::new_err(
186 "expected a string of length 1",
187 ))
188 }
189 }
190
191 #[cfg(feature = "experimental-inspect")]
192 fn type_input() -> TypeInfo {
193 <String>::type_input()
194 }
195}
196
197#[cfg(test)]
198mod tests {
199 use crate::types::any::PyAnyMethods;
200 use crate::Python;
201 use crate::{IntoPy, PyObject, ToPyObject};
202 use std::borrow::Cow;
203
204 #[test]
205 fn test_cow_into_py() {
206 Python::with_gil(|py| {
207 let s = "Hello Python";
208 let py_string: PyObject = Cow::Borrowed(s).into_py(py);
209 assert_eq!(s, py_string.extract::<Cow<'_, str>>(py).unwrap());
210 let py_string: PyObject = Cow::<str>::Owned(s.into()).into_py(py);
211 assert_eq!(s, py_string.extract::<Cow<'_, str>>(py).unwrap());
212 })
213 }
214
215 #[test]
216 fn test_cow_to_object() {
217 Python::with_gil(|py| {
218 let s = "Hello Python";
219 let py_string = Cow::Borrowed(s).to_object(py);
220 assert_eq!(s, py_string.extract::<Cow<'_, str>>(py).unwrap());
221 let py_string = Cow::<str>::Owned(s.into()).to_object(py);
222 assert_eq!(s, py_string.extract::<Cow<'_, str>>(py).unwrap());
223 })
224 }
225
226 #[test]
227 fn test_non_bmp() {
228 Python::with_gil(|py| {
229 let s = "\u{1F30F}";
230 let py_string = s.to_object(py);
231 assert_eq!(s, py_string.extract::<String>(py).unwrap());
232 })
233 }
234
235 #[test]
236 fn test_extract_str() {
237 Python::with_gil(|py| {
238 let s = "Hello Python";
239 let py_string = s.to_object(py);
240
241 let s2: Cow<'_, str> = py_string.bind(py).extract().unwrap();
242 assert_eq!(s, s2);
243 })
244 }
245
246 #[test]
247 fn test_extract_char() {
248 Python::with_gil(|py| {
249 let ch = '😃';
250 let py_string = ch.to_object(py);
251 let ch2: char = py_string.bind(py).extract().unwrap();
252 assert_eq!(ch, ch2);
253 })
254 }
255
256 #[test]
257 fn test_extract_char_err() {
258 Python::with_gil(|py| {
259 let s = "Hello Python";
260 let py_string = s.to_object(py);
261 let err: crate::PyResult<char> = py_string.bind(py).extract();
262 assert!(err
263 .unwrap_err()
264 .to_string()
265 .contains("expected a string of length 1"));
266 })
267 }
268
269 #[test]
270 fn test_string_into_py() {
271 Python::with_gil(|py| {
272 let s = "Hello Python";
273 let s2 = s.to_owned();
274 let s3 = &s2;
275 assert_eq!(
276 s,
277 IntoPy::<PyObject>::into_py(s3, py)
278 .extract::<Cow<'_, str>>(py)
279 .unwrap()
280 );
281 assert_eq!(
282 s,
283 IntoPy::<PyObject>::into_py(s2, py)
284 .extract::<Cow<'_, str>>(py)
285 .unwrap()
286 );
287 assert_eq!(
288 s,
289 IntoPy::<PyObject>::into_py(s, py)
290 .extract::<Cow<'_, str>>(py)
291 .unwrap()
292 );
293 })
294 }
295}