2021-12-03 00:03:32 +00:00
|
|
|
#![cfg(feature = "macros")]
|
|
|
|
|
2020-07-04 15:55:26 +00:00
|
|
|
use pyo3::exceptions::{PyIndexError, PyValueError};
|
2022-08-10 20:03:18 +00:00
|
|
|
use pyo3::types::{IntoPyDict, PyList, PyMapping, PySequence};
|
2023-07-30 20:58:21 +00:00
|
|
|
use pyo3::{ffi, prelude::*};
|
2019-03-29 22:11:32 +00:00
|
|
|
|
2020-05-17 10:45:42 +00:00
|
|
|
use pyo3::py_run;
|
|
|
|
|
2023-09-24 12:34:53 +00:00
|
|
|
#[path = "../src/tests/common.rs"]
|
2020-05-17 10:45:42 +00:00
|
|
|
mod common;
|
|
|
|
|
2019-03-29 22:11:32 +00:00
|
|
|
#[pyclass]
|
|
|
|
struct ByteSequence {
|
|
|
|
elements: Vec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[pymethods]
|
|
|
|
impl ByteSequence {
|
|
|
|
#[new]
|
2024-05-10 10:34:58 +00:00
|
|
|
#[pyo3(signature=(elements = None))]
|
2024-03-21 07:24:40 +00:00
|
|
|
fn new(elements: Option<&Bound<'_, PyList>>) -> PyResult<Self> {
|
2019-03-31 03:25:02 +00:00
|
|
|
if let Some(pylist) = elements {
|
2019-03-29 22:11:32 +00:00
|
|
|
let mut elems = Vec::with_capacity(pylist.len());
|
2022-02-28 08:03:54 +00:00
|
|
|
for pyelem in pylist {
|
2024-03-21 07:24:40 +00:00
|
|
|
let elem = pyelem.extract()?;
|
2019-03-29 22:11:32 +00:00
|
|
|
elems.push(elem);
|
|
|
|
}
|
2019-12-14 14:16:39 +00:00
|
|
|
Ok(Self { elements: elems })
|
2019-03-29 22:11:32 +00:00
|
|
|
} else {
|
2019-12-14 14:16:39 +00:00
|
|
|
Ok(Self {
|
2019-03-29 22:17:07 +00:00
|
|
|
elements: Vec::new(),
|
2019-12-14 14:16:39 +00:00
|
|
|
})
|
2019-03-31 03:25:02 +00:00
|
|
|
}
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-23 10:40:17 +00:00
|
|
|
fn __len__(&self) -> usize {
|
|
|
|
self.elements.len()
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn __getitem__(&self, idx: isize) -> PyResult<u8> {
|
2019-03-29 22:17:07 +00:00
|
|
|
self.elements
|
|
|
|
.get(idx as usize)
|
2019-08-17 12:10:36 +00:00
|
|
|
.copied()
|
2020-08-25 19:33:36 +00:00
|
|
|
.ok_or_else(|| PyIndexError::new_err("list index out of range"))
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
2020-06-23 10:40:17 +00:00
|
|
|
fn __setitem__(&mut self, idx: isize, value: u8) {
|
2019-03-29 22:11:32 +00:00
|
|
|
self.elements[idx as usize] = value;
|
|
|
|
}
|
|
|
|
|
2022-02-15 22:51:37 +00:00
|
|
|
fn __delitem__(&mut self, mut idx: isize) -> PyResult<()> {
|
|
|
|
let self_len = self.elements.len() as isize;
|
|
|
|
if idx < 0 {
|
|
|
|
idx += self_len;
|
|
|
|
}
|
|
|
|
if (idx < self_len) && (idx >= 0) {
|
2019-03-29 22:11:32 +00:00
|
|
|
self.elements.remove(idx as usize);
|
|
|
|
Ok(())
|
|
|
|
} else {
|
2020-08-25 19:33:36 +00:00
|
|
|
Err(PyIndexError::new_err("list index out of range"))
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-20 22:35:08 +00:00
|
|
|
fn __contains__(&self, other: &Bound<'_, PyAny>) -> bool {
|
|
|
|
match other.extract::<u8>() {
|
2020-11-12 11:09:24 +00:00
|
|
|
Ok(x) => self.elements.contains(&x),
|
2020-06-23 10:40:17 +00:00
|
|
|
Err(_) => false,
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-23 08:03:43 +00:00
|
|
|
fn __concat__(&self, other: &Self) -> Self {
|
2019-03-29 22:11:32 +00:00
|
|
|
let mut elements = self.elements.clone();
|
|
|
|
elements.extend_from_slice(&other.elements);
|
2020-06-23 10:40:17 +00:00
|
|
|
Self { elements }
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-23 07:07:28 +00:00
|
|
|
fn __inplace_concat__(mut slf: PyRefMut<'_, Self>, other: &Self) -> Py<Self> {
|
2022-02-23 08:03:43 +00:00
|
|
|
slf.elements.extend_from_slice(&other.elements);
|
|
|
|
slf.into()
|
|
|
|
}
|
|
|
|
|
2019-03-29 22:11:32 +00:00
|
|
|
fn __repeat__(&self, count: isize) -> PyResult<Self> {
|
|
|
|
if count >= 0 {
|
|
|
|
let mut elements = Vec::with_capacity(self.elements.len() * count as usize);
|
|
|
|
for _ in 0..count {
|
|
|
|
elements.extend(&self.elements);
|
|
|
|
}
|
|
|
|
Ok(Self { elements })
|
|
|
|
} else {
|
2020-08-25 19:33:36 +00:00
|
|
|
Err(PyValueError::new_err("invalid repeat count"))
|
2022-02-23 08:03:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-23 07:07:28 +00:00
|
|
|
fn __inplace_repeat__(mut slf: PyRefMut<'_, Self>, count: isize) -> PyResult<Py<Self>> {
|
2022-02-23 08:03:43 +00:00
|
|
|
if count >= 0 {
|
|
|
|
let mut elements = Vec::with_capacity(slf.elements.len() * count as usize);
|
|
|
|
for _ in 0..count {
|
|
|
|
elements.extend(&slf.elements);
|
|
|
|
}
|
|
|
|
slf.elements = elements;
|
|
|
|
Ok(slf.into())
|
|
|
|
} else {
|
|
|
|
Err(PyValueError::new_err("invalid repeat count"))
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-14 07:34:05 +00:00
|
|
|
/// Return a dict with `s = ByteSequence([1, 2, 3])`.
|
2024-02-10 12:59:55 +00:00
|
|
|
fn seq_dict(py: Python<'_>) -> Bound<'_, pyo3::types::PyDict> {
|
2024-02-18 18:27:19 +00:00
|
|
|
let d = [("ByteSequence", py.get_type_bound::<ByteSequence>())].into_py_dict_bound(py);
|
2021-03-14 07:34:05 +00:00
|
|
|
// Though we can construct `s` in Rust, let's test `__new__` works.
|
|
|
|
py_run!(py, *d, "s = ByteSequence([1, 2, 3])");
|
|
|
|
d
|
|
|
|
}
|
|
|
|
|
2019-03-29 22:11:32 +00:00
|
|
|
#[test]
|
|
|
|
fn test_getitem() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
|
|
|
|
|
|
|
py_assert!(py, *d, "s[0] == 1");
|
|
|
|
py_assert!(py, *d, "s[1] == 2");
|
|
|
|
py_assert!(py, *d, "s[2] == 3");
|
|
|
|
py_expect_exception!(py, *d, "print(s[-4])", PyIndexError);
|
|
|
|
py_expect_exception!(py, *d, "print(s[4])", PyIndexError);
|
|
|
|
});
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_setitem() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
2019-03-29 22:11:32 +00:00
|
|
|
|
2022-07-19 17:34:23 +00:00
|
|
|
py_run!(py, *d, "s[0] = 4; assert list(s) == [4, 2, 3]");
|
|
|
|
py_expect_exception!(py, *d, "s[0] = 'hello'", PyTypeError);
|
|
|
|
});
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_delitem() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
2024-02-18 18:27:19 +00:00
|
|
|
let d = [("ByteSequence", py.get_type_bound::<ByteSequence>())].into_py_dict_bound(py);
|
2022-07-19 17:34:23 +00:00
|
|
|
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[0]; assert list(s) == [2, 3]"
|
|
|
|
);
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[1]; assert list(s) == [1, 3]"
|
|
|
|
);
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[-1]; assert list(s) == [1, 2]"
|
|
|
|
);
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[-2]; assert list(s) == [1, 3]"
|
|
|
|
);
|
|
|
|
py_expect_exception!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[-4]; print(list(s))",
|
|
|
|
PyIndexError
|
|
|
|
);
|
|
|
|
py_expect_exception!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2, 3]); del s[4]",
|
|
|
|
PyIndexError
|
|
|
|
);
|
|
|
|
});
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_contains() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
|
|
|
|
|
|
|
py_assert!(py, *d, "1 in s");
|
|
|
|
py_assert!(py, *d, "2 in s");
|
|
|
|
py_assert!(py, *d, "3 in s");
|
|
|
|
py_assert!(py, *d, "4 not in s");
|
|
|
|
py_assert!(py, *d, "'hello' not in s");
|
|
|
|
});
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_concat() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
2021-03-14 07:34:05 +00:00
|
|
|
|
2022-07-19 17:34:23 +00:00
|
|
|
py_run!(
|
2021-03-14 07:34:05 +00:00
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s1 = ByteSequence([1, 2]); s2 = ByteSequence([3, 4]); assert list(s1 + s2) == [1, 2, 3, 4]"
|
|
|
|
);
|
2022-07-19 17:34:23 +00:00
|
|
|
py_expect_exception!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s1 = ByteSequence([1, 2]); s2 = 'hello'; s1 + s2",
|
|
|
|
PyTypeError
|
|
|
|
);
|
|
|
|
});
|
2019-03-29 22:11:32 +00:00
|
|
|
}
|
|
|
|
|
2019-03-31 03:25:02 +00:00
|
|
|
#[test]
|
|
|
|
fn test_inplace_concat() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
|
|
|
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s += ByteSequence([4, 5]); assert list(s) == [1, 2, 3, 4, 5]"
|
|
|
|
);
|
|
|
|
py_expect_exception!(py, *d, "s += 'hello'", PyTypeError);
|
|
|
|
});
|
2019-03-31 03:25:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_repeat() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let d = seq_dict(py);
|
2019-03-31 03:25:02 +00:00
|
|
|
|
2022-07-19 17:34:23 +00:00
|
|
|
py_run!(py, *d, "s2 = s * 2; assert list(s2) == [1, 2, 3, 1, 2, 3]");
|
|
|
|
py_expect_exception!(py, *d, "s2 = s * -1", PyValueError);
|
|
|
|
});
|
2019-03-31 03:25:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_inplace_repeat() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
2024-02-18 18:27:19 +00:00
|
|
|
let d = [("ByteSequence", py.get_type_bound::<ByteSequence>())].into_py_dict_bound(py);
|
2022-07-19 17:34:23 +00:00
|
|
|
|
|
|
|
py_run!(
|
|
|
|
py,
|
|
|
|
*d,
|
|
|
|
"s = ByteSequence([1, 2]); s *= 3; assert list(s) == [1, 2, 1, 2, 1, 2]"
|
|
|
|
);
|
|
|
|
py_expect_exception!(py, *d, "s = ByteSequence([1, 2]); s *= -1", PyValueError);
|
|
|
|
});
|
2019-03-31 03:25:02 +00:00
|
|
|
}
|
2020-05-17 10:45:42 +00:00
|
|
|
|
|
|
|
// Check that #[pyo3(get, set)] works correctly for Vec<PyObject>
|
|
|
|
|
2024-05-11 14:48:45 +00:00
|
|
|
#[cfg(feature = "py-clone")]
|
2020-05-17 10:45:42 +00:00
|
|
|
#[pyclass]
|
|
|
|
struct GenericList {
|
|
|
|
#[pyo3(get, set)]
|
|
|
|
items: Vec<PyObject>,
|
|
|
|
}
|
|
|
|
|
2024-05-11 14:48:45 +00:00
|
|
|
#[cfg(feature = "py-clone")]
|
2020-05-17 10:45:42 +00:00
|
|
|
#[test]
|
|
|
|
fn test_generic_list_get() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
|
|
|
let list: PyObject = GenericList {
|
|
|
|
items: [1, 2, 3].iter().map(|i| i.to_object(py)).collect(),
|
|
|
|
}
|
|
|
|
.into_py(py);
|
2020-05-17 10:45:42 +00:00
|
|
|
|
2022-07-19 17:34:23 +00:00
|
|
|
py_assert!(py, list, "list.items == [1, 2, 3]");
|
|
|
|
});
|
2020-05-17 10:45:42 +00:00
|
|
|
}
|
|
|
|
|
2024-05-11 14:48:45 +00:00
|
|
|
#[cfg(feature = "py-clone")]
|
2020-05-17 10:45:42 +00:00
|
|
|
#[test]
|
|
|
|
fn test_generic_list_set() {
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
2024-02-20 07:45:47 +00:00
|
|
|
let list = Bound::new(py, GenericList { items: vec![] }).unwrap();
|
2022-07-19 17:34:23 +00:00
|
|
|
|
|
|
|
py_run!(py, list, "list.items = [1, 2, 3]");
|
|
|
|
assert!(list
|
|
|
|
.borrow()
|
|
|
|
.items
|
|
|
|
.iter()
|
|
|
|
.zip(&[1u32, 2, 3])
|
2024-06-13 18:24:13 +00:00
|
|
|
.all(|(a, b)| a.bind(py).eq(b.into_py(py)).unwrap()));
|
2022-07-19 17:34:23 +00:00
|
|
|
});
|
2020-05-17 10:45:42 +00:00
|
|
|
}
|
2020-06-23 09:07:16 +00:00
|
|
|
|
2022-08-19 12:36:34 +00:00
|
|
|
#[pyclass(sequence)]
|
2020-06-23 09:07:16 +00:00
|
|
|
struct OptionList {
|
|
|
|
#[pyo3(get, set)]
|
|
|
|
items: Vec<Option<i64>>,
|
|
|
|
}
|
|
|
|
|
2022-02-15 22:51:37 +00:00
|
|
|
#[pymethods]
|
|
|
|
impl OptionList {
|
2022-08-19 12:36:34 +00:00
|
|
|
fn __len__(&self) -> usize {
|
|
|
|
self.items.len()
|
|
|
|
}
|
|
|
|
|
2020-06-23 09:07:16 +00:00
|
|
|
fn __getitem__(&self, idx: isize) -> PyResult<Option<i64>> {
|
|
|
|
match self.items.get(idx as usize) {
|
|
|
|
Some(x) => Ok(*x),
|
2020-08-25 19:33:36 +00:00
|
|
|
None => Err(PyIndexError::new_err("Index out of bounds")),
|
2020-06-23 09:07:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_option_list_get() {
|
|
|
|
// Regression test for #798
|
2022-07-19 17:34:23 +00:00
|
|
|
Python::with_gil(|py| {
|
2024-02-20 07:45:47 +00:00
|
|
|
let list = Py::new(
|
2022-07-19 17:34:23 +00:00
|
|
|
py,
|
|
|
|
OptionList {
|
|
|
|
items: vec![Some(1), None],
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
py_assert!(py, list, "list[0] == 1");
|
|
|
|
py_assert!(py, list, "list[1] == None");
|
|
|
|
py_expect_exception!(py, list, "list[2]", PyIndexError);
|
|
|
|
});
|
2020-06-23 09:07:16 +00:00
|
|
|
}
|
2022-08-10 20:03:18 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sequence_is_not_mapping() {
|
2023-02-22 21:46:42 +00:00
|
|
|
Python::with_gil(|py| {
|
2024-02-20 07:45:47 +00:00
|
|
|
let list = Bound::new(
|
2023-02-22 21:46:42 +00:00
|
|
|
py,
|
|
|
|
OptionList {
|
|
|
|
items: vec![Some(1), None],
|
|
|
|
},
|
|
|
|
)
|
2024-02-20 07:45:47 +00:00
|
|
|
.unwrap()
|
|
|
|
.into_any();
|
2022-08-10 20:03:18 +00:00
|
|
|
|
2023-02-22 21:46:42 +00:00
|
|
|
PySequence::register::<OptionList>(py).unwrap();
|
2022-08-10 20:03:18 +00:00
|
|
|
|
2024-02-20 07:45:47 +00:00
|
|
|
assert!(list.downcast::<PyMapping>().is_err());
|
|
|
|
assert!(list.downcast::<PySequence>().is_ok());
|
2023-02-22 21:46:42 +00:00
|
|
|
})
|
2022-08-10 20:03:18 +00:00
|
|
|
}
|
2022-08-19 12:36:34 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn sequence_length() {
|
|
|
|
Python::with_gil(|py| {
|
2024-02-20 07:45:47 +00:00
|
|
|
let list = Bound::new(
|
2022-08-19 12:36:34 +00:00
|
|
|
py,
|
|
|
|
OptionList {
|
|
|
|
items: vec![Some(1), None],
|
|
|
|
},
|
|
|
|
)
|
2024-02-20 07:45:47 +00:00
|
|
|
.unwrap()
|
|
|
|
.into_any();
|
2022-08-19 12:36:34 +00:00
|
|
|
|
|
|
|
assert_eq!(list.len().unwrap(), 2);
|
|
|
|
assert_eq!(unsafe { ffi::PySequence_Length(list.as_ptr()) }, 2);
|
|
|
|
|
|
|
|
assert_eq!(unsafe { ffi::PyMapping_Length(list.as_ptr()) }, -1);
|
|
|
|
unsafe { ffi::PyErr_Clear() };
|
|
|
|
})
|
|
|
|
}
|