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