pyo3/types/
sequence.rs

1use crate::err::{self, DowncastError, PyErr, PyResult};
2use crate::exceptions::PyTypeError;
3use crate::ffi_ptr_ext::FfiPtrExt;
4#[cfg(feature = "experimental-inspect")]
5use crate::inspect::types::TypeInfo;
6use crate::instance::Bound;
7use crate::internal_tricks::get_ssize_index;
8use crate::py_result_ext::PyResultExt;
9use crate::sync::GILOnceCell;
10use crate::type_object::PyTypeInfo;
11use crate::types::{any::PyAnyMethods, PyAny, PyList, PyString, PyTuple, PyType};
12#[cfg(feature = "gil-refs")]
13use crate::{err::PyDowncastError, PyNativeType};
14use crate::{ffi, FromPyObject, Py, PyTypeCheck, Python, ToPyObject};
15
16/// Represents a reference to a Python object supporting the sequence protocol.
17///
18/// Values of this type are accessed via PyO3's smart pointers, e.g. as
19/// [`Py<PySequence>`][crate::Py] or [`Bound<'py, PySequence>`][Bound].
20///
21/// For APIs available on sequence objects, see the [`PySequenceMethods`] trait which is implemented for
22/// [`Bound<'py, PySequence>`][Bound].
23#[repr(transparent)]
24pub struct PySequence(PyAny);
25pyobject_native_type_named!(PySequence);
26pyobject_native_type_extract!(PySequence);
27
28impl PySequence {
29    /// Register a pyclass as a subclass of `collections.abc.Sequence` (from the Python standard
30    /// library). This is equivalent to `collections.abc.Sequence.register(T)` in Python.
31    /// This registration is required for a pyclass to be downcastable from `PyAny` to `PySequence`.
32    pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> {
33        let ty = T::type_object_bound(py);
34        get_sequence_abc(py)?.call_method1("register", (ty,))?;
35        Ok(())
36    }
37}
38
39#[cfg(feature = "gil-refs")]
40impl PySequence {
41    /// Returns the number of objects in sequence.
42    ///
43    /// This is equivalent to the Python expression `len(self)`.
44    #[inline]
45    pub fn len(&self) -> PyResult<usize> {
46        self.as_borrowed().len()
47    }
48
49    /// Returns whether the sequence is empty.
50    #[inline]
51    pub fn is_empty(&self) -> PyResult<bool> {
52        self.as_borrowed().is_empty()
53    }
54
55    /// Returns the concatenation of `self` and `other`.
56    ///
57    /// This is equivalent to the Python expression `self + other`.
58    #[inline]
59    pub fn concat(&self, other: &PySequence) -> PyResult<&PySequence> {
60        self.as_borrowed()
61            .concat(&other.as_borrowed())
62            .map(Bound::into_gil_ref)
63    }
64
65    /// Returns the result of repeating a sequence object `count` times.
66    ///
67    /// This is equivalent to the Python expression `self * count`.
68    #[inline]
69    pub fn repeat(&self, count: usize) -> PyResult<&PySequence> {
70        self.as_borrowed().repeat(count).map(Bound::into_gil_ref)
71    }
72
73    /// Concatenates `self` and `other`, in place if possible.
74    ///
75    /// This is equivalent to the Python expression `self.__iadd__(other)`.
76    ///
77    /// The Python statement `self += other` is syntactic sugar for `self =
78    /// self.__iadd__(other)`.  `__iadd__` should modify and return `self` if
79    /// possible, but create and return a new object if not.
80    #[inline]
81    pub fn in_place_concat(&self, other: &PySequence) -> PyResult<&PySequence> {
82        self.as_borrowed()
83            .in_place_concat(&other.as_borrowed())
84            .map(Bound::into_gil_ref)
85    }
86
87    /// Repeats the sequence object `count` times and updates `self`, if possible.
88    ///
89    /// This is equivalent to the Python expression `self.__imul__(other)`.
90    ///
91    /// The Python statement `self *= other` is syntactic sugar for `self =
92    /// self.__imul__(other)`.  `__imul__` should modify and return `self` if
93    /// possible, but create and return a new object if not.
94    #[inline]
95    pub fn in_place_repeat(&self, count: usize) -> PyResult<&PySequence> {
96        self.as_borrowed()
97            .in_place_repeat(count)
98            .map(Bound::into_gil_ref)
99    }
100
101    /// Returns the `index`th element of the Sequence.
102    ///
103    /// This is equivalent to the Python expression `self[index]` without support of negative indices.
104    #[inline]
105    pub fn get_item(&self, index: usize) -> PyResult<&PyAny> {
106        self.as_borrowed().get_item(index).map(Bound::into_gil_ref)
107    }
108
109    /// Returns the slice of sequence object between `begin` and `end`.
110    ///
111    /// This is equivalent to the Python expression `self[begin:end]`.
112    #[inline]
113    pub fn get_slice(&self, begin: usize, end: usize) -> PyResult<&PySequence> {
114        self.as_borrowed()
115            .get_slice(begin, end)
116            .map(Bound::into_gil_ref)
117    }
118
119    /// Assigns object `item` to the `i`th element of self.
120    ///
121    /// This is equivalent to the Python statement `self[i] = v`.
122    #[inline]
123    pub fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
124    where
125        I: ToPyObject,
126    {
127        self.as_borrowed().set_item(i, item)
128    }
129
130    /// Deletes the `i`th element of self.
131    ///
132    /// This is equivalent to the Python statement `del self[i]`.
133    #[inline]
134    pub fn del_item(&self, i: usize) -> PyResult<()> {
135        self.as_borrowed().del_item(i)
136    }
137
138    /// Assigns the sequence `v` to the slice of `self` from `i1` to `i2`.
139    ///
140    /// This is equivalent to the Python statement `self[i1:i2] = v`.
141    #[inline]
142    pub fn set_slice(&self, i1: usize, i2: usize, v: &PyAny) -> PyResult<()> {
143        self.as_borrowed().set_slice(i1, i2, &v.as_borrowed())
144    }
145
146    /// Deletes the slice from `i1` to `i2` from `self`.
147    ///
148    /// This is equivalent to the Python statement `del self[i1:i2]`.
149    #[inline]
150    pub fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()> {
151        self.as_borrowed().del_slice(i1, i2)
152    }
153
154    /// Returns the number of occurrences of `value` in self, that is, return the
155    /// number of keys for which `self[key] == value`.
156    #[inline]
157    #[cfg(not(any(PyPy, GraalPy)))]
158    pub fn count<V>(&self, value: V) -> PyResult<usize>
159    where
160        V: ToPyObject,
161    {
162        self.as_borrowed().count(value)
163    }
164
165    /// Determines if self contains `value`.
166    ///
167    /// This is equivalent to the Python expression `value in self`.
168    #[inline]
169    pub fn contains<V>(&self, value: V) -> PyResult<bool>
170    where
171        V: ToPyObject,
172    {
173        self.as_borrowed().contains(value)
174    }
175
176    /// Returns the first index `i` for which `self[i] == value`.
177    ///
178    /// This is equivalent to the Python expression `self.index(value)`.
179    #[inline]
180    pub fn index<V>(&self, value: V) -> PyResult<usize>
181    where
182        V: ToPyObject,
183    {
184        self.as_borrowed().index(value)
185    }
186
187    /// Returns a fresh list based on the Sequence.
188    #[inline]
189    pub fn to_list(&self) -> PyResult<&PyList> {
190        self.as_borrowed().to_list().map(Bound::into_gil_ref)
191    }
192
193    /// Returns a fresh tuple based on the Sequence.
194    #[inline]
195    pub fn to_tuple(&self) -> PyResult<&PyTuple> {
196        self.as_borrowed().to_tuple().map(Bound::into_gil_ref)
197    }
198}
199
200/// Implementation of functionality for [`PySequence`].
201///
202/// These methods are defined for the `Bound<'py, PySequence>` smart pointer, so to use method call
203/// syntax these methods are separated into a trait, because stable Rust does not yet support
204/// `arbitrary_self_types`.
205#[doc(alias = "PySequence")]
206pub trait PySequenceMethods<'py>: crate::sealed::Sealed {
207    /// Returns the number of objects in sequence.
208    ///
209    /// This is equivalent to the Python expression `len(self)`.
210    fn len(&self) -> PyResult<usize>;
211
212    /// Returns whether the sequence is empty.
213    fn is_empty(&self) -> PyResult<bool>;
214
215    /// Returns the concatenation of `self` and `other`.
216    ///
217    /// This is equivalent to the Python expression `self + other`.
218    fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
219
220    /// Returns the result of repeating a sequence object `count` times.
221    ///
222    /// This is equivalent to the Python expression `self * count`.
223    fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
224
225    /// Concatenates `self` and `other`, in place if possible.
226    ///
227    /// This is equivalent to the Python expression `self.__iadd__(other)`.
228    ///
229    /// The Python statement `self += other` is syntactic sugar for `self =
230    /// self.__iadd__(other)`.  `__iadd__` should modify and return `self` if
231    /// possible, but create and return a new object if not.
232    fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
233
234    /// Repeats the sequence object `count` times and updates `self`, if possible.
235    ///
236    /// This is equivalent to the Python expression `self.__imul__(other)`.
237    ///
238    /// The Python statement `self *= other` is syntactic sugar for `self =
239    /// self.__imul__(other)`.  `__imul__` should modify and return `self` if
240    /// possible, but create and return a new object if not.
241    fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
242
243    /// Returns the `index`th element of the Sequence.
244    ///
245    /// This is equivalent to the Python expression `self[index]` without support of negative indices.
246    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
247
248    /// Returns the slice of sequence object between `begin` and `end`.
249    ///
250    /// This is equivalent to the Python expression `self[begin:end]`.
251    fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>>;
252
253    /// Assigns object `item` to the `i`th element of self.
254    ///
255    /// This is equivalent to the Python statement `self[i] = v`.
256    fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
257    where
258        I: ToPyObject;
259
260    /// Deletes the `i`th element of self.
261    ///
262    /// This is equivalent to the Python statement `del self[i]`.
263    fn del_item(&self, i: usize) -> PyResult<()>;
264
265    /// Assigns the sequence `v` to the slice of `self` from `i1` to `i2`.
266    ///
267    /// This is equivalent to the Python statement `self[i1:i2] = v`.
268    fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()>;
269
270    /// Deletes the slice from `i1` to `i2` from `self`.
271    ///
272    /// This is equivalent to the Python statement `del self[i1:i2]`.
273    fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()>;
274
275    /// Returns the number of occurrences of `value` in self, that is, return the
276    /// number of keys for which `self[key] == value`.
277    #[cfg(not(PyPy))]
278    fn count<V>(&self, value: V) -> PyResult<usize>
279    where
280        V: ToPyObject;
281
282    /// Determines if self contains `value`.
283    ///
284    /// This is equivalent to the Python expression `value in self`.
285    fn contains<V>(&self, value: V) -> PyResult<bool>
286    where
287        V: ToPyObject;
288
289    /// Returns the first index `i` for which `self[i] == value`.
290    ///
291    /// This is equivalent to the Python expression `self.index(value)`.
292    fn index<V>(&self, value: V) -> PyResult<usize>
293    where
294        V: ToPyObject;
295
296    /// Returns a fresh list based on the Sequence.
297    fn to_list(&self) -> PyResult<Bound<'py, PyList>>;
298
299    /// Returns a fresh tuple based on the Sequence.
300    fn to_tuple(&self) -> PyResult<Bound<'py, PyTuple>>;
301}
302
303impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> {
304    #[inline]
305    fn len(&self) -> PyResult<usize> {
306        let v = unsafe { ffi::PySequence_Size(self.as_ptr()) };
307        crate::err::error_on_minusone(self.py(), v)?;
308        Ok(v as usize)
309    }
310
311    #[inline]
312    fn is_empty(&self) -> PyResult<bool> {
313        self.len().map(|l| l == 0)
314    }
315
316    #[inline]
317    fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>> {
318        unsafe {
319            ffi::PySequence_Concat(self.as_ptr(), other.as_ptr())
320                .assume_owned_or_err(self.py())
321                .downcast_into_unchecked()
322        }
323    }
324
325    #[inline]
326    fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>> {
327        unsafe {
328            ffi::PySequence_Repeat(self.as_ptr(), get_ssize_index(count))
329                .assume_owned_or_err(self.py())
330                .downcast_into_unchecked()
331        }
332    }
333
334    #[inline]
335    fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>> {
336        unsafe {
337            ffi::PySequence_InPlaceConcat(self.as_ptr(), other.as_ptr())
338                .assume_owned_or_err(self.py())
339                .downcast_into_unchecked()
340        }
341    }
342
343    #[inline]
344    fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>> {
345        unsafe {
346            ffi::PySequence_InPlaceRepeat(self.as_ptr(), get_ssize_index(count))
347                .assume_owned_or_err(self.py())
348                .downcast_into_unchecked()
349        }
350    }
351
352    #[inline]
353    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
354        unsafe {
355            ffi::PySequence_GetItem(self.as_ptr(), get_ssize_index(index))
356                .assume_owned_or_err(self.py())
357        }
358    }
359
360    #[inline]
361    fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>> {
362        unsafe {
363            ffi::PySequence_GetSlice(self.as_ptr(), get_ssize_index(begin), get_ssize_index(end))
364                .assume_owned_or_err(self.py())
365                .downcast_into_unchecked()
366        }
367    }
368
369    #[inline]
370    fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
371    where
372        I: ToPyObject,
373    {
374        fn inner(seq: &Bound<'_, PySequence>, i: usize, item: Bound<'_, PyAny>) -> PyResult<()> {
375            err::error_on_minusone(seq.py(), unsafe {
376                ffi::PySequence_SetItem(seq.as_ptr(), get_ssize_index(i), item.as_ptr())
377            })
378        }
379
380        let py = self.py();
381        inner(self, i, item.to_object(py).into_bound(py))
382    }
383
384    #[inline]
385    fn del_item(&self, i: usize) -> PyResult<()> {
386        err::error_on_minusone(self.py(), unsafe {
387            ffi::PySequence_DelItem(self.as_ptr(), get_ssize_index(i))
388        })
389    }
390
391    #[inline]
392    fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()> {
393        err::error_on_minusone(self.py(), unsafe {
394            ffi::PySequence_SetSlice(
395                self.as_ptr(),
396                get_ssize_index(i1),
397                get_ssize_index(i2),
398                v.as_ptr(),
399            )
400        })
401    }
402
403    #[inline]
404    fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()> {
405        err::error_on_minusone(self.py(), unsafe {
406            ffi::PySequence_DelSlice(self.as_ptr(), get_ssize_index(i1), get_ssize_index(i2))
407        })
408    }
409
410    #[inline]
411    #[cfg(not(PyPy))]
412    fn count<V>(&self, value: V) -> PyResult<usize>
413    where
414        V: ToPyObject,
415    {
416        fn inner(seq: &Bound<'_, PySequence>, value: Bound<'_, PyAny>) -> PyResult<usize> {
417            let r = unsafe { ffi::PySequence_Count(seq.as_ptr(), value.as_ptr()) };
418            crate::err::error_on_minusone(seq.py(), r)?;
419            Ok(r as usize)
420        }
421
422        let py = self.py();
423        inner(self, value.to_object(py).into_bound(py))
424    }
425
426    #[inline]
427    fn contains<V>(&self, value: V) -> PyResult<bool>
428    where
429        V: ToPyObject,
430    {
431        fn inner(seq: &Bound<'_, PySequence>, value: Bound<'_, PyAny>) -> PyResult<bool> {
432            let r = unsafe { ffi::PySequence_Contains(seq.as_ptr(), value.as_ptr()) };
433            match r {
434                0 => Ok(false),
435                1 => Ok(true),
436                _ => Err(PyErr::fetch(seq.py())),
437            }
438        }
439
440        let py = self.py();
441        inner(self, value.to_object(py).into_bound(py))
442    }
443
444    #[inline]
445    fn index<V>(&self, value: V) -> PyResult<usize>
446    where
447        V: ToPyObject,
448    {
449        fn inner(seq: &Bound<'_, PySequence>, value: Bound<'_, PyAny>) -> PyResult<usize> {
450            let r = unsafe { ffi::PySequence_Index(seq.as_ptr(), value.as_ptr()) };
451            crate::err::error_on_minusone(seq.py(), r)?;
452            Ok(r as usize)
453        }
454
455        let py = self.py();
456        inner(self, value.to_object(self.py()).into_bound(py))
457    }
458
459    #[inline]
460    fn to_list(&self) -> PyResult<Bound<'py, PyList>> {
461        unsafe {
462            ffi::PySequence_List(self.as_ptr())
463                .assume_owned_or_err(self.py())
464                .downcast_into_unchecked()
465        }
466    }
467
468    #[inline]
469    fn to_tuple(&self) -> PyResult<Bound<'py, PyTuple>> {
470        unsafe {
471            ffi::PySequence_Tuple(self.as_ptr())
472                .assume_owned_or_err(self.py())
473                .downcast_into_unchecked()
474        }
475    }
476}
477
478#[inline]
479#[cfg(feature = "gil-refs")]
480fn sequence_len(seq: &PySequence) -> usize {
481    seq.len().expect("failed to get sequence length")
482}
483
484#[inline]
485#[cfg(feature = "gil-refs")]
486fn sequence_slice(seq: &PySequence, start: usize, end: usize) -> &PySequence {
487    seq.get_slice(start, end)
488        .expect("sequence slice operation failed")
489}
490
491#[cfg(feature = "gil-refs")]
492index_impls!(PySequence, "sequence", sequence_len, sequence_slice);
493
494impl<'py, T> FromPyObject<'py> for Vec<T>
495where
496    T: FromPyObject<'py>,
497{
498    fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
499        if obj.is_instance_of::<PyString>() {
500            return Err(PyTypeError::new_err("Can't extract `str` to `Vec`"));
501        }
502        extract_sequence(obj)
503    }
504
505    #[cfg(feature = "experimental-inspect")]
506    fn type_input() -> TypeInfo {
507        TypeInfo::sequence_of(T::type_input())
508    }
509}
510
511fn extract_sequence<'py, T>(obj: &Bound<'py, PyAny>) -> PyResult<Vec<T>>
512where
513    T: FromPyObject<'py>,
514{
515    // Types that pass `PySequence_Check` usually implement enough of the sequence protocol
516    // to support this function and if not, we will only fail extraction safely.
517    let seq = unsafe {
518        if ffi::PySequence_Check(obj.as_ptr()) != 0 {
519            obj.downcast_unchecked::<PySequence>()
520        } else {
521            return Err(DowncastError::new(obj, "Sequence").into());
522        }
523    };
524
525    let mut v = Vec::with_capacity(seq.len().unwrap_or(0));
526    for item in seq.iter()? {
527        v.push(item?.extract::<T>()?);
528    }
529    Ok(v)
530}
531
532fn get_sequence_abc(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
533    static SEQUENCE_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new();
534
535    SEQUENCE_ABC.get_or_try_init_type_ref(py, "collections.abc", "Sequence")
536}
537
538impl PyTypeCheck for PySequence {
539    const NAME: &'static str = "Sequence";
540
541    #[inline]
542    fn type_check(object: &Bound<'_, PyAny>) -> bool {
543        // Using `is_instance` for `collections.abc.Sequence` is slow, so provide
544        // optimized cases for list and tuples as common well-known sequences
545        PyList::is_type_of_bound(object)
546            || PyTuple::is_type_of_bound(object)
547            || get_sequence_abc(object.py())
548                .and_then(|abc| object.is_instance(abc))
549                .unwrap_or_else(|err| {
550                    err.write_unraisable_bound(object.py(), Some(&object.as_borrowed()));
551                    false
552                })
553    }
554}
555
556#[cfg(feature = "gil-refs")]
557#[allow(deprecated)]
558impl<'v> crate::PyTryFrom<'v> for PySequence {
559    /// Downcasting to `PySequence` requires the concrete class to be a subclass (or registered
560    /// subclass) of `collections.abc.Sequence` (from the Python standard library) - i.e.
561    /// `isinstance(<class>, collections.abc.Sequence) == True`.
562    fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
563        let value = value.into();
564
565        if PySequence::type_check(&value.as_borrowed()) {
566            unsafe { return Ok(value.downcast_unchecked::<PySequence>()) }
567        }
568
569        Err(PyDowncastError::new(value, "Sequence"))
570    }
571
572    fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> {
573        value.into().downcast()
574    }
575
576    #[inline]
577    unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PySequence {
578        let ptr = value.into() as *const _ as *const PySequence;
579        &*ptr
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    use crate::types::{PyAnyMethods, PyList, PySequence, PySequenceMethods, PyTuple};
586    use crate::{PyObject, Python, ToPyObject};
587
588    fn get_object() -> PyObject {
589        // Convenience function for getting a single unique object
590        Python::with_gil(|py| {
591            let obj = py.eval_bound("object()", None, None).unwrap();
592
593            obj.to_object(py)
594        })
595    }
596
597    #[test]
598    fn test_numbers_are_not_sequences() {
599        Python::with_gil(|py| {
600            let v = 42i32;
601            assert!(v.to_object(py).downcast_bound::<PySequence>(py).is_err());
602        });
603    }
604
605    #[test]
606    fn test_strings_are_sequences() {
607        Python::with_gil(|py| {
608            let v = "London Calling";
609            assert!(v.to_object(py).downcast_bound::<PySequence>(py).is_ok());
610        });
611    }
612
613    #[test]
614    fn test_strings_cannot_be_extracted_to_vec() {
615        Python::with_gil(|py| {
616            let v = "London Calling";
617            let ob = v.to_object(py);
618
619            assert!(ob.extract::<Vec<String>>(py).is_err());
620            assert!(ob.extract::<Vec<char>>(py).is_err());
621        });
622    }
623
624    #[test]
625    fn test_seq_empty() {
626        Python::with_gil(|py| {
627            let v: Vec<i32> = vec![];
628            let ob = v.to_object(py);
629            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
630            assert_eq!(0, seq.len().unwrap());
631
632            let needle = 7i32.to_object(py);
633            assert!(!seq.contains(&needle).unwrap());
634        });
635    }
636
637    #[test]
638    fn test_seq_is_empty() {
639        Python::with_gil(|py| {
640            let list = vec![1].to_object(py);
641            let seq = list.downcast_bound::<PySequence>(py).unwrap();
642            assert!(!seq.is_empty().unwrap());
643            let vec: Vec<u32> = Vec::new();
644            let empty_list = vec.to_object(py);
645            let empty_seq = empty_list.downcast_bound::<PySequence>(py).unwrap();
646            assert!(empty_seq.is_empty().unwrap());
647        });
648    }
649
650    #[test]
651    fn test_seq_contains() {
652        Python::with_gil(|py| {
653            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
654            let ob = v.to_object(py);
655            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
656            assert_eq!(6, seq.len().unwrap());
657
658            let bad_needle = 7i32.to_object(py);
659            assert!(!seq.contains(&bad_needle).unwrap());
660
661            let good_needle = 8i32.to_object(py);
662            assert!(seq.contains(&good_needle).unwrap());
663
664            let type_coerced_needle = 8f32.to_object(py);
665            assert!(seq.contains(&type_coerced_needle).unwrap());
666        });
667    }
668
669    #[test]
670    fn test_seq_get_item() {
671        Python::with_gil(|py| {
672            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
673            let ob = v.to_object(py);
674            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
675            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
676            assert_eq!(1, seq.get_item(1).unwrap().extract::<i32>().unwrap());
677            assert_eq!(2, seq.get_item(2).unwrap().extract::<i32>().unwrap());
678            assert_eq!(3, seq.get_item(3).unwrap().extract::<i32>().unwrap());
679            assert_eq!(5, seq.get_item(4).unwrap().extract::<i32>().unwrap());
680            assert_eq!(8, seq.get_item(5).unwrap().extract::<i32>().unwrap());
681            assert!(seq.get_item(10).is_err());
682        });
683    }
684
685    #[test]
686    #[cfg(feature = "gil-refs")]
687    #[allow(deprecated)]
688    fn test_seq_index_trait() {
689        Python::with_gil(|py| {
690            let v: Vec<i32> = vec![1, 1, 2];
691            let ob = v.to_object(py);
692            let seq = ob.downcast::<PySequence>(py).unwrap();
693            assert_eq!(1, seq[0].extract::<i32>().unwrap());
694            assert_eq!(1, seq[1].extract::<i32>().unwrap());
695            assert_eq!(2, seq[2].extract::<i32>().unwrap());
696        });
697    }
698
699    #[test]
700    #[should_panic = "index 7 out of range for sequence"]
701    #[cfg(feature = "gil-refs")]
702    #[allow(deprecated)]
703    fn test_seq_index_trait_panic() {
704        Python::with_gil(|py| {
705            let v: Vec<i32> = vec![1, 1, 2];
706            let ob = v.to_object(py);
707            let seq = ob.downcast::<PySequence>(py).unwrap();
708            let _ = &seq[7];
709        });
710    }
711
712    #[test]
713    #[cfg(feature = "gil-refs")]
714    #[allow(deprecated)]
715    fn test_seq_index_trait_ranges() {
716        Python::with_gil(|py| {
717            let v: Vec<i32> = vec![1, 1, 2];
718            let ob = v.to_object(py);
719            let seq = ob.downcast::<PySequence>(py).unwrap();
720            assert_eq!(vec![1, 2], seq[1..3].extract::<Vec<i32>>().unwrap());
721            assert_eq!(Vec::<i32>::new(), seq[3..3].extract::<Vec<i32>>().unwrap());
722            assert_eq!(vec![1, 2], seq[1..].extract::<Vec<i32>>().unwrap());
723            assert_eq!(Vec::<i32>::new(), seq[3..].extract::<Vec<i32>>().unwrap());
724            assert_eq!(vec![1, 1, 2], seq[..].extract::<Vec<i32>>().unwrap());
725            assert_eq!(vec![1, 2], seq[1..=2].extract::<Vec<i32>>().unwrap());
726            assert_eq!(vec![1, 1], seq[..2].extract::<Vec<i32>>().unwrap());
727            assert_eq!(vec![1, 1], seq[..=1].extract::<Vec<i32>>().unwrap());
728        })
729    }
730
731    #[test]
732    #[should_panic = "range start index 5 out of range for sequence of length 3"]
733    #[cfg(feature = "gil-refs")]
734    #[allow(deprecated)]
735    fn test_seq_index_trait_range_panic_start() {
736        Python::with_gil(|py| {
737            let v: Vec<i32> = vec![1, 1, 2];
738            let ob = v.to_object(py);
739            let seq = ob.downcast::<PySequence>(py).unwrap();
740            seq[5..10].extract::<Vec<i32>>().unwrap();
741        })
742    }
743
744    #[test]
745    #[should_panic = "range end index 10 out of range for sequence of length 3"]
746    #[cfg(feature = "gil-refs")]
747    #[allow(deprecated)]
748    fn test_seq_index_trait_range_panic_end() {
749        Python::with_gil(|py| {
750            let v: Vec<i32> = vec![1, 1, 2];
751            let ob = v.to_object(py);
752            let seq = ob.downcast::<PySequence>(py).unwrap();
753            seq[1..10].extract::<Vec<i32>>().unwrap();
754        })
755    }
756
757    #[test]
758    #[should_panic = "slice index starts at 2 but ends at 1"]
759    #[cfg(feature = "gil-refs")]
760    #[allow(deprecated)]
761    fn test_seq_index_trait_range_panic_wrong_order() {
762        Python::with_gil(|py| {
763            let v: Vec<i32> = vec![1, 1, 2];
764            let ob = v.to_object(py);
765            let seq = ob.downcast::<PySequence>(py).unwrap();
766            #[allow(clippy::reversed_empty_ranges)]
767            seq[2..1].extract::<Vec<i32>>().unwrap();
768        })
769    }
770
771    #[test]
772    #[should_panic = "range start index 8 out of range for sequence of length 3"]
773    #[cfg(feature = "gil-refs")]
774    #[allow(deprecated)]
775    fn test_seq_index_trait_range_from_panic() {
776        Python::with_gil(|py| {
777            let v: Vec<i32> = vec![1, 1, 2];
778            let ob = v.to_object(py);
779            let seq = ob.downcast::<PySequence>(py).unwrap();
780            seq[8..].extract::<Vec<i32>>().unwrap();
781        })
782    }
783
784    #[test]
785    fn test_seq_del_item() {
786        Python::with_gil(|py| {
787            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
788            let ob = v.to_object(py);
789            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
790            assert!(seq.del_item(10).is_err());
791            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
792            assert!(seq.del_item(0).is_ok());
793            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
794            assert!(seq.del_item(0).is_ok());
795            assert_eq!(2, seq.get_item(0).unwrap().extract::<i32>().unwrap());
796            assert!(seq.del_item(0).is_ok());
797            assert_eq!(3, seq.get_item(0).unwrap().extract::<i32>().unwrap());
798            assert!(seq.del_item(0).is_ok());
799            assert_eq!(5, seq.get_item(0).unwrap().extract::<i32>().unwrap());
800            assert!(seq.del_item(0).is_ok());
801            assert_eq!(8, seq.get_item(0).unwrap().extract::<i32>().unwrap());
802            assert!(seq.del_item(0).is_ok());
803            assert_eq!(0, seq.len().unwrap());
804            assert!(seq.del_item(0).is_err());
805        });
806    }
807
808    #[test]
809    fn test_seq_set_item() {
810        Python::with_gil(|py| {
811            let v: Vec<i32> = vec![1, 2];
812            let ob = v.to_object(py);
813            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
814            assert_eq!(2, seq.get_item(1).unwrap().extract::<i32>().unwrap());
815            assert!(seq.set_item(1, 10).is_ok());
816            assert_eq!(10, seq.get_item(1).unwrap().extract::<i32>().unwrap());
817        });
818    }
819
820    #[test]
821    fn test_seq_set_item_refcnt() {
822        let obj = get_object();
823
824        Python::with_gil(|py| {
825            let v: Vec<i32> = vec![1, 2];
826            let ob = v.to_object(py);
827            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
828            assert!(seq.set_item(1, &obj).is_ok());
829            assert!(seq.get_item(1).unwrap().as_ptr() == obj.as_ptr());
830        });
831
832        Python::with_gil(move |py| {
833            assert_eq!(1, obj.get_refcnt(py));
834        });
835    }
836
837    #[test]
838    fn test_seq_get_slice() {
839        Python::with_gil(|py| {
840            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
841            let ob = v.to_object(py);
842            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
843            assert_eq!(
844                [1, 2, 3],
845                seq.get_slice(1, 4).unwrap().extract::<[i32; 3]>().unwrap()
846            );
847            assert_eq!(
848                [3, 5, 8],
849                seq.get_slice(3, 100)
850                    .unwrap()
851                    .extract::<[i32; 3]>()
852                    .unwrap()
853            );
854        });
855    }
856
857    #[test]
858    fn test_set_slice() {
859        Python::with_gil(|py| {
860            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
861            let w: Vec<i32> = vec![7, 4];
862            let ob = v.to_object(py);
863            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
864            let ins = w.to_object(py);
865            seq.set_slice(1, 4, ins.bind(py)).unwrap();
866            assert_eq!([1, 7, 4, 5, 8], seq.extract::<[i32; 5]>().unwrap());
867            seq.set_slice(3, 100, &PyList::empty_bound(py)).unwrap();
868            assert_eq!([1, 7, 4], seq.extract::<[i32; 3]>().unwrap());
869        });
870    }
871
872    #[test]
873    fn test_del_slice() {
874        Python::with_gil(|py| {
875            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
876            let ob = v.to_object(py);
877            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
878            seq.del_slice(1, 4).unwrap();
879            assert_eq!([1, 5, 8], seq.extract::<[i32; 3]>().unwrap());
880            seq.del_slice(1, 100).unwrap();
881            assert_eq!([1], seq.extract::<[i32; 1]>().unwrap());
882        });
883    }
884
885    #[test]
886    fn test_seq_index() {
887        Python::with_gil(|py| {
888            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
889            let ob = v.to_object(py);
890            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
891            assert_eq!(0, seq.index(1i32).unwrap());
892            assert_eq!(2, seq.index(2i32).unwrap());
893            assert_eq!(3, seq.index(3i32).unwrap());
894            assert_eq!(4, seq.index(5i32).unwrap());
895            assert_eq!(5, seq.index(8i32).unwrap());
896            assert!(seq.index(42i32).is_err());
897        });
898    }
899
900    #[test]
901    #[cfg(not(any(PyPy, GraalPy)))]
902    fn test_seq_count() {
903        Python::with_gil(|py| {
904            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
905            let ob = v.to_object(py);
906            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
907            assert_eq!(2, seq.count(1i32).unwrap());
908            assert_eq!(1, seq.count(2i32).unwrap());
909            assert_eq!(1, seq.count(3i32).unwrap());
910            assert_eq!(1, seq.count(5i32).unwrap());
911            assert_eq!(1, seq.count(8i32).unwrap());
912            assert_eq!(0, seq.count(42i32).unwrap());
913        });
914    }
915
916    #[test]
917    fn test_seq_iter() {
918        Python::with_gil(|py| {
919            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
920            let ob = v.to_object(py);
921            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
922            let mut idx = 0;
923            for el in seq.iter().unwrap() {
924                assert_eq!(v[idx], el.unwrap().extract::<i32>().unwrap());
925                idx += 1;
926            }
927            assert_eq!(idx, v.len());
928        });
929    }
930
931    #[test]
932    fn test_seq_strings() {
933        Python::with_gil(|py| {
934            let v = vec!["It", "was", "the", "worst", "of", "times"];
935            let ob = v.to_object(py);
936            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
937
938            let bad_needle = "blurst".to_object(py);
939            assert!(!seq.contains(bad_needle).unwrap());
940
941            let good_needle = "worst".to_object(py);
942            assert!(seq.contains(good_needle).unwrap());
943        });
944    }
945
946    #[test]
947    fn test_seq_concat() {
948        Python::with_gil(|py| {
949            let v: Vec<i32> = vec![1, 2, 3];
950            let ob = v.to_object(py);
951            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
952            let concat_seq = seq.concat(seq).unwrap();
953            assert_eq!(6, concat_seq.len().unwrap());
954            let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
955            for (el, cc) in concat_seq.iter().unwrap().zip(concat_v) {
956                assert_eq!(cc, el.unwrap().extract::<i32>().unwrap());
957            }
958        });
959    }
960
961    #[test]
962    fn test_seq_concat_string() {
963        Python::with_gil(|py| {
964            let v = "string";
965            let ob = v.to_object(py);
966            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
967            let concat_seq = seq.concat(seq).unwrap();
968            assert_eq!(12, concat_seq.len().unwrap());
969            let concat_v = "stringstring".to_owned();
970            for (el, cc) in seq.iter().unwrap().zip(concat_v.chars()) {
971                assert_eq!(cc, el.unwrap().extract::<char>().unwrap());
972            }
973        });
974    }
975
976    #[test]
977    fn test_seq_repeat() {
978        Python::with_gil(|py| {
979            let v = vec!["foo", "bar"];
980            let ob = v.to_object(py);
981            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
982            let repeat_seq = seq.repeat(3).unwrap();
983            assert_eq!(6, repeat_seq.len().unwrap());
984            let repeated = ["foo", "bar", "foo", "bar", "foo", "bar"];
985            for (el, rpt) in repeat_seq.iter().unwrap().zip(repeated.iter()) {
986                assert_eq!(*rpt, el.unwrap().extract::<String>().unwrap());
987            }
988        });
989    }
990
991    #[test]
992    fn test_seq_inplace() {
993        Python::with_gil(|py| {
994            let v = vec!["foo", "bar"];
995            let ob = v.to_object(py);
996            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
997            let rep_seq = seq.in_place_repeat(3).unwrap();
998            assert_eq!(6, seq.len().unwrap());
999            assert!(seq.is(&rep_seq));
1000
1001            let conc_seq = seq.in_place_concat(seq).unwrap();
1002            assert_eq!(12, seq.len().unwrap());
1003            assert!(seq.is(&conc_seq));
1004        });
1005    }
1006
1007    #[test]
1008    fn test_list_coercion() {
1009        Python::with_gil(|py| {
1010            let v = vec!["foo", "bar"];
1011            let ob = v.to_object(py);
1012            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
1013            assert!(seq
1014                .to_list()
1015                .unwrap()
1016                .eq(PyList::new_bound(py, &v))
1017                .unwrap());
1018        });
1019    }
1020
1021    #[test]
1022    fn test_strings_coerce_to_lists() {
1023        Python::with_gil(|py| {
1024            let v = "foo";
1025            let ob = v.to_object(py);
1026            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
1027            assert!(seq
1028                .to_list()
1029                .unwrap()
1030                .eq(PyList::new_bound(py, ["f", "o", "o"]))
1031                .unwrap());
1032        });
1033    }
1034
1035    #[test]
1036    fn test_tuple_coercion() {
1037        Python::with_gil(|py| {
1038            let v = ("foo", "bar");
1039            let ob = v.to_object(py);
1040            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
1041            assert!(seq
1042                .to_tuple()
1043                .unwrap()
1044                .eq(PyTuple::new_bound(py, ["foo", "bar"]))
1045                .unwrap());
1046        });
1047    }
1048
1049    #[test]
1050    fn test_lists_coerce_to_tuples() {
1051        Python::with_gil(|py| {
1052            let v = vec!["foo", "bar"];
1053            let ob = v.to_object(py);
1054            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
1055            assert!(seq
1056                .to_tuple()
1057                .unwrap()
1058                .eq(PyTuple::new_bound(py, &v))
1059                .unwrap());
1060        });
1061    }
1062
1063    #[test]
1064    fn test_extract_tuple_to_vec() {
1065        Python::with_gil(|py| {
1066            let v: Vec<i32> = py
1067                .eval_bound("(1, 2)", None, None)
1068                .unwrap()
1069                .extract()
1070                .unwrap();
1071            assert!(v == [1, 2]);
1072        });
1073    }
1074
1075    #[test]
1076    fn test_extract_range_to_vec() {
1077        Python::with_gil(|py| {
1078            let v: Vec<i32> = py
1079                .eval_bound("range(1, 5)", None, None)
1080                .unwrap()
1081                .extract()
1082                .unwrap();
1083            assert!(v == [1, 2, 3, 4]);
1084        });
1085    }
1086
1087    #[test]
1088    fn test_extract_bytearray_to_vec() {
1089        Python::with_gil(|py| {
1090            let v: Vec<u8> = py
1091                .eval_bound("bytearray(b'abc')", None, None)
1092                .unwrap()
1093                .extract()
1094                .unwrap();
1095            assert!(v == b"abc");
1096        });
1097    }
1098
1099    #[test]
1100    fn test_seq_downcast_unchecked() {
1101        Python::with_gil(|py| {
1102            let v = vec!["foo", "bar"];
1103            let ob = v.to_object(py);
1104            let seq = ob.downcast_bound::<PySequence>(py).unwrap();
1105            let type_ptr = seq.as_ref();
1106            let seq_from = unsafe { type_ptr.downcast_unchecked::<PySequence>() };
1107            assert!(seq_from.to_list().is_ok());
1108        });
1109    }
1110
1111    #[test]
1112    #[cfg(feature = "gil-refs")]
1113    #[allow(deprecated)]
1114    fn test_seq_try_from() {
1115        use crate::PyTryFrom;
1116        Python::with_gil(|py| {
1117            let list = PyList::empty(py);
1118            let _ = <PySequence as PyTryFrom>::try_from(list).unwrap();
1119            let _ = PySequence::try_from_exact(list).unwrap();
1120        });
1121    }
1122}