cpython/objects/
sequence.rs

1// Copyright (c) 2015 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19use std::mem;
20
21use crate::buffer;
22use crate::conversion::{FromPyObject, ToPyObject};
23use crate::err;
24use crate::err::{result_cast_from_owned_ptr, result_from_owned_ptr, PyErr, PyResult};
25use crate::ffi;
26use crate::ffi::Py_ssize_t;
27use crate::objects::{PyIterator, PyList, PyObject, PyTuple};
28use crate::python::{PyClone, PyDrop, Python, PythonObject, ToPythonPointer};
29
30/// Represents a reference to a python object supporting the sequence protocol.
31pub struct PySequence(PyObject);
32
33pyobject_newtype!(PySequence, PySequence_Check);
34
35impl PySequence {
36    /// Returns the number of objects in sequence. This is equivalent to Python `len()`.
37    #[inline]
38    pub fn len(&self, py: Python) -> PyResult<isize> {
39        let v = unsafe { ffi::PySequence_Size(self.0.as_ptr()) };
40        if v == -1 {
41            Err(PyErr::fetch(py))
42        } else {
43            Ok(v as isize)
44        }
45    }
46
47    /// Return the concatenation of o1 and o2. Equivalent to python `o1 + o2`
48    #[inline]
49    pub fn concat(&self, py: Python, other: &PySequence) -> PyResult<PyObject> {
50        unsafe {
51            err::result_from_owned_ptr(py, ffi::PySequence_Concat(self.as_ptr(), other.as_ptr()))
52        }
53    }
54
55    /// Return the result of repeating sequence object o count times.
56    /// Equivalent to python `o * count`
57    /// NB: Python accepts negative counts; it returns an empty Sequence.
58    #[inline]
59    pub fn repeat(&self, py: Python, count: isize) -> PyResult<PyObject> {
60        unsafe {
61            err::result_from_owned_ptr(
62                py,
63                ffi::PySequence_Repeat(self.as_ptr(), count as Py_ssize_t),
64            )
65        }
66    }
67
68    /// Return the concatenation of o1 and o2 on success. Equivalent to python `o1 += o2`
69    #[inline]
70    pub fn in_place_concat(&self, py: Python, other: &PySequence) -> PyResult<PyObject> {
71        unsafe {
72            result_from_owned_ptr(
73                py,
74                ffi::PySequence_InPlaceConcat(self.as_ptr(), other.as_ptr()),
75            )
76        }
77    }
78
79    /// Return the result of repeating sequence object o count times.
80    /// Equivalent to python `o *= count`
81    /// NB: Python accepts negative counts; it empties the Sequence.
82    #[inline]
83    pub fn in_place_repeat(&self, py: Python, count: isize) -> PyResult<PyObject> {
84        unsafe {
85            result_from_owned_ptr(
86                py,
87                ffi::PySequence_InPlaceRepeat(self.as_ptr(), count as Py_ssize_t),
88            )
89        }
90    }
91
92    /// Return the ith element of the Sequence. Equivalent to python `o[index]`
93    #[inline]
94    pub fn get_item(&self, py: Python, index: isize) -> PyResult<PyObject> {
95        unsafe {
96            result_from_owned_ptr(
97                py,
98                ffi::PySequence_GetItem(self.as_ptr(), index as Py_ssize_t),
99            )
100        }
101    }
102
103    /// Return the slice of sequence object o between begin and end.
104    /// This is the equivalent of the Python expression `o[begin:end]`
105    #[inline]
106    pub fn get_slice(&self, py: Python, begin: isize, end: isize) -> PyResult<PyObject> {
107        unsafe {
108            result_from_owned_ptr(
109                py,
110                ffi::PySequence_GetSlice(self.as_ptr(), begin as Py_ssize_t, end as Py_ssize_t),
111            )
112        }
113    }
114
115    /// Assign object v to the ith element of o.
116    /// Equivalent to Python statement `o[i] = v`
117    #[inline]
118    pub fn set_item(&self, py: Python, i: isize, v: &PyObject) -> PyResult<()> {
119        unsafe {
120            err::error_on_minusone(
121                py,
122                ffi::PySequence_SetItem(self.as_ptr(), i as Py_ssize_t, v.as_ptr()),
123            )
124        }
125    }
126
127    /// Delete the ith element of object o.
128    /// Python statement `del o[i]`
129    #[inline]
130    pub fn del_item(&self, py: Python, i: isize) -> PyResult<()> {
131        unsafe {
132            err::error_on_minusone(py, ffi::PySequence_DelItem(self.as_ptr(), i as Py_ssize_t))
133        }
134    }
135
136    /// Assign the sequence object v to the slice in sequence object o from i1 to i2.
137    /// This is the equivalent of the Python statement `o[i1:i2] = v`
138    #[inline]
139    pub fn set_slice(&self, py: Python, i1: isize, i2: isize, v: &PyObject) -> PyResult<()> {
140        unsafe {
141            err::error_on_minusone(
142                py,
143                ffi::PySequence_SetSlice(
144                    self.as_ptr(),
145                    i1 as Py_ssize_t,
146                    i2 as Py_ssize_t,
147                    v.as_ptr(),
148                ),
149            )
150        }
151    }
152
153    /// Delete the slice in sequence object o from i1 to i2.
154    /// equivalent of the Python statement `del o[i1:i2]`
155    #[inline]
156    pub fn del_slice(&self, py: Python, i1: isize, i2: isize) -> PyResult<()> {
157        unsafe {
158            err::error_on_minusone(
159                py,
160                ffi::PySequence_DelSlice(self.as_ptr(), i1 as Py_ssize_t, i2 as Py_ssize_t),
161            )
162        }
163    }
164
165    /// Return the number of occurrences of value in o, that is, return the number of keys for
166    /// which `o[key] == value`
167    #[inline]
168    pub fn count<V>(&self, py: Python, value: V) -> PyResult<usize>
169    where
170        V: ToPyObject,
171    {
172        let r = value.with_borrowed_ptr(py, |ptr| unsafe {
173            ffi::PySequence_Count(self.as_ptr(), ptr)
174        });
175        if r == -1 {
176            Err(PyErr::fetch(py))
177        } else {
178            Ok(r as usize)
179        }
180    }
181
182    /// Determine if o contains value. this is equivalent to the Python expression `value in o`
183    #[inline]
184    pub fn contains<V>(&self, py: Python, value: V) -> PyResult<bool>
185    where
186        V: ToPyObject,
187    {
188        let r = value.with_borrowed_ptr(py, |ptr| unsafe {
189            ffi::PySequence_Contains(self.as_ptr(), ptr)
190        });
191        match r {
192            0 => Ok(false),
193            1 => Ok(true),
194            _ => Err(PyErr::fetch(py)),
195        }
196    }
197
198    /// Return the first index i for which o[i] == value.
199    /// This is equivalent to the Python expression `o.index(value)`
200    #[inline]
201    pub fn index<V>(&self, py: Python, value: V) -> PyResult<usize>
202    where
203        V: ToPyObject,
204    {
205        let r = value.with_borrowed_ptr(py, |ptr| unsafe {
206            ffi::PySequence_Index(self.as_ptr(), ptr)
207        });
208        if r == -1 {
209            Err(PyErr::fetch(py))
210        } else {
211            Ok(r as usize)
212        }
213    }
214
215    /// Return a fresh list based on the Sequence.
216    #[inline]
217    pub fn list(&self, py: Python) -> PyResult<PyList> {
218        unsafe { result_cast_from_owned_ptr(py, ffi::PySequence_List(self.as_ptr())) }
219    }
220
221    /// Return a fresh tuple based on the Sequence.
222    #[inline]
223    pub fn tuple(&self, py: Python) -> PyResult<PyTuple> {
224        unsafe { result_cast_from_owned_ptr(py, ffi::PySequence_Tuple(self.as_ptr())) }
225    }
226
227    #[inline]
228    pub fn iter<'p>(&self, py: Python<'p>) -> PyResult<PyIterator<'p>> {
229        use crate::objectprotocol::ObjectProtocol;
230        self.as_object().iter(py)
231    }
232}
233
234/// Uses the sequence protocol and converts each individual element
235/// via `impl FromPyObject for T`.
236///
237/// Note: when using `--features nightly`, a specialization of this impl
238/// may use the buffer protocol to perform a more efficient bulk copy.
239#[cfg(not(feature = "nightly"))]
240impl<'s, T> FromPyObject<'s> for Vec<T>
241where
242    for<'a> T: FromPyObject<'a>,
243{
244    fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
245        extract_sequence(py, obj)
246    }
247}
248
249/// Uses the sequence protocol and converts each individual element
250/// via `impl FromPyObject for T`.
251///
252/// Note: when using `--features nightly`, a specialization of this impl
253/// may use the buffer protocol to perform a more efficient bulk copy.
254#[cfg(feature = "nightly")]
255impl<'s, T> FromPyObject<'s> for Vec<T>
256where
257    for<'a> T: FromPyObject<'a>,
258{
259    default fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
260        extract_sequence(py, obj)
261    }
262}
263
264/// If the Python object is a single-dimensional [buffer] of format `c` or `B` (C: `char` or `unsigned char`),
265/// returns an owned copy of the data in the buffer.
266/// Otherwise, uses the sequence protocol and converts each individual element
267/// via `impl FromPyObject for u8`.
268///
269/// [buffer]: https://docs.python.org/3/c-api/buffer.html
270#[cfg(feature = "nightly")]
271impl<'s, T> FromPyObject<'s> for Vec<T>
272where
273    for<'a> T: FromPyObject<'a> + buffer::Element + Copy,
274{
275    fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
276        extract_buffer_or_sequence(py, obj)
277    }
278}
279
280pub(crate) fn extract_buffer_or_sequence<T>(py: Python, obj: &PyObject) -> PyResult<Vec<T>>
281where
282    for<'a> T: FromPyObject<'a> + buffer::Element + Copy,
283{
284    // first try buffer protocol
285    if let Ok(buf) = buffer::PyBuffer::get(py, obj) {
286        if buf.dimensions() == 1 {
287            if let Ok(v) = buf.to_vec::<T>(py) {
288                buf.release_ref(py);
289                return Ok(v);
290            }
291        }
292        buf.release_ref(py);
293    }
294    // fall back to sequence protocol
295    extract_sequence(py, obj)
296}
297
298fn extract_sequence<T>(py: Python, obj: &PyObject) -> PyResult<Vec<T>>
299where
300    for<'a> T: FromPyObject<'a>,
301{
302    let seq = obj.cast_as::<PySequence>(py)?;
303    let mut v = Vec::new();
304    for item in seq.iter(py)? {
305        let item = item?;
306        v.push(T::extract(py, &item)?);
307        item.release_ref(py);
308    }
309    Ok(v)
310}
311
312#[cfg(test)]
313mod test {
314    use crate::conversion::ToPyObject;
315    use crate::objects::{PyIterator, PyList, PySequence, PyTuple};
316    use crate::python::{Python, PythonObject};
317
318    #[test]
319    fn test_numbers_are_not_sequences() {
320        let gil = Python::acquire_gil();
321        let py = gil.python();
322        let v = 42i32;
323        assert!(v
324            .to_py_object(py)
325            .into_object()
326            .cast_into::<PySequence>(py)
327            .is_err());
328    }
329
330    #[test]
331    fn test_strings_are_sequences() {
332        let gil = Python::acquire_gil();
333        let py = gil.python();
334        let v = "London Calling";
335        assert!(v
336            .to_py_object(py)
337            .into_object()
338            .cast_into::<PySequence>(py)
339            .is_ok());
340    }
341    #[test]
342    fn test_seq_empty() {
343        let gil = Python::acquire_gil();
344        let py = gil.python();
345        let v: Vec<i32> = vec![];
346        let seq = v
347            .to_py_object(py)
348            .into_object()
349            .cast_into::<PySequence>(py)
350            .unwrap();
351        assert_eq!(0, seq.len(py).unwrap());
352
353        let needle = 7i32.to_py_object(py).into_object();
354        assert_eq!(false, seq.contains(py, &needle).unwrap());
355    }
356
357    #[test]
358    fn test_seq_contains() {
359        let gil = Python::acquire_gil();
360        let py = gil.python();
361        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
362        let seq = v
363            .to_py_object(py)
364            .into_object()
365            .cast_into::<PySequence>(py)
366            .unwrap();
367        assert_eq!(6, seq.len(py).unwrap());
368
369        let bad_needle = 7i32.to_py_object(py).into_object();
370        assert_eq!(false, seq.contains(py, &bad_needle).unwrap());
371
372        let good_needle = 8i32.to_py_object(py).into_object();
373        assert_eq!(true, seq.contains(py, &good_needle).unwrap());
374
375        let type_coerced_needle = 8f32.to_py_object(py).into_object();
376        assert_eq!(true, seq.contains(py, &type_coerced_needle).unwrap());
377    }
378
379    #[test]
380    fn test_seq_get_item() {
381        let gil = Python::acquire_gil();
382        let py = gil.python();
383        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
384        let seq = v
385            .to_py_object(py)
386            .into_object()
387            .cast_into::<PySequence>(py)
388            .unwrap();
389        assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
390        assert_eq!(1, seq.get_item(py, 1).unwrap().extract::<i32>(py).unwrap());
391        assert_eq!(2, seq.get_item(py, 2).unwrap().extract::<i32>(py).unwrap());
392        assert_eq!(3, seq.get_item(py, 3).unwrap().extract::<i32>(py).unwrap());
393        assert_eq!(5, seq.get_item(py, 4).unwrap().extract::<i32>(py).unwrap());
394        assert_eq!(8, seq.get_item(py, 5).unwrap().extract::<i32>(py).unwrap());
395        assert_eq!(8, seq.get_item(py, -1).unwrap().extract::<i32>(py).unwrap());
396        assert_eq!(5, seq.get_item(py, -2).unwrap().extract::<i32>(py).unwrap());
397        assert_eq!(3, seq.get_item(py, -3).unwrap().extract::<i32>(py).unwrap());
398        assert_eq!(2, seq.get_item(py, -4).unwrap().extract::<i32>(py).unwrap());
399        assert_eq!(1, seq.get_item(py, -5).unwrap().extract::<i32>(py).unwrap());
400        assert!(seq.get_item(py, 10).is_err());
401    }
402
403    // fn test_get_slice() {}
404    // fn test_set_slice() {}
405    // fn test_del_slice() {}
406
407    #[test]
408    fn test_seq_del_item() {
409        let gil = Python::acquire_gil();
410        let py = gil.python();
411        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
412        let seq = v
413            .to_py_object(py)
414            .into_object()
415            .cast_into::<PySequence>(py)
416            .unwrap();
417        assert!(seq.del_item(py, 10).is_err());
418        assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
419        assert!(seq.del_item(py, 0).is_ok());
420        assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
421        assert!(seq.del_item(py, 0).is_ok());
422        assert_eq!(2, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
423        assert!(seq.del_item(py, 0).is_ok());
424        assert_eq!(3, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
425        assert!(seq.del_item(py, 0).is_ok());
426        assert_eq!(5, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
427        assert!(seq.del_item(py, 0).is_ok());
428        assert_eq!(8, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
429        assert!(seq.del_item(py, 0).is_ok());
430        assert_eq!(0, seq.len(py).unwrap());
431        assert!(seq.del_item(py, 0).is_err());
432    }
433
434    #[test]
435    fn test_seq_index() {
436        let gil = Python::acquire_gil();
437        let py = gil.python();
438        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
439        let seq = v
440            .to_py_object(py)
441            .into_object()
442            .cast_into::<PySequence>(py)
443            .unwrap();
444        assert_eq!(0, seq.index(py, 1i32).unwrap());
445        assert_eq!(2, seq.index(py, 2i32).unwrap());
446        assert_eq!(3, seq.index(py, 3i32).unwrap());
447        assert_eq!(4, seq.index(py, 5i32).unwrap());
448        assert_eq!(5, seq.index(py, 8i32).unwrap());
449        assert!(seq.index(py, 42i32).is_err());
450    }
451
452    #[test]
453    fn test_seq_count() {
454        let gil = Python::acquire_gil();
455        let py = gil.python();
456        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
457        let seq = v
458            .to_py_object(py)
459            .into_object()
460            .cast_into::<PySequence>(py)
461            .unwrap();
462        assert_eq!(2, seq.count(py, 1i32).unwrap());
463        assert_eq!(1, seq.count(py, 2i32).unwrap());
464        assert_eq!(1, seq.count(py, 3i32).unwrap());
465        assert_eq!(1, seq.count(py, 5i32).unwrap());
466        assert_eq!(1, seq.count(py, 8i32).unwrap());
467        assert_eq!(0, seq.count(py, 42i32).unwrap());
468    }
469
470    #[test]
471    fn test_seq_iter() {
472        let gil = Python::acquire_gil();
473        let py = gil.python();
474        let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
475        let seq = v
476            .to_py_object(py)
477            .into_object()
478            .cast_into::<PySequence>(py)
479            .unwrap();
480        let mut idx = 0;
481        for el in seq.iter(py).unwrap() {
482            assert_eq!(v[idx], el.unwrap().extract::<i32>(py).unwrap());
483            idx += 1;
484        }
485        assert_eq!(idx, v.len());
486    }
487
488    #[test]
489    fn test_seq_strings() {
490        let gil = Python::acquire_gil();
491        let py = gil.python();
492        let v = vec!["It", "was", "the", "worst", "of", "times"];
493        let seq = v
494            .to_py_object(py)
495            .into_object()
496            .cast_into::<PySequence>(py)
497            .unwrap();
498
499        let bad_needle = "blurst".to_py_object(py);
500        assert_eq!(false, seq.contains(py, bad_needle).unwrap());
501
502        let good_needle = "worst".to_py_object(py);
503        assert_eq!(true, seq.contains(py, good_needle).unwrap());
504    }
505
506    #[test]
507    fn test_seq_concat() {
508        let gil = Python::acquire_gil();
509        let py = gil.python();
510        let v: Vec<i32> = vec![1, 2, 3];
511        let seq = v
512            .to_py_object(py)
513            .into_object()
514            .cast_into::<PySequence>(py)
515            .unwrap();
516        let concat_seq = seq
517            .concat(py, &seq)
518            .unwrap()
519            .cast_into::<PySequence>(py)
520            .unwrap();
521        assert_eq!(6, concat_seq.len(py).unwrap());
522        let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
523        for (el, cc) in seq.iter(py).unwrap().zip(concat_v) {
524            assert_eq!(cc, el.unwrap().extract::<i32>(py).unwrap());
525        }
526    }
527
528    #[test]
529    fn test_seq_concat_string() {
530        let gil = Python::acquire_gil();
531        let py = gil.python();
532        let v = "string";
533        let seq = v
534            .to_py_object(py)
535            .into_object()
536            .cast_into::<PySequence>(py)
537            .unwrap();
538        let concat_seq = seq
539            .concat(py, &seq)
540            .unwrap()
541            .cast_into::<PySequence>(py)
542            .unwrap();
543        assert_eq!(12, concat_seq.len(py).unwrap());
544        /*let concat_v = "stringstring".to_owned();
545        for (el, cc) in seq.iter(py).unwrap().zip(concat_v.chars()) {
546            assert_eq!(cc, el.unwrap().extract::<char>(py).unwrap()); //TODO: extract::<char>() is not implemented
547        }*/
548    }
549
550    #[test]
551    fn test_seq_repeat() {
552        let gil = Python::acquire_gil();
553        let py = gil.python();
554        let v = vec!["foo", "bar"];
555        let seq = v
556            .to_py_object(py)
557            .into_object()
558            .cast_into::<PySequence>(py)
559            .unwrap();
560        let repeat_seq = seq
561            .repeat(py, 3)
562            .unwrap()
563            .cast_into::<PySequence>(py)
564            .unwrap();
565        assert_eq!(6, repeat_seq.len(py).unwrap());
566        let repeated = vec!["foo", "bar", "foo", "bar", "foo", "bar"];
567        for (el, rpt) in seq.iter(py).unwrap().zip(repeated.iter()) {
568            assert_eq!(*rpt, el.unwrap().extract::<String>(py).unwrap());
569        }
570    }
571
572    #[test]
573    fn test_list_coercion() {
574        let gil = Python::acquire_gil();
575        let py = gil.python();
576        let v = vec!["foo", "bar"];
577        let seq = v
578            .to_py_object(py)
579            .into_object()
580            .cast_into::<PySequence>(py)
581            .unwrap();
582        assert!(seq.list(py).is_ok());
583    }
584
585    #[test]
586    fn test_strings_coerce_to_lists() {
587        let gil = Python::acquire_gil();
588        let py = gil.python();
589        let v = "foo";
590        let seq = v
591            .to_py_object(py)
592            .into_object()
593            .cast_into::<PySequence>(py)
594            .unwrap();
595        assert!(seq.list(py).is_ok());
596    }
597
598    #[test]
599    fn test_tuple_coercion() {
600        let gil = Python::acquire_gil();
601        let py = gil.python();
602        let v = ("foo", "bar");
603        let seq = v
604            .to_py_object(py)
605            .into_object()
606            .cast_into::<PySequence>(py)
607            .unwrap();
608        assert!(seq.tuple(py).is_ok());
609    }
610
611    #[test]
612    fn test_lists_coerce_to_tuples() {
613        let gil = Python::acquire_gil();
614        let py = gil.python();
615        let v = vec!["foo", "bar"];
616        let seq = v
617            .to_py_object(py)
618            .into_object()
619            .cast_into::<PySequence>(py)
620            .unwrap();
621        assert!(seq.tuple(py).is_ok());
622    }
623
624    #[test]
625    fn test_extract_tuple_to_vec() {
626        let gil = Python::acquire_gil();
627        let py = gil.python();
628        let v: Vec<i32> = py.eval("(1, 2)", None, None).unwrap().extract(py).unwrap();
629        assert!(v == [1, 2]);
630    }
631
632    #[test]
633    fn test_extract_range_to_vec() {
634        let gil = Python::acquire_gil();
635        let py = gil.python();
636        let v: Vec<i32> = py
637            .eval("range(1, 5)", None, None)
638            .unwrap()
639            .extract(py)
640            .unwrap();
641        assert!(v == [1, 2, 3, 4]);
642    }
643
644    #[test]
645    fn test_extract_bytearray_to_vec() {
646        let gil = Python::acquire_gil();
647        let py = gil.python();
648        let v: Vec<u8> = py
649            .eval("bytearray(b'abc')", None, None)
650            .unwrap()
651            .extract(py)
652            .unwrap();
653        assert!(v == b"abc");
654    }
655}