Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/5661.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add FFI definition `PyIter_NextItem` on Python 3.14 and up, and `compat::PyIter_NextItem` for older versions.
1 change: 1 addition & 0 deletions newsfragments/5661.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use `PyIter_NextItem` in `PyIterator::next` implementation.
3 changes: 3 additions & 0 deletions pyo3-ffi/src/abstract_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ extern "C" {
#[cfg_attr(PyPy, link_name = "PyPyIter_Check")]
pub fn PyIter_Check(obj: *mut PyObject) -> c_int;

#[cfg(Py_3_14)]
#[cfg_attr(PyPy, link_name = "PyPyIter_NextItem")]
pub fn PyIter_NextItem(iter: *mut PyObject, item: *mut *mut PyObject) -> c_int;
#[cfg_attr(PyPy, link_name = "PyPyIter_Next")]
pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject;
#[cfg(all(not(PyPy), Py_3_10))]
Expand Down
19 changes: 19 additions & 0 deletions pyo3-ffi/src/compat/py_3_14.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,22 @@ compat_function!(
}
}
);

compat_function!(
originally_defined_for(Py_3_14);

#[inline]
pub unsafe fn PyIter_NextItem(
iter: *mut crate::PyObject,
item: *mut *mut crate::PyObject,
) -> std::ffi::c_int {
*item = crate::PyIter_Next(iter);
if !(*item).is_null() {
1
} else if crate::PyErr_Occurred().is_null() {
0
} else {
-1
}
}
);
26 changes: 10 additions & 16 deletions src/types/iterator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::instance::Borrowed;
use crate::py_result_ext::PyResultExt;
use crate::sync::PyOnceLock;
#[cfg(Py_LIMITED_API)]
Expand Down Expand Up @@ -105,9 +104,17 @@ impl<'py> Iterator for Bound<'py, PyIterator> {
/// If an exception occurs, returns `Some(Err(..))`.
/// Further `next()` calls after an exception occurs are likely
/// to repeatedly result in the same exception.
#[inline]
fn next(&mut self) -> Option<Self::Item> {
Borrowed::from(&*self).next()
let py = self.py();
let mut item = std::ptr::null_mut();

// SAFETY: `self` is a valid iterator object, `item` is a valid pointer to receive the next item
match unsafe { ffi::compat::PyIter_NextItem(self.as_ptr(), &mut item) } {
std::ffi::c_int::MIN..=-1 => Some(Err(PyErr::fetch(py))),
0 => None,
// SAFETY: `item` is guaranteed to be a non-null strong reference
1..=std::ffi::c_int::MAX => Some(Ok(unsafe { item.assume_owned_unchecked(py) })),
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
Expand Down Expand Up @@ -141,19 +148,6 @@ fn length_hint(iter: &Bound<'_, PyIterator>) -> PyResult<usize> {
length_hint.call1((iter, 0))?.extract()
}

impl<'py> Borrowed<'_, 'py, PyIterator> {
// TODO: this method is on Borrowed so that &'py PyIterator can use this; once that
// implementation is deleted this method should be moved to the `Bound<'py, PyIterator> impl
fn next(self) -> Option<PyResult<Bound<'py, PyAny>>> {
let py = self.py();

match unsafe { ffi::PyIter_Next(self.as_ptr()).assume_owned_or_opt(py) } {
Some(obj) => Some(Ok(obj)),
None => PyErr::take(py).map(Err),
}
}
}

impl<'py> IntoIterator for &Bound<'py, PyIterator> {
type Item = PyResult<Bound<'py, PyAny>>;
type IntoIter = Bound<'py, PyIterator>;
Expand Down
Loading