From c807b740573eb1a5d84d037b668529cb7785d976 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Sat, 21 Dec 2024 18:07:19 +0000 Subject: [PATCH 01/16] Add news item --- newsfragments/4811-packaging.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4811-packaging.md diff --git a/newsfragments/4811-packaging.md b/newsfragments/4811-packaging.md new file mode 100644 index 00000000000..cad2a26146b --- /dev/null +++ b/newsfragments/4811-packaging.md @@ -0,0 +1 @@ +Bump supported cpython version to 3.14 for testing From f7c94263121d946f60307943c0f626296f31e999 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Sun, 22 Dec 2024 08:50:05 -0500 Subject: [PATCH 02/16] Move news file --- newsfragments/{4811-packaging.md => 4811.fixed.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename newsfragments/{4811-packaging.md => 4811.fixed.md} (100%) diff --git a/newsfragments/4811-packaging.md b/newsfragments/4811.fixed.md similarity index 100% rename from newsfragments/4811-packaging.md rename to newsfragments/4811.fixed.md From 35c7e3d1ed41cf7b471b4a038e7395f573bbce3c Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Sun, 22 Dec 2024 08:50:54 -0500 Subject: [PATCH 03/16] Fix version limit check in noxfile.py --- noxfile.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/noxfile.py b/noxfile.py index 9f94175ab16..d18ed63e556 100644 --- a/noxfile.py +++ b/noxfile.py @@ -41,7 +41,7 @@ PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src" PYO3_GUIDE_TARGET = PYO3_TARGET / "guide" PYO3_DOCS_TARGET = PYO3_TARGET / "doc" -PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13") +PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14") PYPY_VERSIONS = ("3.9", "3.10") FREE_THREADED_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) @@ -677,8 +677,8 @@ def test_version_limits(session: nox.Session): config_file.set("CPython", "3.6") _run_cargo(session, "check", env=env, expect_error=True) - assert "3.14" not in PY_VERSIONS - config_file.set("CPython", "3.14") + assert "3.15" not in PY_VERSIONS + config_file.set("CPython", "3.15") _run_cargo(session, "check", env=env, expect_error=True) # 3.14 CPython should build with forward compatibility From 845d5627326eb3ac4b67790f0debe331865748c8 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Tue, 24 Dec 2024 12:20:11 +0000 Subject: [PATCH 04/16] Bump Python version for testing debug builds --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 929955c7084..1eae4b0bdaf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -539,8 +539,8 @@ jobs: components: rust-src - name: Install python3 standalone debug build with nox run: | - PBS_RELEASE="20241016" - PBS_PYTHON_VERSION="3.13.0" + PBS_RELEASE="20241219" + PBS_PYTHON_VERSION="3.13.1" PBS_ARCHIVE="cpython-${PBS_PYTHON_VERSION}+${PBS_RELEASE}-x86_64-unknown-linux-gnu-debug-full.tar.zst" wget "https://github.com/indygreg/python-build-standalone/releases/download/${PBS_RELEASE}/${PBS_ARCHIVE}" tar -I zstd -xf "${PBS_ARCHIVE}" From 56eaa4eda1c8a589ec4bd8f8aac17c3b0d2b3003 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Tue, 24 Dec 2024 12:25:39 +0000 Subject: [PATCH 05/16] 3.14 is available from GH's setup-python action --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1eae4b0bdaf..31fe1dec85d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -252,6 +252,7 @@ jobs: "3.11", "3.12", "3.13", + "3.14-dev", "pypy3.9", "pypy3.10", "graalpy24.0", From 85d22a9fddb553a57153dbbd8df1e0c0e8f08b75 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Fri, 17 Jan 2025 17:58:49 +0000 Subject: [PATCH 06/16] Bump maximum supported CPython version in pyo3-ffi --- pyo3-ffi/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-ffi/build.rs b/pyo3-ffi/build.rs index ea023de75fa..9f282a6c3b7 100644 --- a/pyo3-ffi/build.rs +++ b/pyo3-ffi/build.rs @@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions { min: PythonVersion { major: 3, minor: 7 }, max: PythonVersion { major: 3, - minor: 13, + minor: 14, }, }; From dab5748e4d24a0ee3d9436ff859293f23dbcc96a Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Tue, 21 Jan 2025 15:41:30 +0000 Subject: [PATCH 07/16] Bump ABI3_MAX_MINOR This adds the `Py_3_14` macro for conditional compilation. --- pyo3-build-config/src/impl_.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index f130c2d6557..2c4955dcc6f 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -40,7 +40,7 @@ const MINIMUM_SUPPORTED_VERSION_GRAALPY: PythonVersion = PythonVersion { }; /// Maximum Python version that can be used as minimum required Python version with abi3. -pub(crate) const ABI3_MAX_MINOR: u8 = 12; +pub(crate) const ABI3_MAX_MINOR: u8 = 13; #[cfg(test)] thread_local! { From cd272b335ec03bc9f56c63cc9d6acb9753296823 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Tue, 21 Jan 2025 15:46:29 +0000 Subject: [PATCH 08/16] Rework PyASCIIObject and PyUnicodeObject to be compatible with 3.14 Due to python/cpython#128196, data types within `PyASCIIObject.state` have changed, resulting in test failures when building against 3.14. --- pyo3-ffi/src/cpython/unicodeobject.rs | 242 +++++++++++++++++++++++++- 1 file changed, 237 insertions(+), 5 deletions(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index fae626b8d25..03b7c54e5f9 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -2,7 +2,7 @@ use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; use libc::wchar_t; -use std::os::raw::{c_char, c_int, c_uint, c_void}; +use std::os::raw::{c_char, c_int, c_uint, c_void, c_ushort}; // skipped Py_UNICODE_ISSPACE() // skipped Py_UNICODE_ISLOWER() @@ -119,8 +119,10 @@ where #[cfg(not(GraalPy))] const STATE_INTERNED_INDEX: usize = 0; -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] const STATE_INTERNED_WIDTH: u8 = 2; +#[cfg(all(not(GraalPy), Py_3_14))] +const STATE_INTERNED_WIDTH: u8 = 16; #[cfg(not(GraalPy))] const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize; @@ -138,6 +140,12 @@ const STATE_ASCII_INDEX: usize = #[cfg(not(GraalPy))] const STATE_ASCII_WIDTH: u8 = 1; +#[cfg(all(not(GraalPy), Py_3_14))] +const STATE_STATICALLY_ALLOCATED_INDEX: usize = + (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; + #[cfg(all(not(GraalPy), Py_3_14))] +const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; + #[cfg(not(any(Py_3_12, GraalPy)))] const STATE_READY_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; @@ -164,6 +172,7 @@ struct PyASCIIObjectState { #[cfg(not(GraalPy))] #[allow(clippy::useless_transmute)] impl PyASCIIObjectState { + #[cfg(not(Py_3_14))] #[inline] unsafe fn interned(&self) -> c_uint { std::mem::transmute( @@ -172,6 +181,7 @@ impl PyASCIIObjectState { ) } + #[cfg(not(Py_3_14))] #[inline] unsafe fn set_interned(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -179,11 +189,33 @@ impl PyASCIIObjectState { .set(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH, val as u64) } + #[inline] + unsafe fn interned(&self) -> u16 { + std::mem::transmute( + self.bitfield + .get(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH) as u16, + ) + } + + #[inline] + unsafe fn set_interned(&mut self, val: u16) { + let val: u16 = std::mem::transmute(val); + self.bitfield + .set(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH, val as u64) + } + + #[cfg(not(Py_3_14))] #[inline] unsafe fn kind(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as u32) } + #[inline] + unsafe fn kind(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as c_ushort) + } + + #[cfg(not(Py_3_14))] #[inline] unsafe fn set_kind(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -191,23 +223,46 @@ impl PyASCIIObjectState { .set(STATE_KIND_INDEX, STATE_KIND_WIDTH, val as u64) } + #[inline] + unsafe fn set_kind(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_KIND_INDEX, STATE_KIND_WIDTH, val as u64) + } + + #[cfg(not(Py_3_14))] #[inline] unsafe fn compact(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as u32) } #[inline] + unsafe fn compact(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as c_ushort) + } + + #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_compact(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield .set(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH, val as u64) } + #[inline] + unsafe fn set_compact(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH, val as u64) + } + + #[cfg(not(Py_3_14))] #[inline] unsafe fn ascii(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as u32) } + #[cfg(not(Py_3_14))] #[inline] unsafe fn set_ascii(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -215,6 +270,44 @@ impl PyASCIIObjectState { .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) } + #[inline] + unsafe fn ascii(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as c_ushort) + } + + #[inline] + unsafe fn set_ascii(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) + } + + #[cfg(not(Py_3_14))] + #[inline] + unsafe fn statically_allocated(&self) -> c_uint { + std::mem::transmute(self.bitfield.get(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH) as u32) + } + + #[cfg(not(Py_3_14))] + #[inline] + unsafe fn set_statically_allocated(&mut self, val: c_uint) { + let val: u32 = std::mem::transmute(val); + self.bitfield + .set(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH, val as u64) + } + + #[inline] + unsafe fn statically_allocated(&self) -> c_ushort { + std::mem::transmute(self.bitfield.get(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH) as c_ushort) + } + + #[inline] + unsafe fn set_statically_allocated(&mut self, val: c_ushort) { + let val: c_ushort = std::mem::transmute(val); + self.bitfield + .set(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH, val as u64) + } + #[cfg(not(Py_3_12))] #[inline] unsafe fn ready(&self) -> c_uint { @@ -258,12 +351,29 @@ pub struct PyASCIIObject { /// Rust doesn't expose bitfields. So we have accessor functions for /// retrieving values. /// + /// Before 3.12: /// unsigned int interned:2; // SSTATE_* constants. /// unsigned int kind:3; // PyUnicode_*_KIND constants. /// unsigned int compact:1; /// unsigned int ascii:1; /// unsigned int ready:1; /// unsigned int :24; + /// + /// 3.12 and 3.13: + /// unsigned int interned:2; // SSTATE_* constants. + /// unsigned int kind:3; // PyUnicode_*_KIND constants. + /// unsigned int compact:1; + /// unsigned int ascii:1; + /// unsigned int statically_allocated:1; + /// unsigned int :24; + /// + /// 3.14 and later: + /// uint16_t interned; // SSTATE_* constants. + /// unsigned short kind:3; // PyUnicode_*_KIND constants. + /// unsigned short compact:1; + /// unsigned short ascii:1; + /// unsigned int statically_allocated:1; + /// unsigned int :10; pub state: u32, #[cfg(not(Py_3_12))] pub wstr: *mut wchar_t, @@ -278,6 +388,7 @@ impl PyASCIIObject { /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn interned(&self) -> c_uint { PyASCIIObjectState::from(self.state).interned() } @@ -289,56 +400,125 @@ impl PyASCIIObject { /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_interned(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_interned(val); self.state = u32::from(state); } + #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 + /// Get the `interned` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], + /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. + #[inline] + pub unsafe fn interned(&self) -> u16 { + PyASCIIObjectState::from(self.state).interned() + } + + #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 + /// Set the `interned` field of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is not [`SSTATE_NOT_INTERNED`], + /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or + /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. + #[inline] + pub unsafe fn set_interned(&mut self, val: u16) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_interned(val); + self.state = u32::from(state); + } + /// Get the `kind` field of the [`PyASCIIObject`] state bitfield. /// /// Returns one of: #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn kind(&self) -> c_uint { PyASCIIObjectState::from(self.state).kind() } + /// Get the `kind` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns one of: + #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] + /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. + #[inline] + pub unsafe fn kind(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).kind() + } + /// Set the `kind` field of the [`PyASCIIObject`] state bitfield. /// /// Calling this function with an argument that is not #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_kind(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_kind(val); self.state = u32::from(state); } + /// Set the `kind` field of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is not + #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] + /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. + #[inline] + pub unsafe fn set_kind(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_kind(val); + self.state = u32::from(state); + } + /// Get the `compact` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn compact(&self) -> c_uint { PyASCIIObjectState::from(self.state).compact() } + /// Get the `compact` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + pub unsafe fn compact(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).compact() + } + /// Set the `compact` flag of the [`PyASCIIObject`] state bitfield. /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_compact(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_compact(val); self.state = u32::from(state); } + /// Set the `compact` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + pub unsafe fn set_compact(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_compact(val); + self.state = u32::from(state); + } + /// Get the `ascii` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn ascii(&self) -> c_uint { PyASCIIObjectState::from(self.state).ascii() } @@ -347,12 +527,31 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_ascii(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_ascii(val); self.state = u32::from(state); } + /// Get the `ascii` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + pub unsafe fn ascii(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).ascii() + } + + /// Set the `ascii` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + pub unsafe fn set_ascii(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_ascii(val); + self.state = u32::from(state); + } + /// Get the `ready` field of the [`PyASCIIObject`] state bitfield. /// /// Returns either `0` or `1`. @@ -413,7 +612,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; #[cfg(Py_3_12)] pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -423,12 +622,28 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).ascii() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).compact() } +#[cfg(not(GraalPy))] +#[inline] +pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_ushort { + debug_assert!(crate::PyUnicode_Check(op) != 0); + #[cfg(not(Py_3_12))] + debug_assert!(PyUnicode_IS_READY(op) != 0); + + (*(op as *mut PyASCIIObject)).ascii() +} + +#[cfg(not(GraalPy))] +#[inline] +pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_ushort { + (*(op as *mut PyASCIIObject)).compact() +} + #[cfg(not(GraalPy))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { @@ -439,10 +654,17 @@ pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { #[deprecated(note = "Removed in Python 3.12")] pub const PyUnicode_WCHAR_KIND: c_uint = 0; +#[cfg(not(Py_3_14))] pub const PyUnicode_1BYTE_KIND: c_uint = 1; +#[cfg(not(Py_3_14))] pub const PyUnicode_2BYTE_KIND: c_uint = 2; +#[cfg(not(Py_3_14))] pub const PyUnicode_4BYTE_KIND: c_uint = 4; +pub const PyUnicode_1BYTE_KIND: c_ushort = 1; +pub const PyUnicode_2BYTE_KIND: c_ushort = 2; +pub const PyUnicode_4BYTE_KIND: c_ushort = 4; + #[cfg(not(any(GraalPy, PyPy)))] #[inline] pub unsafe fn PyUnicode_1BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS1 { @@ -461,7 +683,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { PyUnicode_DATA(op) as *mut Py_UCS4 } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -471,6 +693,16 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).kind() } +#[cfg(not(GraalPy))] +#[inline] +pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_ushort { + debug_assert!(crate::PyUnicode_Check(op) != 0); + #[cfg(not(Py_3_12))] + debug_assert!(PyUnicode_IS_READY(op) != 0); + + (*(op as *mut PyASCIIObject)).kind() +} + #[cfg(not(GraalPy))] #[inline] pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { From 4d2a5dfc806223b69b1b60ca13904742b4d6a85d Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Tue, 21 Jan 2025 15:53:47 +0000 Subject: [PATCH 09/16] Run `cargo fmt --all` --- pyo3-ffi/src/cpython/unicodeobject.rs | 32 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index 03b7c54e5f9..9e7801a472e 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -2,7 +2,7 @@ use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; use libc::wchar_t; -use std::os::raw::{c_char, c_int, c_uint, c_void, c_ushort}; +use std::os::raw::{c_char, c_int, c_uint, c_ushort, c_void}; // skipped Py_UNICODE_ISSPACE() // skipped Py_UNICODE_ISLOWER() @@ -143,7 +143,7 @@ const STATE_ASCII_WIDTH: u8 = 1; #[cfg(all(not(GraalPy), Py_3_14))] const STATE_STATICALLY_ALLOCATED_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; - #[cfg(all(not(GraalPy), Py_3_14))] +#[cfg(all(not(GraalPy), Py_3_14))] const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; #[cfg(not(any(Py_3_12, GraalPy)))] @@ -285,27 +285,39 @@ impl PyASCIIObjectState { #[cfg(not(Py_3_14))] #[inline] unsafe fn statically_allocated(&self) -> c_uint { - std::mem::transmute(self.bitfield.get(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH) as u32) + std::mem::transmute(self.bitfield.get( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + ) as u32) } #[cfg(not(Py_3_14))] #[inline] unsafe fn set_statically_allocated(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); - self.bitfield - .set(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH, val as u64) + self.bitfield.set( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + val as u64, + ) } #[inline] unsafe fn statically_allocated(&self) -> c_ushort { - std::mem::transmute(self.bitfield.get(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH) as c_ushort) + std::mem::transmute(self.bitfield.get( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + ) as c_ushort) } #[inline] unsafe fn set_statically_allocated(&mut self, val: c_ushort) { let val: c_ushort = std::mem::transmute(val); - self.bitfield - .set(STATE_STATICALLY_ALLOCATED_INDEX, STATE_STATICALLY_ALLOCATED_WIDTH, val as u64) + self.bitfield.set( + STATE_STATICALLY_ALLOCATED_INDEX, + STATE_STATICALLY_ALLOCATED_WIDTH, + val as u64, + ) } #[cfg(not(Py_3_12))] @@ -358,7 +370,7 @@ pub struct PyASCIIObject { /// unsigned int ascii:1; /// unsigned int ready:1; /// unsigned int :24; - /// + /// /// 3.12 and 3.13: /// unsigned int interned:2; // SSTATE_* constants. /// unsigned int kind:3; // PyUnicode_*_KIND constants. @@ -366,7 +378,7 @@ pub struct PyASCIIObject { /// unsigned int ascii:1; /// unsigned int statically_allocated:1; /// unsigned int :24; - /// + /// /// 3.14 and later: /// uint16_t interned; // SSTATE_* constants. /// unsigned short kind:3; // PyUnicode_*_KIND constants. From eeca5e4a9bb88334607b0518c1138f4eae585cea Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 10:30:40 -0500 Subject: [PATCH 10/16] Actually add Py_3_14 as a legitimate macro When `rustc` is invoked, the macro is included with the `--check-cfg` flag, but not with the `--cfg` flag. This caused errors about duplicate definitions to spew out when building with stable Rust toolchains. --- pyo3-build-config/src/impl_.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 2c4955dcc6f..79e46e00218 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -179,7 +179,7 @@ impl InterpreterConfig { let mut out = vec![]; - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { + for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor + 1 { out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); } @@ -3116,7 +3116,7 @@ mod tests { implementation: PythonImplementation::CPython, version: PythonVersion { major: 3, - minor: 13, + minor: 14, }, shared: true, abi3: false, @@ -3140,6 +3140,7 @@ mod tests { "cargo:rustc-cfg=Py_3_11".to_owned(), "cargo:rustc-cfg=Py_3_12".to_owned(), "cargo:rustc-cfg=Py_3_13".to_owned(), + "cargo:rustc-cfg=Py_3_14".to_owned(), "cargo:rustc-cfg=Py_GIL_DISABLED".to_owned(), ] ); From b010670a791ad622e67701c6d8811d5dfba485f9 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 13:47:12 -0500 Subject: [PATCH 11/16] Revert "Actually add Py_3_14 as a legitimate macro" This reverts commit 5da57afaee1975643bcf8e61dc9b1ce9d320d47c. --- pyo3-build-config/src/impl_.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pyo3-build-config/src/impl_.rs b/pyo3-build-config/src/impl_.rs index 79e46e00218..2c4955dcc6f 100644 --- a/pyo3-build-config/src/impl_.rs +++ b/pyo3-build-config/src/impl_.rs @@ -179,7 +179,7 @@ impl InterpreterConfig { let mut out = vec![]; - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor + 1 { + for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); } @@ -3116,7 +3116,7 @@ mod tests { implementation: PythonImplementation::CPython, version: PythonVersion { major: 3, - minor: 14, + minor: 13, }, shared: true, abi3: false, @@ -3140,7 +3140,6 @@ mod tests { "cargo:rustc-cfg=Py_3_11".to_owned(), "cargo:rustc-cfg=Py_3_12".to_owned(), "cargo:rustc-cfg=Py_3_13".to_owned(), - "cargo:rustc-cfg=Py_3_14".to_owned(), "cargo:rustc-cfg=Py_GIL_DISABLED".to_owned(), ] ); From bf6e59e3637739cafd0c24bb3a6f6b0b6c5c3162 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 14:19:19 -0500 Subject: [PATCH 12/16] Fix version macro placement for 3.14-specific getters and setters --- pyo3-ffi/src/cpython/unicodeobject.rs | 60 ++++++++++++++------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index 9e7801a472e..2bf5bac8919 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -140,10 +140,10 @@ const STATE_ASCII_INDEX: usize = #[cfg(not(GraalPy))] const STATE_ASCII_WIDTH: u8 = 1; -#[cfg(all(not(GraalPy), Py_3_14))] +#[cfg(all(not(GraalPy), Py_3_12))] const STATE_STATICALLY_ALLOCATED_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; -#[cfg(all(not(GraalPy), Py_3_14))] +#[cfg(all(not(GraalPy), Py_3_12))] const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; #[cfg(not(any(Py_3_12, GraalPy)))] @@ -172,7 +172,6 @@ struct PyASCIIObjectState { #[cfg(not(GraalPy))] #[allow(clippy::useless_transmute)] impl PyASCIIObjectState { - #[cfg(not(Py_3_14))] #[inline] unsafe fn interned(&self) -> c_uint { std::mem::transmute( @@ -181,7 +180,6 @@ impl PyASCIIObjectState { ) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn set_interned(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -190,6 +188,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn interned(&self) -> u16 { std::mem::transmute( self.bitfield @@ -198,24 +197,24 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn set_interned(&mut self, val: u16) { let val: u16 = std::mem::transmute(val); self.bitfield .set(STATE_INTERNED_INDEX, STATE_INTERNED_WIDTH, val as u64) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn kind(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as u32) } #[inline] + #[cfg(Py_3_14)] unsafe fn kind(&self) -> c_ushort { std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as c_ushort) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn set_kind(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -224,25 +223,25 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn set_kind(&mut self, val: c_ushort) { let val: c_ushort = std::mem::transmute(val); self.bitfield .set(STATE_KIND_INDEX, STATE_KIND_WIDTH, val as u64) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn compact(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as u32) } #[inline] + #[cfg(Py_3_14)] unsafe fn compact(&self) -> c_ushort { std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as c_ushort) } #[inline] - #[cfg(not(Py_3_14))] unsafe fn set_compact(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -250,19 +249,18 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn set_compact(&mut self, val: c_ushort) { let val: c_ushort = std::mem::transmute(val); self.bitfield .set(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH, val as u64) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn ascii(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as u32) } - #[cfg(not(Py_3_14))] #[inline] unsafe fn set_ascii(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -271,18 +269,20 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn ascii(&self) -> c_ushort { std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as c_ushort) } #[inline] + #[cfg(Py_3_14)] unsafe fn set_ascii(&mut self, val: c_ushort) { let val: c_ushort = std::mem::transmute(val); self.bitfield .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) } - #[cfg(not(Py_3_14))] + #[cfg(all(Py_3_12, not(Py_3_14)))] #[inline] unsafe fn statically_allocated(&self) -> c_uint { std::mem::transmute(self.bitfield.get( @@ -291,7 +291,7 @@ impl PyASCIIObjectState { ) as u32) } - #[cfg(not(Py_3_14))] + #[cfg(all(Py_3_12, not(Py_3_14)))] #[inline] unsafe fn set_statically_allocated(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); @@ -303,6 +303,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn statically_allocated(&self) -> c_ushort { std::mem::transmute(self.bitfield.get( STATE_STATICALLY_ALLOCATED_INDEX, @@ -311,6 +312,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(Py_3_14)] unsafe fn set_statically_allocated(&mut self, val: c_ushort) { let val: c_ushort = std::mem::transmute(val); self.bitfield.set( @@ -400,7 +402,6 @@ impl PyASCIIObject { /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn interned(&self) -> c_uint { PyASCIIObjectState::from(self.state).interned() } @@ -412,7 +413,6 @@ impl PyASCIIObject { /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn set_interned(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_interned(val); @@ -425,6 +425,7 @@ impl PyASCIIObject { /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. #[inline] + #[cfg(Py_3_14)] pub unsafe fn interned(&self) -> u16 { PyASCIIObjectState::from(self.state).interned() } @@ -436,6 +437,7 @@ impl PyASCIIObject { /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. #[inline] + #[cfg(Py_3_14)] pub unsafe fn set_interned(&mut self, val: u16) { let mut state = PyASCIIObjectState::from(self.state); state.set_interned(val); @@ -448,7 +450,6 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn kind(&self) -> c_uint { PyASCIIObjectState::from(self.state).kind() } @@ -459,6 +460,7 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. #[inline] + #[cfg(Py_3_14)] pub unsafe fn kind(&self) -> c_ushort { PyASCIIObjectState::from(self.state).kind() } @@ -469,7 +471,6 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn set_kind(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_kind(val); @@ -482,6 +483,7 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. #[inline] + #[cfg(Py_3_14)] pub unsafe fn set_kind(&mut self, val: c_ushort) { let mut state = PyASCIIObjectState::from(self.state); state.set_kind(val); @@ -492,7 +494,6 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn compact(&self) -> c_uint { PyASCIIObjectState::from(self.state).compact() } @@ -501,6 +502,7 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] + #[cfg(Py_3_14)] pub unsafe fn compact(&self) -> c_ushort { PyASCIIObjectState::from(self.state).compact() } @@ -509,7 +511,6 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn set_compact(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_compact(val); @@ -520,6 +521,7 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(Py_3_14)] pub unsafe fn set_compact(&mut self, val: c_ushort) { let mut state = PyASCIIObjectState::from(self.state); state.set_compact(val); @@ -530,7 +532,6 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn ascii(&self) -> c_uint { PyASCIIObjectState::from(self.state).ascii() } @@ -539,7 +540,6 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] - #[cfg(not(Py_3_14))] pub unsafe fn set_ascii(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_ascii(val); @@ -550,6 +550,7 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] + #[cfg(Py_3_14)] pub unsafe fn ascii(&self) -> c_ushort { PyASCIIObjectState::from(self.state).ascii() } @@ -558,6 +559,7 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(Py_3_14)] pub unsafe fn set_ascii(&mut self, val: c_ushort) { let mut state = PyASCIIObjectState::from(self.state); state.set_ascii(val); @@ -624,7 +626,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; #[cfg(Py_3_12)] pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; -#[cfg(all(not(GraalPy), not(Py_3_14)))] +#[cfg(not(GraalPy))] #[inline] pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -634,13 +636,13 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).ascii() } -#[cfg(all(not(GraalPy), not(Py_3_14)))] +#[cfg(not(GraalPy))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).compact() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), Py_3_14))] #[inline] pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_ushort { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -650,7 +652,7 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_ushort { (*(op as *mut PyASCIIObject)).ascii() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), Py_3_14))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_ushort { (*(op as *mut PyASCIIObject)).compact() @@ -666,15 +668,15 @@ pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { #[deprecated(note = "Removed in Python 3.12")] pub const PyUnicode_WCHAR_KIND: c_uint = 0; -#[cfg(not(Py_3_14))] pub const PyUnicode_1BYTE_KIND: c_uint = 1; -#[cfg(not(Py_3_14))] pub const PyUnicode_2BYTE_KIND: c_uint = 2; -#[cfg(not(Py_3_14))] pub const PyUnicode_4BYTE_KIND: c_uint = 4; +#[cfg(Py_3_14)] pub const PyUnicode_1BYTE_KIND: c_ushort = 1; +#[cfg(Py_3_14)] pub const PyUnicode_2BYTE_KIND: c_ushort = 2; +#[cfg(Py_3_14)] pub const PyUnicode_4BYTE_KIND: c_ushort = 4; #[cfg(not(any(GraalPy, PyPy)))] @@ -695,7 +697,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { PyUnicode_DATA(op) as *mut Py_UCS4 } -#[cfg(all(not(GraalPy), not(Py_3_14)))] +#[cfg(not(GraalPy))] #[inline] pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -705,7 +707,7 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).kind() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), Py_3_14))] #[inline] pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_ushort { debug_assert!(crate::PyUnicode_Check(op) != 0); From b495b4ee2bbba819d6cb339a850a6f5a1c5a6cf9 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 14:27:50 -0500 Subject: [PATCH 13/16] Import 'c_ushort' only if compiling against CPython 3.14 or later --- pyo3-ffi/src/cpython/unicodeobject.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index 2bf5bac8919..1edbb0dc8a9 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -2,7 +2,9 @@ use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; use libc::wchar_t; -use std::os::raw::{c_char, c_int, c_uint, c_ushort, c_void}; +use std::os::raw::{c_char, c_int, c_uint, c_void}; +#[cfg(Py_3_14)] +use std::os::raw::c_ushort; // skipped Py_UNICODE_ISSPACE() // skipped Py_UNICODE_ISLOWER() From 127216afac1f1ccc490f0a7961aff5cf373e9c18 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 14:54:06 -0500 Subject: [PATCH 14/16] Add wrapper functions for the statically_allocated field --- pyo3-ffi/src/cpython/unicodeobject.rs | 44 +++++++++++++++++++++++++-- src/ffi/tests.rs | 5 +++ 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index 1edbb0dc8a9..a8878d1e1f2 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -1,10 +1,10 @@ #[cfg(not(PyPy))] use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; -use libc::wchar_t; -use std::os::raw::{c_char, c_int, c_uint, c_void}; +use libc::{c_ushort, wchar_t}; #[cfg(Py_3_14)] use std::os::raw::c_ushort; +use std::os::raw::{c_char, c_int, c_uint, c_void}; // skipped Py_UNICODE_ISSPACE() // skipped Py_UNICODE_ISLOWER() @@ -587,6 +587,46 @@ impl PyASCIIObject { state.set_ready(val); self.state = u32::from(state); } + + /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(all(Py_3_12, not(Py_3_14)))] + pub unsafe fn statically_allocated(&self) -> c_uint { + PyASCIIObjectState::from(self.state).statically_allocated() + } + + /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(all(Py_3_12, not(Py_3_14)))] + pub unsafe fn set_statically_allocated(&mut self, val: c_uint) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_statically_allocated(val); + self.state = u32::from(state); + } + + /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. + /// + /// Returns either `0` or `1`. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn statically_allocated(&self) -> c_ushort { + PyASCIIObjectState::from(self.state).statically_allocated() + } + + /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] + #[cfg(Py_3_14)] + pub unsafe fn set_statically_allocated(&mut self, val: c_ushort) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_statically_allocated(val); + self.state = u32::from(state); + } } #[repr(C)] diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 3396e409368..95bc235aeef 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -158,6 +158,11 @@ fn ascii_object_bitfield() { o.set_ready(1); #[cfg(not(Py_3_12))] assert_eq!(o.ready(), 1); + + #[cfg(Py_3_12)] + o.set_statically_allocated(1); + #[cfg(Py_3_12)] + assert_eq!(o.statically_allocated(), 1); } } From c3247c37fe53909914b705e74d5a2e213ff3c0e5 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 15:19:57 -0500 Subject: [PATCH 15/16] Remove unused libc::c_ushort --- pyo3-ffi/src/cpython/unicodeobject.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index a8878d1e1f2..105f7d1056d 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -1,7 +1,7 @@ #[cfg(not(PyPy))] use crate::Py_hash_t; use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; -use libc::{c_ushort, wchar_t}; +use libc::wchar_t; #[cfg(Py_3_14)] use std::os::raw::c_ushort; use std::os::raw::{c_char, c_int, c_uint, c_void}; From 8af42021eb43f1e25e4b04bb7c51a5555080e7f2 Mon Sep 17 00:00:00 2001 From: Charlie Lin Date: Thu, 23 Jan 2025 15:49:06 -0500 Subject: [PATCH 16/16] Add (hopefully) final version-specific macros --- pyo3-ffi/src/cpython/unicodeobject.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/pyo3-ffi/src/cpython/unicodeobject.rs b/pyo3-ffi/src/cpython/unicodeobject.rs index 105f7d1056d..31c96c042f8 100644 --- a/pyo3-ffi/src/cpython/unicodeobject.rs +++ b/pyo3-ffi/src/cpython/unicodeobject.rs @@ -175,6 +175,7 @@ struct PyASCIIObjectState { #[allow(clippy::useless_transmute)] impl PyASCIIObjectState { #[inline] + #[cfg(not(Py_3_14))] unsafe fn interned(&self) -> c_uint { std::mem::transmute( self.bitfield @@ -183,6 +184,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_interned(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -207,6 +209,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn kind(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_KIND_INDEX, STATE_KIND_WIDTH) as u32) } @@ -218,6 +221,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_kind(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -233,6 +237,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn compact(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_COMPACT_INDEX, STATE_COMPACT_WIDTH) as u32) } @@ -244,6 +249,7 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_compact(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -259,11 +265,13 @@ impl PyASCIIObjectState { } #[inline] + #[cfg(not(Py_3_14))] unsafe fn ascii(&self) -> c_uint { std::mem::transmute(self.bitfield.get(STATE_ASCII_INDEX, STATE_ASCII_WIDTH) as u32) } #[inline] + #[cfg(not(Py_3_14))] unsafe fn set_ascii(&mut self, val: c_uint) { let val: u32 = std::mem::transmute(val); self.bitfield @@ -404,6 +412,7 @@ impl PyASCIIObject { /// Returns one of: [`SSTATE_NOT_INTERNED`], [`SSTATE_INTERNED_MORTAL`], /// [`SSTATE_INTERNED_IMMORTAL`], or [`SSTATE_INTERNED_IMMORTAL_STATIC`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn interned(&self) -> c_uint { PyASCIIObjectState::from(self.state).interned() } @@ -415,6 +424,7 @@ impl PyASCIIObject { /// [`SSTATE_INTERNED_MORTAL`], [`SSTATE_INTERNED_IMMORTAL`], or /// [`SSTATE_INTERNED_IMMORTAL_STATIC`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_interned(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_interned(val); @@ -452,6 +462,7 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`]. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn kind(&self) -> c_uint { PyASCIIObjectState::from(self.state).kind() } @@ -473,6 +484,7 @@ impl PyASCIIObject { #[cfg_attr(not(Py_3_12), doc = "[`PyUnicode_WCHAR_KIND`], ")] /// [`PyUnicode_1BYTE_KIND`], [`PyUnicode_2BYTE_KIND`], or [`PyUnicode_4BYTE_KIND`] is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_kind(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_kind(val); @@ -496,6 +508,7 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn compact(&self) -> c_uint { PyASCIIObjectState::from(self.state).compact() } @@ -513,6 +526,7 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_compact(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_compact(val); @@ -534,6 +548,7 @@ impl PyASCIIObject { /// /// Returns either `0` or `1`. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn ascii(&self) -> c_uint { PyASCIIObjectState::from(self.state).ascii() } @@ -542,6 +557,7 @@ impl PyASCIIObject { /// /// Calling this function with an argument that is neither `0` nor `1` is invalid. #[inline] + #[cfg(not(Py_3_14))] pub unsafe fn set_ascii(&mut self, val: c_uint) { let mut state = PyASCIIObjectState::from(self.state); state.set_ascii(val); @@ -668,7 +684,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; #[cfg(Py_3_12)] pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0); @@ -678,7 +694,7 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).ascii() } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { (*(op as *mut PyASCIIObject)).compact() @@ -710,8 +726,11 @@ pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { #[deprecated(note = "Removed in Python 3.12")] pub const PyUnicode_WCHAR_KIND: c_uint = 0; +#[cfg(not(Py_3_14))] pub const PyUnicode_1BYTE_KIND: c_uint = 1; +#[cfg(not(Py_3_14))] pub const PyUnicode_2BYTE_KIND: c_uint = 2; +#[cfg(not(Py_3_14))] pub const PyUnicode_4BYTE_KIND: c_uint = 4; #[cfg(Py_3_14)] @@ -739,7 +758,7 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { PyUnicode_DATA(op) as *mut Py_UCS4 } -#[cfg(not(GraalPy))] +#[cfg(all(not(GraalPy), not(Py_3_14)))] #[inline] pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { debug_assert!(crate::PyUnicode_Check(op) != 0);