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::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 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}