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#[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 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 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#[doc(alias = "PySequence")]
72pub trait PySequenceMethods<'py>: crate::sealed::Sealed {
73 fn len(&self) -> PyResult<usize>;
77
78 fn is_empty(&self) -> PyResult<bool>;
80
81 fn concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
85
86 fn repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
90
91 fn in_place_concat(&self, other: &Bound<'_, PySequence>) -> PyResult<Bound<'py, PySequence>>;
99
100 fn in_place_repeat(&self, count: usize) -> PyResult<Bound<'py, PySequence>>;
108
109 fn get_item(&self, index: usize) -> PyResult<Bound<'py, PyAny>>;
113
114 fn get_slice(&self, begin: usize, end: usize) -> PyResult<Bound<'py, PySequence>>;
118
119 fn set_item<I>(&self, i: usize, item: I) -> PyResult<()>
123 where
124 I: IntoPyObject<'py>;
125
126 fn del_item(&self, i: usize) -> PyResult<()>;
130
131 fn set_slice(&self, i1: usize, i2: usize, v: &Bound<'_, PyAny>) -> PyResult<()>;
135
136 fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()>;
140
141 #[cfg(not(PyPy))]
144 fn count<V>(&self, value: V) -> PyResult<usize>
145 where
146 V: IntoPyObject<'py>;
147
148 fn contains<V>(&self, value: V) -> PyResult<bool>
152 where
153 V: IntoPyObject<'py>;
154
155 fn index<V>(&self, value: V) -> PyResult<usize>
159 where
160 V: IntoPyObject<'py>;
161
162 fn to_list(&self) -> PyResult<Bound<'py, PyList>>;
164
165 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 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}