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#[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 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 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#[doc(alias = "PySequence")]
68pub trait PySequenceMethods<'py>: crate::sealed::Sealed {
69 fn len(&self) -> PyResult<usize>;
73
74 fn is_empty(&self) -> PyResult<bool>;
76
77 fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
81
82 fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
86
87 fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
95
96 fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
104
105 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
109
110 fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>>;
114
115 fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
119 where
120 I: IntoPyObject<'py>;
121
122 fn del_item(&self, i: usize) -> PyResult<()>;
126
127 fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()>;
131
132 fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()>;
136
137 #[cfg(not(PyPy))]
140 fn count<V>(&self, value: V) -> PyResult<usize>
141 where
142 V: IntoPyObject<'py>;
143
144 fn contains<V>(&self, value: V) -> PyResult<bool>
148 where
149 V: IntoPyObject<'py>;
150
151 fn index<V>(&self, value: V) -> PyResult<usize>
155 where
156 V: IntoPyObject<'py>;
157
158 fn to_list(&self) -> PyResult<Bound<'py, PyList>>;
160
161 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 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}