use std::mem;
use crate::buffer;
use crate::conversion::{FromPyObject, ToPyObject};
use crate::err;
use crate::err::{result_cast_from_owned_ptr, result_from_owned_ptr, PyErr, PyResult};
use crate::ffi;
use crate::ffi::Py_ssize_t;
use crate::objects::{PyIterator, PyList, PyObject, PyTuple};
use crate::python::{PyClone, PyDrop, Python, PythonObject, ToPythonPointer};
pub struct PySequence(PyObject);
pyobject_newtype!(PySequence, PySequence_Check);
impl PySequence {
#[inline]
pub fn len(&self, py: Python) -> PyResult<isize> {
let v = unsafe { ffi::PySequence_Size(self.0.as_ptr()) };
if v == -1 {
Err(PyErr::fetch(py))
} else {
Ok(v as isize)
}
}
#[inline]
pub fn concat(&self, py: Python, other: &PySequence) -> PyResult<PyObject> {
unsafe {
err::result_from_owned_ptr(py, ffi::PySequence_Concat(self.as_ptr(), other.as_ptr()))
}
}
#[inline]
pub fn repeat(&self, py: Python, count: isize) -> PyResult<PyObject> {
unsafe {
err::result_from_owned_ptr(
py,
ffi::PySequence_Repeat(self.as_ptr(), count as Py_ssize_t),
)
}
}
#[inline]
pub fn in_place_concat(&self, py: Python, other: &PySequence) -> PyResult<PyObject> {
unsafe {
result_from_owned_ptr(
py,
ffi::PySequence_InPlaceConcat(self.as_ptr(), other.as_ptr()),
)
}
}
#[inline]
pub fn in_place_repeat(&self, py: Python, count: isize) -> PyResult<PyObject> {
unsafe {
result_from_owned_ptr(
py,
ffi::PySequence_InPlaceRepeat(self.as_ptr(), count as Py_ssize_t),
)
}
}
#[inline]
pub fn get_item(&self, py: Python, index: isize) -> PyResult<PyObject> {
unsafe {
result_from_owned_ptr(
py,
ffi::PySequence_GetItem(self.as_ptr(), index as Py_ssize_t),
)
}
}
#[inline]
pub fn get_slice(&self, py: Python, begin: isize, end: isize) -> PyResult<PyObject> {
unsafe {
result_from_owned_ptr(
py,
ffi::PySequence_GetSlice(self.as_ptr(), begin as Py_ssize_t, end as Py_ssize_t),
)
}
}
#[inline]
pub fn set_item(&self, py: Python, i: isize, v: &PyObject) -> PyResult<()> {
unsafe {
err::error_on_minusone(
py,
ffi::PySequence_SetItem(self.as_ptr(), i as Py_ssize_t, v.as_ptr()),
)
}
}
#[inline]
pub fn del_item(&self, py: Python, i: isize) -> PyResult<()> {
unsafe {
err::error_on_minusone(py, ffi::PySequence_DelItem(self.as_ptr(), i as Py_ssize_t))
}
}
#[inline]
pub fn set_slice(&self, py: Python, i1: isize, i2: isize, v: &PyObject) -> PyResult<()> {
unsafe {
err::error_on_minusone(
py,
ffi::PySequence_SetSlice(
self.as_ptr(),
i1 as Py_ssize_t,
i2 as Py_ssize_t,
v.as_ptr(),
),
)
}
}
#[inline]
pub fn del_slice(&self, py: Python, i1: isize, i2: isize) -> PyResult<()> {
unsafe {
err::error_on_minusone(
py,
ffi::PySequence_DelSlice(self.as_ptr(), i1 as Py_ssize_t, i2 as Py_ssize_t),
)
}
}
#[inline]
pub fn count<V>(&self, py: Python, value: V) -> PyResult<usize>
where
V: ToPyObject,
{
let r = value.with_borrowed_ptr(py, |ptr| unsafe {
ffi::PySequence_Count(self.as_ptr(), ptr)
});
if r == -1 {
Err(PyErr::fetch(py))
} else {
Ok(r as usize)
}
}
#[inline]
pub fn contains<V>(&self, py: Python, value: V) -> PyResult<bool>
where
V: ToPyObject,
{
let r = value.with_borrowed_ptr(py, |ptr| unsafe {
ffi::PySequence_Contains(self.as_ptr(), ptr)
});
match r {
0 => Ok(false),
1 => Ok(true),
_ => Err(PyErr::fetch(py)),
}
}
#[inline]
pub fn index<V>(&self, py: Python, value: V) -> PyResult<usize>
where
V: ToPyObject,
{
let r = value.with_borrowed_ptr(py, |ptr| unsafe {
ffi::PySequence_Index(self.as_ptr(), ptr)
});
if r == -1 {
Err(PyErr::fetch(py))
} else {
Ok(r as usize)
}
}
#[inline]
pub fn list(&self, py: Python) -> PyResult<PyList> {
unsafe { result_cast_from_owned_ptr(py, ffi::PySequence_List(self.as_ptr())) }
}
#[inline]
pub fn tuple(&self, py: Python) -> PyResult<PyTuple> {
unsafe { result_cast_from_owned_ptr(py, ffi::PySequence_Tuple(self.as_ptr())) }
}
#[inline]
pub fn iter<'p>(&self, py: Python<'p>) -> PyResult<PyIterator<'p>> {
use crate::objectprotocol::ObjectProtocol;
self.as_object().iter(py)
}
}
#[cfg(not(feature = "nightly"))]
impl<'s, T> FromPyObject<'s> for Vec<T>
where
for<'a> T: FromPyObject<'a>,
{
fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
extract_sequence(py, obj)
}
}
#[cfg(feature = "nightly")]
impl<'s, T> FromPyObject<'s> for Vec<T>
where
for<'a> T: FromPyObject<'a>,
{
default fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
extract_sequence(py, obj)
}
}
#[cfg(feature = "nightly")]
impl<'s, T> FromPyObject<'s> for Vec<T>
where
for<'a> T: FromPyObject<'a> + buffer::Element + Copy,
{
fn extract(py: Python, obj: &'s PyObject) -> PyResult<Self> {
extract_buffer_or_sequence(py, obj)
}
}
pub(crate) fn extract_buffer_or_sequence<T>(py: Python, obj: &PyObject) -> PyResult<Vec<T>>
where
for<'a> T: FromPyObject<'a> + buffer::Element + Copy,
{
if let Ok(buf) = buffer::PyBuffer::get(py, obj) {
if buf.dimensions() == 1 {
if let Ok(v) = buf.to_vec::<T>(py) {
buf.release_ref(py);
return Ok(v);
}
}
buf.release_ref(py);
}
extract_sequence(py, obj)
}
fn extract_sequence<T>(py: Python, obj: &PyObject) -> PyResult<Vec<T>>
where
for<'a> T: FromPyObject<'a>,
{
let seq = obj.cast_as::<PySequence>(py)?;
let mut v = Vec::new();
for item in seq.iter(py)? {
let item = item?;
v.push(T::extract(py, &item)?);
item.release_ref(py);
}
Ok(v)
}
#[cfg(test)]
mod test {
use crate::conversion::ToPyObject;
use crate::objects::{PyIterator, PyList, PySequence, PyTuple};
use crate::python::{Python, PythonObject};
#[test]
fn test_numbers_are_not_sequences() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = 42i32;
assert!(v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.is_err());
}
#[test]
fn test_strings_are_sequences() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "London Calling";
assert!(v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.is_ok());
}
#[test]
fn test_seq_empty() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(0, seq.len(py).unwrap());
let needle = 7i32.to_py_object(py).into_object();
assert_eq!(false, seq.contains(py, &needle).unwrap());
}
#[test]
fn test_seq_contains() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(6, seq.len(py).unwrap());
let bad_needle = 7i32.to_py_object(py).into_object();
assert_eq!(false, seq.contains(py, &bad_needle).unwrap());
let good_needle = 8i32.to_py_object(py).into_object();
assert_eq!(true, seq.contains(py, &good_needle).unwrap());
let type_coerced_needle = 8f32.to_py_object(py).into_object();
assert_eq!(true, seq.contains(py, &type_coerced_needle).unwrap());
}
#[test]
fn test_seq_get_item() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert_eq!(1, seq.get_item(py, 1).unwrap().extract::<i32>(py).unwrap());
assert_eq!(2, seq.get_item(py, 2).unwrap().extract::<i32>(py).unwrap());
assert_eq!(3, seq.get_item(py, 3).unwrap().extract::<i32>(py).unwrap());
assert_eq!(5, seq.get_item(py, 4).unwrap().extract::<i32>(py).unwrap());
assert_eq!(8, seq.get_item(py, 5).unwrap().extract::<i32>(py).unwrap());
assert_eq!(8, seq.get_item(py, -1).unwrap().extract::<i32>(py).unwrap());
assert_eq!(5, seq.get_item(py, -2).unwrap().extract::<i32>(py).unwrap());
assert_eq!(3, seq.get_item(py, -3).unwrap().extract::<i32>(py).unwrap());
assert_eq!(2, seq.get_item(py, -4).unwrap().extract::<i32>(py).unwrap());
assert_eq!(1, seq.get_item(py, -5).unwrap().extract::<i32>(py).unwrap());
assert!(seq.get_item(py, 10).is_err());
}
#[test]
fn test_seq_del_item() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert!(seq.del_item(py, 10).is_err());
assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(1, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(2, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(3, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(5, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(8, seq.get_item(py, 0).unwrap().extract::<i32>(py).unwrap());
assert!(seq.del_item(py, 0).is_ok());
assert_eq!(0, seq.len(py).unwrap());
assert!(seq.del_item(py, 0).is_err());
}
#[test]
fn test_seq_index() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(0, seq.index(py, 1i32).unwrap());
assert_eq!(2, seq.index(py, 2i32).unwrap());
assert_eq!(3, seq.index(py, 3i32).unwrap());
assert_eq!(4, seq.index(py, 5i32).unwrap());
assert_eq!(5, seq.index(py, 8i32).unwrap());
assert!(seq.index(py, 42i32).is_err());
}
#[test]
fn test_seq_count() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(2, seq.count(py, 1i32).unwrap());
assert_eq!(1, seq.count(py, 2i32).unwrap());
assert_eq!(1, seq.count(py, 3i32).unwrap());
assert_eq!(1, seq.count(py, 5i32).unwrap());
assert_eq!(1, seq.count(py, 8i32).unwrap());
assert_eq!(0, seq.count(py, 42i32).unwrap());
}
#[test]
fn test_seq_iter() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 1, 2, 3, 5, 8];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
let mut idx = 0;
for el in seq.iter(py).unwrap() {
assert_eq!(v[idx], el.unwrap().extract::<i32>(py).unwrap());
idx += 1;
}
assert_eq!(idx, v.len());
}
#[test]
fn test_seq_strings() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = vec!["It", "was", "the", "worst", "of", "times"];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
let bad_needle = "blurst".to_py_object(py);
assert_eq!(false, seq.contains(py, bad_needle).unwrap());
let good_needle = "worst".to_py_object(py);
assert_eq!(true, seq.contains(py, good_needle).unwrap());
}
#[test]
fn test_seq_concat() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = vec![1, 2, 3];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
let concat_seq = seq
.concat(py, &seq)
.unwrap()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(6, concat_seq.len(py).unwrap());
let concat_v: Vec<i32> = vec![1, 2, 3, 1, 2, 3];
for (el, cc) in seq.iter(py).unwrap().zip(concat_v) {
assert_eq!(cc, el.unwrap().extract::<i32>(py).unwrap());
}
}
#[test]
fn test_seq_concat_string() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "string";
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
let concat_seq = seq
.concat(py, &seq)
.unwrap()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(12, concat_seq.len(py).unwrap());
}
#[test]
fn test_seq_repeat() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = vec!["foo", "bar"];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
let repeat_seq = seq
.repeat(py, 3)
.unwrap()
.cast_into::<PySequence>(py)
.unwrap();
assert_eq!(6, repeat_seq.len(py).unwrap());
let repeated = vec!["foo", "bar", "foo", "bar", "foo", "bar"];
for (el, rpt) in seq.iter(py).unwrap().zip(repeated.iter()) {
assert_eq!(*rpt, el.unwrap().extract::<String>(py).unwrap());
}
}
#[test]
fn test_list_coercion() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = vec!["foo", "bar"];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert!(seq.list(py).is_ok());
}
#[test]
fn test_strings_coerce_to_lists() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = "foo";
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert!(seq.list(py).is_ok());
}
#[test]
fn test_tuple_coercion() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = ("foo", "bar");
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert!(seq.tuple(py).is_ok());
}
#[test]
fn test_lists_coerce_to_tuples() {
let gil = Python::acquire_gil();
let py = gil.python();
let v = vec!["foo", "bar"];
let seq = v
.to_py_object(py)
.into_object()
.cast_into::<PySequence>(py)
.unwrap();
assert!(seq.tuple(py).is_ok());
}
#[test]
fn test_extract_tuple_to_vec() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = py.eval("(1, 2)", None, None).unwrap().extract(py).unwrap();
assert!(v == [1, 2]);
}
#[test]
fn test_extract_range_to_vec() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<i32> = py
.eval("range(1, 5)", None, None)
.unwrap()
.extract(py)
.unwrap();
assert!(v == [1, 2, 3, 4]);
}
#[test]
fn test_extract_bytearray_to_vec() {
let gil = Python::acquire_gil();
let py = gil.python();
let v: Vec<u8> = py
.eval("bytearray(b'abc')", None, None)
.unwrap()
.extract(py)
.unwrap();
assert!(v == b"abc");
}
}