1 | use crate::err::{self, PyDowncastError, PyErr, PyResult}; |
2 | use crate::exceptions::PyTypeError; |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use crate::internal_tricks::get_ssize_index; |
6 | use crate::sync::GILOnceCell; |
7 | use crate::type_object::PyTypeInfo; |
8 | use crate::types::{PyAny, PyList, PyString, PyTuple, PyType}; |
9 | use crate::{ffi, PyNativeType, PyObject, ToPyObject}; |
10 | use crate::{FromPyObject, PyTryFrom}; |
11 | use crate::{Py, Python}; |
12 | |
13 | /// Represents a reference to a Python object supporting the sequence protocol. |
14 | #[repr (transparent)] |
15 | pub struct PySequence(PyAny); |
16 | pyobject_native_type_named!(PySequence); |
17 | pyobject_native_type_extract!(PySequence); |
18 | |
19 | impl PySequence { |
20 | /// Returns the number of objects in sequence. |
21 | /// |
22 | /// This is equivalent to the Python expression `len(self)`. |
23 | #[inline ] |
24 | pub fn len(&self) -> PyResult<usize> { |
25 | let v = unsafe { ffi::PySequence_Size(self.as_ptr()) }; |
26 | crate::err::error_on_minusone(self.py(), v)?; |
27 | Ok(v as usize) |
28 | } |
29 | |
30 | /// Returns whether the sequence is empty. |
31 | #[inline ] |
32 | pub fn is_empty(&self) -> PyResult<bool> { |
33 | self.len().map(|l| l == 0) |
34 | } |
35 | |
36 | /// Returns the concatenation of `self` and `other`. |
37 | /// |
38 | /// This is equivalent to the Python expression `self + other`. |
39 | #[inline ] |
40 | pub fn concat(&self, other: &PySequence) -> PyResult<&PySequence> { |
41 | unsafe { |
42 | self.py() |
43 | .from_owned_ptr_or_err(ffi::PySequence_Concat(self.as_ptr(), other.as_ptr())) |
44 | } |
45 | } |
46 | |
47 | /// Returns the result of repeating a sequence object `count` times. |
48 | /// |
49 | /// This is equivalent to the Python expression `self * count`. |
50 | #[inline ] |
51 | pub fn repeat(&self, count: usize) -> PyResult<&PySequence> { |
52 | unsafe { |
53 | self.py().from_owned_ptr_or_err(ffi::PySequence_Repeat( |
54 | self.as_ptr(), |
55 | get_ssize_index(count), |
56 | )) |
57 | } |
58 | } |
59 | |
60 | /// Concatenates `self` and `other`, in place if possible. |
61 | /// |
62 | /// This is equivalent to the Python expression `self.__iadd__(other)`. |
63 | /// |
64 | /// The Python statement `self += other` is syntactic sugar for `self = |
65 | /// self.__iadd__(other)`. `__iadd__` should modify and return `self` if |
66 | /// possible, but create and return a new object if not. |
67 | #[inline ] |
68 | pub fn in_place_concat(&self, other: &PySequence) -> PyResult<&PySequence> { |
69 | unsafe { |
70 | self.py() |
71 | .from_owned_ptr_or_err(ffi::PySequence_InPlaceConcat(self.as_ptr(), other.as_ptr())) |
72 | } |
73 | } |
74 | |
75 | /// Repeats the sequence object `count` times and updates `self`, if possible. |
76 | /// |
77 | /// This is equivalent to the Python expression `self.__imul__(other)`. |
78 | /// |
79 | /// The Python statement `self *= other` is syntactic sugar for `self = |
80 | /// self.__imul__(other)`. `__imul__` should modify and return `self` if |
81 | /// possible, but create and return a new object if not. |
82 | #[inline ] |
83 | pub fn in_place_repeat(&self, count: usize) -> PyResult<&PySequence> { |
84 | unsafe { |
85 | self.py() |
86 | .from_owned_ptr_or_err(ffi::PySequence_InPlaceRepeat( |
87 | self.as_ptr(), |
88 | get_ssize_index(count), |
89 | )) |
90 | } |
91 | } |
92 | |
93 | /// Returns the `index`th element of the Sequence. |
94 | /// |
95 | /// This is equivalent to the Python expression `self[index]` without support of negative indices. |
96 | #[inline ] |
97 | pub fn get_item(&self, index: usize) -> PyResult<&PyAny> { |
98 | unsafe { |
99 | self.py().from_owned_ptr_or_err(ffi::PySequence_GetItem( |
100 | self.as_ptr(), |
101 | get_ssize_index(index), |
102 | )) |
103 | } |
104 | } |
105 | |
106 | /// Returns the slice of sequence object between `begin` and `end`. |
107 | /// |
108 | /// This is equivalent to the Python expression `self[begin:end]`. |
109 | #[inline ] |
110 | pub fn get_slice(&self, begin: usize, end: usize) -> PyResult<&PySequence> { |
111 | unsafe { |
112 | self.py().from_owned_ptr_or_err(ffi::PySequence_GetSlice( |
113 | self.as_ptr(), |
114 | get_ssize_index(begin), |
115 | get_ssize_index(end), |
116 | )) |
117 | } |
118 | } |
119 | |
120 | /// Assigns object `item` to the `i`th element of self. |
121 | /// |
122 | /// This is equivalent to the Python statement `self[i] = v`. |
123 | #[inline ] |
124 | pub fn set_item<I>(&self, i: usize, item: I) -> PyResult<()> |
125 | where |
126 | I: ToPyObject, |
127 | { |
128 | fn inner(seq: &PySequence, i: usize, item: PyObject) -> PyResult<()> { |
129 | err::error_on_minusone(seq.py(), unsafe { |
130 | ffi::PySequence_SetItem(seq.as_ptr(), get_ssize_index(i), item.as_ptr()) |
131 | }) |
132 | } |
133 | |
134 | inner(self, i, item.to_object(self.py())) |
135 | } |
136 | |
137 | /// Deletes the `i`th element of self. |
138 | /// |
139 | /// This is equivalent to the Python statement `del self[i]`. |
140 | #[inline ] |
141 | pub fn del_item(&self, i: usize) -> PyResult<()> { |
142 | err::error_on_minusone(self.py(), unsafe { |
143 | ffi::PySequence_DelItem(self.as_ptr(), get_ssize_index(i)) |
144 | }) |
145 | } |
146 | |
147 | /// Assigns the sequence `v` to the slice of `self` from `i1` to `i2`. |
148 | /// |
149 | /// This is equivalent to the Python statement `self[i1:i2] = v`. |
150 | #[inline ] |
151 | pub fn set_slice(&self, i1: usize, i2: usize, v: &PyAny) -> PyResult<()> { |
152 | err::error_on_minusone(self.py(), unsafe { |
153 | ffi::PySequence_SetSlice( |
154 | self.as_ptr(), |
155 | get_ssize_index(i1), |
156 | get_ssize_index(i2), |
157 | v.as_ptr(), |
158 | ) |
159 | }) |
160 | } |
161 | |
162 | /// Deletes the slice from `i1` to `i2` from `self`. |
163 | /// |
164 | /// This is equivalent to the Python statement `del self[i1:i2]`. |
165 | #[inline ] |
166 | pub fn del_slice(&self, i1: usize, i2: usize) -> PyResult<()> { |
167 | err::error_on_minusone(self.py(), unsafe { |
168 | ffi::PySequence_DelSlice(self.as_ptr(), get_ssize_index(i1), get_ssize_index(i2)) |
169 | }) |
170 | } |
171 | |
172 | /// Returns the number of occurrences of `value` in self, that is, return the |
173 | /// number of keys for which `self[key] == value`. |
174 | #[inline ] |
175 | #[cfg (not(PyPy))] |
176 | pub fn count<V>(&self, value: V) -> PyResult<usize> |
177 | where |
178 | V: ToPyObject, |
179 | { |
180 | fn inner(seq: &PySequence, value: PyObject) -> PyResult<usize> { |
181 | let r = unsafe { ffi::PySequence_Count(seq.as_ptr(), value.as_ptr()) }; |
182 | crate::err::error_on_minusone(seq.py(), r)?; |
183 | Ok(r as usize) |
184 | } |
185 | |
186 | inner(self, value.to_object(self.py())) |
187 | } |
188 | |
189 | /// Determines if self contains `value`. |
190 | /// |
191 | /// This is equivalent to the Python expression `value in self`. |
192 | #[inline ] |
193 | pub fn contains<V>(&self, value: V) -> PyResult<bool> |
194 | where |
195 | V: ToPyObject, |
196 | { |
197 | fn inner(seq: &PySequence, value: PyObject) -> PyResult<bool> { |
198 | let r = unsafe { ffi::PySequence_Contains(seq.as_ptr(), value.as_ptr()) }; |
199 | match r { |
200 | 0 => Ok(false), |
201 | 1 => Ok(true), |
202 | _ => Err(PyErr::fetch(seq.py())), |
203 | } |
204 | } |
205 | |
206 | inner(self, value.to_object(self.py())) |
207 | } |
208 | |
209 | /// Returns the first index `i` for which `self[i] == value`. |
210 | /// |
211 | /// This is equivalent to the Python expression `self.index(value)`. |
212 | #[inline ] |
213 | pub fn index<V>(&self, value: V) -> PyResult<usize> |
214 | where |
215 | V: ToPyObject, |
216 | { |
217 | fn inner(seq: &PySequence, value: PyObject) -> PyResult<usize> { |
218 | let r = unsafe { ffi::PySequence_Index(seq.as_ptr(), value.as_ptr()) }; |
219 | crate::err::error_on_minusone(seq.py(), r)?; |
220 | Ok(r as usize) |
221 | } |
222 | |
223 | inner(self, value.to_object(self.py())) |
224 | } |
225 | |
226 | /// Returns a fresh list based on the Sequence. |
227 | #[inline ] |
228 | pub fn to_list(&self) -> PyResult<&PyList> { |
229 | unsafe { |
230 | self.py() |
231 | .from_owned_ptr_or_err(ffi::PySequence_List(self.as_ptr())) |
232 | } |
233 | } |
234 | |
235 | /// Returns a fresh list based on the Sequence. |
236 | #[inline ] |
237 | #[deprecated (since = "0.19.0" , note = "renamed to .to_list()" )] |
238 | pub fn list(&self) -> PyResult<&PyList> { |
239 | self.to_list() |
240 | } |
241 | |
242 | /// Returns a fresh tuple based on the Sequence. |
243 | #[inline ] |
244 | pub fn to_tuple(&self) -> PyResult<&PyTuple> { |
245 | unsafe { |
246 | self.py() |
247 | .from_owned_ptr_or_err(ffi::PySequence_Tuple(self.as_ptr())) |
248 | } |
249 | } |
250 | |
251 | /// Returns a fresh tuple based on the Sequence. |
252 | #[inline ] |
253 | #[deprecated (since = "0.19.0" , note = "renamed to .to_tuple()" )] |
254 | pub fn tuple(&self) -> PyResult<&PyTuple> { |
255 | self.to_tuple() |
256 | } |
257 | |
258 | /// Register a pyclass as a subclass of `collections.abc.Sequence` (from the Python standard |
259 | /// library). This is equvalent to `collections.abc.Sequence.register(T)` in Python. |
260 | /// This registration is required for a pyclass to be downcastable from `PyAny` to `PySequence`. |
261 | pub fn register<T: PyTypeInfo>(py: Python<'_>) -> PyResult<()> { |
262 | let ty = T::type_object(py); |
263 | get_sequence_abc(py)?.call_method1("register" , (ty,))?; |
264 | Ok(()) |
265 | } |
266 | } |
267 | |
268 | #[inline ] |
269 | fn sequence_len(seq: &PySequence) -> usize { |
270 | seq.len().expect(msg:"failed to get sequence length" ) |
271 | } |
272 | |
273 | #[inline ] |
274 | fn sequence_slice(seq: &PySequence, start: usize, end: usize) -> &PySequence { |
275 | seq.get_slice(start, end) |
276 | .expect(msg:"sequence slice operation failed" ) |
277 | } |
278 | |
279 | index_impls!(PySequence, "sequence" , sequence_len, sequence_slice); |
280 | |
281 | impl<'a, T> FromPyObject<'a> for Vec<T> |
282 | where |
283 | T: FromPyObject<'a>, |
284 | { |
285 | fn extract(obj: &'a PyAny) -> PyResult<Self> { |
286 | if obj.is_instance_of::<PyString>() { |
287 | return Err(PyTypeError::new_err(args:"Can't extract `str` to `Vec`" )); |
288 | } |
289 | extract_sequence(obj) |
290 | } |
291 | |
292 | #[cfg (feature = "experimental-inspect" )] |
293 | fn type_input() -> TypeInfo { |
294 | TypeInfo::sequence_of(T::type_input()) |
295 | } |
296 | } |
297 | |
298 | fn extract_sequence<'s, T>(obj: &'s PyAny) -> PyResult<Vec<T>> |
299 | where |
300 | T: FromPyObject<'s>, |
301 | { |
302 | // Types that pass `PySequence_Check` usually implement enough of the sequence protocol |
303 | // to support this function and if not, we will only fail extraction safely. |
304 | let seq: &PySequence = unsafe { |
305 | if ffi::PySequence_Check(obj.as_ptr()) != 0 { |
306 | obj.downcast_unchecked() |
307 | } else { |
308 | return Err(PyDowncastError::new(from:obj, to:"Sequence" ).into()); |
309 | } |
310 | }; |
311 | |
312 | let mut v: Vec = Vec::with_capacity(seq.len().unwrap_or(default:0)); |
313 | for item: Result<&PyAny, PyErr> in seq.iter()? { |
314 | v.push(item?.extract::<T>()?); |
315 | } |
316 | Ok(v) |
317 | } |
318 | |
319 | static SEQUENCE_ABC: GILOnceCell<Py<PyType>> = GILOnceCell::new(); |
320 | |
321 | fn get_sequence_abc(py: Python<'_>) -> PyResult<&PyType> { |
322 | SEQUENCE_ABC |
323 | .get_or_try_init(py, || { |
324 | py.import("collections.abc" )?.getattr("Sequence" )?.extract() |
325 | }) |
326 | .map(|ty: &Py| ty.as_ref(py)) |
327 | } |
328 | |
329 | impl<'v> PyTryFrom<'v> for PySequence { |
330 | /// Downcasting to `PySequence` requires the concrete class to be a subclass (or registered |
331 | /// subclass) of `collections.abc.Sequence` (from the Python standard library) - i.e. |
332 | /// `isinstance(<class>, collections.abc.Sequence) == True`. |
333 | fn try_from<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { |
334 | let value = value.into(); |
335 | |
336 | // Using `is_instance` for `collections.abc.Sequence` is slow, so provide |
337 | // optimized cases for list and tuples as common well-known sequences |
338 | if PyList::is_type_of(value) |
339 | || PyTuple::is_type_of(value) |
340 | || get_sequence_abc(value.py()) |
341 | .and_then(|abc| value.is_instance(abc)) |
342 | // TODO: surface errors in this chain to the user |
343 | .unwrap_or(false) |
344 | { |
345 | unsafe { return Ok(value.downcast_unchecked::<PySequence>()) } |
346 | } |
347 | |
348 | Err(PyDowncastError::new(value, "Sequence" )) |
349 | } |
350 | |
351 | fn try_from_exact<V: Into<&'v PyAny>>(value: V) -> Result<&'v PySequence, PyDowncastError<'v>> { |
352 | value.into().downcast() |
353 | } |
354 | |
355 | #[inline ] |
356 | unsafe fn try_from_unchecked<V: Into<&'v PyAny>>(value: V) -> &'v PySequence { |
357 | let ptr = value.into() as *const _ as *const PySequence; |
358 | &*ptr |
359 | } |
360 | } |
361 | |
362 | impl Py<PySequence> { |
363 | /// Borrows a GIL-bound reference to the PySequence. By binding to the GIL lifetime, this |
364 | /// allows the GIL-bound reference to not require `Python` for any of its methods. |
365 | /// |
366 | /// ``` |
367 | /// # use pyo3::prelude::*; |
368 | /// # use pyo3::types::{PyList, PySequence}; |
369 | /// # Python::with_gil(|py| { |
370 | /// let seq: Py<PySequence> = PyList::empty(py).as_sequence().into(); |
371 | /// let seq: &PySequence = seq.as_ref(py); |
372 | /// assert_eq!(seq.len().unwrap(), 0); |
373 | /// # }); |
374 | /// ``` |
375 | pub fn as_ref<'py>(&'py self, _py: Python<'py>) -> &'py PySequence { |
376 | let any = self.as_ptr() as *const PyAny; |
377 | unsafe { PyNativeType::unchecked_downcast(&*any) } |
378 | } |
379 | |
380 | /// Similar to [`as_ref`](#method.as_ref), and also consumes this `Py` and registers the |
381 | /// Python object reference in PyO3's object storage. The reference count for the Python |
382 | /// object will not be decreased until the GIL lifetime ends. |
383 | pub fn into_ref(self, py: Python<'_>) -> &PySequence { |
384 | unsafe { py.from_owned_ptr(self.into_ptr()) } |
385 | } |
386 | } |
387 | |
388 | #[cfg (test)] |
389 | mod tests { |
390 | use crate::types::{PyList, PySequence, PyTuple}; |
391 | use crate::{Py, PyObject, Python, ToPyObject}; |
392 | |
393 | fn get_object() -> PyObject { |
394 | // Convenience function for getting a single unique object |
395 | Python::with_gil(|py| { |
396 | let obj = py.eval("object()" , None, None).unwrap(); |
397 | |
398 | obj.to_object(py) |
399 | }) |
400 | } |
401 | |
402 | #[test ] |
403 | fn test_numbers_are_not_sequences() { |
404 | Python::with_gil(|py| { |
405 | let v = 42i32; |
406 | assert!(v.to_object(py).downcast::<PySequence>(py).is_err()); |
407 | }); |
408 | } |
409 | |
410 | #[test ] |
411 | fn test_strings_are_sequences() { |
412 | Python::with_gil(|py| { |
413 | let v = "London Calling" ; |
414 | assert!(v.to_object(py).downcast::<PySequence>(py).is_ok()); |
415 | }); |
416 | } |
417 | |
418 | #[test ] |
419 | fn test_strings_cannot_be_extracted_to_vec() { |
420 | Python::with_gil(|py| { |
421 | let v = "London Calling" ; |
422 | let ob = v.to_object(py); |
423 | |
424 | assert!(ob.extract::<Vec<&str>>(py).is_err()); |
425 | assert!(ob.extract::<Vec<String>>(py).is_err()); |
426 | assert!(ob.extract::<Vec<char>>(py).is_err()); |
427 | }); |
428 | } |
429 | |
430 | #[test ] |
431 | fn test_seq_empty() { |
432 | Python::with_gil(|py| { |
433 | let v: Vec<i32> = vec![]; |
434 | let ob = v.to_object(py); |
435 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
436 | assert_eq!(0, seq.len().unwrap()); |
437 | |
438 | let needle = 7i32.to_object(py); |
439 | assert!(!seq.contains(&needle).unwrap()); |
440 | }); |
441 | } |
442 | |
443 | #[test ] |
444 | fn test_seq_is_empty() { |
445 | Python::with_gil(|py| { |
446 | let list = vec![1].to_object(py); |
447 | let seq = list.downcast::<PySequence>(py).unwrap(); |
448 | assert!(!seq.is_empty().unwrap()); |
449 | let vec: Vec<u32> = Vec::new(); |
450 | let empty_list = vec.to_object(py); |
451 | let empty_seq = empty_list.downcast::<PySequence>(py).unwrap(); |
452 | assert!(empty_seq.is_empty().unwrap()); |
453 | }); |
454 | } |
455 | |
456 | #[test ] |
457 | fn test_seq_contains() { |
458 | Python::with_gil(|py| { |
459 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
460 | let ob = v.to_object(py); |
461 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
462 | assert_eq!(6, seq.len().unwrap()); |
463 | |
464 | let bad_needle = 7i32.to_object(py); |
465 | assert!(!seq.contains(&bad_needle).unwrap()); |
466 | |
467 | let good_needle = 8i32.to_object(py); |
468 | assert!(seq.contains(&good_needle).unwrap()); |
469 | |
470 | let type_coerced_needle = 8f32.to_object(py); |
471 | assert!(seq.contains(&type_coerced_needle).unwrap()); |
472 | }); |
473 | } |
474 | |
475 | #[test ] |
476 | fn test_seq_get_item() { |
477 | Python::with_gil(|py| { |
478 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
479 | let ob = v.to_object(py); |
480 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
481 | assert_eq!(1, seq.get_item(0).unwrap().extract::<i32>().unwrap()); |
482 | assert_eq!(1, seq.get_item(1).unwrap().extract::<i32>().unwrap()); |
483 | assert_eq!(2, seq.get_item(2).unwrap().extract::<i32>().unwrap()); |
484 | assert_eq!(3, seq.get_item(3).unwrap().extract::<i32>().unwrap()); |
485 | assert_eq!(5, seq.get_item(4).unwrap().extract::<i32>().unwrap()); |
486 | assert_eq!(8, seq.get_item(5).unwrap().extract::<i32>().unwrap()); |
487 | assert!(seq.get_item(10).is_err()); |
488 | }); |
489 | } |
490 | |
491 | #[test ] |
492 | fn test_seq_index_trait() { |
493 | Python::with_gil(|py| { |
494 | let v: Vec<i32> = vec![1, 1, 2]; |
495 | let ob = v.to_object(py); |
496 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
497 | assert_eq!(1, seq[0].extract::<i32>().unwrap()); |
498 | assert_eq!(1, seq[1].extract::<i32>().unwrap()); |
499 | assert_eq!(2, seq[2].extract::<i32>().unwrap()); |
500 | }); |
501 | } |
502 | |
503 | #[test ] |
504 | #[should_panic = "index 7 out of range for sequence" ] |
505 | fn test_seq_index_trait_panic() { |
506 | Python::with_gil(|py| { |
507 | let v: Vec<i32> = vec![1, 1, 2]; |
508 | let ob = v.to_object(py); |
509 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
510 | let _ = &seq[7]; |
511 | }); |
512 | } |
513 | |
514 | #[test ] |
515 | fn test_seq_index_trait_ranges() { |
516 | Python::with_gil(|py| { |
517 | let v: Vec<i32> = vec![1, 1, 2]; |
518 | let ob = v.to_object(py); |
519 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
520 | assert_eq!(vec![1, 2], seq[1..3].extract::<Vec<i32>>().unwrap()); |
521 | assert_eq!(Vec::<i32>::new(), seq[3..3].extract::<Vec<i32>>().unwrap()); |
522 | assert_eq!(vec![1, 2], seq[1..].extract::<Vec<i32>>().unwrap()); |
523 | assert_eq!(Vec::<i32>::new(), seq[3..].extract::<Vec<i32>>().unwrap()); |
524 | assert_eq!(vec![1, 1, 2], seq[..].extract::<Vec<i32>>().unwrap()); |
525 | assert_eq!(vec![1, 2], seq[1..=2].extract::<Vec<i32>>().unwrap()); |
526 | assert_eq!(vec![1, 1], seq[..2].extract::<Vec<i32>>().unwrap()); |
527 | assert_eq!(vec![1, 1], seq[..=1].extract::<Vec<i32>>().unwrap()); |
528 | }) |
529 | } |
530 | |
531 | #[test ] |
532 | #[should_panic = "range start index 5 out of range for sequence of length 3" ] |
533 | fn test_seq_index_trait_range_panic_start() { |
534 | Python::with_gil(|py| { |
535 | let v: Vec<i32> = vec![1, 1, 2]; |
536 | let ob = v.to_object(py); |
537 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
538 | seq[5..10].extract::<Vec<i32>>().unwrap(); |
539 | }) |
540 | } |
541 | |
542 | #[test ] |
543 | #[should_panic = "range end index 10 out of range for sequence of length 3" ] |
544 | fn test_seq_index_trait_range_panic_end() { |
545 | Python::with_gil(|py| { |
546 | let v: Vec<i32> = vec![1, 1, 2]; |
547 | let ob = v.to_object(py); |
548 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
549 | seq[1..10].extract::<Vec<i32>>().unwrap(); |
550 | }) |
551 | } |
552 | |
553 | #[test ] |
554 | #[should_panic = "slice index starts at 2 but ends at 1" ] |
555 | fn test_seq_index_trait_range_panic_wrong_order() { |
556 | Python::with_gil(|py| { |
557 | let v: Vec<i32> = vec![1, 1, 2]; |
558 | let ob = v.to_object(py); |
559 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
560 | #[allow (clippy::reversed_empty_ranges)] |
561 | seq[2..1].extract::<Vec<i32>>().unwrap(); |
562 | }) |
563 | } |
564 | |
565 | #[test ] |
566 | #[should_panic = "range start index 8 out of range for sequence of length 3" ] |
567 | fn test_seq_index_trait_range_from_panic() { |
568 | Python::with_gil(|py| { |
569 | let v: Vec<i32> = vec![1, 1, 2]; |
570 | let ob = v.to_object(py); |
571 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
572 | seq[8..].extract::<Vec<i32>>().unwrap(); |
573 | }) |
574 | } |
575 | |
576 | #[test ] |
577 | fn test_seq_del_item() { |
578 | Python::with_gil(|py| { |
579 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
580 | let ob = v.to_object(py); |
581 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
582 | assert!(seq.del_item(10).is_err()); |
583 | assert_eq!(1, seq[0].extract::<i32>().unwrap()); |
584 | assert!(seq.del_item(0).is_ok()); |
585 | assert_eq!(1, seq[0].extract::<i32>().unwrap()); |
586 | assert!(seq.del_item(0).is_ok()); |
587 | assert_eq!(2, seq[0].extract::<i32>().unwrap()); |
588 | assert!(seq.del_item(0).is_ok()); |
589 | assert_eq!(3, seq[0].extract::<i32>().unwrap()); |
590 | assert!(seq.del_item(0).is_ok()); |
591 | assert_eq!(5, seq[0].extract::<i32>().unwrap()); |
592 | assert!(seq.del_item(0).is_ok()); |
593 | assert_eq!(8, seq[0].extract::<i32>().unwrap()); |
594 | assert!(seq.del_item(0).is_ok()); |
595 | assert_eq!(0, seq.len().unwrap()); |
596 | assert!(seq.del_item(0).is_err()); |
597 | }); |
598 | } |
599 | |
600 | #[test ] |
601 | fn test_seq_set_item() { |
602 | Python::with_gil(|py| { |
603 | let v: Vec<i32> = vec![1, 2]; |
604 | let ob = v.to_object(py); |
605 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
606 | assert_eq!(2, seq[1].extract::<i32>().unwrap()); |
607 | assert!(seq.set_item(1, 10).is_ok()); |
608 | assert_eq!(10, seq[1].extract::<i32>().unwrap()); |
609 | }); |
610 | } |
611 | |
612 | #[test ] |
613 | fn test_seq_set_item_refcnt() { |
614 | let obj = get_object(); |
615 | |
616 | Python::with_gil(|py| { |
617 | let v: Vec<i32> = vec![1, 2]; |
618 | let ob = v.to_object(py); |
619 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
620 | assert!(seq.set_item(1, &obj).is_ok()); |
621 | assert!(seq[1].as_ptr() == obj.as_ptr()); |
622 | }); |
623 | |
624 | Python::with_gil(|py| { |
625 | assert_eq!(1, obj.get_refcnt(py)); |
626 | }); |
627 | } |
628 | |
629 | #[test ] |
630 | fn test_seq_get_slice() { |
631 | Python::with_gil(|py| { |
632 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
633 | let ob = v.to_object(py); |
634 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
635 | assert_eq!( |
636 | [1, 2, 3], |
637 | seq.get_slice(1, 4).unwrap().extract::<[i32; 3]>().unwrap() |
638 | ); |
639 | assert_eq!( |
640 | [3, 5, 8], |
641 | seq.get_slice(3, 100) |
642 | .unwrap() |
643 | .extract::<[i32; 3]>() |
644 | .unwrap() |
645 | ); |
646 | }); |
647 | } |
648 | |
649 | #[test ] |
650 | fn test_set_slice() { |
651 | Python::with_gil(|py| { |
652 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
653 | let w: Vec<i32> = vec![7, 4]; |
654 | let ob = v.to_object(py); |
655 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
656 | let ins = w.to_object(py); |
657 | seq.set_slice(1, 4, ins.as_ref(py)).unwrap(); |
658 | assert_eq!([1, 7, 4, 5, 8], seq.extract::<[i32; 5]>().unwrap()); |
659 | seq.set_slice(3, 100, PyList::empty(py)).unwrap(); |
660 | assert_eq!([1, 7, 4], seq.extract::<[i32; 3]>().unwrap()); |
661 | }); |
662 | } |
663 | |
664 | #[test ] |
665 | fn test_del_slice() { |
666 | Python::with_gil(|py| { |
667 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
668 | let ob = v.to_object(py); |
669 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
670 | seq.del_slice(1, 4).unwrap(); |
671 | assert_eq!([1, 5, 8], seq.extract::<[i32; 3]>().unwrap()); |
672 | seq.del_slice(1, 100).unwrap(); |
673 | assert_eq!([1], seq.extract::<[i32; 1]>().unwrap()); |
674 | }); |
675 | } |
676 | |
677 | #[test ] |
678 | fn test_seq_index() { |
679 | Python::with_gil(|py| { |
680 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
681 | let ob = v.to_object(py); |
682 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
683 | assert_eq!(0, seq.index(1i32).unwrap()); |
684 | assert_eq!(2, seq.index(2i32).unwrap()); |
685 | assert_eq!(3, seq.index(3i32).unwrap()); |
686 | assert_eq!(4, seq.index(5i32).unwrap()); |
687 | assert_eq!(5, seq.index(8i32).unwrap()); |
688 | assert!(seq.index(42i32).is_err()); |
689 | }); |
690 | } |
691 | |
692 | #[test ] |
693 | #[cfg (not(PyPy))] |
694 | fn test_seq_count() { |
695 | Python::with_gil(|py| { |
696 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
697 | let ob = v.to_object(py); |
698 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
699 | assert_eq!(2, seq.count(1i32).unwrap()); |
700 | assert_eq!(1, seq.count(2i32).unwrap()); |
701 | assert_eq!(1, seq.count(3i32).unwrap()); |
702 | assert_eq!(1, seq.count(5i32).unwrap()); |
703 | assert_eq!(1, seq.count(8i32).unwrap()); |
704 | assert_eq!(0, seq.count(42i32).unwrap()); |
705 | }); |
706 | } |
707 | |
708 | #[test ] |
709 | fn test_seq_iter() { |
710 | Python::with_gil(|py| { |
711 | let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8]; |
712 | let ob = v.to_object(py); |
713 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
714 | let mut idx = 0; |
715 | for el in seq.iter().unwrap() { |
716 | assert_eq!(v[idx], el.unwrap().extract::<i32>().unwrap()); |
717 | idx += 1; |
718 | } |
719 | assert_eq!(idx, v.len()); |
720 | }); |
721 | } |
722 | |
723 | #[test ] |
724 | fn test_seq_strings() { |
725 | Python::with_gil(|py| { |
726 | let v = vec!["It" , "was" , "the" , "worst" , "of" , "times" ]; |
727 | let ob = v.to_object(py); |
728 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
729 | |
730 | let bad_needle = "blurst" .to_object(py); |
731 | assert!(!seq.contains(bad_needle).unwrap()); |
732 | |
733 | let good_needle = "worst" .to_object(py); |
734 | assert!(seq.contains(good_needle).unwrap()); |
735 | }); |
736 | } |
737 | |
738 | #[test ] |
739 | fn test_seq_concat() { |
740 | Python::with_gil(|py| { |
741 | let v: Vec<i32> = vec![1, 2, 3]; |
742 | let ob = v.to_object(py); |
743 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
744 | let concat_seq = seq.concat(seq).unwrap(); |
745 | assert_eq!(6, concat_seq.len().unwrap()); |
746 | let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3]; |
747 | for (el, cc) in concat_seq.iter().unwrap().zip(concat_v) { |
748 | assert_eq!(cc, el.unwrap().extract::<i32>().unwrap()); |
749 | } |
750 | }); |
751 | } |
752 | |
753 | #[test ] |
754 | fn test_seq_concat_string() { |
755 | Python::with_gil(|py| { |
756 | let v = "string" ; |
757 | let ob = v.to_object(py); |
758 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
759 | let concat_seq = seq.concat(seq).unwrap(); |
760 | assert_eq!(12, concat_seq.len().unwrap()); |
761 | let concat_v = "stringstring" .to_owned(); |
762 | for (el, cc) in seq.iter().unwrap().zip(concat_v.chars()) { |
763 | assert_eq!(cc, el.unwrap().extract::<char>().unwrap()); |
764 | } |
765 | }); |
766 | } |
767 | |
768 | #[test ] |
769 | fn test_seq_repeat() { |
770 | Python::with_gil(|py| { |
771 | let v = vec!["foo" , "bar" ]; |
772 | let ob = v.to_object(py); |
773 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
774 | let repeat_seq = seq.repeat(3).unwrap(); |
775 | assert_eq!(6, repeat_seq.len().unwrap()); |
776 | let repeated = ["foo" , "bar" , "foo" , "bar" , "foo" , "bar" ]; |
777 | for (el, rpt) in repeat_seq.iter().unwrap().zip(repeated.iter()) { |
778 | assert_eq!(*rpt, el.unwrap().extract::<String>().unwrap()); |
779 | } |
780 | }); |
781 | } |
782 | |
783 | #[test ] |
784 | fn test_seq_inplace() { |
785 | Python::with_gil(|py| { |
786 | let v = vec!["foo" , "bar" ]; |
787 | let ob = v.to_object(py); |
788 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
789 | let rep_seq = seq.in_place_repeat(3).unwrap(); |
790 | assert_eq!(6, seq.len().unwrap()); |
791 | assert!(seq.is(rep_seq)); |
792 | |
793 | let conc_seq = seq.in_place_concat(seq).unwrap(); |
794 | assert_eq!(12, seq.len().unwrap()); |
795 | assert!(seq.is(conc_seq)); |
796 | }); |
797 | } |
798 | |
799 | #[test ] |
800 | fn test_list_coercion() { |
801 | Python::with_gil(|py| { |
802 | let v = vec!["foo" , "bar" ]; |
803 | let ob = v.to_object(py); |
804 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
805 | assert!(seq.to_list().unwrap().eq(PyList::new(py, &v)).unwrap()); |
806 | #[allow (deprecated)] |
807 | { |
808 | assert!(seq.list().is_ok()); |
809 | } |
810 | }); |
811 | } |
812 | |
813 | #[test ] |
814 | fn test_strings_coerce_to_lists() { |
815 | Python::with_gil(|py| { |
816 | let v = "foo" ; |
817 | let ob = v.to_object(py); |
818 | let seq: &PySequence = ob.downcast(py).unwrap(); |
819 | assert!(seq |
820 | .to_list() |
821 | .unwrap() |
822 | .eq(PyList::new(py, ["f" , "o" , "o" ])) |
823 | .unwrap()); |
824 | #[allow (deprecated)] |
825 | { |
826 | assert!(seq.list().is_ok()); |
827 | } |
828 | }); |
829 | } |
830 | |
831 | #[test ] |
832 | fn test_tuple_coercion() { |
833 | Python::with_gil(|py| { |
834 | let v = ("foo" , "bar" ); |
835 | let ob = v.to_object(py); |
836 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
837 | assert!(seq |
838 | .to_tuple() |
839 | .unwrap() |
840 | .eq(PyTuple::new(py, ["foo" , "bar" ])) |
841 | .unwrap()); |
842 | #[allow (deprecated)] |
843 | { |
844 | assert!(seq.tuple().is_ok()); |
845 | } |
846 | }); |
847 | } |
848 | |
849 | #[test ] |
850 | fn test_lists_coerce_to_tuples() { |
851 | Python::with_gil(|py| { |
852 | let v = vec!["foo" , "bar" ]; |
853 | let ob = v.to_object(py); |
854 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
855 | assert!(seq.to_tuple().unwrap().eq(PyTuple::new(py, &v)).unwrap()); |
856 | #[allow (deprecated)] |
857 | { |
858 | assert!(seq.tuple().is_ok()); |
859 | } |
860 | }); |
861 | } |
862 | |
863 | #[test ] |
864 | fn test_extract_tuple_to_vec() { |
865 | Python::with_gil(|py| { |
866 | let v: Vec<i32> = py.eval("(1, 2)" , None, None).unwrap().extract().unwrap(); |
867 | assert!(v == [1, 2]); |
868 | }); |
869 | } |
870 | |
871 | #[test ] |
872 | fn test_extract_range_to_vec() { |
873 | Python::with_gil(|py| { |
874 | let v: Vec<i32> = py |
875 | .eval("range(1, 5)" , None, None) |
876 | .unwrap() |
877 | .extract() |
878 | .unwrap(); |
879 | assert!(v == [1, 2, 3, 4]); |
880 | }); |
881 | } |
882 | |
883 | #[test ] |
884 | fn test_extract_bytearray_to_vec() { |
885 | Python::with_gil(|py| { |
886 | let v: Vec<u8> = py |
887 | .eval("bytearray(b'abc')" , None, None) |
888 | .unwrap() |
889 | .extract() |
890 | .unwrap(); |
891 | assert!(v == b"abc" ); |
892 | }); |
893 | } |
894 | |
895 | #[test ] |
896 | fn test_seq_downcast_unchecked() { |
897 | Python::with_gil(|py| { |
898 | let v = vec!["foo" , "bar" ]; |
899 | let ob = v.to_object(py); |
900 | let seq = ob.downcast::<PySequence>(py).unwrap(); |
901 | let type_ptr = seq.as_ref(); |
902 | let seq_from = unsafe { type_ptr.downcast_unchecked::<PySequence>() }; |
903 | assert!(seq_from.to_list().is_ok()); |
904 | }); |
905 | } |
906 | |
907 | #[test ] |
908 | fn test_as_ref() { |
909 | Python::with_gil(|py| { |
910 | let seq: Py<PySequence> = PyList::empty(py).as_sequence().into(); |
911 | let seq_ref: &PySequence = seq.as_ref(py); |
912 | assert_eq!(seq_ref.len().unwrap(), 0); |
913 | }) |
914 | } |
915 | |
916 | #[test ] |
917 | fn test_into_ref() { |
918 | Python::with_gil(|py| { |
919 | let bare_seq = PyList::empty(py).as_sequence(); |
920 | assert_eq!(bare_seq.get_refcnt(), 1); |
921 | let seq: Py<PySequence> = bare_seq.into(); |
922 | assert_eq!(bare_seq.get_refcnt(), 2); |
923 | let seq_ref = seq.into_ref(py); |
924 | assert_eq!(seq_ref.len().unwrap(), 0); |
925 | assert_eq!(seq_ref.get_refcnt(), 2); |
926 | }) |
927 | } |
928 | } |
929 | |