From 8bf0aee9c33c8596d2d7680c612b2eac1a8eb1cb Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Fri, 3 Apr 2026 16:01:04 -0600 Subject: [PATCH 1/9] pyo3-ffi: remove _PyUnicode_COMPACT_DATA from tests --- newsfragments/3762.changed.md | 1 + src/ffi/tests.rs | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) create mode 100644 newsfragments/3762.changed.md diff --git a/newsfragments/3762.changed.md b/newsfragments/3762.changed.md new file mode 100644 index 00000000000..6366903123e --- /dev/null +++ b/newsfragments/3762.changed.md @@ -0,0 +1 @@ +Removed use of the CPython-internal `_PyUnicode_COMPACT_DATA` symbol from PyO3's FFI test suite. diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 8b5226dd444..203669083c0 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -200,8 +200,6 @@ fn ascii() { // 2 and 4 byte macros return nonsense for this string instance. assert_eq!(PyUnicode_KIND(ptr), PyUnicode_1BYTE_KIND); - #[cfg(not(Py_3_14))] - assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null()); // _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings. assert!(!PyUnicode_DATA(ptr).is_null()); @@ -243,8 +241,6 @@ fn ucs4() { assert!(!PyUnicode_4BYTE_DATA(ptr).is_null()); assert_eq!(PyUnicode_KIND(ptr), PyUnicode_4BYTE_KIND); - #[cfg(not(Py_3_14))] - assert!(!_PyUnicode_COMPACT_DATA(ptr).is_null()); // _PyUnicode_NONCOMPACT_DATA isn't valid for compact strings. assert!(!PyUnicode_DATA(ptr).is_null()); From 8603c03b0c293b27a3d47cdc7f0bb86d0178bdf8 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Fri, 3 Apr 2026 16:01:24 -0600 Subject: [PATCH 2/9] pyo3-ffi: deprecate _PySet_NextEntry --- newsfragments/3762.changed.md | 1 + pyo3-ffi/src/setobject.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/newsfragments/3762.changed.md b/newsfragments/3762.changed.md index 6366903123e..137fc6ce5da 100644 --- a/newsfragments/3762.changed.md +++ b/newsfragments/3762.changed.md @@ -1 +1,2 @@ Removed use of the CPython-internal `_PyUnicode_COMPACT_DATA` symbol from PyO3's FFI test suite. +Deprecated `_PySet_NextEntry` in `pyo3-ffi`; it is a CPython-internal API with no stability guarantee. diff --git a/pyo3-ffi/src/setobject.rs b/pyo3-ffi/src/setobject.rs index b56ed5d1cd5..8416da17a8f 100644 --- a/pyo3-ffi/src/setobject.rs +++ b/pyo3-ffi/src/setobject.rs @@ -42,6 +42,7 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { extern_libpython! { #[cfg(not(Py_LIMITED_API))] + #[deprecated(note = "`_PySet_NextEntry` is a CPython-internal API with no stability guarantee; iterate sets using `PyObject_GetIter` / `PyIter_Next` instead")] #[cfg_attr(PyPy, link_name = "_PyPySet_NextEntry")] pub fn _PySet_NextEntry( set: *mut PyObject, From 7a5fee94adf6ef219c86f320ad816663ec1641c1 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Fri, 3 Apr 2026 16:02:01 -0600 Subject: [PATCH 3/9] pyo3-ffi: deprecate _PyLong_NumBits, use bit_length() for Python < 3.13 --- newsfragments/3762.changed.md | 1 + pyo3-ffi/src/longobject.rs | 1 + src/conversions/num_bigint.rs | 21 +++------------------ 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/newsfragments/3762.changed.md b/newsfragments/3762.changed.md index 137fc6ce5da..74eff9053e9 100644 --- a/newsfragments/3762.changed.md +++ b/newsfragments/3762.changed.md @@ -1,2 +1,3 @@ Removed use of the CPython-internal `_PyUnicode_COMPACT_DATA` symbol from PyO3's FFI test suite. Deprecated `_PySet_NextEntry` in `pyo3-ffi`; it is a CPython-internal API with no stability guarantee. +Deprecated `_PyLong_NumBits` in `pyo3-ffi` and replaced its internal use with `int.bit_length()` for Python < 3.13 builds. diff --git a/pyo3-ffi/src/longobject.rs b/pyo3-ffi/src/longobject.rs index 7aab545777e..6a6ef3d6ee8 100644 --- a/pyo3-ffi/src/longobject.rs +++ b/pyo3-ffi/src/longobject.rs @@ -85,6 +85,7 @@ extern_libpython! { #[cfg(not(Py_LIMITED_API))] extern_libpython! { #[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")] + #[deprecated(note = "`_PyLong_NumBits` is a CPython-internal API; use `PyLong_AsNativeBytes` on Python 3.13+ or `int.bit_length()` instead")] pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t; } diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index cdc77d8b095..c11ca92e6a5 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -318,24 +318,9 @@ fn int_to_py_bytes<'py>( #[inline] #[cfg(any(not(Py_3_13), Py_LIMITED_API))] fn int_n_bits(long: &Bound<'_, PyInt>) -> PyResult { - let py = long.py(); - #[cfg(not(Py_LIMITED_API))] - { - // fast path - let n_bits = unsafe { crate::ffi::_PyLong_NumBits(long.as_ptr()) }; - if n_bits == (-1isize as usize) { - return Err(crate::PyErr::fetch(py)); - } - Ok(n_bits) - } - - #[cfg(Py_LIMITED_API)] - { - // slow path - use crate::types::PyAnyMethods; - long.call_method0(crate::intern!(py, "bit_length")) - .and_then(|any| any.extract()) - } + use crate::types::PyAnyMethods; + long.call_method0(crate::intern!(long.py(), "bit_length")) + .and_then(|any| any.extract()) } #[cfg(test)] From 87f688b1b86fd7c505e5c7fa59053dd37cd5d205 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Thu, 9 Apr 2026 07:04:07 -0600 Subject: [PATCH 4/9] Rename news to match PR id --- newsfragments/{3762.changed.md => 5946.changed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename newsfragments/{3762.changed.md => 5946.changed.md} (100%) diff --git a/newsfragments/3762.changed.md b/newsfragments/5946.changed.md similarity index 100% rename from newsfragments/3762.changed.md rename to newsfragments/5946.changed.md From ca7a51187a9d1b767617494fdc6c101ffe808c71 Mon Sep 17 00:00:00 2001 From: codeguru42 Date: Thu, 9 Apr 2026 09:00:53 -0600 Subject: [PATCH 5/9] Format with nox --- pyo3-ffi/src/longobject.rs | 4 +++- pyo3-ffi/src/setobject.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyo3-ffi/src/longobject.rs b/pyo3-ffi/src/longobject.rs index 6a6ef3d6ee8..2f12e7639ce 100644 --- a/pyo3-ffi/src/longobject.rs +++ b/pyo3-ffi/src/longobject.rs @@ -85,7 +85,9 @@ extern_libpython! { #[cfg(not(Py_LIMITED_API))] extern_libpython! { #[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")] - #[deprecated(note = "`_PyLong_NumBits` is a CPython-internal API; use `PyLong_AsNativeBytes` on Python 3.13+ or `int.bit_length()` instead")] + #[deprecated( + note = "`_PyLong_NumBits` is a CPython-internal API; use `PyLong_AsNativeBytes` on Python 3.13+ or `int.bit_length()` instead" + )] pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t; } diff --git a/pyo3-ffi/src/setobject.rs b/pyo3-ffi/src/setobject.rs index 8416da17a8f..c101ffec02a 100644 --- a/pyo3-ffi/src/setobject.rs +++ b/pyo3-ffi/src/setobject.rs @@ -42,7 +42,9 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { extern_libpython! { #[cfg(not(Py_LIMITED_API))] - #[deprecated(note = "`_PySet_NextEntry` is a CPython-internal API with no stability guarantee; iterate sets using `PyObject_GetIter` / `PyIter_Next` instead")] + #[deprecated( + note = "`_PySet_NextEntry` is a CPython-internal API with no stability guarantee; iterate sets using `PyObject_GetIter` / `PyIter_Next` instead" + )] #[cfg_attr(PyPy, link_name = "_PyPySet_NextEntry")] pub fn _PySet_NextEntry( set: *mut PyObject, From ff36964b939a7454c1fe3a5b13a6fcfe7fc5d7d0 Mon Sep 17 00:00:00 2001 From: Code Apprentice Date: Sat, 11 Apr 2026 18:11:50 -0600 Subject: [PATCH 6/9] Completly remove _PyLong_NumBits other than Python 3.13 Co-authored-by: David Hewitt --- pyo3-ffi/src/longobject.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyo3-ffi/src/longobject.rs b/pyo3-ffi/src/longobject.rs index 2f12e7639ce..5efa5c67dfa 100644 --- a/pyo3-ffi/src/longobject.rs +++ b/pyo3-ffi/src/longobject.rs @@ -85,9 +85,8 @@ extern_libpython! { #[cfg(not(Py_LIMITED_API))] extern_libpython! { #[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")] - #[deprecated( - note = "`_PyLong_NumBits` is a CPython-internal API; use `PyLong_AsNativeBytes` on Python 3.13+ or `int.bit_length()` instead" - )] + #[cfg(not(Py_3_13)] + #[doc(hidden)] pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t; } From 929b5b6d4dca0d33a6b993641afe556ad53f4d28 Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Sat, 11 Apr 2026 18:25:12 -0600 Subject: [PATCH 7/9] Completely remove _PySet_NextEntry and update journal --- newsfragments/5946.changed.md | 4 ++-- pyo3-ffi/src/setobject.rs | 13 +------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/newsfragments/5946.changed.md b/newsfragments/5946.changed.md index 74eff9053e9..5e606663272 100644 --- a/newsfragments/5946.changed.md +++ b/newsfragments/5946.changed.md @@ -1,3 +1,3 @@ Removed use of the CPython-internal `_PyUnicode_COMPACT_DATA` symbol from PyO3's FFI test suite. -Deprecated `_PySet_NextEntry` in `pyo3-ffi`; it is a CPython-internal API with no stability guarantee. -Deprecated `_PyLong_NumBits` in `pyo3-ffi` and replaced its internal use with `int.bit_length()` for Python < 3.13 builds. +Removed `_PySet_NextEntry` in `pyo3-ffi`; it is a CPython-internal API with no stability guarantee. +Removed `_PyLong_NumBits` in `pyo3-ffi` and replaced its internal use with `int.bit_length()` for Python < 3.13 builds. diff --git a/pyo3-ffi/src/setobject.rs b/pyo3-ffi/src/setobject.rs index c101ffec02a..8a22415257f 100644 --- a/pyo3-ffi/src/setobject.rs +++ b/pyo3-ffi/src/setobject.rs @@ -41,18 +41,7 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { // skipped _PySet_Dummy extern_libpython! { - #[cfg(not(Py_LIMITED_API))] - #[deprecated( - note = "`_PySet_NextEntry` is a CPython-internal API with no stability guarantee; iterate sets using `PyObject_GetIter` / `PyIter_Next` instead" - )] - #[cfg_attr(PyPy, link_name = "_PyPySet_NextEntry")] - pub fn _PySet_NextEntry( - set: *mut PyObject, - pos: *mut Py_ssize_t, - key: *mut *mut PyObject, - hash: *mut super::Py_hash_t, - ) -> c_int; - + // skipped non-limited _PySet_NextEntry // skipped non-limited _PySet_Update } From 3d80a93c5d6baf5a72202af767776b39f50a0cdd Mon Sep 17 00:00:00 2001 From: Code-Apprentice Date: Sat, 11 Apr 2026 18:28:38 -0600 Subject: [PATCH 8/9] Fix mismatched parentheses --- pyo3-ffi/src/longobject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-ffi/src/longobject.rs b/pyo3-ffi/src/longobject.rs index 5efa5c67dfa..4ecd22ce8fc 100644 --- a/pyo3-ffi/src/longobject.rs +++ b/pyo3-ffi/src/longobject.rs @@ -85,7 +85,7 @@ extern_libpython! { #[cfg(not(Py_LIMITED_API))] extern_libpython! { #[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")] - #[cfg(not(Py_3_13)] + #[cfg(not(Py_3_13))] #[doc(hidden)] pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t; } From 40114a618936e9b7aa8d3baabe11b528d089b6ea Mon Sep 17 00:00:00 2001 From: codeguru42 Date: Sun, 12 Apr 2026 16:03:25 -0600 Subject: [PATCH 9/9] Remove macro branch for _PySet_NextEntry --- pyo3-ffi/src/impl_/macros.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pyo3-ffi/src/impl_/macros.rs b/pyo3-ffi/src/impl_/macros.rs index c493157186c..c5ca00a029e 100644 --- a/pyo3-ffi/src/impl_/macros.rs +++ b/pyo3-ffi/src/impl_/macros.rs @@ -248,12 +248,6 @@ macro_rules! extern_libpython_maybe_private_fn { ) => { extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } }; - ( - [_PySet_NextEntry] - $(#[$attrs:meta])* $vis:vis fn $name:ident($($args:tt)*) $(-> $ret:ty)? - ) => { - extern_libpython_cpython_private_fn! { $(#[$attrs])* $vis $name($($args)*) $(-> $ret)? } - }; ( [$name:ident] $(#[$attrs:meta])* $vis:vis fn $fn_name:ident($($args:tt)*) $(-> $ret:ty)?