Skip to main content

pyo3/types/
sequence.rs

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