diff --git a/newsfragments/2914.changed.md b/newsfragments/2914.changed.md new file mode 100644 index 00000000..bea926c4 --- /dev/null +++ b/newsfragments/2914.changed.md @@ -0,0 +1 @@ +FFI definition `PyIter_Check` on CPython 3.7 now does the equivalent for `hasattr(type(obj), "__next__")`, which works correctly on all platforms and adds support for `abi3`. diff --git a/pyo3-ffi/src/abstract_.rs b/pyo3-ffi/src/abstract_.rs index 6f954bc1..24ad4005 100644 --- a/pyo3-ffi/src/abstract_.rs +++ b/pyo3-ffi/src/abstract_.rs @@ -92,19 +92,17 @@ extern "C" { } // Before 3.8 PyIter_Check was defined in CPython as a macro, -// which uses Py_TYPE so cannot work on the limited ABI. +// but the implementation of that in PyO3 did not work, see +// https://github.com/PyO3/pyo3/pull/2914 // -// From 3.10 onwards CPython removed the macro completely, -// so PyO3 only uses this on 3.7 unlimited API. -#[cfg(not(any(Py_3_8, Py_LIMITED_API, PyPy)))] +// This is a slow implementation which should function equivalently. +#[cfg(not(any(Py_3_8, PyPy)))] #[inline] pub unsafe fn PyIter_Check(o: *mut PyObject) -> c_int { - (match (*crate::Py_TYPE(o)).tp_iternext { - Some(tp_iternext) => { - tp_iternext as *const std::os::raw::c_void != crate::_PyObject_NextNotImplemented as _ - } - None => false, - }) as c_int + crate::PyObject_HasAttrString( + crate::Py_TYPE(o).cast(), + "__next__\0".as_ptr() as *const c_char, + ) } extern "C" { diff --git a/src/types/iterator.rs b/src/types/iterator.rs index 9d68ca4f..cfbbd31b 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -3,7 +3,6 @@ // based on Daniel Grunwald's https://github.com/dgrunwald/rust-cpython use crate::{ffi, AsPyPointer, IntoPyPointer, Py, PyAny, PyErr, PyNativeType, PyResult, Python}; -#[cfg(any(not(Py_LIMITED_API), Py_3_8))] use crate::{PyDowncastError, PyTryFrom}; /// A Python iterator object. @@ -29,7 +28,6 @@ use crate::{PyDowncastError, PyTryFrom}; #[repr(transparent)] pub struct PyIterator(PyAny); pyobject_native_type_named!(PyIterator); -#[cfg(any(not(Py_LIMITED_API), Py_3_8))] pyobject_native_type_extract!(PyIterator); impl PyIterator { @@ -64,7 +62,6 @@ impl<'p> Iterator for &'p PyIterator { } // PyIter_Check does not exist in the limited API until 3.8 -#[cfg(any(not(Py_LIMITED_API), Py_3_8))] impl<'v> PyTryFrom<'v> for PyIterator { fn try_from>(value: V) -> Result<&'v PyIterator, PyDowncastError<'v>> { let value = value.into(); @@ -218,7 +215,7 @@ def fibonacci(target): } #[test] - #[cfg(any(not(Py_LIMITED_API), Py_3_8))] + fn iterator_try_from() { Python::with_gil(|py| { let obj: Py = vec![10, 20].to_object(py).as_ref(py).iter().unwrap().into(); @@ -250,7 +247,6 @@ def fibonacci(target): } #[test] - #[cfg(any(not(Py_LIMITED_API), Py_3_8))] #[cfg(feature = "macros")] fn python_class_not_iterator() { use crate::PyErr; @@ -298,7 +294,6 @@ def fibonacci(target): } #[test] - #[cfg(any(not(Py_LIMITED_API), Py_3_8))] #[cfg(feature = "macros")] fn python_class_iterator() { #[crate::pyfunction(crate = "crate")]