pyo3/types/
sequence.rs

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