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 fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
142 ob.downcast::<PyString>()?.to_str()
143 }
144
145 #[cfg(feature = "experimental-inspect")]
146 fn type_input() -> TypeInfo {
147 <String as crate::FromPyObject>::type_input()
148 }
149}
150
151impl<'a> crate::conversion::FromPyObjectBound<'a, '_> for Cow<'a, str> {
152 fn from_py_object_bound(ob: crate::Borrowed<'a, '_, PyAny>) -> PyResult<Self> {
153 ob.downcast::<PyString>()?.to_cow()
154 }
155
156 #[cfg(feature = "experimental-inspect")]
157 fn type_input() -> TypeInfo {
158 <String as crate::FromPyObject>::type_input()
159 }
160}
161
162impl FromPyObject<'_> for String {
165 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
166 obj.downcast::<PyString>()?.to_cow().map(Cow::into_owned)
167 }
168
169 #[cfg(feature = "experimental-inspect")]
170 fn type_input() -> TypeInfo {
171 Self::type_output()
172 }
173}
174
175impl FromPyObject<'_> for char {
176 fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
177 let s = obj.downcast::<PyString>()?.to_cow()?;
178 let mut iter = s.chars();
179 if let (Some(ch), None) = (iter.next(), iter.next()) {
180 Ok(ch)
181 } else {
182 Err(crate::exceptions::PyValueError::new_err(
183 "expected a string of length 1",
184 ))
185 }
186 }
187
188 #[cfg(feature = "experimental-inspect")]
189 fn type_input() -> TypeInfo {
190 <String>::type_input()
191 }
192}
193
194#[cfg(test)]
195mod tests {
196 use crate::types::any::PyAnyMethods;
197 use crate::{IntoPyObject, Python};
198 use std::borrow::Cow;
199
200 #[test]
201 fn test_cow_into_pyobject() {
202 Python::with_gil(|py| {
203 let s = "Hello Python";
204 let py_string = Cow::Borrowed(s).into_pyobject(py).unwrap();
205 assert_eq!(s, py_string.extract::<Cow<'_, str>>().unwrap());
206 let py_string = Cow::<str>::Owned(s.into()).into_pyobject(py).unwrap();
207 assert_eq!(s, py_string.extract::<Cow<'_, str>>().unwrap());
208 })
209 }
210
211 #[test]
212 fn test_non_bmp() {
213 Python::with_gil(|py| {
214 let s = "\u{1F30F}";
215 let py_string = s.into_pyobject(py).unwrap();
216 assert_eq!(s, py_string.extract::<String>().unwrap());
217 })
218 }
219
220 #[test]
221 fn test_extract_str() {
222 Python::with_gil(|py| {
223 let s = "Hello Python";
224 let py_string = s.into_pyobject(py).unwrap();
225
226 let s2: Cow<'_, str> = py_string.extract().unwrap();
227 assert_eq!(s, s2);
228 })
229 }
230
231 #[test]
232 fn test_extract_char() {
233 Python::with_gil(|py| {
234 let ch = '😃';
235 let py_string = ch.into_pyobject(py).unwrap();
236 let ch2: char = py_string.extract().unwrap();
237 assert_eq!(ch, ch2);
238 })
239 }
240
241 #[test]
242 fn test_extract_char_err() {
243 Python::with_gil(|py| {
244 let s = "Hello Python";
245 let py_string = s.into_pyobject(py).unwrap();
246 let err: crate::PyResult<char> = py_string.extract();
247 assert!(err
248 .unwrap_err()
249 .to_string()
250 .contains("expected a string of length 1"));
251 })
252 }
253
254 #[test]
255 fn test_string_into_pyobject() {
256 Python::with_gil(|py| {
257 let s = "Hello Python";
258 let s2 = s.to_owned();
259 let s3 = &s2;
260 assert_eq!(
261 s,
262 s3.into_pyobject(py)
263 .unwrap()
264 .extract::<Cow<'_, str>>()
265 .unwrap()
266 );
267 assert_eq!(
268 s,
269 s2.into_pyobject(py)
270 .unwrap()
271 .extract::<Cow<'_, str>>()
272 .unwrap()
273 );
274 assert_eq!(
275 s,
276 s.into_pyobject(py)
277 .unwrap()
278 .extract::<Cow<'_, str>>()
279 .unwrap()
280 );
281 })
282 }
283}