⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here

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};
12use crate::{
13    ffi, Borrowed, BoundObject, FromPyObject, IntoPyObject, IntoPyObjectExt, Py, PyTypeCheck,
14    Python,
15};
16
17/// Represents a reference to a Python object supporting the sequence protocol.
18///
19/// Values of this type are accessed via PyO3's smart pointers, e.g. as
20/// [`Py<PySequence>`][crate::Py] or [`Bound<'py, PySequence>`][Bound].
21///
22/// For APIs available on sequence objects, see the [`PySequenceMethods`] trait which is implemented for
23/// [`Bound<'py, PySequence>`][Bound].
24#[repr(transparent)]
25pub struct PySequence(PyAny);
26pyobject_native_type_named!(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(py);
34        get_sequence_abc(py)?.call_method1("register", (ty,))?;
35        Ok(())
36    }
37}
38
39/// Implementation of functionality for [`PySequence`].
40///
41/// These methods are defined for the `Bound<'py, PySequence>` smart pointer, so to use method call
42/// syntax these methods are separated into a trait, because stable Rust does not yet support
43/// `arbitrary_self_types`.
44#[doc(alias = "PySequence")]
45pub trait PySequenceMethods<'py>: crate::sealed::Sealed {
46    /// Returns the number of objects in sequence.
47    ///
48    /// This is equivalent to the Python expression `len(self)`.
49    fn len(&self) -> PyResult<usize>;
50
51    /// Returns whether the sequence is empty.
52    fn is_empty(&self) -> PyResult<bool>;
53
54    /// Returns the concatenation of `self` and `other`.
55    ///
56    /// This is equivalent to the Python expression `self + other`.
57    fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
58
59    /// Returns the result of repeating a sequence object `count` times.
60    ///
61    /// This is equivalent to the Python expression `self * count`.
62    fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
63
64    /// Concatenates `self` and `other`, in place if possible.
65    ///
66    /// This is equivalent to the Python expression `self.__iadd__(other)`.
67    ///
68    /// The Python statement `self += other` is syntactic sugar for `self =
69    /// self.__iadd__(other)`.  `__iadd__` should modify and return `self` if
70    /// possible, but create and return a new object if not.
71    fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
72
73    /// Repeats the sequence object `count` times and updates `self`, if possible.
74    ///
75    /// This is equivalent to the Python expression `self.__imul__(other)`.
76    ///
77    /// The Python statement `self *= other` is syntactic sugar for `self =
78    /// self.__imul__(other)`.  `__imul__` should modify and return `self` if
79    /// possible, but create and return a new object if not.
80    fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
81
82    /// Returns the `index`th element of the Sequence.
83    ///
84    /// This is equivalent to the Python expression `self[index]` without support of negative indices.
85    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
86
87    /// Returns the slice of sequence object between `begin` and `end`.
88    ///
89    /// This is equivalent to the Python expression `self[begin:end]`.
90    fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>>;
91
92    /// Assigns object `item` to the `i`th element of self.
93    ///
94    /// This is equivalent to the Python statement `self[i] = v`.
95    fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
96    where
97        I: IntoPyObject<'py>;
98
99    /// Deletes the `i`th element of self.
100    ///
101    /// This is equivalent to the Python statement `del self[i]`.
102    fn del_item(&self, i: usize) -> PyResult<()>;
103
104    /// Assigns the sequence `v` to the slice of `self` from `i1` to `i2`.
105    ///
106    /// This is equivalent to the Python statement `self[i1:i2] = v`.
107    fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()>;
108
109    /// Deletes the slice from `i1` to `i2` from `self`.
110    ///
111    /// This is equivalent to the Python statement `del self[i1:i2]`.
112    fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()>;
113
114    /// Returns the number of occurrences of `value` in self, that is, return the
115    /// number of keys for which `self[key] == value`.
116    #[cfg(not(PyPy))]
117    fn count<V>(&self, value: V) -> PyResult<usize>
118    where
119        V: IntoPyObject<'py>;
120
121    /// Determines if self contains `value`.
122    ///
123    /// This is equivalent to the Python expression `value in self`.
124    fn contains<V>(&self, value: V) -> PyResult<bool>
125    where
126        V: IntoPyObject<'py>;
127
128    /// Returns the first index `i` for which `self[i] == value`.
129    ///
130    /// This is equivalent to the Python expression `self.index(value)`.
131    fn index<V>(&self, value: V) -> PyResult<usize>
132    where
133        V: IntoPyObject<'py>;
134
135    /// Returns a fresh list based on the Sequence.
136    fn to_list(&self) -> PyResult<Bound<'py, PyList>>;
137
138    /// Returns a fresh tuple based on the Sequence.
139    fn to_tuple(&self) -> PyResult<Bound<'py, PyTuple>>;
140}
141
142impl<'py> PySequenceMethods<'py> for Bound<'py, PySequence> {
143    #[inline]
144    fn len(&self) -> PyResult<usize> {
145        let v = unsafe { ffi::PySequence_Size(self.as_ptr()) };
146        crate::err::error_on_minusone(self.py(), v)?;
147        Ok(v as usize)
148    }
149
150    #[inline]
151    fn is_empty(&self) -> PyResult<bool> {
152        self.len().map(|l| l == 0)
153    }
154
155    #[inline]
156    fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>> {
157        unsafe {
158            ffi::PySequence_Concat(self.as_ptr(), other.as_ptr())
159                .assume_owned_or_err(self.py())
160                .downcast_into_unchecked()
161        }
162    }
163
164    #[inline]
165    fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>> {
166        unsafe {
167            ffi::PySequence_Repeat(self.as_ptr(), get_ssize_index(count))
168                .assume_owned_or_err(self.py())
169                .downcast_into_unchecked()
170        }
171    }
172
173    #[inline]
174    fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>> {
175        unsafe {
176            ffi::PySequence_InPlaceConcat(self.as_ptr(), other.as_ptr())
177                .assume_owned_or_err(self.py())
178                .downcast_into_unchecked()
179        }
180    }
181
182    #[inline]
183    fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>> {
184        unsafe {
185            ffi::PySequence_InPlaceRepeat(self.as_ptr(), get_ssize_index(count))
186                .assume_owned_or_err(self.py())
187                .downcast_into_unchecked()
188        }
189    }
190
191    #[inline]
192    fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>> {
193        unsafe {
194            ffi::PySequence_GetItem(self.as_ptr(), get_ssize_index(index))
195                .assume_owned_or_err(self.py())
196        }
197    }
198
199    #[inline]
200    fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>> {
201        unsafe {
202            ffi::PySequence_GetSlice(self.as_ptr(), get_ssize_index(begin), get_ssize_index(end))
203                .assume_owned_or_err(self.py())
204                .downcast_into_unchecked()
205        }
206    }
207
208    #[inline]
209    fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
210    where
211        I: IntoPyObject<'py>,
212    {
213        fn inner(
214            seq: &Bound<'_, PySequence>,
215            i: usize,
216            item: Borrowed<'_, '_, PyAny>,
217        ) -> PyResult<()> {
218            err::error_on_minusone(seq.py(), unsafe {
219                ffi::PySequence_SetItem(seq.as_ptr(), get_ssize_index(i), item.as_ptr())
220            })
221        }
222
223        let py = self.py();
224        inner(
225            self,
226            i,
227            item.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
228        )
229    }
230
231    #[inline]
232    fn del_item(&self, i: usize) -> PyResult<()> {
233        err::error_on_minusone(self.py(), unsafe {
234            ffi::PySequence_DelItem(self.as_ptr(), get_ssize_index(i))
235        })
236    }
237
238    #[inline]
239    fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()> {
240        err::error_on_minusone(self.py(), unsafe {
241            ffi::PySequence_SetSlice(
242                self.as_ptr(),
243                get_ssize_index(i1),
244                get_ssize_index(i2),
245                v.as_ptr(),
246            )
247        })
248    }
249
250    #[inline]
251    fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()> {
252        err::error_on_minusone(self.py(), unsafe {
253            ffi::PySequence_DelSlice(self.as_ptr(), get_ssize_index(i1), get_ssize_index(i2))
254        })
255    }
256
257    #[inline]
258    #[cfg(not(PyPy))]
259    fn count<V>(&self, value: V) -> PyResult<usize>
260    where
261        V: IntoPyObject<'py>,
262    {
263        fn inner(seq: &Bound<'_, PySequence>, value: Borrowed<'_, '_, PyAny>) -> PyResult<usize> {
264            let r = unsafe { ffi::PySequence_Count(seq.as_ptr(), value.as_ptr()) };
265            crate::err::error_on_minusone(seq.py(), r)?;
266            Ok(r as usize)
267        }
268
269        let py = self.py();
270        inner(
271            self,
272            value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
273        )
274    }
275
276    #[inline]
277    fn contains<V>(&self, value: V) -> PyResult<bool>
278    where
279        V: IntoPyObject<'py>,
280    {
281        fn inner(seq: &Bound<'_, PySequence>, value: Borrowed<'_, '_, PyAny>) -> PyResult<bool> {
282            let r = unsafe { ffi::PySequence_Contains(seq.as_ptr(), value.as_ptr()) };
283            match r {
284                0 => Ok(false),
285                1 => Ok(true),
286                _ => Err(PyErr::fetch(seq.py())),
287            }
288        }
289
290        let py = self.py();
291        inner(
292            self,
293            value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
294        )
295    }
296
297    #[inline]
298    fn index<V>(&self, value: V) -> PyResult<usize>
299    where
300        V: IntoPyObject<'py>,
301    {
302        fn inner(seq: &Bound<'_, PySequence>, value: Borrowed<'_, '_, PyAny>) -> PyResult<usize> {
303            let r = unsafe { ffi::PySequence_Index(seq.as_ptr(), value.as_ptr()) };
304            crate::err::error_on_minusone(seq.py(), r)?;
305            Ok(r as usize)
306        }
307
308        let py = self.py();
309        inner(
310            self,
311            value.into_pyobject_or_pyerr(py)?.into_any().as_borrowed(),
312        )
313    }
314
315    #[inline]
316    fn to_list(&self) -> PyResult<Bound<'py, PyList>> {
317        unsafe {
318            ffi::PySequence_List(self.as_ptr())
319                .assume_owned_or_err(self.py())
320                .downcast_into_unchecked()
321        }
322    }
323
324    #[inline]
325    fn to_tuple(&self) -> PyResult<Bound<'py, PyTuple>> {
326        unsafe {
327            ffi::PySequence_Tuple(self.as_ptr())
328                .assume_owned_or_err(self.py())
329                .downcast_into_unchecked()
330        }
331    }
332}
333
334impl<'py, T> FromPyObject<'py> for Vec<T>
335where
336    T: FromPyObject<'py>,
337{
338    fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
339        if obj.is_instance_of::<PyString>() {
340            return Err(PyTypeError::new_err("Can't extract `str` to `Vec`"));
341        }
342        extract_sequence(obj)
343    }
344
345    #[cfg(feature = "experimental-inspect")]
346    fn type_input() -> TypeInfo {
347        TypeInfo::sequence_of(T::type_input())
348    }
349}
350
351fn extract_sequence<'py, T>(obj: &Bound<'py, PyAny>) -> PyResult<Vec<T>>
352where
353    T: FromPyObject<'py>,
354{
355    // Types that pass `PySequence_Check` usually implement enough of the sequence protocol
356    // to support this function and if not, we will only fail extraction safely.
357    let seq = unsafe {
358        if ffi::PySequence_Check(obj.as_ptr()) != 0 {
359            obj.downcast_unchecked::<PySequence>()
360        } else {
361            return Err(DowncastError::new(obj, "Sequence").into());
362        }
363    };
364
365    let mut v = Vec::with_capacity(seq.len().unwrap_or(0));
366    for item in seq.try_iter()? {
367        v.push(item?.extract::<T>()?);
368    }
369    Ok(v)
370}
371
372fn get_sequence_abc(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
373    static SEQUENCE_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new();
374
375    SEQUENCE_ABC.import(py, "collections.abc", "Sequence")
376}
377
378impl PyTypeCheck for PySequence {
379    const NAME: &'static str = "Sequence";
380
381    #[inline]
382    fn type_check(object: &Bound<'_, PyAny>) -> bool {
383        // Using `is_instance` for `collections.abc.Sequence` is slow, so provide
384        // optimized cases for list and tuples as common well-known sequences
385        PyList::is_type_of(object)
386            || PyTuple::is_type_of(object)
387            || get_sequence_abc(object.py())
388                .and_then(|abc| object.is_instance(abc))
389                .unwrap_or_else(|err| {
390                    err.write_unraisable(object.py(), Some(object));
391                    false
392                })
393    }
394}
395
396#[cfg(test)]
397mod tests {
398    use crate::types::{PyAnyMethods, PyList, PySequence, PySequenceMethods, PyTuple};
399    use crate::{ffi, IntoPyObject, PyObject, Python};
400
401    fn get_object() -> PyObject {
402        // Convenience function for getting a single unique object
403        Python::with_gil(|py| {
404            let obj = py.eval(ffi::c_str!("object()"), None, None).unwrap();
405
406            obj.into_pyobject(py).unwrap().unbind()
407        })
408    }
409
410    #[test]
411    fn test_numbers_are_not_sequences() {
412        Python::with_gil(|py| {
413            let v = 42i32;
414            assert!(v
415                .into_pyobject(py)
416                .unwrap()
417                .downcast::<PySequence>()
418                .is_err());
419        });
420    }
421
422    #[test]
423    fn test_strings_are_sequences() {
424        Python::with_gil(|py| {
425            let v = "London Calling";
426            assert!(v
427                .into_pyobject(py)
428                .unwrap()
429                .downcast::<PySequence>()
430                .is_ok());
431        });
432    }
433
434    #[test]
435    fn test_strings_cannot_be_extracted_to_vec() {
436        Python::with_gil(|py| {
437            let v = "London Calling";
438            let ob = v.into_pyobject(py).unwrap();
439
440            assert!(ob.extract::<Vec<String>>().is_err());
441            assert!(ob.extract::<Vec<char>>().is_err());
442        });
443    }
444
445    #[test]
446    fn test_seq_empty() {
447        Python::with_gil(|py| {
448            let v: Vec<i32> = vec![];
449            let ob = v.into_pyobject(py).unwrap();
450            let seq = ob.downcast::<PySequence>().unwrap();
451            assert_eq!(0, seq.len().unwrap());
452
453            let needle = 7i32.into_pyobject(py).unwrap();
454            assert!(!seq.contains(&needle).unwrap());
455        });
456    }
457
458    #[test]
459    fn test_seq_is_empty() {
460        Python::with_gil(|py| {
461            let list = vec![1].into_pyobject(py).unwrap();
462            let seq = list.downcast::<PySequence>().unwrap();
463            assert!(!seq.is_empty().unwrap());
464            let vec: Vec<u32> = Vec::new();
465            let empty_list = vec.into_pyobject(py).unwrap();
466            let empty_seq = empty_list.downcast::<PySequence>().unwrap();
467            assert!(empty_seq.is_empty().unwrap());
468        });
469    }
470
471    #[test]
472    fn test_seq_contains() {
473        Python::with_gil(|py| {
474            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
475            let ob = v.into_pyobject(py).unwrap();
476            let seq = ob.downcast::<PySequence>().unwrap();
477            assert_eq!(6, seq.len().unwrap());
478
479            let bad_needle = 7i32.into_pyobject(py).unwrap();
480            assert!(!seq.contains(&bad_needle).unwrap());
481
482            let good_needle = 8i32.into_pyobject(py).unwrap();
483            assert!(seq.contains(&good_needle).unwrap());
484
485            let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
486            assert!(seq.contains(&type_coerced_needle).unwrap());
487        });
488    }
489
490    #[test]
491    fn test_seq_get_item() {
492        Python::with_gil(|py| {
493            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
494            let ob = v.into_pyobject(py).unwrap();
495            let seq = ob.downcast::<PySequence>().unwrap();
496            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
497            assert_eq!(1, seq.get_item(1).unwrap().extract::<i32>().unwrap());
498            assert_eq!(2, seq.get_item(2).unwrap().extract::<i32>().unwrap());
499            assert_eq!(3, seq.get_item(3).unwrap().extract::<i32>().unwrap());
500            assert_eq!(5, seq.get_item(4).unwrap().extract::<i32>().unwrap());
501            assert_eq!(8, seq.get_item(5).unwrap().extract::<i32>().unwrap());
502            assert!(seq.get_item(10).is_err());
503        });
504    }
505
506    #[test]
507    fn test_seq_del_item() {
508        Python::with_gil(|py| {
509            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
510            let ob = v.into_pyobject(py).unwrap();
511            let seq = ob.downcast::<PySequence>().unwrap();
512            assert!(seq.del_item(10).is_err());
513            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
514            assert!(seq.del_item(0).is_ok());
515            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
516            assert!(seq.del_item(0).is_ok());
517            assert_eq!(2, seq.get_item(0).unwrap().extract::<i32>().unwrap());
518            assert!(seq.del_item(0).is_ok());
519            assert_eq!(3, seq.get_item(0).unwrap().extract::<i32>().unwrap());
520            assert!(seq.del_item(0).is_ok());
521            assert_eq!(5, seq.get_item(0).unwrap().extract::<i32>().unwrap());
522            assert!(seq.del_item(0).is_ok());
523            assert_eq!(8, seq.get_item(0).unwrap().extract::<i32>().unwrap());
524            assert!(seq.del_item(0).is_ok());
525            assert_eq!(0, seq.len().unwrap());
526            assert!(seq.del_item(0).is_err());
527        });
528    }
529
530    #[test]
531    fn test_seq_set_item() {
532        Python::with_gil(|py| {
533            let v: Vec<i32> = vec![1, 2];
534            let ob = v.into_pyobject(py).unwrap();
535            let seq = ob.downcast::<PySequence>().unwrap();
536            assert_eq!(2, seq.get_item(1).unwrap().extract::<i32>().unwrap());
537            assert!(seq.set_item(1, 10).is_ok());
538            assert_eq!(10, seq.get_item(1).unwrap().extract::<i32>().unwrap());
539        });
540    }
541
542    #[test]
543    fn test_seq_set_item_refcnt() {
544        let obj = get_object();
545
546        Python::with_gil(|py| {
547            let v: Vec<i32> = vec![1, 2];
548            let ob = v.into_pyobject(py).unwrap();
549            let seq = ob.downcast::<PySequence>().unwrap();
550            assert!(seq.set_item(1, &obj).is_ok());
551            assert!(seq.get_item(1).unwrap().as_ptr() == obj.as_ptr());
552        });
553
554        Python::with_gil(move |py| {
555            assert_eq!(1, obj.get_refcnt(py));
556        });
557    }
558
559    #[test]
560    fn test_seq_get_slice() {
561        Python::with_gil(|py| {
562            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
563            let ob = v.into_pyobject(py).unwrap();
564            let seq = ob.downcast::<PySequence>().unwrap();
565            assert_eq!(
566                [1, 2, 3],
567                seq.get_slice(1, 4).unwrap().extract::<[i32; 3]>().unwrap()
568            );
569            assert_eq!(
570                [3, 5, 8],
571                seq.get_slice(3, 100)
572                    .unwrap()
573                    .extract::<[i32; 3]>()
574                    .unwrap()
575            );
576        });
577    }
578
579    #[test]
580    fn test_set_slice() {
581        Python::with_gil(|py| {
582            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
583            let w: Vec<i32> = vec![7, 4];
584            let ob = v.into_pyobject(py).unwrap();
585            let seq = ob.downcast::<PySequence>().unwrap();
586            let ins = w.into_pyobject(py).unwrap();
587            seq.set_slice(1, 4, &ins).unwrap();
588            assert_eq!([1, 7, 4, 5, 8], seq.extract::<[i32; 5]>().unwrap());
589            seq.set_slice(3, 100, &PyList::empty(py)).unwrap();
590            assert_eq!([1, 7, 4], seq.extract::<[i32; 3]>().unwrap());
591        });
592    }
593
594    #[test]
595    fn test_del_slice() {
596        Python::with_gil(|py| {
597            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
598            let ob = v.into_pyobject(py).unwrap();
599            let seq = ob.downcast::<PySequence>().unwrap();
600            seq.del_slice(1, 4).unwrap();
601            assert_eq!([1, 5, 8], seq.extract::<[i32; 3]>().unwrap());
602            seq.del_slice(1, 100).unwrap();
603            assert_eq!([1], seq.extract::<[i32; 1]>().unwrap());
604        });
605    }
606
607    #[test]
608    fn test_seq_index() {
609        Python::with_gil(|py| {
610            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
611            let ob = v.into_pyobject(py).unwrap();
612            let seq = ob.downcast::<PySequence>().unwrap();
613            assert_eq!(0, seq.index(1i32).unwrap());
614            assert_eq!(2, seq.index(2i32).unwrap());
615            assert_eq!(3, seq.index(3i32).unwrap());
616            assert_eq!(4, seq.index(5i32).unwrap());
617            assert_eq!(5, seq.index(8i32).unwrap());
618            assert!(seq.index(42i32).is_err());
619        });
620    }
621
622    #[test]
623    #[cfg(not(any(PyPy, GraalPy)))]
624    fn test_seq_count() {
625        Python::with_gil(|py| {
626            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
627            let ob = v.into_pyobject(py).unwrap();
628            let seq = ob.downcast::<PySequence>().unwrap();
629            assert_eq!(2, seq.count(1i32).unwrap());
630            assert_eq!(1, seq.count(2i32).unwrap());
631            assert_eq!(1, seq.count(3i32).unwrap());
632            assert_eq!(1, seq.count(5i32).unwrap());
633            assert_eq!(1, seq.count(8i32).unwrap());
634            assert_eq!(0, seq.count(42i32).unwrap());
635        });
636    }
637
638    #[test]
639    fn test_seq_iter() {
640        Python::with_gil(|py| {
641            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
642            let ob = (&v).into_pyobject(py).unwrap();
643            let seq = ob.downcast::<PySequence>().unwrap();
644            let mut idx = 0;
645            for el in seq.try_iter().unwrap() {
646                assert_eq!(v[idx], el.unwrap().extract::<i32>().unwrap());
647                idx += 1;
648            }
649            assert_eq!(idx, v.len());
650        });
651    }
652
653    #[test]
654    fn test_seq_strings() {
655        Python::with_gil(|py| {
656            let v = vec!["It", "was", "the", "worst", "of", "times"];
657            let ob = v.into_pyobject(py).unwrap();
658            let seq = ob.downcast::<PySequence>().unwrap();
659
660            let bad_needle = "blurst".into_pyobject(py).unwrap();
661            assert!(!seq.contains(bad_needle).unwrap());
662
663            let good_needle = "worst".into_pyobject(py).unwrap();
664            assert!(seq.contains(good_needle).unwrap());
665        });
666    }
667
668    #[test]
669    fn test_seq_concat() {
670        Python::with_gil(|py| {
671            let v: Vec<i32> = vec![1, 2, 3];
672            let ob = v.into_pyobject(py).unwrap();
673            let seq = ob.downcast::<PySequence>().unwrap();
674            let concat_seq = seq.concat(seq).unwrap();
675            assert_eq!(6, concat_seq.len().unwrap());
676            let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
677            for (el, cc) in concat_seq.try_iter().unwrap().zip(concat_v) {
678                assert_eq!(cc, el.unwrap().extract::<i32>().unwrap());
679            }
680        });
681    }
682
683    #[test]
684    fn test_seq_concat_string() {
685        Python::with_gil(|py| {
686            let v = "string";
687            let ob = v.into_pyobject(py).unwrap();
688            let seq = ob.downcast::<PySequence>().unwrap();
689            let concat_seq = seq.concat(seq).unwrap();
690            assert_eq!(12, concat_seq.len().unwrap());
691            let concat_v = "stringstring".to_owned();
692            for (el, cc) in seq.try_iter().unwrap().zip(concat_v.chars()) {
693                assert_eq!(cc, el.unwrap().extract::<char>().unwrap());
694            }
695        });
696    }
697
698    #[test]
699    fn test_seq_repeat() {
700        Python::with_gil(|py| {
701            let v = vec!["foo", "bar"];
702            let ob = v.into_pyobject(py).unwrap();
703            let seq = ob.downcast::<PySequence>().unwrap();
704            let repeat_seq = seq.repeat(3).unwrap();
705            assert_eq!(6, repeat_seq.len().unwrap());
706            let repeated = ["foo", "bar", "foo", "bar", "foo", "bar"];
707            for (el, rpt) in repeat_seq.try_iter().unwrap().zip(repeated.iter()) {
708                assert_eq!(*rpt, el.unwrap().extract::<String>().unwrap());
709            }
710        });
711    }
712
713    #[test]
714    fn test_seq_inplace() {
715        Python::with_gil(|py| {
716            let v = vec!["foo", "bar"];
717            let ob = v.into_pyobject(py).unwrap();
718            let seq = ob.downcast::<PySequence>().unwrap();
719            let rep_seq = seq.in_place_repeat(3).unwrap();
720            assert_eq!(6, seq.len().unwrap());
721            assert!(seq.is(&rep_seq));
722
723            let conc_seq = seq.in_place_concat(seq).unwrap();
724            assert_eq!(12, seq.len().unwrap());
725            assert!(seq.is(&conc_seq));
726        });
727    }
728
729    #[test]
730    fn test_list_coercion() {
731        Python::with_gil(|py| {
732            let v = vec!["foo", "bar"];
733            let ob = (&v).into_pyobject(py).unwrap();
734            let seq = ob.downcast::<PySequence>().unwrap();
735            assert!(seq
736                .to_list()
737                .unwrap()
738                .eq(PyList::new(py, &v).unwrap())
739                .unwrap());
740        });
741    }
742
743    #[test]
744    fn test_strings_coerce_to_lists() {
745        Python::with_gil(|py| {
746            let v = "foo";
747            let ob = v.into_pyobject(py).unwrap();
748            let seq = ob.downcast::<PySequence>().unwrap();
749            assert!(seq
750                .to_list()
751                .unwrap()
752                .eq(PyList::new(py, ["f", "o", "o"]).unwrap())
753                .unwrap());
754        });
755    }
756
757    #[test]
758    fn test_tuple_coercion() {
759        Python::with_gil(|py| {
760            let v = ("foo", "bar");
761            let ob = v.into_pyobject(py).unwrap();
762            let seq = ob.downcast::<PySequence>().unwrap();
763            assert!(seq
764                .to_tuple()
765                .unwrap()
766                .eq(PyTuple::new(py, ["foo", "bar"]).unwrap())
767                .unwrap());
768        });
769    }
770
771    #[test]
772    fn test_lists_coerce_to_tuples() {
773        Python::with_gil(|py| {
774            let v = vec!["foo", "bar"];
775            let ob = (&v).into_pyobject(py).unwrap();
776            let seq = ob.downcast::<PySequence>().unwrap();
777            assert!(seq
778                .to_tuple()
779                .unwrap()
780                .eq(PyTuple::new(py, &v).unwrap())
781                .unwrap());
782        });
783    }
784
785    #[test]
786    fn test_extract_tuple_to_vec() {
787        Python::with_gil(|py| {
788            let v: Vec<i32> = py
789                .eval(ffi::c_str!("(1, 2)"), None, None)
790                .unwrap()
791                .extract()
792                .unwrap();
793            assert!(v == [1, 2]);
794        });
795    }
796
797    #[test]
798    fn test_extract_range_to_vec() {
799        Python::with_gil(|py| {
800            let v: Vec<i32> = py
801                .eval(ffi::c_str!("range(1, 5)"), None, None)
802                .unwrap()
803                .extract()
804                .unwrap();
805            assert!(v == [1, 2, 3, 4]);
806        });
807    }
808
809    #[test]
810    fn test_extract_bytearray_to_vec() {
811        Python::with_gil(|py| {
812            let v: Vec<u8> = py
813                .eval(ffi::c_str!("bytearray(b'abc')"), None, None)
814                .unwrap()
815                .extract()
816                .unwrap();
817            assert!(v == b"abc");
818        });
819    }
820
821    #[test]
822    fn test_seq_downcast_unchecked() {
823        Python::with_gil(|py| {
824            let v = vec!["foo", "bar"];
825            let ob = v.into_pyobject(py).unwrap();
826            let seq = ob.downcast::<PySequence>().unwrap();
827            let type_ptr = seq.as_ref();
828            let seq_from = unsafe { type_ptr.downcast_unchecked::<PySequence>() };
829            assert!(seq_from.to_list().is_ok());
830        });
831    }
832}