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::PyOnceLock;
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 castable 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                .cast_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                .cast_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                .cast_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                .cast_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                .cast_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                .cast_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                .cast_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.cast_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: PyOnceLock<Py<PyType>> = PyOnceLock::new();
374
375    SEQUENCE_ABC.import(py, "collections.abc", "Sequence")
376}
377
378impl PyTypeCheck for PySequence {
379    const NAME: &'static str = "Sequence";
380    #[cfg(feature = "experimental-inspect")]
381    const PYTHON_TYPE: &'static str = "collections.abc.Sequence";
382
383    #[inline]
384    fn type_check(object: &Bound<'_, PyAny>) -> bool {
385        // Using `is_instance` for `collections.abc.Sequence` is slow, so provide
386        // optimized cases for list and tuples as common well-known sequences
387        PyList::is_type_of(object)
388            || PyTuple::is_type_of(object)
389            || get_sequence_abc(object.py())
390                .and_then(|abc| object.is_instance(abc))
391                .unwrap_or_else(|err| {
392                    err.write_unraisable(object.py(), Some(object));
393                    false
394                })
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use crate::types::{PyAnyMethods, PyList, PySequence, PySequenceMethods, PyTuple};
401    use crate::{ffi, IntoPyObject, Py, PyAny, Python};
402    use std::ptr;
403
404    fn get_object() -> Py<PyAny> {
405        // Convenience function for getting a single unique object
406        Python::attach(|py| {
407            let obj = py.eval(ffi::c_str!("object()"), None, None).unwrap();
408
409            obj.into_pyobject(py).unwrap().unbind()
410        })
411    }
412
413    #[test]
414    fn test_numbers_are_not_sequences() {
415        Python::attach(|py| {
416            let v = 42i32;
417            assert!(v.into_pyobject(py).unwrap().cast::<PySequence>().is_err());
418        });
419    }
420
421    #[test]
422    fn test_strings_are_sequences() {
423        Python::attach(|py| {
424            let v = "London Calling";
425            assert!(v.into_pyobject(py).unwrap().cast::<PySequence>().is_ok());
426        });
427    }
428
429    #[test]
430    fn test_strings_cannot_be_extracted_to_vec() {
431        Python::attach(|py| {
432            let v = "London Calling";
433            let ob = v.into_pyobject(py).unwrap();
434
435            assert!(ob.extract::<Vec<String>>().is_err());
436            assert!(ob.extract::<Vec<char>>().is_err());
437        });
438    }
439
440    #[test]
441    fn test_seq_empty() {
442        Python::attach(|py| {
443            let v: Vec<i32> = vec![];
444            let ob = v.into_pyobject(py).unwrap();
445            let seq = ob.cast::<PySequence>().unwrap();
446            assert_eq!(0, seq.len().unwrap());
447
448            let needle = 7i32.into_pyobject(py).unwrap();
449            assert!(!seq.contains(&needle).unwrap());
450        });
451    }
452
453    #[test]
454    fn test_seq_is_empty() {
455        Python::attach(|py| {
456            let list = vec![1].into_pyobject(py).unwrap();
457            let seq = list.cast::<PySequence>().unwrap();
458            assert!(!seq.is_empty().unwrap());
459            let vec: Vec<u32> = Vec::new();
460            let empty_list = vec.into_pyobject(py).unwrap();
461            let empty_seq = empty_list.cast::<PySequence>().unwrap();
462            assert!(empty_seq.is_empty().unwrap());
463        });
464    }
465
466    #[test]
467    fn test_seq_contains() {
468        Python::attach(|py| {
469            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
470            let ob = v.into_pyobject(py).unwrap();
471            let seq = ob.cast::<PySequence>().unwrap();
472            assert_eq!(6, seq.len().unwrap());
473
474            let bad_needle = 7i32.into_pyobject(py).unwrap();
475            assert!(!seq.contains(&bad_needle).unwrap());
476
477            let good_needle = 8i32.into_pyobject(py).unwrap();
478            assert!(seq.contains(&good_needle).unwrap());
479
480            let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
481            assert!(seq.contains(&type_coerced_needle).unwrap());
482        });
483    }
484
485    #[test]
486    fn test_seq_get_item() {
487        Python::attach(|py| {
488            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
489            let ob = v.into_pyobject(py).unwrap();
490            let seq = ob.cast::<PySequence>().unwrap();
491            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
492            assert_eq!(1, seq.get_item(1).unwrap().extract::<i32>().unwrap());
493            assert_eq!(2, seq.get_item(2).unwrap().extract::<i32>().unwrap());
494            assert_eq!(3, seq.get_item(3).unwrap().extract::<i32>().unwrap());
495            assert_eq!(5, seq.get_item(4).unwrap().extract::<i32>().unwrap());
496            assert_eq!(8, seq.get_item(5).unwrap().extract::<i32>().unwrap());
497            assert!(seq.get_item(10).is_err());
498        });
499    }
500
501    #[test]
502    fn test_seq_del_item() {
503        Python::attach(|py| {
504            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
505            let ob = v.into_pyobject(py).unwrap();
506            let seq = ob.cast::<PySequence>().unwrap();
507            assert!(seq.del_item(10).is_err());
508            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
509            assert!(seq.del_item(0).is_ok());
510            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
511            assert!(seq.del_item(0).is_ok());
512            assert_eq!(2, seq.get_item(0).unwrap().extract::<i32>().unwrap());
513            assert!(seq.del_item(0).is_ok());
514            assert_eq!(3, seq.get_item(0).unwrap().extract::<i32>().unwrap());
515            assert!(seq.del_item(0).is_ok());
516            assert_eq!(5, seq.get_item(0).unwrap().extract::<i32>().unwrap());
517            assert!(seq.del_item(0).is_ok());
518            assert_eq!(8, seq.get_item(0).unwrap().extract::<i32>().unwrap());
519            assert!(seq.del_item(0).is_ok());
520            assert_eq!(0, seq.len().unwrap());
521            assert!(seq.del_item(0).is_err());
522        });
523    }
524
525    #[test]
526    fn test_seq_set_item() {
527        Python::attach(|py| {
528            let v: Vec<i32> = vec![1, 2];
529            let ob = v.into_pyobject(py).unwrap();
530            let seq = ob.cast::<PySequence>().unwrap();
531            assert_eq!(2, seq.get_item(1).unwrap().extract::<i32>().unwrap());
532            assert!(seq.set_item(1, 10).is_ok());
533            assert_eq!(10, seq.get_item(1).unwrap().extract::<i32>().unwrap());
534        });
535    }
536
537    #[test]
538    fn test_seq_set_item_refcnt() {
539        let obj = get_object();
540
541        Python::attach(|py| {
542            let v: Vec<i32> = vec![1, 2];
543            let ob = v.into_pyobject(py).unwrap();
544            let seq = ob.cast::<PySequence>().unwrap();
545            assert!(seq.set_item(1, &obj).is_ok());
546            assert!(ptr::eq(seq.get_item(1).unwrap().as_ptr(), obj.as_ptr()));
547        });
548
549        Python::attach(move |py| {
550            assert_eq!(1, obj.get_refcnt(py));
551        });
552    }
553
554    #[test]
555    fn test_seq_get_slice() {
556        Python::attach(|py| {
557            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
558            let ob = v.into_pyobject(py).unwrap();
559            let seq = ob.cast::<PySequence>().unwrap();
560            assert_eq!(
561                [1, 2, 3],
562                seq.get_slice(1, 4).unwrap().extract::<[i32; 3]>().unwrap()
563            );
564            assert_eq!(
565                [3, 5, 8],
566                seq.get_slice(3, 100)
567                    .unwrap()
568                    .extract::<[i32; 3]>()
569                    .unwrap()
570            );
571        });
572    }
573
574    #[test]
575    fn test_set_slice() {
576        Python::attach(|py| {
577            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
578            let w: Vec<i32> = vec![7, 4];
579            let ob = v.into_pyobject(py).unwrap();
580            let seq = ob.cast::<PySequence>().unwrap();
581            let ins = w.into_pyobject(py).unwrap();
582            seq.set_slice(1, 4, &ins).unwrap();
583            assert_eq!([1, 7, 4, 5, 8], seq.extract::<[i32; 5]>().unwrap());
584            seq.set_slice(3, 100, &PyList::empty(py)).unwrap();
585            assert_eq!([1, 7, 4], seq.extract::<[i32; 3]>().unwrap());
586        });
587    }
588
589    #[test]
590    fn test_del_slice() {
591        Python::attach(|py| {
592            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
593            let ob = v.into_pyobject(py).unwrap();
594            let seq = ob.cast::<PySequence>().unwrap();
595            seq.del_slice(1, 4).unwrap();
596            assert_eq!([1, 5, 8], seq.extract::<[i32; 3]>().unwrap());
597            seq.del_slice(1, 100).unwrap();
598            assert_eq!([1], seq.extract::<[i32; 1]>().unwrap());
599        });
600    }
601
602    #[test]
603    fn test_seq_index() {
604        Python::attach(|py| {
605            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
606            let ob = v.into_pyobject(py).unwrap();
607            let seq = ob.cast::<PySequence>().unwrap();
608            assert_eq!(0, seq.index(1i32).unwrap());
609            assert_eq!(2, seq.index(2i32).unwrap());
610            assert_eq!(3, seq.index(3i32).unwrap());
611            assert_eq!(4, seq.index(5i32).unwrap());
612            assert_eq!(5, seq.index(8i32).unwrap());
613            assert!(seq.index(42i32).is_err());
614        });
615    }
616
617    #[test]
618    #[cfg(not(any(PyPy, GraalPy)))]
619    fn test_seq_count() {
620        Python::attach(|py| {
621            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
622            let ob = v.into_pyobject(py).unwrap();
623            let seq = ob.cast::<PySequence>().unwrap();
624            assert_eq!(2, seq.count(1i32).unwrap());
625            assert_eq!(1, seq.count(2i32).unwrap());
626            assert_eq!(1, seq.count(3i32).unwrap());
627            assert_eq!(1, seq.count(5i32).unwrap());
628            assert_eq!(1, seq.count(8i32).unwrap());
629            assert_eq!(0, seq.count(42i32).unwrap());
630        });
631    }
632
633    #[test]
634    fn test_seq_iter() {
635        Python::attach(|py| {
636            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
637            let ob = (&v).into_pyobject(py).unwrap();
638            let seq = ob.cast::<PySequence>().unwrap();
639            let mut idx = 0;
640            for el in seq.try_iter().unwrap() {
641                assert_eq!(v[idx], el.unwrap().extract::<i32>().unwrap());
642                idx += 1;
643            }
644            assert_eq!(idx, v.len());
645        });
646    }
647
648    #[test]
649    fn test_seq_strings() {
650        Python::attach(|py| {
651            let v = vec!["It", "was", "the", "worst", "of", "times"];
652            let ob = v.into_pyobject(py).unwrap();
653            let seq = ob.cast::<PySequence>().unwrap();
654
655            let bad_needle = "blurst".into_pyobject(py).unwrap();
656            assert!(!seq.contains(bad_needle).unwrap());
657
658            let good_needle = "worst".into_pyobject(py).unwrap();
659            assert!(seq.contains(good_needle).unwrap());
660        });
661    }
662
663    #[test]
664    fn test_seq_concat() {
665        Python::attach(|py| {
666            let v: Vec<i32> = vec![1, 2, 3];
667            let ob = v.into_pyobject(py).unwrap();
668            let seq = ob.cast::<PySequence>().unwrap();
669            let concat_seq = seq.concat(seq).unwrap();
670            assert_eq!(6, concat_seq.len().unwrap());
671            let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
672            for (el, cc) in concat_seq.try_iter().unwrap().zip(concat_v) {
673                assert_eq!(cc, el.unwrap().extract::<i32>().unwrap());
674            }
675        });
676    }
677
678    #[test]
679    fn test_seq_concat_string() {
680        Python::attach(|py| {
681            let v = "string";
682            let ob = v.into_pyobject(py).unwrap();
683            let seq = ob.cast::<PySequence>().unwrap();
684            let concat_seq = seq.concat(seq).unwrap();
685            assert_eq!(12, concat_seq.len().unwrap());
686            let concat_v = "stringstring".to_owned();
687            for (el, cc) in seq.try_iter().unwrap().zip(concat_v.chars()) {
688                assert_eq!(cc, el.unwrap().extract::<char>().unwrap());
689            }
690        });
691    }
692
693    #[test]
694    fn test_seq_repeat() {
695        Python::attach(|py| {
696            let v = vec!["foo", "bar"];
697            let ob = v.into_pyobject(py).unwrap();
698            let seq = ob.cast::<PySequence>().unwrap();
699            let repeat_seq = seq.repeat(3).unwrap();
700            assert_eq!(6, repeat_seq.len().unwrap());
701            let repeated = ["foo", "bar", "foo", "bar", "foo", "bar"];
702            for (el, rpt) in repeat_seq.try_iter().unwrap().zip(repeated.iter()) {
703                assert_eq!(*rpt, el.unwrap().extract::<String>().unwrap());
704            }
705        });
706    }
707
708    #[test]
709    fn test_seq_inplace() {
710        Python::attach(|py| {
711            let v = vec!["foo", "bar"];
712            let ob = v.into_pyobject(py).unwrap();
713            let seq = ob.cast::<PySequence>().unwrap();
714            let rep_seq = seq.in_place_repeat(3).unwrap();
715            assert_eq!(6, seq.len().unwrap());
716            assert!(seq.is(&rep_seq));
717
718            let conc_seq = seq.in_place_concat(seq).unwrap();
719            assert_eq!(12, seq.len().unwrap());
720            assert!(seq.is(&conc_seq));
721        });
722    }
723
724    #[test]
725    fn test_list_coercion() {
726        Python::attach(|py| {
727            let v = vec!["foo", "bar"];
728            let ob = (&v).into_pyobject(py).unwrap();
729            let seq = ob.cast::<PySequence>().unwrap();
730            assert!(seq
731                .to_list()
732                .unwrap()
733                .eq(PyList::new(py, &v).unwrap())
734                .unwrap());
735        });
736    }
737
738    #[test]
739    fn test_strings_coerce_to_lists() {
740        Python::attach(|py| {
741            let v = "foo";
742            let ob = v.into_pyobject(py).unwrap();
743            let seq = ob.cast::<PySequence>().unwrap();
744            assert!(seq
745                .to_list()
746                .unwrap()
747                .eq(PyList::new(py, ["f", "o", "o"]).unwrap())
748                .unwrap());
749        });
750    }
751
752    #[test]
753    fn test_tuple_coercion() {
754        Python::attach(|py| {
755            let v = ("foo", "bar");
756            let ob = v.into_pyobject(py).unwrap();
757            let seq = ob.cast::<PySequence>().unwrap();
758            assert!(seq
759                .to_tuple()
760                .unwrap()
761                .eq(PyTuple::new(py, ["foo", "bar"]).unwrap())
762                .unwrap());
763        });
764    }
765
766    #[test]
767    fn test_lists_coerce_to_tuples() {
768        Python::attach(|py| {
769            let v = vec!["foo", "bar"];
770            let ob = (&v).into_pyobject(py).unwrap();
771            let seq = ob.cast::<PySequence>().unwrap();
772            assert!(seq
773                .to_tuple()
774                .unwrap()
775                .eq(PyTuple::new(py, &v).unwrap())
776                .unwrap());
777        });
778    }
779
780    #[test]
781    fn test_extract_tuple_to_vec() {
782        Python::attach(|py| {
783            let v: Vec<i32> = py
784                .eval(ffi::c_str!("(1, 2)"), None, None)
785                .unwrap()
786                .extract()
787                .unwrap();
788            assert!(v == [1, 2]);
789        });
790    }
791
792    #[test]
793    fn test_extract_range_to_vec() {
794        Python::attach(|py| {
795            let v: Vec<i32> = py
796                .eval(ffi::c_str!("range(1, 5)"), None, None)
797                .unwrap()
798                .extract()
799                .unwrap();
800            assert!(v == [1, 2, 3, 4]);
801        });
802    }
803
804    #[test]
805    fn test_extract_bytearray_to_vec() {
806        Python::attach(|py| {
807            let v: Vec<u8> = py
808                .eval(ffi::c_str!("bytearray(b'abc')"), None, None)
809                .unwrap()
810                .extract()
811                .unwrap();
812            assert!(v == b"abc");
813        });
814    }
815
816    #[test]
817    fn test_seq_cast_unchecked() {
818        Python::attach(|py| {
819            let v = vec!["foo", "bar"];
820            let ob = v.into_pyobject(py).unwrap();
821            let seq = ob.cast::<PySequence>().unwrap();
822            let type_ptr = seq.as_any();
823            let seq_from = unsafe { type_ptr.cast_unchecked::<PySequence>() };
824            assert!(seq_from.to_list().is_ok());
825        });
826    }
827}