Use CPython stable APIs for implementing tuples.

Refs #1125

Reduces the number of compilation failures with `--features abi3` from 56 to 53.
This commit is contained in:
Alex Gaynor 2020-08-31 08:12:01 -04:00
parent 3151c4876e
commit 958ce796e9

View file

@ -42,7 +42,7 @@ impl PyTuple {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
unsafe { unsafe {
// non-negative Py_ssize_t should always fit into Rust uint // non-negative Py_ssize_t should always fit into Rust uint
ffi::PyTuple_GET_SIZE(self.as_ptr()) as usize ffi::PyTuple_Size(self.as_ptr()) as usize
} }
} }
@ -62,8 +62,7 @@ impl PyTuple {
/// Takes a slice of the tuple from `low` to the end and returns it as a new tuple. /// Takes a slice of the tuple from `low` to the end and returns it as a new tuple.
pub fn split_from(&self, low: isize) -> &PyTuple { pub fn split_from(&self, low: isize) -> &PyTuple {
unsafe { unsafe {
let ptr = let ptr = ffi::PyTuple_GetSlice(self.as_ptr(), low, self.len() as Py_ssize_t);
ffi::PyTuple_GetSlice(self.as_ptr(), low, ffi::PyTuple_GET_SIZE(self.as_ptr()));
self.py().from_owned_ptr(ptr) self.py().from_owned_ptr(ptr)
} }
} }
@ -75,11 +74,14 @@ impl PyTuple {
assert!(index < self.len()); assert!(index < self.len());
unsafe { unsafe {
self.py() self.py()
.from_borrowed_ptr(ffi::PyTuple_GET_ITEM(self.as_ptr(), index as Py_ssize_t)) .from_borrowed_ptr(ffi::PyTuple_GetItem(self.as_ptr(), index as Py_ssize_t))
} }
} }
/// Returns `self` as a slice of objects. /// Returns `self` as a slice of objects.
///
/// Not available when compiled with Py_LIMITED_API.
#[cfg(not(Py_LIMITED_API))]
pub fn as_slice(&self) -> &[&PyAny] { pub fn as_slice(&self) -> &[&PyAny] {
// This is safe because &PyAny has the same memory layout as *mut ffi::PyObject, // This is safe because &PyAny has the same memory layout as *mut ffi::PyObject,
// and because tuples are immutable. // and because tuples are immutable.
@ -93,16 +95,18 @@ impl PyTuple {
/// Returns an iterator over the tuple items. /// Returns an iterator over the tuple items.
pub fn iter(&self) -> PyTupleIterator { pub fn iter(&self) -> PyTupleIterator {
PyTupleIterator { PyTupleIterator {
slice: self.as_slice(), tuple: self,
index: 0, index: 0,
length: self.len(),
} }
} }
} }
/// Used by `PyTuple::iter()`. /// Used by `PyTuple::iter()`.
pub struct PyTupleIterator<'a> { pub struct PyTupleIterator<'a> {
slice: &'a [&'a PyAny], tuple: &'a PyTuple,
index: usize, index: usize,
length: usize,
} }
impl<'a> Iterator for PyTupleIterator<'a> { impl<'a> Iterator for PyTupleIterator<'a> {
@ -110,8 +114,8 @@ impl<'a> Iterator for PyTupleIterator<'a> {
#[inline] #[inline]
fn next(&mut self) -> Option<&'a PyAny> { fn next(&mut self) -> Option<&'a PyAny> {
if self.index < self.slice.len() { if self.index < self.length {
let item = self.slice[self.index]; let item = self.tuple.get_item(self.index);
self.index += 1; self.index += 1;
Some(item) Some(item)
} else { } else {
@ -172,10 +176,9 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
fn extract(obj: &'s PyAny) -> PyResult<Self> fn extract(obj: &'s PyAny) -> PyResult<Self>
{ {
let t = <PyTuple as PyTryFrom>::try_from(obj)?; let t = <PyTuple as PyTryFrom>::try_from(obj)?;
let slice = t.as_slice();
if t.len() == $length { if t.len() == $length {
Ok(( Ok((
$(slice[$n].extract::<$T>()?,)+ $(t.get_item($n).extract::<$T>()?,)+
)) ))
} else { } else {
Err(wrong_tuple_length(t, $length)) Err(wrong_tuple_length(t, $length))
@ -296,4 +299,19 @@ mod test {
assert_eq!(i + 1, item.extract().unwrap()); assert_eq!(i + 1, item.extract().unwrap());
} }
} }
#[test]
#[cfg(not(Py_LIMITED_API))]
fn test_as_slice() {
let gil = Python::acquire_gil();
let py = gil.python();
let ob = (1, 2, 3).to_object(py);
let tuple = <PyTuple as PyTryFrom>::try_from(ob.as_ref(py)).unwrap();
let slice = tuple.as_slice();
assert_eq!(3, slice.len());
assert_eq!(1, slice[0].extract().unwrap());
assert_eq!(2, slice[1].extract().unwrap());
assert_eq!(3, slice[2].extract().unwrap());
}
} }