1 | use std::iter::FusedIterator; |
2 | |
3 | use crate::ffi::{self, Py_ssize_t}; |
4 | #[cfg (feature = "experimental-inspect" )] |
5 | use crate::inspect::types::TypeInfo; |
6 | use crate::internal_tricks::get_ssize_index; |
7 | use crate::types::PyList; |
8 | use crate::types::PySequence; |
9 | use crate::{ |
10 | exceptions, FromPyObject, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject, |
11 | }; |
12 | |
13 | #[inline ] |
14 | #[track_caller ] |
15 | fn new_from_iter( |
16 | py: Python<'_>, |
17 | elements: &mut dyn ExactSizeIterator<Item = PyObject>, |
18 | ) -> Py<PyTuple> { |
19 | unsafe { |
20 | // PyTuple_New checks for overflow but has a bad error message, so we check ourselves |
21 | let len: Py_ssize_t = elements |
22 | .len() |
23 | .try_into() |
24 | .expect("out of range integral type conversion attempted on `elements.len()`" ); |
25 | |
26 | let ptr = ffi::PyTuple_New(len); |
27 | |
28 | // - Panics if the ptr is null |
29 | // - Cleans up the tuple if `convert` or the asserts panic |
30 | let tup: Py<PyTuple> = Py::from_owned_ptr(py, ptr); |
31 | |
32 | let mut counter: Py_ssize_t = 0; |
33 | |
34 | for obj in elements.take(len as usize) { |
35 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
36 | ffi::PyTuple_SET_ITEM(ptr, counter, obj.into_ptr()); |
37 | #[cfg (any(Py_LIMITED_API, PyPy))] |
38 | ffi::PyTuple_SetItem(ptr, counter, obj.into_ptr()); |
39 | counter += 1; |
40 | } |
41 | |
42 | assert!(elements.next().is_none(), "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation." ); |
43 | assert_eq!(len, counter, "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation." ); |
44 | |
45 | tup |
46 | } |
47 | } |
48 | |
49 | /// Represents a Python `tuple` object. |
50 | /// |
51 | /// This type is immutable. |
52 | #[repr (transparent)] |
53 | pub struct PyTuple(PyAny); |
54 | |
55 | pyobject_native_type_core!(PyTuple, pyobject_native_static_type_object!(ffi::PyTuple_Type), #checkfunction=ffi::PyTuple_Check); |
56 | |
57 | impl PyTuple { |
58 | /// Constructs a new tuple with the given elements. |
59 | /// |
60 | /// If you want to create a [`PyTuple`] with elements of different or unknown types, or from an |
61 | /// iterable that doesn't implement [`ExactSizeIterator`], create a Rust tuple with the given |
62 | /// elements and convert it at once using `into_py`. |
63 | /// |
64 | /// # Examples |
65 | /// |
66 | /// ```rust |
67 | /// use pyo3::prelude::*; |
68 | /// use pyo3::types::PyTuple; |
69 | /// |
70 | /// # fn main() { |
71 | /// Python::with_gil(|py| { |
72 | /// let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5]; |
73 | /// let tuple: &PyTuple = PyTuple::new(py, elements); |
74 | /// assert_eq!(format!("{:?}" , tuple), "(0, 1, 2, 3, 4, 5)" ); |
75 | /// }); |
76 | /// # } |
77 | /// ``` |
78 | /// |
79 | /// # Panics |
80 | /// |
81 | /// This function will panic if `element`'s [`ExactSizeIterator`] implementation is incorrect. |
82 | /// All standard library structures implement this trait correctly, if they do, so calling this |
83 | /// function using [`Vec`]`<T>` or `&[T]` will always succeed. |
84 | #[track_caller ] |
85 | pub fn new<T, U>( |
86 | py: Python<'_>, |
87 | elements: impl IntoIterator<Item = T, IntoIter = U>, |
88 | ) -> &PyTuple |
89 | where |
90 | T: ToPyObject, |
91 | U: ExactSizeIterator<Item = T>, |
92 | { |
93 | let mut elements = elements.into_iter().map(|e| e.to_object(py)); |
94 | let tup = new_from_iter(py, &mut elements); |
95 | tup.into_ref(py) |
96 | } |
97 | |
98 | /// Constructs an empty tuple (on the Python side, a singleton object). |
99 | pub fn empty(py: Python<'_>) -> &PyTuple { |
100 | unsafe { py.from_owned_ptr(ffi::PyTuple_New(0)) } |
101 | } |
102 | |
103 | /// Gets the length of the tuple. |
104 | pub fn len(&self) -> usize { |
105 | unsafe { |
106 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
107 | let size = ffi::PyTuple_GET_SIZE(self.as_ptr()); |
108 | #[cfg (any(Py_LIMITED_API, PyPy))] |
109 | let size = ffi::PyTuple_Size(self.as_ptr()); |
110 | // non-negative Py_ssize_t should always fit into Rust uint |
111 | size as usize |
112 | } |
113 | } |
114 | |
115 | /// Checks if the tuple is empty. |
116 | pub fn is_empty(&self) -> bool { |
117 | self.len() == 0 |
118 | } |
119 | |
120 | /// Returns `self` cast as a `PySequence`. |
121 | pub fn as_sequence(&self) -> &PySequence { |
122 | unsafe { self.downcast_unchecked() } |
123 | } |
124 | |
125 | /// Takes the slice `self[low:high]` and returns it as a new tuple. |
126 | /// |
127 | /// Indices must be nonnegative, and out-of-range indices are clipped to |
128 | /// `self.len()`. |
129 | pub fn get_slice(&self, low: usize, high: usize) -> &PyTuple { |
130 | unsafe { |
131 | self.py().from_owned_ptr(ffi::PyTuple_GetSlice( |
132 | self.as_ptr(), |
133 | get_ssize_index(low), |
134 | get_ssize_index(high), |
135 | )) |
136 | } |
137 | } |
138 | |
139 | /// Gets the tuple item at the specified index. |
140 | /// # Example |
141 | /// ``` |
142 | /// use pyo3::{prelude::*, types::PyTuple}; |
143 | /// |
144 | /// # fn main() -> PyResult<()> { |
145 | /// Python::with_gil(|py| -> PyResult<()> { |
146 | /// let ob = (1, 2, 3).to_object(py); |
147 | /// let tuple: &PyTuple = ob.downcast(py).unwrap(); |
148 | /// let obj = tuple.get_item(0); |
149 | /// assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1); |
150 | /// Ok(()) |
151 | /// }) |
152 | /// # } |
153 | /// ``` |
154 | pub fn get_item(&self, index: usize) -> PyResult<&PyAny> { |
155 | unsafe { |
156 | let item = ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t); |
157 | self.py().from_borrowed_ptr_or_err(item) |
158 | } |
159 | } |
160 | |
161 | /// Gets the tuple item at the specified index. Undefined behavior on bad index. Use with caution. |
162 | /// |
163 | /// # Safety |
164 | /// |
165 | /// Caller must verify that the index is within the bounds of the tuple. |
166 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
167 | pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny { |
168 | let item = ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t); |
169 | self.py().from_borrowed_ptr(item) |
170 | } |
171 | |
172 | /// Returns `self` as a slice of objects. |
173 | #[cfg (not(Py_LIMITED_API))] |
174 | pub fn as_slice(&self) -> &[&PyAny] { |
175 | // This is safe because &PyAny has the same memory layout as *mut ffi::PyObject, |
176 | // and because tuples are immutable. |
177 | unsafe { |
178 | let ptr = self.as_ptr() as *mut ffi::PyTupleObject; |
179 | let slice = std::slice::from_raw_parts((*ptr).ob_item.as_ptr(), self.len()); |
180 | &*(slice as *const [*mut ffi::PyObject] as *const [&PyAny]) |
181 | } |
182 | } |
183 | |
184 | /// Determines if self contains `value`. |
185 | /// |
186 | /// This is equivalent to the Python expression `value in self`. |
187 | #[inline ] |
188 | pub fn contains<V>(&self, value: V) -> PyResult<bool> |
189 | where |
190 | V: ToPyObject, |
191 | { |
192 | self.as_sequence().contains(value) |
193 | } |
194 | |
195 | /// Returns the first index `i` for which `self[i] == value`. |
196 | /// |
197 | /// This is equivalent to the Python expression `self.index(value)`. |
198 | #[inline ] |
199 | pub fn index<V>(&self, value: V) -> PyResult<usize> |
200 | where |
201 | V: ToPyObject, |
202 | { |
203 | self.as_sequence().index(value) |
204 | } |
205 | |
206 | /// Returns an iterator over the tuple items. |
207 | pub fn iter(&self) -> PyTupleIterator<'_> { |
208 | PyTupleIterator { |
209 | tuple: self, |
210 | index: 0, |
211 | length: self.len(), |
212 | } |
213 | } |
214 | |
215 | /// Return a new list containing the contents of this tuple; equivalent to the Python expression `list(tuple)`. |
216 | /// |
217 | /// This method is equivalent to `self.as_sequence().to_list()` and faster than `PyList::new(py, self)`. |
218 | pub fn to_list(&self) -> &PyList { |
219 | self.as_sequence() |
220 | .to_list() |
221 | .expect("failed to convert tuple to list" ) |
222 | } |
223 | } |
224 | |
225 | index_impls!(PyTuple, "tuple" , PyTuple::len, PyTuple::get_slice); |
226 | |
227 | /// Used by `PyTuple::iter()`. |
228 | pub struct PyTupleIterator<'a> { |
229 | tuple: &'a PyTuple, |
230 | index: usize, |
231 | length: usize, |
232 | } |
233 | |
234 | impl<'a> PyTupleIterator<'a> { |
235 | unsafe fn get_item(&self, index: usize) -> &'a PyAny { |
236 | #[cfg (any(Py_LIMITED_API, PyPy))] |
237 | let item = self.tuple.get_item(index).expect("tuple.get failed" ); |
238 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
239 | let item: &PyAny = self.tuple.get_item_unchecked(index); |
240 | item |
241 | } |
242 | } |
243 | |
244 | impl<'a> Iterator for PyTupleIterator<'a> { |
245 | type Item = &'a PyAny; |
246 | |
247 | #[inline ] |
248 | fn next(&mut self) -> Option<Self::Item> { |
249 | if self.index < self.length { |
250 | let item: &PyAny = unsafe { self.get_item(self.index) }; |
251 | self.index += 1; |
252 | Some(item) |
253 | } else { |
254 | None |
255 | } |
256 | } |
257 | |
258 | #[inline ] |
259 | fn size_hint(&self) -> (usize, Option<usize>) { |
260 | let len: usize = self.len(); |
261 | (len, Some(len)) |
262 | } |
263 | } |
264 | |
265 | impl<'a> DoubleEndedIterator for PyTupleIterator<'a> { |
266 | #[inline ] |
267 | fn next_back(&mut self) -> Option<Self::Item> { |
268 | if self.index < self.length { |
269 | let item: &PyAny = unsafe { self.get_item(self.length - 1) }; |
270 | self.length -= 1; |
271 | Some(item) |
272 | } else { |
273 | None |
274 | } |
275 | } |
276 | } |
277 | |
278 | impl<'a> ExactSizeIterator for PyTupleIterator<'a> { |
279 | fn len(&self) -> usize { |
280 | self.length.saturating_sub(self.index) |
281 | } |
282 | } |
283 | |
284 | impl FusedIterator for PyTupleIterator<'_> {} |
285 | |
286 | impl<'a> IntoIterator for &'a PyTuple { |
287 | type Item = &'a PyAny; |
288 | type IntoIter = PyTupleIterator<'a>; |
289 | |
290 | fn into_iter(self) -> Self::IntoIter { |
291 | self.iter() |
292 | } |
293 | } |
294 | |
295 | #[cold ] |
296 | fn wrong_tuple_length(t: &PyTuple, expected_length: usize) -> PyErr { |
297 | let msg: String = format!( |
298 | "expected tuple of length {}, but got tuple of length {}" , |
299 | expected_length, |
300 | t.len() |
301 | ); |
302 | exceptions::PyValueError::new_err(args:msg) |
303 | } |
304 | |
305 | macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+} => { |
306 | impl <$($T: ToPyObject),+> ToPyObject for ($($T,)+) { |
307 | fn to_object(&self, py: Python<'_>) -> PyObject { |
308 | array_into_tuple(py, [$(self.$n.to_object(py)),+]).into() |
309 | } |
310 | } |
311 | impl <$($T: IntoPy<PyObject>),+> IntoPy<PyObject> for ($($T,)+) { |
312 | fn into_py(self, py: Python<'_>) -> PyObject { |
313 | array_into_tuple(py, [$(self.$n.into_py(py)),+]).into() |
314 | } |
315 | |
316 | #[cfg(feature = "experimental-inspect" )] |
317 | fn type_output() -> TypeInfo { |
318 | TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+])) |
319 | } |
320 | } |
321 | |
322 | impl <$($T: IntoPy<PyObject>),+> IntoPy<Py<PyTuple>> for ($($T,)+) { |
323 | fn into_py(self, py: Python<'_>) -> Py<PyTuple> { |
324 | array_into_tuple(py, [$(self.$n.into_py(py)),+]) |
325 | } |
326 | |
327 | #[cfg(feature = "experimental-inspect" )] |
328 | fn type_output() -> TypeInfo { |
329 | TypeInfo::Tuple(Some(vec![$( $T::type_output() ),+])) |
330 | } |
331 | } |
332 | |
333 | impl<'s, $($T: FromPyObject<'s>),+> FromPyObject<'s> for ($($T,)+) { |
334 | fn extract(obj: &'s PyAny) -> PyResult<Self> |
335 | { |
336 | let t: &PyTuple = obj.downcast()?; |
337 | if t.len() == $length { |
338 | #[cfg(any(Py_LIMITED_API, PyPy))] |
339 | return Ok(($(t.get_item($n)?.extract::<$T>()?,)+)); |
340 | |
341 | #[cfg(not(any(Py_LIMITED_API, PyPy)))] |
342 | unsafe {return Ok(($(t.get_item_unchecked($n).extract::<$T>()?,)+));} |
343 | } else { |
344 | Err(wrong_tuple_length(t, $length)) |
345 | } |
346 | } |
347 | |
348 | #[cfg(feature = "experimental-inspect" )] |
349 | fn type_input() -> TypeInfo { |
350 | TypeInfo::Tuple(Some(vec![$( $T::type_input() ),+])) |
351 | } |
352 | } |
353 | }); |
354 | |
355 | fn array_into_tuple<const N: usize>(py: Python<'_>, array: [PyObject; N]) -> Py<PyTuple> { |
356 | unsafe { |
357 | let ptr: *mut PyObject = ffi::PyTuple_New(N.try_into().expect(msg:"0 < N <= 12" )); |
358 | let tup: Py = Py::from_owned_ptr(py, ptr); |
359 | for (index: usize, obj: Py) in array.into_iter().enumerate() { |
360 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
361 | ffi::PyTuple_SET_ITEM(op:ptr, i:index as ffi::Py_ssize_t, v:obj.into_ptr()); |
362 | #[cfg (any(Py_LIMITED_API, PyPy))] |
363 | ffi::PyTuple_SetItem(ptr, index as ffi::Py_ssize_t, obj.into_ptr()); |
364 | } |
365 | tup |
366 | } |
367 | } |
368 | |
369 | tuple_conversion!(1, (ref0, 0, T0)); |
370 | tuple_conversion!(2, (ref0, 0, T0), (ref1, 1, T1)); |
371 | tuple_conversion!(3, (ref0, 0, T0), (ref1, 1, T1), (ref2, 2, T2)); |
372 | tuple_conversion!( |
373 | 4, |
374 | (ref0, 0, T0), |
375 | (ref1, 1, T1), |
376 | (ref2, 2, T2), |
377 | (ref3, 3, T3) |
378 | ); |
379 | tuple_conversion!( |
380 | 5, |
381 | (ref0, 0, T0), |
382 | (ref1, 1, T1), |
383 | (ref2, 2, T2), |
384 | (ref3, 3, T3), |
385 | (ref4, 4, T4) |
386 | ); |
387 | tuple_conversion!( |
388 | 6, |
389 | (ref0, 0, T0), |
390 | (ref1, 1, T1), |
391 | (ref2, 2, T2), |
392 | (ref3, 3, T3), |
393 | (ref4, 4, T4), |
394 | (ref5, 5, T5) |
395 | ); |
396 | tuple_conversion!( |
397 | 7, |
398 | (ref0, 0, T0), |
399 | (ref1, 1, T1), |
400 | (ref2, 2, T2), |
401 | (ref3, 3, T3), |
402 | (ref4, 4, T4), |
403 | (ref5, 5, T5), |
404 | (ref6, 6, T6) |
405 | ); |
406 | tuple_conversion!( |
407 | 8, |
408 | (ref0, 0, T0), |
409 | (ref1, 1, T1), |
410 | (ref2, 2, T2), |
411 | (ref3, 3, T3), |
412 | (ref4, 4, T4), |
413 | (ref5, 5, T5), |
414 | (ref6, 6, T6), |
415 | (ref7, 7, T7) |
416 | ); |
417 | tuple_conversion!( |
418 | 9, |
419 | (ref0, 0, T0), |
420 | (ref1, 1, T1), |
421 | (ref2, 2, T2), |
422 | (ref3, 3, T3), |
423 | (ref4, 4, T4), |
424 | (ref5, 5, T5), |
425 | (ref6, 6, T6), |
426 | (ref7, 7, T7), |
427 | (ref8, 8, T8) |
428 | ); |
429 | tuple_conversion!( |
430 | 10, |
431 | (ref0, 0, T0), |
432 | (ref1, 1, T1), |
433 | (ref2, 2, T2), |
434 | (ref3, 3, T3), |
435 | (ref4, 4, T4), |
436 | (ref5, 5, T5), |
437 | (ref6, 6, T6), |
438 | (ref7, 7, T7), |
439 | (ref8, 8, T8), |
440 | (ref9, 9, T9) |
441 | ); |
442 | tuple_conversion!( |
443 | 11, |
444 | (ref0, 0, T0), |
445 | (ref1, 1, T1), |
446 | (ref2, 2, T2), |
447 | (ref3, 3, T3), |
448 | (ref4, 4, T4), |
449 | (ref5, 5, T5), |
450 | (ref6, 6, T6), |
451 | (ref7, 7, T7), |
452 | (ref8, 8, T8), |
453 | (ref9, 9, T9), |
454 | (ref10, 10, T10) |
455 | ); |
456 | |
457 | tuple_conversion!( |
458 | 12, |
459 | (ref0, 0, T0), |
460 | (ref1, 1, T1), |
461 | (ref2, 2, T2), |
462 | (ref3, 3, T3), |
463 | (ref4, 4, T4), |
464 | (ref5, 5, T5), |
465 | (ref6, 6, T6), |
466 | (ref7, 7, T7), |
467 | (ref8, 8, T8), |
468 | (ref9, 9, T9), |
469 | (ref10, 10, T10), |
470 | (ref11, 11, T11) |
471 | ); |
472 | |
473 | #[cfg (test)] |
474 | mod tests { |
475 | use crate::types::{PyAny, PyList, PyTuple}; |
476 | use crate::{Python, ToPyObject}; |
477 | use std::collections::HashSet; |
478 | |
479 | #[test ] |
480 | fn test_new() { |
481 | Python::with_gil(|py| { |
482 | let ob = PyTuple::new(py, [1, 2, 3]); |
483 | assert_eq!(3, ob.len()); |
484 | let ob: &PyAny = ob.into(); |
485 | assert_eq!((1, 2, 3), ob.extract().unwrap()); |
486 | |
487 | let mut map = HashSet::new(); |
488 | map.insert(1); |
489 | map.insert(2); |
490 | PyTuple::new(py, map); |
491 | }); |
492 | } |
493 | |
494 | #[test ] |
495 | fn test_len() { |
496 | Python::with_gil(|py| { |
497 | let ob = (1, 2, 3).to_object(py); |
498 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
499 | assert_eq!(3, tuple.len()); |
500 | let ob: &PyAny = tuple.into(); |
501 | assert_eq!((1, 2, 3), ob.extract().unwrap()); |
502 | }); |
503 | } |
504 | |
505 | #[test ] |
506 | fn test_slice() { |
507 | Python::with_gil(|py| { |
508 | let tup = PyTuple::new(py, [2, 3, 5, 7]); |
509 | let slice = tup.get_slice(1, 3); |
510 | assert_eq!(2, slice.len()); |
511 | let slice = tup.get_slice(1, 7); |
512 | assert_eq!(3, slice.len()); |
513 | }); |
514 | } |
515 | |
516 | #[test ] |
517 | fn test_iter() { |
518 | Python::with_gil(|py| { |
519 | let ob = (1, 2, 3).to_object(py); |
520 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
521 | assert_eq!(3, tuple.len()); |
522 | let mut iter = tuple.iter(); |
523 | |
524 | assert_eq!(iter.size_hint(), (3, Some(3))); |
525 | |
526 | assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
527 | assert_eq!(iter.size_hint(), (2, Some(2))); |
528 | |
529 | assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
530 | assert_eq!(iter.size_hint(), (1, Some(1))); |
531 | |
532 | assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
533 | assert_eq!(iter.size_hint(), (0, Some(0))); |
534 | |
535 | assert!(iter.next().is_none()); |
536 | assert!(iter.next().is_none()); |
537 | }); |
538 | } |
539 | |
540 | #[test ] |
541 | fn test_iter_rev() { |
542 | Python::with_gil(|py| { |
543 | let ob = (1, 2, 3).to_object(py); |
544 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
545 | assert_eq!(3, tuple.len()); |
546 | let mut iter = tuple.iter().rev(); |
547 | |
548 | assert_eq!(iter.size_hint(), (3, Some(3))); |
549 | |
550 | assert_eq!(3_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
551 | assert_eq!(iter.size_hint(), (2, Some(2))); |
552 | |
553 | assert_eq!(2_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
554 | assert_eq!(iter.size_hint(), (1, Some(1))); |
555 | |
556 | assert_eq!(1_i32, iter.next().unwrap().extract::<'_, i32>().unwrap()); |
557 | assert_eq!(iter.size_hint(), (0, Some(0))); |
558 | |
559 | assert!(iter.next().is_none()); |
560 | assert!(iter.next().is_none()); |
561 | }); |
562 | } |
563 | |
564 | #[test ] |
565 | fn test_into_iter() { |
566 | Python::with_gil(|py| { |
567 | let ob = (1, 2, 3).to_object(py); |
568 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
569 | assert_eq!(3, tuple.len()); |
570 | |
571 | for (i, item) in tuple.iter().enumerate() { |
572 | assert_eq!(i + 1, item.extract::<'_, usize>().unwrap()); |
573 | } |
574 | }); |
575 | } |
576 | |
577 | #[test ] |
578 | #[cfg (not(Py_LIMITED_API))] |
579 | fn test_as_slice() { |
580 | Python::with_gil(|py| { |
581 | let ob = (1, 2, 3).to_object(py); |
582 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
583 | |
584 | let slice = tuple.as_slice(); |
585 | assert_eq!(3, slice.len()); |
586 | assert_eq!(1_i32, slice[0].extract::<'_, i32>().unwrap()); |
587 | assert_eq!(2_i32, slice[1].extract::<'_, i32>().unwrap()); |
588 | assert_eq!(3_i32, slice[2].extract::<'_, i32>().unwrap()); |
589 | }); |
590 | } |
591 | |
592 | #[test ] |
593 | fn test_tuple_lengths_up_to_12() { |
594 | Python::with_gil(|py| { |
595 | let t0 = (0,).to_object(py); |
596 | let t1 = (0, 1).to_object(py); |
597 | let t2 = (0, 1, 2).to_object(py); |
598 | let t3 = (0, 1, 2, 3).to_object(py); |
599 | let t4 = (0, 1, 2, 3, 4).to_object(py); |
600 | let t5 = (0, 1, 2, 3, 4, 5).to_object(py); |
601 | let t6 = (0, 1, 2, 3, 4, 5, 6).to_object(py); |
602 | let t7 = (0, 1, 2, 3, 4, 5, 6, 7).to_object(py); |
603 | let t8 = (0, 1, 2, 3, 4, 5, 6, 7, 8).to_object(py); |
604 | let t9 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).to_object(py); |
605 | let t10 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10).to_object(py); |
606 | let t11 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11).to_object(py); |
607 | |
608 | assert_eq!(t0.extract::<(i32,)>(py).unwrap(), (0,)); |
609 | assert_eq!(t1.extract::<(i32, i32)>(py).unwrap(), (0, 1,)); |
610 | assert_eq!(t2.extract::<(i32, i32, i32)>(py).unwrap(), (0, 1, 2,)); |
611 | assert_eq!( |
612 | t3.extract::<(i32, i32, i32, i32,)>(py).unwrap(), |
613 | (0, 1, 2, 3,) |
614 | ); |
615 | assert_eq!( |
616 | t4.extract::<(i32, i32, i32, i32, i32,)>(py).unwrap(), |
617 | (0, 1, 2, 3, 4,) |
618 | ); |
619 | assert_eq!( |
620 | t5.extract::<(i32, i32, i32, i32, i32, i32,)>(py).unwrap(), |
621 | (0, 1, 2, 3, 4, 5,) |
622 | ); |
623 | assert_eq!( |
624 | t6.extract::<(i32, i32, i32, i32, i32, i32, i32,)>(py) |
625 | .unwrap(), |
626 | (0, 1, 2, 3, 4, 5, 6,) |
627 | ); |
628 | assert_eq!( |
629 | t7.extract::<(i32, i32, i32, i32, i32, i32, i32, i32,)>(py) |
630 | .unwrap(), |
631 | (0, 1, 2, 3, 4, 5, 6, 7,) |
632 | ); |
633 | assert_eq!( |
634 | t8.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py) |
635 | .unwrap(), |
636 | (0, 1, 2, 3, 4, 5, 6, 7, 8,) |
637 | ); |
638 | assert_eq!( |
639 | t9.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py) |
640 | .unwrap(), |
641 | (0, 1, 2, 3, 4, 5, 6, 7, 8, 9,) |
642 | ); |
643 | assert_eq!( |
644 | t10.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py) |
645 | .unwrap(), |
646 | (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,) |
647 | ); |
648 | assert_eq!( |
649 | t11.extract::<(i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32,)>(py) |
650 | .unwrap(), |
651 | (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,) |
652 | ); |
653 | }) |
654 | } |
655 | |
656 | #[test ] |
657 | fn test_tuple_get_item_invalid_index() { |
658 | Python::with_gil(|py| { |
659 | let ob = (1, 2, 3).to_object(py); |
660 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
661 | let obj = tuple.get_item(5); |
662 | assert!(obj.is_err()); |
663 | assert_eq!( |
664 | obj.unwrap_err().to_string(), |
665 | "IndexError: tuple index out of range" |
666 | ); |
667 | }); |
668 | } |
669 | |
670 | #[test ] |
671 | fn test_tuple_get_item_sanity() { |
672 | Python::with_gil(|py| { |
673 | let ob = (1, 2, 3).to_object(py); |
674 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
675 | let obj = tuple.get_item(0); |
676 | assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 1); |
677 | }); |
678 | } |
679 | |
680 | #[cfg (not(any(Py_LIMITED_API, PyPy)))] |
681 | #[test ] |
682 | fn test_tuple_get_item_unchecked_sanity() { |
683 | Python::with_gil(|py| { |
684 | let ob = (1, 2, 3).to_object(py); |
685 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
686 | let obj = unsafe { tuple.get_item_unchecked(0) }; |
687 | assert_eq!(obj.extract::<i32>().unwrap(), 1); |
688 | }); |
689 | } |
690 | |
691 | #[test ] |
692 | fn test_tuple_index_trait() { |
693 | Python::with_gil(|py| { |
694 | let ob = (1, 2, 3).to_object(py); |
695 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
696 | assert_eq!(1, tuple[0].extract::<i32>().unwrap()); |
697 | assert_eq!(2, tuple[1].extract::<i32>().unwrap()); |
698 | assert_eq!(3, tuple[2].extract::<i32>().unwrap()); |
699 | }); |
700 | } |
701 | |
702 | #[test ] |
703 | #[should_panic ] |
704 | fn test_tuple_index_trait_panic() { |
705 | Python::with_gil(|py| { |
706 | let ob = (1, 2, 3).to_object(py); |
707 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
708 | let _ = &tuple[7]; |
709 | }); |
710 | } |
711 | |
712 | #[test ] |
713 | fn test_tuple_index_trait_ranges() { |
714 | Python::with_gil(|py| { |
715 | let ob = (1, 2, 3).to_object(py); |
716 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
717 | assert_eq!(vec![2, 3], tuple[1..3].extract::<Vec<i32>>().unwrap()); |
718 | assert_eq!( |
719 | Vec::<i32>::new(), |
720 | tuple[3..3].extract::<Vec<i32>>().unwrap() |
721 | ); |
722 | assert_eq!(vec![2, 3], tuple[1..].extract::<Vec<i32>>().unwrap()); |
723 | assert_eq!(Vec::<i32>::new(), tuple[3..].extract::<Vec<i32>>().unwrap()); |
724 | assert_eq!(vec![1, 2, 3], tuple[..].extract::<Vec<i32>>().unwrap()); |
725 | assert_eq!(vec![2, 3], tuple[1..=2].extract::<Vec<i32>>().unwrap()); |
726 | assert_eq!(vec![1, 2], tuple[..2].extract::<Vec<i32>>().unwrap()); |
727 | assert_eq!(vec![1, 2], tuple[..=1].extract::<Vec<i32>>().unwrap()); |
728 | }) |
729 | } |
730 | |
731 | #[test ] |
732 | #[should_panic = "range start index 5 out of range for tuple of length 3" ] |
733 | fn test_tuple_index_trait_range_panic_start() { |
734 | Python::with_gil(|py| { |
735 | let ob = (1, 2, 3).to_object(py); |
736 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
737 | tuple[5..10].extract::<Vec<i32>>().unwrap(); |
738 | }) |
739 | } |
740 | |
741 | #[test ] |
742 | #[should_panic = "range end index 10 out of range for tuple of length 3" ] |
743 | fn test_tuple_index_trait_range_panic_end() { |
744 | Python::with_gil(|py| { |
745 | let ob = (1, 2, 3).to_object(py); |
746 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
747 | tuple[1..10].extract::<Vec<i32>>().unwrap(); |
748 | }) |
749 | } |
750 | |
751 | #[test ] |
752 | #[should_panic = "slice index starts at 2 but ends at 1" ] |
753 | fn test_tuple_index_trait_range_panic_wrong_order() { |
754 | Python::with_gil(|py| { |
755 | let ob = (1, 2, 3).to_object(py); |
756 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
757 | #[allow (clippy::reversed_empty_ranges)] |
758 | tuple[2..1].extract::<Vec<i32>>().unwrap(); |
759 | }) |
760 | } |
761 | |
762 | #[test ] |
763 | #[should_panic = "range start index 8 out of range for tuple of length 3" ] |
764 | fn test_tuple_index_trait_range_from_panic() { |
765 | Python::with_gil(|py| { |
766 | let ob = (1, 2, 3).to_object(py); |
767 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
768 | tuple[8..].extract::<Vec<i32>>().unwrap(); |
769 | }) |
770 | } |
771 | |
772 | #[test ] |
773 | fn test_tuple_contains() { |
774 | Python::with_gil(|py| { |
775 | let ob = (1, 1, 2, 3, 5, 8).to_object(py); |
776 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
777 | assert_eq!(6, tuple.len()); |
778 | |
779 | let bad_needle = 7i32.to_object(py); |
780 | assert!(!tuple.contains(&bad_needle).unwrap()); |
781 | |
782 | let good_needle = 8i32.to_object(py); |
783 | assert!(tuple.contains(&good_needle).unwrap()); |
784 | |
785 | let type_coerced_needle = 8f32.to_object(py); |
786 | assert!(tuple.contains(&type_coerced_needle).unwrap()); |
787 | }); |
788 | } |
789 | |
790 | #[test ] |
791 | fn test_tuple_index() { |
792 | Python::with_gil(|py| { |
793 | let ob = (1, 1, 2, 3, 5, 8).to_object(py); |
794 | let tuple: &PyTuple = ob.downcast(py).unwrap(); |
795 | assert_eq!(0, tuple.index(1i32).unwrap()); |
796 | assert_eq!(2, tuple.index(2i32).unwrap()); |
797 | assert_eq!(3, tuple.index(3i32).unwrap()); |
798 | assert_eq!(4, tuple.index(5i32).unwrap()); |
799 | assert_eq!(5, tuple.index(8i32).unwrap()); |
800 | assert!(tuple.index(42i32).is_err()); |
801 | }); |
802 | } |
803 | |
804 | use std::ops::Range; |
805 | |
806 | // An iterator that lies about its `ExactSizeIterator` implementation. |
807 | // See https://github.com/PyO3/pyo3/issues/2118 |
808 | struct FaultyIter(Range<usize>, usize); |
809 | |
810 | impl Iterator for FaultyIter { |
811 | type Item = usize; |
812 | |
813 | fn next(&mut self) -> Option<Self::Item> { |
814 | self.0.next() |
815 | } |
816 | } |
817 | |
818 | impl ExactSizeIterator for FaultyIter { |
819 | fn len(&self) -> usize { |
820 | self.1 |
821 | } |
822 | } |
823 | |
824 | #[test ] |
825 | #[should_panic ( |
826 | expected = "Attempted to create PyTuple but `elements` was larger than reported by its `ExactSizeIterator` implementation." |
827 | )] |
828 | fn too_long_iterator() { |
829 | Python::with_gil(|py| { |
830 | let iter = FaultyIter(0..usize::MAX, 73); |
831 | let _tuple = PyTuple::new(py, iter); |
832 | }) |
833 | } |
834 | |
835 | #[test ] |
836 | #[should_panic ( |
837 | expected = "Attempted to create PyTuple but `elements` was smaller than reported by its `ExactSizeIterator` implementation." |
838 | )] |
839 | fn too_short_iterator() { |
840 | Python::with_gil(|py| { |
841 | let iter = FaultyIter(0..35, 73); |
842 | let _tuple = PyTuple::new(py, iter); |
843 | }) |
844 | } |
845 | |
846 | #[test ] |
847 | #[should_panic ( |
848 | expected = "out of range integral type conversion attempted on `elements.len()`" |
849 | )] |
850 | fn overflowing_size() { |
851 | Python::with_gil(|py| { |
852 | let iter = FaultyIter(0..0, usize::MAX); |
853 | |
854 | let _tuple = PyTuple::new(py, iter); |
855 | }) |
856 | } |
857 | |
858 | #[cfg (feature = "macros" )] |
859 | #[test ] |
860 | fn bad_clone_mem_leaks() { |
861 | use crate::{IntoPy, Py}; |
862 | use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; |
863 | |
864 | static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0); |
865 | |
866 | #[crate::pyclass ] |
867 | #[pyo3(crate = "crate" )] |
868 | struct Bad(usize); |
869 | |
870 | impl Clone for Bad { |
871 | fn clone(&self) -> Self { |
872 | // This panic should not lead to a memory leak |
873 | assert_ne!(self.0, 42); |
874 | NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst); |
875 | |
876 | Bad(self.0) |
877 | } |
878 | } |
879 | |
880 | impl Drop for Bad { |
881 | fn drop(&mut self) { |
882 | NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst); |
883 | } |
884 | } |
885 | |
886 | impl ToPyObject for Bad { |
887 | fn to_object(&self, py: Python<'_>) -> Py<PyAny> { |
888 | self.to_owned().into_py(py) |
889 | } |
890 | } |
891 | |
892 | struct FaultyIter(Range<usize>, usize); |
893 | |
894 | impl Iterator for FaultyIter { |
895 | type Item = Bad; |
896 | |
897 | fn next(&mut self) -> Option<Self::Item> { |
898 | self.0.next().map(|i| { |
899 | NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst); |
900 | Bad(i) |
901 | }) |
902 | } |
903 | } |
904 | |
905 | impl ExactSizeIterator for FaultyIter { |
906 | fn len(&self) -> usize { |
907 | self.1 |
908 | } |
909 | } |
910 | |
911 | Python::with_gil(|py| { |
912 | std::panic::catch_unwind(|| { |
913 | let iter = FaultyIter(0..50, 50); |
914 | let _tuple = PyTuple::new(py, iter); |
915 | }) |
916 | .unwrap_err(); |
917 | }); |
918 | |
919 | assert_eq!( |
920 | NEEDS_DESTRUCTING_COUNT.load(SeqCst), |
921 | 0, |
922 | "Some destructors did not run" |
923 | ); |
924 | } |
925 | |
926 | #[cfg (feature = "macros" )] |
927 | #[test ] |
928 | fn bad_clone_mem_leaks_2() { |
929 | use crate::{IntoPy, Py}; |
930 | use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; |
931 | |
932 | static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0); |
933 | |
934 | #[crate::pyclass ] |
935 | #[pyo3(crate = "crate" )] |
936 | struct Bad(usize); |
937 | |
938 | impl Clone for Bad { |
939 | fn clone(&self) -> Self { |
940 | // This panic should not lead to a memory leak |
941 | assert_ne!(self.0, 3); |
942 | NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst); |
943 | |
944 | Bad(self.0) |
945 | } |
946 | } |
947 | |
948 | impl Drop for Bad { |
949 | fn drop(&mut self) { |
950 | NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst); |
951 | } |
952 | } |
953 | |
954 | impl ToPyObject for Bad { |
955 | fn to_object(&self, py: Python<'_>) -> Py<PyAny> { |
956 | self.to_owned().into_py(py) |
957 | } |
958 | } |
959 | |
960 | let s = (Bad(1), Bad(2), Bad(3), Bad(4)); |
961 | NEEDS_DESTRUCTING_COUNT.store(4, SeqCst); |
962 | Python::with_gil(|py| { |
963 | std::panic::catch_unwind(|| { |
964 | let _tuple: Py<PyAny> = s.to_object(py); |
965 | }) |
966 | .unwrap_err(); |
967 | }); |
968 | drop(s); |
969 | |
970 | assert_eq!( |
971 | NEEDS_DESTRUCTING_COUNT.load(SeqCst), |
972 | 0, |
973 | "Some destructors did not run" |
974 | ); |
975 | } |
976 | |
977 | #[test ] |
978 | fn test_tuple_to_list() { |
979 | Python::with_gil(|py| { |
980 | let tuple = PyTuple::new(py, vec![1, 2, 3]); |
981 | let list = tuple.to_list(); |
982 | let list_expected = PyList::new(py, vec![1, 2, 3]); |
983 | assert!(list.eq(list_expected).unwrap()); |
984 | }) |
985 | } |
986 | } |
987 | |