Skip to main content

pyo3/types/
list.rs

1use crate::err::{self, PyResult};
2use crate::ffi::{self, Py_ssize_t};
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::internal_tricks::get_ssize_index;
5use crate::types::sequence::PySequenceMethods;
6use crate::types::{PySequence, PyTuple};
7use crate::{Borrowed, Bound, BoundObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, Python};
8use std::iter::FusedIterator;
9#[cfg(feature = "nightly")]
10use std::num::NonZero;
11
12/// Represents a Python `list`.
13///
14/// Values of this type are accessed via PyO3's smart pointers, e.g. as
15/// [`Py<PyList>`][crate::Py] or [`Bound<'py, PyList>`][Bound].
16///
17/// For APIs available on `list` objects, see the [`PyListMethods`] trait which is implemented for
18/// [`Bound<'py, PyList>`][Bound].
19#[repr(transparent)]
20pub struct PyList(PyAny);
21
22pyobject_native_type_core!(
23    PyList,
24    pyobject_native_static_type_object!(ffi::PyList_Type),
25    "builtins", "list",
26    #checkfunction=ffi::PyList_Check
27);
28
29#[cfg(Py_3_12)]
30impl crate::impl_::pyclass::PyClassBaseType for PyList {
31    type LayoutAsBase = crate::impl_::pycell::PyVariableClassObjectBase;
32    type BaseNativeType = Self;
33    type Initializer = crate::impl_::pyclass_init::PyNativeTypeInitializer<Self>;
34    type PyClassMutability = crate::pycell::impl_::ImmutableClass;
35    type Layout<T: crate::impl_::pyclass::PyClassImpl> =
36        crate::impl_::pycell::PyVariableClassObject<T>;
37}
38
39impl PyList {
40    /// Constructs a new list with the given elements.
41    ///
42    /// # Examples
43    ///
44    /// ```rust
45    /// use pyo3::prelude::*;
46    /// use pyo3::types::PyList;
47    ///
48    /// # fn main() -> PyResult<()> {
49    /// Python::attach(|py| {
50    ///     let elements: Vec<i32> = vec![0, 1, 2, 3, 4, 5];
51    ///     let list = PyList::new(py, elements)?;
52    ///     assert_eq!(format!("{:?}", list), "[0, 1, 2, 3, 4, 5]");
53    /// # Ok(())
54    /// })
55    /// # }
56    /// ```
57    ///
58    /// # Panics
59    ///
60    /// This function will panic if `element`'s [`Iterator::size_hint`] implementation is incorrect.
61    /// All standard library structures implement this trait correctly, if they do, so calling this
62    /// function with (for example) [`Vec`]`<T>` or `&[T]` will always succeed.
63    #[track_caller]
64    pub fn new<'py, T>(
65        py: Python<'py>,
66        elements: impl IntoIterator<Item = T>,
67    ) -> PyResult<Bound<'py, PyList>>
68    where
69        T: IntoPyObject<'py>,
70    {
71        let mut elements = elements.into_iter().map(|e| e.into_bound_py_any(py));
72        let (min_len, _) = elements.size_hint();
73
74        // PyList_New checks for overflow but has a bad error message, so we check ourselves
75        let len: Py_ssize_t = min_len
76            .try_into()
77            .expect("out of range integral type conversion attempted on `elements.len()`");
78
79        let list = unsafe { ffi::PyList_New(len).assume_owned(py).cast_into_unchecked() };
80
81        let count = (&mut elements)
82            .take(len as usize)
83            .try_fold(0, |count, item| unsafe {
84                #[cfg(not(Py_LIMITED_API))]
85                ffi::PyList_SET_ITEM(list.as_ptr(), count, item?.into_ptr());
86                #[cfg(Py_LIMITED_API)]
87                ffi::PyList_SetItem(list.as_ptr(), count, item?.into_ptr());
88                Ok::<_, PyErr>(count + 1)
89            })?;
90
91        assert_eq!(len, count, "Attempted to create PyList but `elements` was smaller than reported by its `size_hint` implementation.");
92
93        elements.try_for_each(|item| list.append(item?))?;
94
95        Ok(list)
96    }
97
98    /// Constructs a new empty list.
99    pub fn empty(py: Python<'_>) -> Bound<'_, PyList> {
100        unsafe { ffi::PyList_New(0).assume_owned(py).cast_into_unchecked() }
101    }
102}
103
104/// Implementation of functionality for [`PyList`].
105///
106/// These methods are defined for the `Bound<'py, PyList>` smart pointer, so to use method call
107/// syntax these methods are separated into a trait, because stable Rust does not yet support
108/// `arbitrary_self_types`.
109#[doc(alias = "PyList")]
110pub trait PyListMethods<'py>: crate::sealed::Sealed {
111    /// Returns the length of the list.
112    fn len(&self) -> usize;
113
114    /// Checks if the list is empty.
115    fn is_empty(&self) -> bool;
116
117    /// Returns `self` cast as a `PySequence`.
118    fn as_sequence(&self) -> &Bound<'py, PySequence>;
119
120    /// Returns `self` cast as a `PySequence`.
121    fn into_sequence(self) -> Bound<'py, PySequence>;
122
123    /// Gets the list item at the specified index.
124    /// # Example
125    /// ```
126    /// use pyo3::{prelude::*, types::PyList};
127    /// Python::attach(|py| {
128    ///     let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
129    ///     let obj = list.get_item(0);
130    ///     assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
131    /// });
132    /// ```
133    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
134
135    /// Gets the list item at the specified index. Undefined behavior on bad index, or if the list
136    /// contains a null pointer at the specified index. Use with caution.
137    ///
138    /// # Safety
139    ///
140    /// - Caller must verify that the index is within the bounds of the list.
141    /// - A null pointer is only legal in a list which is in the process of being initialized, callers
142    ///   can typically assume the list item is non-null unless they are knowingly filling an
143    ///   uninitialized list. (If a list were to contain a null pointer element, accessing it from Python
144    ///   typically causes a segfault.)
145    /// - On the free-threaded build, caller must verify they have exclusive access to the list
146    ///   via a lock or by holding the innermost critical section on the list.
147    #[cfg(not(Py_LIMITED_API))]
148    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny>;
149
150    /// Takes the slice `self[low:high]` and returns it as a new list.
151    ///
152    /// Indices must be nonnegative, and out-of-range indices are clipped to
153    /// `self.len()`.
154    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyList>;
155
156    /// Sets the item at the specified index.
157    ///
158    /// Raises `IndexError` if the index is out of range.
159    fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
160    where
161        I: IntoPyObject<'py>;
162
163    /// Deletes the `index`th element of self.
164    ///
165    /// This is equivalent to the Python statement `del self[i]`.
166    fn del_item(&self, index: usize) -> PyResult<()>;
167
168    /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
169    ///
170    /// This is equivalent to the Python statement `self[low:high] = v`.
171    fn set_slice(&self, low: usize, high: usize, seq: &Bound<'_, PyAny>) -> PyResult<()>;
172
173    /// Deletes the slice from `low` to `high` from `self`.
174    ///
175    /// This is equivalent to the Python statement `del self[low:high]`.
176    fn del_slice(&self, low: usize, high: usize) -> PyResult<()>;
177
178    /// Appends an item to the list.
179    fn append<I>(&self, item: I) -> PyResult<()>
180    where
181        I: IntoPyObject<'py>;
182
183    /// Inserts an item at the specified index.
184    ///
185    /// If `index >= self.len()`, inserts at the end.
186    fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
187    where
188        I: IntoPyObject<'py>;
189
190    /// Determines if self contains `value`.
191    ///
192    /// This is equivalent to the Python expression `value in self`.
193    fn contains<V>(&self, value: V) -> PyResult<bool>
194    where
195        V: IntoPyObject<'py>;
196
197    /// Returns the first index `i` for which `self[i] == value`.
198    ///
199    /// This is equivalent to the Python expression `self.index(value)`.
200    fn index<V>(&self, value: V) -> PyResult<usize>
201    where
202        V: IntoPyObject<'py>;
203
204    /// Returns an iterator over this list's items.
205    fn iter(&self) -> BoundListIterator<'py>;
206
207    /// Iterates over the contents of this list while holding a critical section on the list.
208    /// This is useful when the GIL is disabled and the list is shared between threads.
209    /// It is not guaranteed that the list will not be modified during iteration when the
210    /// closure calls arbitrary Python code that releases the critical section held by the
211    /// iterator. Otherwise, the list will not be modified during iteration.
212    ///
213    /// This is equivalent to for_each if the GIL is enabled.
214    fn locked_for_each<F>(&self, closure: F) -> PyResult<()>
215    where
216        F: Fn(Bound<'py, PyAny>) -> PyResult<()>;
217
218    /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
219    fn sort(&self) -> PyResult<()>;
220
221    /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
222    fn reverse(&self) -> PyResult<()>;
223
224    /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
225    ///
226    /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
227    fn to_tuple(&self) -> Bound<'py, PyTuple>;
228}
229
230impl<'py> PyListMethods<'py> for Bound<'py, PyList> {
231    /// Returns the length of the list.
232    fn len(&self) -> usize {
233        unsafe {
234            #[cfg(not(Py_LIMITED_API))]
235            let size = ffi::PyList_GET_SIZE(self.as_ptr());
236            #[cfg(Py_LIMITED_API)]
237            let size = ffi::PyList_Size(self.as_ptr());
238
239            // non-negative Py_ssize_t should always fit into Rust usize
240            size as usize
241        }
242    }
243
244    /// Checks if the list is empty.
245    fn is_empty(&self) -> bool {
246        self.len() == 0
247    }
248
249    /// Returns `self` cast as a `PySequence`.
250    fn as_sequence(&self) -> &Bound<'py, PySequence> {
251        unsafe { self.cast_unchecked() }
252    }
253
254    /// Returns `self` cast as a `PySequence`.
255    fn into_sequence(self) -> Bound<'py, PySequence> {
256        unsafe { self.cast_into_unchecked() }
257    }
258
259    /// Gets the list item at the specified index.
260    /// # Example
261    /// ```
262    /// use pyo3::{prelude::*, types::PyList};
263    /// Python::attach(|py| {
264    ///     let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
265    ///     let obj = list.get_item(0);
266    ///     assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
267    /// });
268    /// ```
269    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
270        unsafe {
271            ffi::compat::PyList_GetItemRef(self.as_ptr(), index as Py_ssize_t)
272                .assume_owned_or_err(self.py())
273        }
274    }
275
276    /// Gets the list item at the specified index. Undefined behavior on bad index. Use with caution.
277    ///
278    /// # Safety
279    ///
280    /// Caller must verify that the index is within the bounds of the list.
281    #[cfg(not(Py_LIMITED_API))]
282    unsafe fn get_item_unchecked(&self, index: usize) -> Bound<'py, PyAny> {
283        // SAFETY: caller has upheld the safety contract
284        unsafe {
285            ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t)
286                .assume_borrowed_unchecked(self.py())
287        }
288        // PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890).
289        .to_owned()
290    }
291
292    /// Takes the slice `self[low:high]` and returns it as a new list.
293    ///
294    /// Indices must be nonnegative, and out-of-range indices are clipped to
295    /// `self.len()`.
296    fn get_slice(&self, low: usize, high: usize) -> Bound<'py, PyList> {
297        unsafe {
298            ffi::PyList_GetSlice(self.as_ptr(), get_ssize_index(low), get_ssize_index(high))
299                .assume_owned(self.py())
300                .cast_into_unchecked()
301        }
302    }
303
304    /// Sets the item at the specified index.
305    ///
306    /// Raises `IndexError` if the index is out of range.
307    fn set_item<I>(&self, index: usize, item: I) -> PyResult<()>
308    where
309        I: IntoPyObject<'py>,
310    {
311        fn inner(list: &Bound<'_, PyList>, index: usize, item: Bound<'_, PyAny>) -> PyResult<()> {
312            err::error_on_minusone(list.py(), unsafe {
313                ffi::PyList_SetItem(list.as_ptr(), get_ssize_index(index), item.into_ptr())
314            })
315        }
316
317        let py = self.py();
318        inner(self, index, item.into_bound_py_any(py)?)
319    }
320
321    /// Deletes the `index`th element of self.
322    ///
323    /// This is equivalent to the Python statement `del self[i]`.
324    #[inline]
325    fn del_item(&self, index: usize) -> PyResult<()> {
326        self.as_sequence().del_item(index)
327    }
328
329    /// Assigns the sequence `seq` to the slice of `self` from `low` to `high`.
330    ///
331    /// This is equivalent to the Python statement `self[low:high] = v`.
332    #[inline]
333    fn set_slice(&self, low: usize, high: usize, seq: &Bound<'_, PyAny>) -> PyResult<()> {
334        err::error_on_minusone(self.py(), unsafe {
335            ffi::PyList_SetSlice(
336                self.as_ptr(),
337                get_ssize_index(low),
338                get_ssize_index(high),
339                seq.as_ptr(),
340            )
341        })
342    }
343
344    /// Deletes the slice from `low` to `high` from `self`.
345    ///
346    /// This is equivalent to the Python statement `del self[low:high]`.
347    #[inline]
348    fn del_slice(&self, low: usize, high: usize) -> PyResult<()> {
349        self.as_sequence().del_slice(low, high)
350    }
351
352    /// Appends an item to the list.
353    fn append<I>(&self, item: I) -> PyResult<()>
354    where
355        I: IntoPyObject<'py>,
356    {
357        fn inner(list: &Bound<'_, PyList>, item: Borrowed<'_, '_, PyAny>) -> PyResult<()> {
358            err::error_on_minusone(list.py(), unsafe {
359                ffi::PyList_Append(list.as_ptr(), item.as_ptr())
360            })
361        }
362
363        let py = self.py();
364        inner(
365            self,
366            item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
367        )
368    }
369
370    /// Inserts an item at the specified index.
371    ///
372    /// If `index >= self.len()`, inserts at the end.
373    fn insert<I>(&self, index: usize, item: I) -> PyResult<()>
374    where
375        I: IntoPyObject<'py>,
376    {
377        fn inner(
378            list: &Bound<'_, PyList>,
379            index: usize,
380            item: Borrowed<'_, '_, PyAny>,
381        ) -> PyResult<()> {
382            err::error_on_minusone(list.py(), unsafe {
383                ffi::PyList_Insert(list.as_ptr(), get_ssize_index(index), item.as_ptr())
384            })
385        }
386
387        let py = self.py();
388        inner(
389            self,
390            index,
391            item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
392        )
393    }
394
395    /// Determines if self contains `value`.
396    ///
397    /// This is equivalent to the Python expression `value in self`.
398    #[inline]
399    fn contains<V>(&self, value: V) -> PyResult<bool>
400    where
401        V: IntoPyObject<'py>,
402    {
403        self.as_sequence().contains(value)
404    }
405
406    /// Returns the first index `i` for which `self[i] == value`.
407    ///
408    /// This is equivalent to the Python expression `self.index(value)`.
409    #[inline]
410    fn index<V>(&self, value: V) -> PyResult<usize>
411    where
412        V: IntoPyObject<'py>,
413    {
414        self.as_sequence().index(value)
415    }
416
417    /// Returns an iterator over this list's items.
418    fn iter(&self) -> BoundListIterator<'py> {
419        BoundListIterator::new(self.clone())
420    }
421
422    /// Iterates over a list while holding a critical section, calling a closure on each item
423    fn locked_for_each<F>(&self, closure: F) -> PyResult<()>
424    where
425        F: Fn(Bound<'py, PyAny>) -> PyResult<()>,
426    {
427        crate::sync::critical_section::with_critical_section(self, || {
428            self.iter().try_for_each(closure)
429        })
430    }
431
432    /// Sorts the list in-place. Equivalent to the Python expression `l.sort()`.
433    fn sort(&self) -> PyResult<()> {
434        err::error_on_minusone(self.py(), unsafe { ffi::PyList_Sort(self.as_ptr()) })
435    }
436
437    /// Reverses the list in-place. Equivalent to the Python expression `l.reverse()`.
438    fn reverse(&self) -> PyResult<()> {
439        err::error_on_minusone(self.py(), unsafe { ffi::PyList_Reverse(self.as_ptr()) })
440    }
441
442    /// Return a new tuple containing the contents of the list; equivalent to the Python expression `tuple(list)`.
443    ///
444    /// This method is equivalent to `self.as_sequence().to_tuple()` and faster than `PyTuple::new(py, this_list)`.
445    fn to_tuple(&self) -> Bound<'py, PyTuple> {
446        unsafe {
447            ffi::PyList_AsTuple(self.as_ptr())
448                .assume_owned(self.py())
449                .cast_into_unchecked()
450        }
451    }
452}
453
454// New types for type checking when using BoundListIterator associated methods, like
455// BoundListIterator::next_unchecked.
456struct Index(usize);
457struct Length(usize);
458
459/// Used by `PyList::iter()`.
460pub struct BoundListIterator<'py> {
461    list: Bound<'py, PyList>,
462    index: Index,
463    length: Length,
464}
465
466impl<'py> BoundListIterator<'py> {
467    fn new(list: Bound<'py, PyList>) -> Self {
468        Self {
469            index: Index(0),
470            length: Length(list.len()),
471            list,
472        }
473    }
474
475    /// # Safety
476    ///
477    /// On the free-threaded build, caller must verify they have exclusive
478    /// access to the list by holding a lock or by holding the innermost
479    /// critical section on the list.
480    #[inline]
481    #[cfg(not(Py_LIMITED_API))]
482    #[deny(unsafe_op_in_unsafe_fn)]
483    unsafe fn next_unchecked(
484        index: &mut Index,
485        length: &mut Length,
486        list: &Bound<'py, PyList>,
487    ) -> Option<Bound<'py, PyAny>> {
488        let length = length.0.min(list.len());
489        let my_index = index.0;
490
491        if index.0 < length {
492            let item = unsafe { list.get_item_unchecked(my_index) };
493            index.0 += 1;
494            Some(item)
495        } else {
496            None
497        }
498    }
499
500    #[cfg(Py_LIMITED_API)]
501    fn next(
502        index: &mut Index,
503        length: &mut Length,
504        list: &Bound<'py, PyList>,
505    ) -> Option<Bound<'py, PyAny>> {
506        let length = length.0.min(list.len());
507        let my_index = index.0;
508
509        if index.0 < length {
510            let item = list.get_item(my_index).expect("get-item failed");
511            index.0 += 1;
512            Some(item)
513        } else {
514            None
515        }
516    }
517
518    #[inline]
519    #[cfg(not(feature = "nightly"))]
520    fn nth(
521        index: &mut Index,
522        length: &mut Length,
523        list: &Bound<'py, PyList>,
524        n: usize,
525    ) -> Option<Bound<'py, PyAny>> {
526        let length = length.0.min(list.len());
527        let target_index = index.0 + n;
528        if target_index < length {
529            let item = {
530                #[cfg(Py_LIMITED_API)]
531                {
532                    list.get_item(target_index).expect("get-item failed")
533                }
534
535                #[cfg(not(Py_LIMITED_API))]
536                {
537                    unsafe { list.get_item_unchecked(target_index) }
538                }
539            };
540            index.0 = target_index + 1;
541            Some(item)
542        } else {
543            None
544        }
545    }
546
547    /// # Safety
548    ///
549    /// On the free-threaded build, caller must verify they have exclusive
550    /// access to the list by holding a lock or by holding the innermost
551    /// critical section on the list.
552    #[inline]
553    #[cfg(not(Py_LIMITED_API))]
554    #[deny(unsafe_op_in_unsafe_fn)]
555    unsafe fn next_back_unchecked(
556        index: &mut Index,
557        length: &mut Length,
558        list: &Bound<'py, PyList>,
559    ) -> Option<Bound<'py, PyAny>> {
560        let current_length = length.0.min(list.len());
561
562        if index.0 < current_length {
563            let item = unsafe { list.get_item_unchecked(current_length - 1) };
564            length.0 = current_length - 1;
565            Some(item)
566        } else {
567            None
568        }
569    }
570
571    #[inline]
572    #[cfg(Py_LIMITED_API)]
573    fn next_back(
574        index: &mut Index,
575        length: &mut Length,
576        list: &Bound<'py, PyList>,
577    ) -> Option<Bound<'py, PyAny>> {
578        let current_length = (length.0).min(list.len());
579
580        if index.0 < current_length {
581            let item = list.get_item(current_length - 1).expect("get-item failed");
582            length.0 = current_length - 1;
583            Some(item)
584        } else {
585            None
586        }
587    }
588
589    #[inline]
590    #[cfg(not(feature = "nightly"))]
591    fn nth_back(
592        index: &mut Index,
593        length: &mut Length,
594        list: &Bound<'py, PyList>,
595        n: usize,
596    ) -> Option<Bound<'py, PyAny>> {
597        let length_size = length.0.min(list.len());
598        if index.0 + n < length_size {
599            let target_index = length_size - n - 1;
600            let item = {
601                #[cfg(not(Py_LIMITED_API))]
602                {
603                    unsafe { list.get_item_unchecked(target_index) }
604                }
605
606                #[cfg(Py_LIMITED_API)]
607                {
608                    list.get_item(target_index).expect("get-item failed")
609                }
610            };
611            length.0 = target_index;
612            Some(item)
613        } else {
614            None
615        }
616    }
617
618    #[allow(dead_code)]
619    fn with_critical_section<R>(
620        &mut self,
621        f: impl FnOnce(&mut Index, &mut Length, &Bound<'py, PyList>) -> R,
622    ) -> R {
623        let Self {
624            index,
625            length,
626            list,
627        } = self;
628        crate::sync::critical_section::with_critical_section(list, || f(index, length, list))
629    }
630}
631
632impl<'py> Iterator for BoundListIterator<'py> {
633    type Item = Bound<'py, PyAny>;
634
635    #[inline]
636    fn next(&mut self) -> Option<Self::Item> {
637        #[cfg(not(Py_LIMITED_API))]
638        {
639            self.with_critical_section(|index, length, list| unsafe {
640                Self::next_unchecked(index, length, list)
641            })
642        }
643        #[cfg(Py_LIMITED_API)]
644        {
645            let Self {
646                index,
647                length,
648                list,
649            } = self;
650            Self::next(index, length, list)
651        }
652    }
653
654    #[inline]
655    #[cfg(not(feature = "nightly"))]
656    fn nth(&mut self, n: usize) -> Option<Self::Item> {
657        self.with_critical_section(|index, length, list| Self::nth(index, length, list, n))
658    }
659
660    #[inline]
661    fn size_hint(&self) -> (usize, Option<usize>) {
662        let len = self.len();
663        (len, Some(len))
664    }
665
666    #[inline]
667    fn count(self) -> usize
668    where
669        Self: Sized,
670    {
671        self.len()
672    }
673
674    #[inline]
675    fn last(mut self) -> Option<Self::Item>
676    where
677        Self: Sized,
678    {
679        self.next_back()
680    }
681
682    #[inline]
683    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
684    fn fold<B, F>(mut self, init: B, mut f: F) -> B
685    where
686        Self: Sized,
687        F: FnMut(B, Self::Item) -> B,
688    {
689        self.with_critical_section(|index, length, list| {
690            let mut accum = init;
691            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
692                accum = f(accum, x);
693            }
694            accum
695        })
696    }
697
698    #[inline]
699    #[cfg(all(Py_GIL_DISABLED, feature = "nightly"))]
700    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
701    where
702        Self: Sized,
703        F: FnMut(B, Self::Item) -> R,
704        R: std::ops::Try<Output = B>,
705    {
706        self.with_critical_section(|index, length, list| {
707            let mut accum = init;
708            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
709                accum = f(accum, x)?
710            }
711            R::from_output(accum)
712        })
713    }
714
715    #[inline]
716    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
717    fn all<F>(&mut self, mut f: F) -> bool
718    where
719        Self: Sized,
720        F: FnMut(Self::Item) -> bool,
721    {
722        self.with_critical_section(|index, length, list| {
723            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
724                if !f(x) {
725                    return false;
726                }
727            }
728            true
729        })
730    }
731
732    #[inline]
733    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
734    fn any<F>(&mut self, mut f: F) -> bool
735    where
736        Self: Sized,
737        F: FnMut(Self::Item) -> bool,
738    {
739        self.with_critical_section(|index, length, list| {
740            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
741                if f(x) {
742                    return true;
743                }
744            }
745            false
746        })
747    }
748
749    #[inline]
750    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
751    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
752    where
753        Self: Sized,
754        P: FnMut(&Self::Item) -> bool,
755    {
756        self.with_critical_section(|index, length, list| {
757            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
758                if predicate(&x) {
759                    return Some(x);
760                }
761            }
762            None
763        })
764    }
765
766    #[inline]
767    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
768    fn find_map<B, F>(&mut self, mut f: F) -> Option<B>
769    where
770        Self: Sized,
771        F: FnMut(Self::Item) -> Option<B>,
772    {
773        self.with_critical_section(|index, length, list| {
774            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
775                if let found @ Some(_) = f(x) {
776                    return found;
777                }
778            }
779            None
780        })
781    }
782
783    #[inline]
784    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
785    fn position<P>(&mut self, mut predicate: P) -> Option<usize>
786    where
787        Self: Sized,
788        P: FnMut(Self::Item) -> bool,
789    {
790        self.with_critical_section(|index, length, list| {
791            let mut acc = 0;
792            while let Some(x) = unsafe { Self::next_unchecked(index, length, list) } {
793                if predicate(x) {
794                    return Some(acc);
795                }
796                acc += 1;
797            }
798            None
799        })
800    }
801
802    #[inline]
803    #[cfg(feature = "nightly")]
804    fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
805        self.with_critical_section(|index, length, list| {
806            let max_len = length.0.min(list.len());
807            let currently_at = index.0;
808            if currently_at >= max_len {
809                if n == 0 {
810                    return Ok(());
811                } else {
812                    return Err(unsafe { NonZero::new_unchecked(n) });
813                }
814            }
815
816            let items_left = max_len - currently_at;
817            if n <= items_left {
818                index.0 += n;
819                Ok(())
820            } else {
821                index.0 = max_len;
822                let remainder = n - items_left;
823                Err(unsafe { NonZero::new_unchecked(remainder) })
824            }
825        })
826    }
827}
828
829impl DoubleEndedIterator for BoundListIterator<'_> {
830    #[inline]
831    fn next_back(&mut self) -> Option<Self::Item> {
832        #[cfg(not(Py_LIMITED_API))]
833        {
834            self.with_critical_section(|index, length, list| unsafe {
835                Self::next_back_unchecked(index, length, list)
836            })
837        }
838        #[cfg(Py_LIMITED_API)]
839        {
840            let Self {
841                index,
842                length,
843                list,
844            } = self;
845            Self::next_back(index, length, list)
846        }
847    }
848
849    #[inline]
850    #[cfg(not(feature = "nightly"))]
851    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
852        self.with_critical_section(|index, length, list| Self::nth_back(index, length, list, n))
853    }
854
855    #[inline]
856    #[cfg(all(Py_GIL_DISABLED, not(feature = "nightly")))]
857    fn rfold<B, F>(mut self, init: B, mut f: F) -> B
858    where
859        Self: Sized,
860        F: FnMut(B, Self::Item) -> B,
861    {
862        self.with_critical_section(|index, length, list| {
863            let mut accum = init;
864            while let Some(x) = unsafe { Self::next_back_unchecked(index, length, list) } {
865                accum = f(accum, x);
866            }
867            accum
868        })
869    }
870
871    #[inline]
872    #[cfg(all(Py_GIL_DISABLED, feature = "nightly"))]
873    fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
874    where
875        Self: Sized,
876        F: FnMut(B, Self::Item) -> R,
877        R: std::ops::Try<Output = B>,
878    {
879        self.with_critical_section(|index, length, list| {
880            let mut accum = init;
881            while let Some(x) = unsafe { Self::next_back_unchecked(index, length, list) } {
882                accum = f(accum, x)?
883            }
884            R::from_output(accum)
885        })
886    }
887
888    #[inline]
889    #[cfg(feature = "nightly")]
890    fn advance_back_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
891        self.with_critical_section(|index, length, list| {
892            let max_len = length.0.min(list.len());
893            let currently_at = index.0;
894            if currently_at >= max_len {
895                if n == 0 {
896                    return Ok(());
897                } else {
898                    return Err(unsafe { NonZero::new_unchecked(n) });
899                }
900            }
901
902            let items_left = max_len - currently_at;
903            if n <= items_left {
904                length.0 = max_len - n;
905                Ok(())
906            } else {
907                length.0 = currently_at;
908                let remainder = n - items_left;
909                Err(unsafe { NonZero::new_unchecked(remainder) })
910            }
911        })
912    }
913}
914
915impl ExactSizeIterator for BoundListIterator<'_> {
916    fn len(&self) -> usize {
917        self.length.0.saturating_sub(self.index.0)
918    }
919}
920
921impl FusedIterator for BoundListIterator<'_> {}
922
923impl<'py> IntoIterator for Bound<'py, PyList> {
924    type Item = Bound<'py, PyAny>;
925    type IntoIter = BoundListIterator<'py>;
926
927    fn into_iter(self) -> Self::IntoIter {
928        BoundListIterator::new(self)
929    }
930}
931
932impl<'py> IntoIterator for &Bound<'py, PyList> {
933    type Item = Bound<'py, PyAny>;
934    type IntoIter = BoundListIterator<'py>;
935
936    fn into_iter(self) -> Self::IntoIter {
937        self.iter()
938    }
939}
940
941#[cfg(test)]
942mod tests {
943    use crate::types::any::PyAnyMethods;
944    use crate::types::list::PyListMethods;
945    use crate::types::sequence::PySequenceMethods;
946    use crate::types::{PyList, PyTuple};
947    use crate::{IntoPyObject, PyResult, Python};
948    #[cfg(feature = "nightly")]
949    use std::num::NonZero;
950
951    #[test]
952    fn test_new() {
953        Python::attach(|py| {
954            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
955            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
956            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
957            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
958            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
959        });
960    }
961
962    #[test]
963    fn test_len() {
964        Python::attach(|py| {
965            let list = PyList::new(py, [1, 2, 3, 4]).unwrap();
966            assert_eq!(4, list.len());
967        });
968    }
969
970    #[test]
971    fn test_get_item() {
972        Python::attach(|py| {
973            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
974            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
975            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
976            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
977            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
978        });
979    }
980
981    #[test]
982    fn test_get_slice() {
983        Python::attach(|py| {
984            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
985            let slice = list.get_slice(1, 3);
986            assert_eq!(2, slice.len());
987            let slice = list.get_slice(1, 7);
988            assert_eq!(3, slice.len());
989        });
990    }
991
992    #[test]
993    fn test_set_item() {
994        Python::attach(|py| {
995            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
996            let val = 42i32.into_pyobject(py).unwrap();
997            let val2 = 42i32.into_pyobject(py).unwrap();
998            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
999            list.set_item(0, val).unwrap();
1000            assert_eq!(42, list.get_item(0).unwrap().extract::<i32>().unwrap());
1001            assert!(list.set_item(10, val2).is_err());
1002        });
1003    }
1004
1005    #[test]
1006    fn test_set_item_refcnt() {
1007        Python::attach(|py| {
1008            let obj = py.eval(c"object()", None, None).unwrap();
1009            let cnt;
1010            {
1011                let v = vec![2];
1012                let ob = v.into_pyobject(py).unwrap();
1013                let list = ob.cast::<PyList>().unwrap();
1014                cnt = obj._get_refcnt();
1015                list.set_item(0, &obj).unwrap();
1016            }
1017
1018            assert_eq!(cnt, obj._get_refcnt());
1019        });
1020    }
1021
1022    #[test]
1023    fn test_insert() {
1024        Python::attach(|py| {
1025            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
1026            let val = 42i32.into_pyobject(py).unwrap();
1027            let val2 = 43i32.into_pyobject(py).unwrap();
1028            assert_eq!(4, list.len());
1029            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1030            list.insert(0, val).unwrap();
1031            list.insert(1000, val2).unwrap();
1032            assert_eq!(6, list.len());
1033            assert_eq!(42, list.get_item(0).unwrap().extract::<i32>().unwrap());
1034            assert_eq!(2, list.get_item(1).unwrap().extract::<i32>().unwrap());
1035            assert_eq!(43, list.get_item(5).unwrap().extract::<i32>().unwrap());
1036        });
1037    }
1038
1039    #[test]
1040    fn test_insert_refcnt() {
1041        Python::attach(|py| {
1042            let cnt;
1043            let obj = py.eval(c"object()", None, None).unwrap();
1044            {
1045                let list = PyList::empty(py);
1046                cnt = obj._get_refcnt();
1047                list.insert(0, &obj).unwrap();
1048            }
1049
1050            assert_eq!(cnt, obj._get_refcnt());
1051        });
1052    }
1053
1054    #[test]
1055    fn test_append() {
1056        Python::attach(|py| {
1057            let list = PyList::new(py, [2]).unwrap();
1058            list.append(3).unwrap();
1059            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1060            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1061        });
1062    }
1063
1064    #[test]
1065    fn test_append_refcnt() {
1066        Python::attach(|py| {
1067            let cnt;
1068            let obj = py.eval(c"object()", None, None).unwrap();
1069            {
1070                let list = PyList::empty(py);
1071                cnt = obj._get_refcnt();
1072                list.append(&obj).unwrap();
1073            }
1074            assert_eq!(cnt, obj._get_refcnt());
1075        });
1076    }
1077
1078    #[test]
1079    fn test_iter() {
1080        Python::attach(|py| {
1081            let v = vec![2, 3, 5, 7];
1082            let list = PyList::new(py, &v).unwrap();
1083            let mut idx = 0;
1084            for el in list {
1085                assert_eq!(v[idx], el.extract::<i32>().unwrap());
1086                idx += 1;
1087            }
1088            assert_eq!(idx, v.len());
1089        });
1090    }
1091
1092    #[test]
1093    fn test_iter_size_hint() {
1094        Python::attach(|py| {
1095            let v = vec![2, 3, 5, 7];
1096            let ob = (&v).into_pyobject(py).unwrap();
1097            let list = ob.cast::<PyList>().unwrap();
1098
1099            let mut iter = list.iter();
1100            assert_eq!(iter.size_hint(), (v.len(), Some(v.len())));
1101            iter.next();
1102            assert_eq!(iter.size_hint(), (v.len() - 1, Some(v.len() - 1)));
1103
1104            // Exhaust iterator.
1105            for _ in &mut iter {}
1106
1107            assert_eq!(iter.size_hint(), (0, Some(0)));
1108        });
1109    }
1110
1111    #[test]
1112    fn test_iter_rev() {
1113        Python::attach(|py| {
1114            let v = vec![2, 3, 5, 7];
1115            let ob = v.into_pyobject(py).unwrap();
1116            let list = ob.cast::<PyList>().unwrap();
1117
1118            let mut iter = list.iter().rev();
1119
1120            assert_eq!(iter.size_hint(), (4, Some(4)));
1121
1122            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 7);
1123            assert_eq!(iter.size_hint(), (3, Some(3)));
1124
1125            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 5);
1126            assert_eq!(iter.size_hint(), (2, Some(2)));
1127
1128            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
1129            assert_eq!(iter.size_hint(), (1, Some(1)));
1130
1131            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 2);
1132            assert_eq!(iter.size_hint(), (0, Some(0)));
1133
1134            assert!(iter.next().is_none());
1135            assert!(iter.next().is_none());
1136        });
1137    }
1138
1139    #[test]
1140    fn test_iter_all() {
1141        Python::attach(|py| {
1142            let list = PyList::new(py, [true, true, true]).unwrap();
1143            assert!(list.iter().all(|x| x.extract::<bool>().unwrap()));
1144
1145            let list = PyList::new(py, [true, false, true]).unwrap();
1146            assert!(!list.iter().all(|x| x.extract::<bool>().unwrap()));
1147        });
1148    }
1149
1150    #[test]
1151    fn test_iter_any() {
1152        Python::attach(|py| {
1153            let list = PyList::new(py, [true, true, true]).unwrap();
1154            assert!(list.iter().any(|x| x.extract::<bool>().unwrap()));
1155
1156            let list = PyList::new(py, [true, false, true]).unwrap();
1157            assert!(list.iter().any(|x| x.extract::<bool>().unwrap()));
1158
1159            let list = PyList::new(py, [false, false, false]).unwrap();
1160            assert!(!list.iter().any(|x| x.extract::<bool>().unwrap()));
1161        });
1162    }
1163
1164    #[test]
1165    fn test_iter_find() {
1166        Python::attach(|py: Python<'_>| {
1167            let list = PyList::new(py, ["hello", "world"]).unwrap();
1168            assert_eq!(
1169                Some("world".to_string()),
1170                list.iter()
1171                    .find(|v| v.extract::<String>().unwrap() == "world")
1172                    .map(|v| v.extract::<String>().unwrap())
1173            );
1174            assert_eq!(
1175                None,
1176                list.iter()
1177                    .find(|v| v.extract::<String>().unwrap() == "foobar")
1178                    .map(|v| v.extract::<String>().unwrap())
1179            );
1180        });
1181    }
1182
1183    #[test]
1184    fn test_iter_position() {
1185        Python::attach(|py: Python<'_>| {
1186            let list = PyList::new(py, ["hello", "world"]).unwrap();
1187            assert_eq!(
1188                Some(1),
1189                list.iter()
1190                    .position(|v| v.extract::<String>().unwrap() == "world")
1191            );
1192            assert_eq!(
1193                None,
1194                list.iter()
1195                    .position(|v| v.extract::<String>().unwrap() == "foobar")
1196            );
1197        });
1198    }
1199
1200    #[test]
1201    fn test_iter_fold() {
1202        Python::attach(|py: Python<'_>| {
1203            let list = PyList::new(py, [1, 2, 3]).unwrap();
1204            let sum = list
1205                .iter()
1206                .fold(0, |acc, v| acc + v.extract::<usize>().unwrap());
1207            assert_eq!(sum, 6);
1208        });
1209    }
1210
1211    #[test]
1212    fn test_iter_fold_out_of_bounds() {
1213        Python::attach(|py: Python<'_>| {
1214            let list = PyList::new(py, [1, 2, 3]).unwrap();
1215            let sum = list.iter().fold(0, |_, _| {
1216                // clear the list to create a pathological fold operation
1217                // that mutates the list as it processes it
1218                for _ in 0..3 {
1219                    list.del_item(0).unwrap();
1220                }
1221                -5
1222            });
1223            assert_eq!(sum, -5);
1224            assert_eq!(list.len(), 0);
1225        });
1226    }
1227
1228    #[test]
1229    fn test_iter_rfold() {
1230        Python::attach(|py: Python<'_>| {
1231            let list = PyList::new(py, [1, 2, 3]).unwrap();
1232            let sum = list
1233                .iter()
1234                .rfold(0, |acc, v| acc + v.extract::<usize>().unwrap());
1235            assert_eq!(sum, 6);
1236        });
1237    }
1238
1239    #[test]
1240    fn test_iter_try_fold() {
1241        Python::attach(|py: Python<'_>| {
1242            let list = PyList::new(py, [1, 2, 3]).unwrap();
1243            let sum = list
1244                .iter()
1245                .try_fold(0, |acc, v| PyResult::Ok(acc + v.extract::<usize>()?))
1246                .unwrap();
1247            assert_eq!(sum, 6);
1248
1249            let list = PyList::new(py, ["foo", "bar"]).unwrap();
1250            assert!(list
1251                .iter()
1252                .try_fold(0, |acc, v| PyResult::Ok(acc + v.extract::<usize>()?))
1253                .is_err());
1254        });
1255    }
1256
1257    #[test]
1258    fn test_iter_try_rfold() {
1259        Python::attach(|py: Python<'_>| {
1260            let list = PyList::new(py, [1, 2, 3]).unwrap();
1261            let sum = list
1262                .iter()
1263                .try_rfold(0, |acc, v| PyResult::Ok(acc + v.extract::<usize>()?))
1264                .unwrap();
1265            assert_eq!(sum, 6);
1266
1267            let list = PyList::new(py, ["foo", "bar"]).unwrap();
1268            assert!(list
1269                .iter()
1270                .try_rfold(0, |acc, v| PyResult::Ok(acc + v.extract::<usize>()?))
1271                .is_err());
1272        });
1273    }
1274
1275    #[test]
1276    fn test_into_iter() {
1277        Python::attach(|py| {
1278            let list = PyList::new(py, [1, 2, 3, 4]).unwrap();
1279            for (i, item) in list.iter().enumerate() {
1280                assert_eq!((i + 1) as i32, item.extract::<i32>().unwrap());
1281            }
1282        });
1283    }
1284
1285    #[test]
1286    fn test_into_iter_bound() {
1287        use crate::types::any::PyAnyMethods;
1288
1289        Python::attach(|py| {
1290            let list = PyList::new(py, [1, 2, 3, 4]).unwrap();
1291            let mut items = vec![];
1292            for item in &list {
1293                items.push(item.extract::<i32>().unwrap());
1294            }
1295            assert_eq!(items, vec![1, 2, 3, 4]);
1296        });
1297    }
1298
1299    #[test]
1300    fn test_as_sequence() {
1301        Python::attach(|py| {
1302            let list = PyList::new(py, [1, 2, 3, 4]).unwrap();
1303
1304            assert_eq!(list.as_sequence().len().unwrap(), 4);
1305            assert_eq!(
1306                list.as_sequence()
1307                    .get_item(1)
1308                    .unwrap()
1309                    .extract::<i32>()
1310                    .unwrap(),
1311                2
1312            );
1313        });
1314    }
1315
1316    #[test]
1317    fn test_into_sequence() {
1318        Python::attach(|py| {
1319            let list = PyList::new(py, [1, 2, 3, 4]).unwrap();
1320
1321            let sequence = list.into_sequence();
1322
1323            assert_eq!(sequence.len().unwrap(), 4);
1324            assert_eq!(sequence.get_item(1).unwrap().extract::<i32>().unwrap(), 2);
1325        });
1326    }
1327
1328    #[test]
1329    fn test_extract() {
1330        Python::attach(|py| {
1331            let v = vec![2, 3, 5, 7];
1332            let list = PyList::new(py, &v).unwrap();
1333            let v2 = list.as_any().extract::<Vec<i32>>().unwrap();
1334            assert_eq!(v, v2);
1335        });
1336    }
1337
1338    #[test]
1339    fn test_sort() {
1340        Python::attach(|py| {
1341            let v = vec![7, 3, 2, 5];
1342            let list = PyList::new(py, &v).unwrap();
1343            assert_eq!(7, list.get_item(0).unwrap().extract::<i32>().unwrap());
1344            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1345            assert_eq!(2, list.get_item(2).unwrap().extract::<i32>().unwrap());
1346            assert_eq!(5, list.get_item(3).unwrap().extract::<i32>().unwrap());
1347            list.sort().unwrap();
1348            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1349            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1350            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
1351            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
1352        });
1353    }
1354
1355    #[test]
1356    fn test_reverse() {
1357        Python::attach(|py| {
1358            let v = vec![2, 3, 5, 7];
1359            let list = PyList::new(py, &v).unwrap();
1360            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1361            assert_eq!(3, list.get_item(1).unwrap().extract::<i32>().unwrap());
1362            assert_eq!(5, list.get_item(2).unwrap().extract::<i32>().unwrap());
1363            assert_eq!(7, list.get_item(3).unwrap().extract::<i32>().unwrap());
1364            list.reverse().unwrap();
1365            assert_eq!(7, list.get_item(0).unwrap().extract::<i32>().unwrap());
1366            assert_eq!(5, list.get_item(1).unwrap().extract::<i32>().unwrap());
1367            assert_eq!(3, list.get_item(2).unwrap().extract::<i32>().unwrap());
1368            assert_eq!(2, list.get_item(3).unwrap().extract::<i32>().unwrap());
1369        });
1370    }
1371
1372    #[test]
1373    fn test_array_into_pyobject() {
1374        Python::attach(|py| {
1375            let array = [1, 2].into_pyobject(py).unwrap();
1376            let list = array.cast::<PyList>().unwrap();
1377            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1378            assert_eq!(2, list.get_item(1).unwrap().extract::<i32>().unwrap());
1379        });
1380    }
1381
1382    #[test]
1383    fn test_list_get_item_invalid_index() {
1384        Python::attach(|py| {
1385            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
1386            let obj = list.get_item(5);
1387            assert!(obj.is_err());
1388            assert_eq!(
1389                obj.unwrap_err().to_string(),
1390                "IndexError: list index out of range"
1391            );
1392        });
1393    }
1394
1395    #[test]
1396    fn test_list_get_item_sanity() {
1397        Python::attach(|py| {
1398            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
1399            let obj = list.get_item(0);
1400            assert_eq!(obj.unwrap().extract::<i32>().unwrap(), 2);
1401        });
1402    }
1403
1404    #[cfg(not(Py_LIMITED_API))]
1405    #[test]
1406    fn test_list_get_item_unchecked_sanity() {
1407        Python::attach(|py| {
1408            let list = PyList::new(py, [2, 3, 5, 7]).unwrap();
1409            let obj = unsafe { list.get_item_unchecked(0) };
1410            assert_eq!(obj.extract::<i32>().unwrap(), 2);
1411        });
1412    }
1413
1414    #[test]
1415    fn test_list_del_item() {
1416        Python::attach(|py| {
1417            let list = PyList::new(py, [1, 1, 2, 3, 5, 8]).unwrap();
1418            assert!(list.del_item(10).is_err());
1419            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1420            assert!(list.del_item(0).is_ok());
1421            assert_eq!(1, list.get_item(0).unwrap().extract::<i32>().unwrap());
1422            assert!(list.del_item(0).is_ok());
1423            assert_eq!(2, list.get_item(0).unwrap().extract::<i32>().unwrap());
1424            assert!(list.del_item(0).is_ok());
1425            assert_eq!(3, list.get_item(0).unwrap().extract::<i32>().unwrap());
1426            assert!(list.del_item(0).is_ok());
1427            assert_eq!(5, list.get_item(0).unwrap().extract::<i32>().unwrap());
1428            assert!(list.del_item(0).is_ok());
1429            assert_eq!(8, list.get_item(0).unwrap().extract::<i32>().unwrap());
1430            assert!(list.del_item(0).is_ok());
1431            assert_eq!(0, list.len());
1432            assert!(list.del_item(0).is_err());
1433        });
1434    }
1435
1436    #[test]
1437    fn test_list_set_slice() {
1438        Python::attach(|py| {
1439            let list = PyList::new(py, [1, 1, 2, 3, 5, 8]).unwrap();
1440            let ins = PyList::new(py, [7, 4]).unwrap();
1441            list.set_slice(1, 4, &ins).unwrap();
1442            assert_eq!([1, 7, 4, 5, 8], list.extract::<[i32; 5]>().unwrap());
1443            list.set_slice(3, 100, &PyList::empty(py)).unwrap();
1444            assert_eq!([1, 7, 4], list.extract::<[i32; 3]>().unwrap());
1445        });
1446    }
1447
1448    #[test]
1449    fn test_list_del_slice() {
1450        Python::attach(|py| {
1451            let list = PyList::new(py, [1, 1, 2, 3, 5, 8]).unwrap();
1452            list.del_slice(1, 4).unwrap();
1453            assert_eq!([1, 5, 8], list.extract::<[i32; 3]>().unwrap());
1454            list.del_slice(1, 100).unwrap();
1455            assert_eq!([1], list.extract::<[i32; 1]>().unwrap());
1456        });
1457    }
1458
1459    #[test]
1460    fn test_list_contains() {
1461        Python::attach(|py| {
1462            let list = PyList::new(py, [1, 1, 2, 3, 5, 8]).unwrap();
1463            assert_eq!(6, list.len());
1464
1465            let bad_needle = 7i32.into_pyobject(py).unwrap();
1466            assert!(!list.contains(&bad_needle).unwrap());
1467
1468            let good_needle = 8i32.into_pyobject(py).unwrap();
1469            assert!(list.contains(&good_needle).unwrap());
1470
1471            let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
1472            assert!(list.contains(&type_coerced_needle).unwrap());
1473        });
1474    }
1475
1476    #[test]
1477    fn test_list_index() {
1478        Python::attach(|py| {
1479            let list = PyList::new(py, [1, 1, 2, 3, 5, 8]).unwrap();
1480            assert_eq!(0, list.index(1i32).unwrap());
1481            assert_eq!(2, list.index(2i32).unwrap());
1482            assert_eq!(3, list.index(3i32).unwrap());
1483            assert_eq!(4, list.index(5i32).unwrap());
1484            assert_eq!(5, list.index(8i32).unwrap());
1485            assert!(list.index(42i32).is_err());
1486        });
1487    }
1488
1489    use std::ops::Range;
1490
1491    // An iterator that lies about its `size_hint` implementation.
1492    // See https://github.com/PyO3/pyo3/issues/2118
1493    struct FaultyIter(Range<usize>, usize);
1494
1495    impl Iterator for FaultyIter {
1496        type Item = usize;
1497
1498        fn next(&mut self) -> Option<Self::Item> {
1499            self.0.next()
1500        }
1501
1502        fn size_hint(&self) -> (usize, Option<usize>) {
1503            (self.1, Some(self.1))
1504        }
1505    }
1506
1507    #[test]
1508    #[should_panic(
1509        expected = "Attempted to create PyList but `elements` was smaller than reported by its `size_hint` implementation."
1510    )]
1511    fn too_short_iterator() {
1512        Python::attach(|py| {
1513            let iter = FaultyIter(0..35, 73);
1514            let _list = PyList::new(py, iter).unwrap();
1515        })
1516    }
1517
1518    #[test]
1519    #[should_panic(
1520        expected = "out of range integral type conversion attempted on `elements.len()`"
1521    )]
1522    fn overflowing_size() {
1523        Python::attach(|py| {
1524            let iter = FaultyIter(0..0, usize::MAX);
1525
1526            let _list = PyList::new(py, iter).unwrap();
1527        })
1528    }
1529
1530    #[test]
1531    #[cfg(panic = "unwind")]
1532    fn bad_intopyobject_doesnt_cause_leaks() {
1533        use crate::types::PyInt;
1534        use std::convert::Infallible;
1535        use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
1536        static NEEDS_DESTRUCTING_COUNT: AtomicUsize = AtomicUsize::new(0);
1537
1538        struct Bad(usize);
1539
1540        impl Drop for Bad {
1541            fn drop(&mut self) {
1542                NEEDS_DESTRUCTING_COUNT.fetch_sub(1, SeqCst);
1543            }
1544        }
1545
1546        impl<'py> IntoPyObject<'py> for Bad {
1547            type Target = PyInt;
1548            type Output = crate::Bound<'py, Self::Target>;
1549            type Error = Infallible;
1550
1551            fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
1552                // This panic should not lead to a memory leak
1553                assert_ne!(self.0, 42);
1554                self.0.into_pyobject(py)
1555            }
1556        }
1557
1558        struct FaultyIter(Range<usize>, usize);
1559
1560        impl Iterator for FaultyIter {
1561            type Item = Bad;
1562
1563            fn next(&mut self) -> Option<Self::Item> {
1564                self.0.next().map(|i| {
1565                    NEEDS_DESTRUCTING_COUNT.fetch_add(1, SeqCst);
1566                    Bad(i)
1567                })
1568            }
1569
1570            fn size_hint(&self) -> (usize, Option<usize>) {
1571                (self.1, Some(self.1))
1572            }
1573        }
1574
1575        Python::attach(|py| {
1576            std::panic::catch_unwind(|| {
1577                let iter = FaultyIter(0..50, 50);
1578                let _list = PyList::new(py, iter).unwrap();
1579            })
1580            .unwrap_err();
1581        });
1582
1583        assert_eq!(
1584            NEEDS_DESTRUCTING_COUNT.load(SeqCst),
1585            0,
1586            "Some destructors did not run"
1587        );
1588    }
1589
1590    #[test]
1591    fn test_list_to_tuple() {
1592        Python::attach(|py| {
1593            let list = PyList::new(py, vec![1, 2, 3]).unwrap();
1594            let tuple = list.to_tuple();
1595            let tuple_expected = PyTuple::new(py, vec![1, 2, 3]).unwrap();
1596            assert!(tuple.eq(tuple_expected).unwrap());
1597        })
1598    }
1599
1600    #[test]
1601    fn test_iter_nth() {
1602        Python::attach(|py| {
1603            let v = vec![6, 7, 8, 9, 10];
1604            let ob = (&v).into_pyobject(py).unwrap();
1605            let list = ob.cast::<PyList>().unwrap();
1606
1607            let mut iter = list.iter();
1608            iter.next();
1609            assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 8);
1610            assert_eq!(iter.nth(1).unwrap().extract::<i32>().unwrap(), 10);
1611            assert!(iter.nth(1).is_none());
1612
1613            let v: Vec<i32> = vec![];
1614            let ob = (&v).into_pyobject(py).unwrap();
1615            let list = ob.cast::<PyList>().unwrap();
1616
1617            let mut iter = list.iter();
1618            iter.next();
1619            assert!(iter.nth(1).is_none());
1620
1621            let v = vec![1, 2, 3];
1622            let ob = (&v).into_pyobject(py).unwrap();
1623            let list = ob.cast::<PyList>().unwrap();
1624
1625            let mut iter = list.iter();
1626            assert!(iter.nth(10).is_none());
1627
1628            let v = vec![6, 7, 8, 9, 10];
1629            let ob = (&v).into_pyobject(py).unwrap();
1630            let list = ob.cast::<PyList>().unwrap();
1631            let mut iter = list.iter();
1632            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 6);
1633            assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 9);
1634            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 10);
1635
1636            let mut iter = list.iter();
1637            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 9);
1638            assert_eq!(iter.nth(2).unwrap().extract::<i32>().unwrap(), 8);
1639            assert!(iter.next().is_none());
1640        });
1641    }
1642
1643    #[test]
1644    fn test_iter_nth_back() {
1645        Python::attach(|py| {
1646            let v = vec![1, 2, 3, 4, 5];
1647            let ob = (&v).into_pyobject(py).unwrap();
1648            let list = ob.cast::<PyList>().unwrap();
1649
1650            let mut iter = list.iter();
1651            assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 5);
1652            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1653            assert!(iter.nth_back(2).is_none());
1654
1655            let v: Vec<i32> = vec![];
1656            let ob = (&v).into_pyobject(py).unwrap();
1657            let list = ob.cast::<PyList>().unwrap();
1658
1659            let mut iter = list.iter();
1660            assert!(iter.nth_back(0).is_none());
1661            assert!(iter.nth_back(1).is_none());
1662
1663            let v = vec![1, 2, 3];
1664            let ob = (&v).into_pyobject(py).unwrap();
1665            let list = ob.cast::<PyList>().unwrap();
1666
1667            let mut iter = list.iter();
1668            assert!(iter.nth_back(5).is_none());
1669
1670            let v = vec![1, 2, 3, 4, 5];
1671            let ob = (&v).into_pyobject(py).unwrap();
1672            let list = ob.cast::<PyList>().unwrap();
1673
1674            let mut iter = list.iter();
1675            iter.next_back(); // Consume the last element
1676            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1677            assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 2);
1678            assert_eq!(iter.nth_back(0).unwrap().extract::<i32>().unwrap(), 1);
1679
1680            let v = vec![1, 2, 3, 4, 5];
1681            let ob = (&v).into_pyobject(py).unwrap();
1682            let list = ob.cast::<PyList>().unwrap();
1683
1684            let mut iter = list.iter();
1685            assert_eq!(iter.nth_back(1).unwrap().extract::<i32>().unwrap(), 4);
1686            assert_eq!(iter.nth_back(2).unwrap().extract::<i32>().unwrap(), 1);
1687
1688            let mut iter2 = list.iter();
1689            iter2.next_back();
1690            assert_eq!(iter2.nth_back(1).unwrap().extract::<i32>().unwrap(), 3);
1691            assert_eq!(iter2.next_back().unwrap().extract::<i32>().unwrap(), 2);
1692
1693            let mut iter3 = list.iter();
1694            iter3.nth(1);
1695            assert_eq!(iter3.nth_back(2).unwrap().extract::<i32>().unwrap(), 3);
1696            assert!(iter3.nth_back(0).is_none());
1697        });
1698    }
1699
1700    #[cfg(feature = "nightly")]
1701    #[test]
1702    fn test_iter_advance_by() {
1703        Python::attach(|py| {
1704            let v = vec![1, 2, 3, 4, 5];
1705            let ob = (&v).into_pyobject(py).unwrap();
1706            let list = ob.cast::<PyList>().unwrap();
1707
1708            let mut iter = list.iter();
1709            assert_eq!(iter.advance_by(2), Ok(()));
1710            assert_eq!(iter.next().unwrap().extract::<i32>().unwrap(), 3);
1711            assert_eq!(iter.advance_by(0), Ok(()));
1712            assert_eq!(iter.advance_by(100), Err(NonZero::new(98).unwrap()));
1713
1714            let mut iter2 = list.iter();
1715            assert_eq!(iter2.advance_by(6), Err(NonZero::new(1).unwrap()));
1716
1717            let mut iter3 = list.iter();
1718            assert_eq!(iter3.advance_by(5), Ok(()));
1719
1720            let mut iter4 = list.iter();
1721            assert_eq!(iter4.advance_by(0), Ok(()));
1722            assert_eq!(iter4.next().unwrap().extract::<i32>().unwrap(), 1);
1723        })
1724    }
1725
1726    #[cfg(feature = "nightly")]
1727    #[test]
1728    fn test_iter_advance_back_by() {
1729        Python::attach(|py| {
1730            let v = vec![1, 2, 3, 4, 5];
1731            let ob = (&v).into_pyobject(py).unwrap();
1732            let list = ob.cast::<PyList>().unwrap();
1733
1734            let mut iter = list.iter();
1735            assert_eq!(iter.advance_back_by(2), Ok(()));
1736            assert_eq!(iter.next_back().unwrap().extract::<i32>().unwrap(), 3);
1737            assert_eq!(iter.advance_back_by(0), Ok(()));
1738            assert_eq!(iter.advance_back_by(100), Err(NonZero::new(98).unwrap()));
1739
1740            let mut iter2 = list.iter();
1741            assert_eq!(iter2.advance_back_by(6), Err(NonZero::new(1).unwrap()));
1742
1743            let mut iter3 = list.iter();
1744            assert_eq!(iter3.advance_back_by(5), Ok(()));
1745
1746            let mut iter4 = list.iter();
1747            assert_eq!(iter4.advance_back_by(0), Ok(()));
1748            assert_eq!(iter4.next_back().unwrap().extract::<i32>().unwrap(), 5);
1749        })
1750    }
1751
1752    #[test]
1753    fn test_iter_last() {
1754        Python::attach(|py| {
1755            let list = PyList::new(py, vec![1, 2, 3]).unwrap();
1756            let last = list.iter().last();
1757            assert_eq!(last.unwrap().extract::<i32>().unwrap(), 3);
1758        })
1759    }
1760
1761    #[test]
1762    fn test_iter_count() {
1763        Python::attach(|py| {
1764            let list = PyList::new(py, vec![1, 2, 3]).unwrap();
1765            assert_eq!(list.iter().count(), 3);
1766        })
1767    }
1768
1769    #[test]
1770    fn test_new_from_non_exact_iter() {
1771        Python::attach(|py| {
1772            let iter = (0..5)
1773                .filter(|_| true) // Filter does not implement ExactSizeIterator
1774                .map(|item| item.into_pyobject(py).unwrap());
1775
1776            assert!(
1777                matches!(iter.size_hint(), (0, _)),
1778                "size_hint lower bound should be 0 because we do not now the final size after filter"
1779            );
1780
1781            let list = PyList::new(py, iter).unwrap();
1782            assert_eq!(
1783                list.len(),
1784                5,
1785                "list should contain all elements even though size_hint is 0"
1786            );
1787        })
1788    }
1789}