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::types::{PyAnyMethods, PyList, PySequence, PySequenceMethods, PyTuple};
364    use crate::{IntoPyObject, Py, PyAny, PyTypeInfo, Python};
365    use std::ptr;
366
367    fn get_object() -> Py<PyAny> {
368        // Convenience function for getting a single unique object
369        Python::attach(|py| {
370            let obj = py.eval(c"object()", None, None).unwrap();
371
372            obj.into_pyobject(py).unwrap().unbind()
373        })
374    }
375
376    #[test]
377    fn test_numbers_are_not_sequences() {
378        Python::attach(|py| {
379            let v = 42i32;
380            assert!(v.into_pyobject(py).unwrap().cast::<PySequence>().is_err());
381        });
382    }
383
384    #[test]
385    fn test_strings_are_sequences() {
386        Python::attach(|py| {
387            let v = "London Calling";
388            assert!(v.into_pyobject(py).unwrap().cast::<PySequence>().is_ok());
389        });
390    }
391
392    #[test]
393    fn test_seq_empty() {
394        Python::attach(|py| {
395            let v: Vec<i32> = vec![];
396            let ob = v.into_pyobject(py).unwrap();
397            let seq = ob.cast::<PySequence>().unwrap();
398            assert_eq!(0, seq.len().unwrap());
399
400            let needle = 7i32.into_pyobject(py).unwrap();
401            assert!(!seq.contains(&needle).unwrap());
402        });
403    }
404
405    #[test]
406    fn test_seq_is_empty() {
407        Python::attach(|py| {
408            let list = vec![1].into_pyobject(py).unwrap();
409            let seq = list.cast::<PySequence>().unwrap();
410            assert!(!seq.is_empty().unwrap());
411            let vec: Vec<u32> = Vec::new();
412            let empty_list = vec.into_pyobject(py).unwrap();
413            let empty_seq = empty_list.cast::<PySequence>().unwrap();
414            assert!(empty_seq.is_empty().unwrap());
415        });
416    }
417
418    #[test]
419    fn test_seq_contains() {
420        Python::attach(|py| {
421            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
422            let ob = v.into_pyobject(py).unwrap();
423            let seq = ob.cast::<PySequence>().unwrap();
424            assert_eq!(6, seq.len().unwrap());
425
426            let bad_needle = 7i32.into_pyobject(py).unwrap();
427            assert!(!seq.contains(&bad_needle).unwrap());
428
429            let good_needle = 8i32.into_pyobject(py).unwrap();
430            assert!(seq.contains(&good_needle).unwrap());
431
432            let type_coerced_needle = 8f32.into_pyobject(py).unwrap();
433            assert!(seq.contains(&type_coerced_needle).unwrap());
434        });
435    }
436
437    #[test]
438    fn test_seq_get_item() {
439        Python::attach(|py| {
440            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
441            let ob = v.into_pyobject(py).unwrap();
442            let seq = ob.cast::<PySequence>().unwrap();
443            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
444            assert_eq!(1, seq.get_item(1).unwrap().extract::<i32>().unwrap());
445            assert_eq!(2, seq.get_item(2).unwrap().extract::<i32>().unwrap());
446            assert_eq!(3, seq.get_item(3).unwrap().extract::<i32>().unwrap());
447            assert_eq!(5, seq.get_item(4).unwrap().extract::<i32>().unwrap());
448            assert_eq!(8, seq.get_item(5).unwrap().extract::<i32>().unwrap());
449            assert!(seq.get_item(10).is_err());
450        });
451    }
452
453    #[test]
454    fn test_seq_del_item() {
455        Python::attach(|py| {
456            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
457            let ob = v.into_pyobject(py).unwrap();
458            let seq = ob.cast::<PySequence>().unwrap();
459            assert!(seq.del_item(10).is_err());
460            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
461            assert!(seq.del_item(0).is_ok());
462            assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap());
463            assert!(seq.del_item(0).is_ok());
464            assert_eq!(2, seq.get_item(0).unwrap().extract::<i32>().unwrap());
465            assert!(seq.del_item(0).is_ok());
466            assert_eq!(3, seq.get_item(0).unwrap().extract::<i32>().unwrap());
467            assert!(seq.del_item(0).is_ok());
468            assert_eq!(5, seq.get_item(0).unwrap().extract::<i32>().unwrap());
469            assert!(seq.del_item(0).is_ok());
470            assert_eq!(8, seq.get_item(0).unwrap().extract::<i32>().unwrap());
471            assert!(seq.del_item(0).is_ok());
472            assert_eq!(0, seq.len().unwrap());
473            assert!(seq.del_item(0).is_err());
474        });
475    }
476
477    #[test]
478    fn test_seq_set_item() {
479        Python::attach(|py| {
480            let v: Vec<i32> = vec![1, 2];
481            let ob = v.into_pyobject(py).unwrap();
482            let seq = ob.cast::<PySequence>().unwrap();
483            assert_eq!(2, seq.get_item(1).unwrap().extract::<i32>().unwrap());
484            assert!(seq.set_item(1, 10).is_ok());
485            assert_eq!(10, seq.get_item(1).unwrap().extract::<i32>().unwrap());
486        });
487    }
488
489    #[test]
490    fn test_seq_set_item_refcnt() {
491        let obj = get_object();
492
493        Python::attach(|py| {
494            let v: Vec<i32> = vec![1, 2];
495            let ob = v.into_pyobject(py).unwrap();
496            let seq = ob.cast::<PySequence>().unwrap();
497            assert!(seq.set_item(1, &obj).is_ok());
498            assert!(ptr::eq(seq.get_item(1).unwrap().as_ptr(), obj.as_ptr()));
499        });
500
501        Python::attach(move |py| {
502            assert_eq!(1, obj.get_refcnt(py));
503        });
504    }
505
506    #[test]
507    fn test_seq_get_slice() {
508        Python::attach(|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.cast::<PySequence>().unwrap();
512            assert_eq!(
513                [1, 2, 3],
514                seq.get_slice(1, 4).unwrap().extract::<[i32; 3]>().unwrap()
515            );
516            assert_eq!(
517                [3, 5, 8],
518                seq.get_slice(3, 100)
519                    .unwrap()
520                    .extract::<[i32; 3]>()
521                    .unwrap()
522            );
523        });
524    }
525
526    #[test]
527    fn test_set_slice() {
528        Python::attach(|py| {
529            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
530            let w: Vec<i32> = vec![7, 4];
531            let ob = v.into_pyobject(py).unwrap();
532            let seq = ob.cast::<PySequence>().unwrap();
533            let ins = w.into_pyobject(py).unwrap();
534            seq.set_slice(1, 4, &ins).unwrap();
535            assert_eq!([1, 7, 4, 5, 8], seq.extract::<[i32; 5]>().unwrap());
536            seq.set_slice(3, 100, &PyList::empty(py)).unwrap();
537            assert_eq!([1, 7, 4], seq.extract::<[i32; 3]>().unwrap());
538        });
539    }
540
541    #[test]
542    fn test_del_slice() {
543        Python::attach(|py| {
544            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
545            let ob = v.into_pyobject(py).unwrap();
546            let seq = ob.cast::<PySequence>().unwrap();
547            seq.del_slice(1, 4).unwrap();
548            assert_eq!([1, 5, 8], seq.extract::<[i32; 3]>().unwrap());
549            seq.del_slice(1, 100).unwrap();
550            assert_eq!([1], seq.extract::<[i32; 1]>().unwrap());
551        });
552    }
553
554    #[test]
555    fn test_seq_index() {
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!(0, seq.index(1i32).unwrap());
561            assert_eq!(2, seq.index(2i32).unwrap());
562            assert_eq!(3, seq.index(3i32).unwrap());
563            assert_eq!(4, seq.index(5i32).unwrap());
564            assert_eq!(5, seq.index(8i32).unwrap());
565            assert!(seq.index(42i32).is_err());
566        });
567    }
568
569    #[test]
570    #[cfg(not(any(PyPy, GraalPy)))]
571    fn test_seq_count() {
572        Python::attach(|py| {
573            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
574            let ob = v.into_pyobject(py).unwrap();
575            let seq = ob.cast::<PySequence>().unwrap();
576            assert_eq!(2, seq.count(1i32).unwrap());
577            assert_eq!(1, seq.count(2i32).unwrap());
578            assert_eq!(1, seq.count(3i32).unwrap());
579            assert_eq!(1, seq.count(5i32).unwrap());
580            assert_eq!(1, seq.count(8i32).unwrap());
581            assert_eq!(0, seq.count(42i32).unwrap());
582        });
583    }
584
585    #[test]
586    fn test_seq_iter() {
587        Python::attach(|py| {
588            let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
589            let ob = (&v).into_pyobject(py).unwrap();
590            let seq = ob.cast::<PySequence>().unwrap();
591            let mut idx = 0;
592            for el in seq.try_iter().unwrap() {
593                assert_eq!(v[idx], el.unwrap().extract::<i32>().unwrap());
594                idx += 1;
595            }
596            assert_eq!(idx, v.len());
597        });
598    }
599
600    #[test]
601    fn test_seq_strings() {
602        Python::attach(|py| {
603            let v = vec!["It", "was", "the", "worst", "of", "times"];
604            let ob = v.into_pyobject(py).unwrap();
605            let seq = ob.cast::<PySequence>().unwrap();
606
607            let bad_needle = "blurst".into_pyobject(py).unwrap();
608            assert!(!seq.contains(bad_needle).unwrap());
609
610            let good_needle = "worst".into_pyobject(py).unwrap();
611            assert!(seq.contains(good_needle).unwrap());
612        });
613    }
614
615    #[test]
616    fn test_seq_concat() {
617        Python::attach(|py| {
618            let v: Vec<i32> = vec![1, 2, 3];
619            let ob = v.into_pyobject(py).unwrap();
620            let seq = ob.cast::<PySequence>().unwrap();
621            let concat_seq = seq.concat(seq).unwrap();
622            assert_eq!(6, concat_seq.len().unwrap());
623            let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
624            for (el, cc) in concat_seq.try_iter().unwrap().zip(concat_v) {
625                assert_eq!(cc, el.unwrap().extract::<i32>().unwrap());
626            }
627        });
628    }
629
630    #[test]
631    fn test_seq_concat_string() {
632        Python::attach(|py| {
633            let v = "string";
634            let ob = v.into_pyobject(py).unwrap();
635            let seq = ob.cast::<PySequence>().unwrap();
636            let concat_seq = seq.concat(seq).unwrap();
637            assert_eq!(12, concat_seq.len().unwrap());
638            let concat_v = "stringstring".to_owned();
639            for (el, cc) in seq.try_iter().unwrap().zip(concat_v.chars()) {
640                assert_eq!(cc, el.unwrap().extract::<char>().unwrap());
641            }
642        });
643    }
644
645    #[test]
646    fn test_seq_repeat() {
647        Python::attach(|py| {
648            let v = vec!["foo", "bar"];
649            let ob = v.into_pyobject(py).unwrap();
650            let seq = ob.cast::<PySequence>().unwrap();
651            let repeat_seq = seq.repeat(3).unwrap();
652            assert_eq!(6, repeat_seq.len().unwrap());
653            let repeated = ["foo", "bar", "foo", "bar", "foo", "bar"];
654            for (el, rpt) in repeat_seq.try_iter().unwrap().zip(repeated.iter()) {
655                assert_eq!(*rpt, el.unwrap().extract::<String>().unwrap());
656            }
657        });
658    }
659
660    #[test]
661    fn test_seq_inplace() {
662        Python::attach(|py| {
663            let v = vec!["foo", "bar"];
664            let ob = v.into_pyobject(py).unwrap();
665            let seq = ob.cast::<PySequence>().unwrap();
666            let rep_seq = seq.in_place_repeat(3).unwrap();
667            assert_eq!(6, seq.len().unwrap());
668            assert!(seq.is(&rep_seq));
669
670            let conc_seq = seq.in_place_concat(seq).unwrap();
671            assert_eq!(12, seq.len().unwrap());
672            assert!(seq.is(&conc_seq));
673        });
674    }
675
676    #[test]
677    fn test_list_coercion() {
678        Python::attach(|py| {
679            let v = vec!["foo", "bar"];
680            let ob = (&v).into_pyobject(py).unwrap();
681            let seq = ob.cast::<PySequence>().unwrap();
682            assert!(seq
683                .to_list()
684                .unwrap()
685                .eq(PyList::new(py, &v).unwrap())
686                .unwrap());
687        });
688    }
689
690    #[test]
691    fn test_strings_coerce_to_lists() {
692        Python::attach(|py| {
693            let v = "foo";
694            let ob = v.into_pyobject(py).unwrap();
695            let seq = ob.cast::<PySequence>().unwrap();
696            assert!(seq
697                .to_list()
698                .unwrap()
699                .eq(PyList::new(py, ["f", "o", "o"]).unwrap())
700                .unwrap());
701        });
702    }
703
704    #[test]
705    fn test_tuple_coercion() {
706        Python::attach(|py| {
707            let v = ("foo", "bar");
708            let ob = v.into_pyobject(py).unwrap();
709            let seq = ob.cast::<PySequence>().unwrap();
710            assert!(seq
711                .to_tuple()
712                .unwrap()
713                .eq(PyTuple::new(py, ["foo", "bar"]).unwrap())
714                .unwrap());
715        });
716    }
717
718    #[test]
719    fn test_lists_coerce_to_tuples() {
720        Python::attach(|py| {
721            let v = vec!["foo", "bar"];
722            let ob = (&v).into_pyobject(py).unwrap();
723            let seq = ob.cast::<PySequence>().unwrap();
724            assert!(seq
725                .to_tuple()
726                .unwrap()
727                .eq(PyTuple::new(py, &v).unwrap())
728                .unwrap());
729        });
730    }
731
732    #[test]
733    fn test_seq_cast_unchecked() {
734        Python::attach(|py| {
735            let v = vec!["foo", "bar"];
736            let ob = v.into_pyobject(py).unwrap();
737            let seq = ob.cast::<PySequence>().unwrap();
738            let type_ptr = seq.as_any();
739            let seq_from = unsafe { type_ptr.cast_unchecked::<PySequence>() };
740            assert!(seq_from.to_list().is_ok());
741        });
742    }
743
744    #[test]
745    fn test_type_object() {
746        Python::attach(|py| {
747            let abc = PySequence::type_object(py);
748            assert!(PyList::empty(py).is_instance(&abc).unwrap());
749            assert!(PyTuple::empty(py).is_instance(&abc).unwrap());
750        })
751    }
752}