diff --git a/.gitignore b/.gitignore index 808eb9d..eea9600 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /orjson-3.10.14.tar.gz /orjson-3.10.16.tar.gz /orjson-3.10.18.tar.gz +/orjson-3.11.0.tar.gz diff --git a/570.patch b/570.patch deleted file mode 100644 index f9e8e1b..0000000 --- a/570.patch +++ /dev/null @@ -1,3856 +0,0 @@ -From 180f5886407e7c1dda34afa6fc7298b34d2925da Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:02:33 +0200 -Subject: [PATCH 1/8] Update pyo3 to 0.25.0 - ---- - Cargo.lock | 4 +- - include/pyo3/pyo3-build-config/Cargo.toml | 6 +- - include/pyo3/pyo3-build-config/src/errors.rs | 2 +- - include/pyo3/pyo3-build-config/src/impl_.rs | 41 +- - include/pyo3/pyo3-build-config/src/lib.rs | 63 +-- - include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS | 2 +- - include/pyo3/pyo3-ffi/Cargo.toml | 10 +- - include/pyo3/pyo3-ffi/build.rs | 74 ++- - include/pyo3/pyo3-ffi/src/abstract_.rs | 41 +- - include/pyo3/pyo3-ffi/src/bytearrayobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/code.rs | 2 +- - include/pyo3/pyo3-ffi/src/compat/mod.rs | 2 + - include/pyo3/pyo3-ffi/src/compat/py_3_10.rs | 26 ++ - include/pyo3/pyo3-ffi/src/compat/py_3_13.rs | 15 + - include/pyo3/pyo3-ffi/src/compat/py_3_14.rs | 26 ++ - .../pyo3/pyo3-ffi/src/cpython/abstract_.rs | 32 +- - .../pyo3/pyo3-ffi/src/cpython/bytesobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/cpython/code.rs | 232 ++-------- - include/pyo3/pyo3-ffi/src/cpython/compile.rs | 63 ++- - .../pyo3-ffi/src/cpython/critical_section.rs | 14 +- - .../pyo3/pyo3-ffi/src/cpython/descrobject.rs | 5 +- - .../pyo3/pyo3-ffi/src/cpython/dictobject.rs | 16 +- - .../pyo3/pyo3-ffi/src/cpython/frameobject.rs | 5 +- - .../pyo3/pyo3-ffi/src/cpython/funcobject.rs | 2 + - .../pyo3/pyo3-ffi/src/cpython/genobject.rs | 14 +- - include/pyo3/pyo3-ffi/src/cpython/import.rs | 10 +- - .../pyo3/pyo3-ffi/src/cpython/initconfig.rs | 10 + - include/pyo3/pyo3-ffi/src/cpython/lock.rs | 9 - - .../pyo3/pyo3-ffi/src/cpython/longobject.rs | 12 - - include/pyo3/pyo3-ffi/src/cpython/mod.rs | 5 +- - include/pyo3/pyo3-ffi/src/cpython/object.rs | 8 +- - include/pyo3/pyo3-ffi/src/cpython/objimpl.rs | 2 +- - include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs | 2 + - include/pyo3/pyo3-ffi/src/cpython/pyframe.rs | 5 +- - include/pyo3/pyo3-ffi/src/cpython/pyhash.rs | 38 ++ - include/pyo3/pyo3-ffi/src/cpython/pystate.rs | 16 +- - .../pyo3/pyo3-ffi/src/cpython/tupleobject.rs | 4 +- - .../pyo3-ffi/src/cpython/unicodeobject.rs | 117 ++++- - .../pyo3-ffi/src/cpython/weakrefobject.rs | 1 + - include/pyo3/pyo3-ffi/src/datetime.rs | 10 +- - include/pyo3/pyo3-ffi/src/dictobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/floatobject.rs | 2 +- - .../pyo3/pyo3-ffi/src/genericaliasobject.rs | 12 + - include/pyo3/pyo3-ffi/src/lib.rs | 23 +- - include/pyo3/pyo3-ffi/src/longobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/memoryobject.rs | 5 +- - include/pyo3/pyo3-ffi/src/methodobject.rs | 8 +- - include/pyo3/pyo3-ffi/src/modsupport.rs | 7 +- - include/pyo3/pyo3-ffi/src/object.rs | 422 ++++-------------- - include/pyo3/pyo3-ffi/src/pyarena.rs | 2 +- - include/pyo3/pyo3-ffi/src/pybuffer.rs | 6 +- - include/pyo3/pyo3-ffi/src/pyerrors.rs | 1 + - include/pyo3/pyo3-ffi/src/pyframe.rs | 2 +- - include/pyo3/pyo3-ffi/src/pyhash.rs | 30 +- - include/pyo3/pyo3-ffi/src/pyport.rs | 9 +- - include/pyo3/pyo3-ffi/src/pystate.rs | 78 +++- - include/pyo3/pyo3-ffi/src/pythonrun.rs | 6 +- - include/pyo3/pyo3-ffi/src/refcount.rs | 369 +++++++++++++++ - include/pyo3/pyo3-ffi/src/setobject.rs | 6 +- - include/pyo3/pyo3-ffi/src/weakrefobject.rs | 8 +- - 60 files changed, 1065 insertions(+), 885 deletions(-) - create mode 100644 include/pyo3/pyo3-ffi/src/compat/py_3_14.rs - create mode 100644 include/pyo3/pyo3-ffi/src/cpython/pyhash.rs - create mode 100644 include/pyo3/pyo3-ffi/src/genericaliasobject.rs - create mode 100644 include/pyo3/pyo3-ffi/src/refcount.rs - -diff --git a/Cargo.lock b/Cargo.lock -index 03fdf346..c4b1eb1c 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -190,7 +190,7 @@ dependencies = [ - - [[package]] - name = "pyo3-build-config" --version = "0.23.3" -+version = "0.25.0" - dependencies = [ - "once_cell", - "target-lexicon", -@@ -198,7 +198,7 @@ dependencies = [ - - [[package]] - name = "pyo3-ffi" --version = "0.23.3" -+version = "0.25.0" - dependencies = [ - "libc", - "pyo3-build-config", -diff --git a/include/pyo3/pyo3-build-config/Cargo.toml b/include/pyo3/pyo3-build-config/Cargo.toml -index 0facef81..54d5cb49 100644 ---- a/include/pyo3/pyo3-build-config/Cargo.toml -+++ b/include/pyo3/pyo3-build-config/Cargo.toml -@@ -1,6 +1,6 @@ - [package] - name = "pyo3-build-config" --version = "0.23.3" -+version = "0.25.0" - description = "Build configuration for the PyO3 ecosystem" - authors = ["PyO3 Project and Contributors "] - keywords = ["pyo3", "python", "cpython", "ffi"] -@@ -36,7 +36,9 @@ abi3-py38 = ["abi3-py39"] - abi3-py39 = ["abi3-py310"] - abi3-py310 = ["abi3-py311"] - abi3-py311 = ["abi3-py312"] --abi3-py312 = ["abi3"] -+abi3-py312 = ["abi3-py313"] -+abi3-py313 = ["abi3-py314"] -+abi3-py314 = ["abi3"] - - [package.metadata.docs.rs] - features = ["resolve-config"] -diff --git a/include/pyo3/pyo3-build-config/src/errors.rs b/include/pyo3/pyo3-build-config/src/errors.rs -index 87c59a99..b11d02fd 100644 ---- a/include/pyo3/pyo3-build-config/src/errors.rs -+++ b/include/pyo3/pyo3-build-config/src/errors.rs -@@ -68,7 +68,7 @@ impl std::fmt::Display for ErrorReport<'_> { - writeln!(f, "\ncaused by:")?; - let mut index = 0; - while let Some(some_source) = source { -- writeln!(f, " - {}: {}", index, some_source)?; -+ writeln!(f, " - {index}: {some_source}")?; - source = some_source.source(); - index += 1; - } -diff --git a/include/pyo3/pyo3-build-config/src/impl_.rs b/include/pyo3/pyo3-build-config/src/impl_.rs -index 0f90d573..9485b8a0 100644 ---- a/include/pyo3/pyo3-build-config/src/impl_.rs -+++ b/include/pyo3/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! { -@@ -58,7 +58,7 @@ pub fn cargo_env_var(var: &str) -> Option { - /// the variable changes. - pub fn env_var(var: &str) -> Option { - if cfg!(feature = "resolve-config") { -- println!("cargo:rerun-if-env-changed={}", var); -+ println!("cargo:rerun-if-env-changed={var}"); - } - #[cfg(test)] - { -@@ -180,9 +180,8 @@ impl InterpreterConfig { - let mut out = vec![]; - - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { -- out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); -+ out.push(format!("cargo:rustc-cfg=Py_3_{i}")); - } -- println!("cargo::rustc-check-cfg=cfg(Py_3_14)"); - - match self.implementation { - PythonImplementation::CPython => {} -@@ -200,7 +199,7 @@ impl InterpreterConfig { - BuildFlag::Py_GIL_DISABLED => { - out.push("cargo:rustc-cfg=Py_GIL_DISABLED".to_owned()) - } -- flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag)), -+ flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{flag}\"")), - } - } - -@@ -339,7 +338,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) - - let lib_dir = if cfg!(windows) { - map.get("base_prefix") -- .map(|base_prefix| format!("{}\\libs", base_prefix)) -+ .map(|base_prefix| format!("{base_prefix}\\libs")) - } else { - map.get("libdir").cloned() - }; -@@ -667,7 +666,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) - write_option_line!(python_framework_prefix)?; - write_line!(suppress_build_script_link_lines)?; - for line in &self.extra_build_script_lines { -- writeln!(writer, "extra_build_script_line={}", line) -+ writeln!(writer, "extra_build_script_line={line}") - .context("failed to write extra_build_script_line")?; - } - Ok(()) -@@ -854,7 +853,7 @@ fn is_abi3() -> bool { - /// Must be called from a PyO3 crate build script. - pub fn get_abi3_version() -> Option { - let minor_version = (MINIMUM_SUPPORTED_VERSION.minor..=ABI3_MAX_MINOR) -- .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); -+ .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{i}")).is_some()); - minor_version.map(|minor| PythonVersion { major: 3, minor }) - } - -@@ -1122,8 +1121,8 @@ pub enum BuildFlag { - impl Display for BuildFlag { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { -- BuildFlag::Other(flag) => write!(f, "{}", flag), -- _ => write!(f, "{:?}", self), -+ BuildFlag::Other(flag) => write!(f, "{flag}"), -+ _ => write!(f, "{self:?}"), - } - } - } -@@ -1147,7 +1146,7 @@ impl FromStr for BuildFlag { - /// PyO3 will pick these up and pass to rustc via `--cfg=py_sys_config={varname}`; - /// this allows using them conditional cfg attributes in the .rs files, so - /// --/// ```rust -+/// ```rust,no_run - /// #[cfg(py_sys_config="{varname}")] - /// # struct Foo; - /// ``` -@@ -1203,7 +1202,7 @@ impl BuildFlags { - - for k in &BuildFlags::ALL { - use std::fmt::Write; -- writeln!(&mut script, "print(config.get('{}', '0'))", k).unwrap(); -+ writeln!(&mut script, "print(config.get('{k}', '0'))").unwrap(); - } - - let stdout = run_python_script(interpreter.as_ref(), &script)?; -@@ -1241,7 +1240,7 @@ impl Display for BuildFlags { - } else { - write!(f, ",")?; - } -- write!(f, "{}", flag)?; -+ write!(f, "{flag}")?; - } - Ok(()) - } -@@ -1307,6 +1306,10 @@ pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result Result - - fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { - let pypy_version_pat = if let Some(v) = v { -- format!("pypy{}", v) -+ format!("pypy{v}") - } else { - "pypy3.".into() - }; -@@ -1433,7 +1436,7 @@ fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { - - fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { - let graalpy_version_pat = if let Some(v) = v { -- format!("graalpy{}", v) -+ format!("graalpy{v}") - } else { - "graalpy2".into() - }; -@@ -1442,7 +1445,7 @@ fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { - - fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { - let cpython_version_pat = if let Some(v) = v { -- format!("python{}", v) -+ format!("python{v}") - } else { - "python3.".into() - }; -@@ -1756,7 +1759,7 @@ fn default_lib_name_unix( - ) -> Result { - match implementation { - PythonImplementation::CPython => match ld_version { -- Some(ld_version) => Ok(format!("python{}", ld_version)), -+ Some(ld_version) => Ok(format!("python{ld_version}")), - None => { - if version > PythonVersion::PY37 { - // PEP 3149 ABI version tags are finally gone -@@ -1773,7 +1776,7 @@ fn default_lib_name_unix( - } - }, - PythonImplementation::PyPy => match ld_version { -- Some(ld_version) => Ok(format!("pypy{}-c", ld_version)), -+ Some(ld_version) => Ok(format!("pypy{ld_version}-c")), - None => Ok(format!("pypy{}.{}-c", version.major, version.minor)), - }, - -@@ -2002,7 +2005,7 @@ fn unescape(escaped: &str) -> Vec { - } - } - -- bytes.push(unhex(chunk[0]) << 4 | unhex(chunk[1])); -+ bytes.push((unhex(chunk[0]) << 4) | unhex(chunk[1])); - } - - bytes -diff --git a/include/pyo3/pyo3-build-config/src/lib.rs b/include/pyo3/pyo3-build-config/src/lib.rs -index 9070f6d7..f47c16f4 100644 ---- a/include/pyo3/pyo3-build-config/src/lib.rs -+++ b/include/pyo3/pyo3-build-config/src/lib.rs -@@ -46,7 +46,7 @@ use target_lexicon::OperatingSystem; - pub fn use_pyo3_cfgs() { - print_expected_cfgs(); - for cargo_command in get().build_script_outputs() { -- println!("{}", cargo_command) -+ println!("{cargo_command}") - } - } - -@@ -102,12 +102,7 @@ fn _add_python_framework_link_args( - ) { - if matches!(triple.operating_system, OperatingSystem::Darwin(_)) && link_libpython { - if let Some(framework_prefix) = interpreter_config.python_framework_prefix.as_ref() { -- writeln!( -- writer, -- "cargo:rustc-link-arg=-Wl,-rpath,{}", -- framework_prefix -- ) -- .unwrap(); -+ writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{framework_prefix}").unwrap(); - } - } - } -@@ -168,40 +163,36 @@ fn resolve_cross_compile_config_path() -> Option { - }) - } - -+/// Helper to print a feature cfg with a minimum rust version required. -+fn print_feature_cfg(minor_version_required: u32, cfg: &str) { -+ let minor_version = rustc_minor_version().unwrap_or(0); -+ -+ if minor_version >= minor_version_required { -+ println!("cargo:rustc-cfg={cfg}"); -+ } -+ -+ // rustc 1.80.0 stabilized `rustc-check-cfg` feature, don't emit before -+ if minor_version >= 80 { -+ println!("cargo:rustc-check-cfg=cfg({cfg})"); -+ } -+} -+ - /// Use certain features if we detect the compiler being used supports them. - /// - /// Features may be removed or added as MSRV gets bumped or new features become available, - /// so this function is unstable. - #[doc(hidden)] - pub fn print_feature_cfgs() { -- let rustc_minor_version = rustc_minor_version().unwrap_or(0); -- -- if rustc_minor_version >= 70 { -- println!("cargo:rustc-cfg=rustc_has_once_lock"); -- } -- -- // invalid_from_utf8 lint was added in Rust 1.74 -- if rustc_minor_version >= 74 { -- println!("cargo:rustc-cfg=invalid_from_utf8_lint"); -- } -- -- if rustc_minor_version >= 79 { -- println!("cargo:rustc-cfg=c_str_lit"); -- } -- -+ print_feature_cfg(70, "rustc_has_once_lock"); -+ print_feature_cfg(70, "cargo_toml_lints"); -+ print_feature_cfg(71, "rustc_has_extern_c_unwind"); -+ print_feature_cfg(74, "invalid_from_utf8_lint"); -+ print_feature_cfg(79, "c_str_lit"); - // Actually this is available on 1.78, but we should avoid - // https://github.com/rust-lang/rust/issues/124651 just in case -- if rustc_minor_version >= 79 { -- println!("cargo:rustc-cfg=diagnostic_namespace"); -- } -- -- if rustc_minor_version >= 83 { -- println!("cargo:rustc-cfg=io_error_more"); -- } -- -- if rustc_minor_version >= 85 { -- println!("cargo:rustc-cfg=fn_ptr_eq"); -- } -+ print_feature_cfg(79, "diagnostic_namespace"); -+ print_feature_cfg(83, "io_error_more"); -+ print_feature_cfg(85, "fn_ptr_eq"); - } - - /// Registers `pyo3`s config names as reachable cfg expressions -@@ -220,14 +211,8 @@ pub fn print_expected_cfgs() { - println!("cargo:rustc-check-cfg=cfg(PyPy)"); - println!("cargo:rustc-check-cfg=cfg(GraalPy)"); - println!("cargo:rustc-check-cfg=cfg(py_sys_config, values(\"Py_DEBUG\", \"Py_REF_DEBUG\", \"Py_TRACE_REFS\", \"COUNT_ALLOCS\"))"); -- println!("cargo:rustc-check-cfg=cfg(invalid_from_utf8_lint)"); - println!("cargo:rustc-check-cfg=cfg(pyo3_disable_reference_pool)"); - println!("cargo:rustc-check-cfg=cfg(pyo3_leak_on_drop_without_reference_pool)"); -- println!("cargo:rustc-check-cfg=cfg(diagnostic_namespace)"); -- println!("cargo:rustc-check-cfg=cfg(c_str_lit)"); -- println!("cargo:rustc-check-cfg=cfg(rustc_has_once_lock)"); -- println!("cargo:rustc-check-cfg=cfg(io_error_more)"); -- println!("cargo:rustc-check-cfg=cfg(fn_ptr_eq)"); - - // allow `Py_3_*` cfgs from the minimum supported version up to the - // maximum minor version (+1 for development for the next) -diff --git a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -index 4502d777..8b20727d 100644 ---- a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -+++ b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -@@ -3,4 +3,4 @@ for binary compatibility, with additional metadata to support PyPy. - - For original implementations please see: - - https://github.com/python/cpython -- - https://foss.heptapod.net/pypy/pypy -+ - https://github.com/pypy/pypy -diff --git a/include/pyo3/pyo3-ffi/Cargo.toml b/include/pyo3/pyo3-ffi/Cargo.toml -index 14a4f958..73ea84ab 100644 ---- a/include/pyo3/pyo3-ffi/Cargo.toml -+++ b/include/pyo3/pyo3-ffi/Cargo.toml -@@ -1,6 +1,6 @@ - [package] - name = "pyo3-ffi" --version = "0.23.3" -+version = "0.25.0" - description = "Python-API bindings for the PyO3 ecosystem" - authors = ["PyO3 Project and Contributors "] - keywords = ["pyo3", "python", "cpython", "ffi"] -@@ -12,7 +12,7 @@ edition = "2021" - links = "python" - - [dependencies] --libc = "0.2" -+libc = "0.2.62" - - [features] - -@@ -32,7 +32,9 @@ abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"] - abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"] - abi3-py310 = ["abi3-py311", "pyo3-build-config/abi3-py310"] - abi3-py311 = ["abi3-py312", "pyo3-build-config/abi3-py311"] --abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312"] -+abi3-py312 = ["abi3-py313", "pyo3-build-config/abi3-py312"] -+abi3-py313 = ["abi3-py314", "pyo3-build-config/abi3-py313"] -+abi3-py314 = ["abi3", "pyo3-build-config/abi3-py314"] - - # Automatically generates `python3.dll` import libraries for Windows targets. - generate-import-lib = ["pyo3-build-config/python3-dll-a"] -@@ -41,4 +43,4 @@ generate-import-lib = ["pyo3-build-config/python3-dll-a"] - paste = "1" - - [build-dependencies] --pyo3-build-config = { path = "../pyo3-build-config", features = ["resolve-config"] } -+pyo3-build-config = { path = "../pyo3-build-config", version = "=0.25.0", features = ["resolve-config"] } -diff --git a/include/pyo3/pyo3-ffi/build.rs b/include/pyo3/pyo3-ffi/build.rs -index b0f1c28d..6776cd80 100644 ---- a/include/pyo3/pyo3-ffi/build.rs -+++ b/include/pyo3/pyo3-ffi/build.rs -@@ -4,9 +4,8 @@ use pyo3_build_config::{ - cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config, - InterpreterConfig, PythonVersion, - }, -- warn, BuildFlag, PythonImplementation, -+ warn, PythonImplementation, - }; --use std::ops::Not; - - /// Minimum Python version PyO3 supports. - struct SupportedVersions { -@@ -18,15 +17,15 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions { - min: PythonVersion { major: 3, minor: 7 }, - max: PythonVersion { - major: 3, -- minor: 13, -+ minor: 14, - }, - }; - - const SUPPORTED_VERSIONS_PYPY: SupportedVersions = SupportedVersions { -- min: PythonVersion { major: 3, minor: 7 }, -+ min: PythonVersion { major: 3, minor: 9 }, - max: PythonVersion { - major: 3, -- minor: 10, -+ minor: 11, - }, - }; - -@@ -57,15 +56,22 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - interpreter_config.version, - versions.min, - ); -- ensure!( -- interpreter_config.version <= versions.max || env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), -- "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -- = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", -- interpreter_config.version, -- versions.max, -- std::env::var("CARGO_PKG_VERSION").unwrap(), -- ); -+ if interpreter_config.version > versions.max { -+ ensure!(!interpreter_config.is_free_threaded(), -+ "The configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -+ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -+ = help: The free-threaded build of CPython does not support the limited API so this check cannot be suppressed.", -+ interpreter_config.version, versions.max, std::env::var("CARGO_PKG_VERSION").unwrap() -+ ); -+ ensure!(env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), -+ "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -+ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -+ = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", -+ interpreter_config.version, -+ versions.max, -+ std::env::var("CARGO_PKG_VERSION").unwrap(), -+ ); -+ } - } - PythonImplementation::PyPy => { - let versions = SUPPORTED_VERSIONS_PYPY; -@@ -107,10 +113,16 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - - if interpreter_config.abi3 { - match interpreter_config.implementation { -- PythonImplementation::CPython => {} -+ PythonImplementation::CPython => { -+ if interpreter_config.is_free_threaded() { -+ warn!( -+ "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." -+ ) -+ } -+ } - PythonImplementation::PyPy => warn!( - "PyPy does not yet support abi3 so the build artifacts will be version-specific. \ -- See https://foss.heptapod.net/pypy/pypy/-/issues/3397 for more information." -+ See https://github.com/pypy/pypy/issues/3397 for more information." - ), - PythonImplementation::GraalPy => warn!( - "GraalPy does not support abi3 so the build artifacts will be version-specific." -@@ -121,29 +133,6 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - Ok(()) - } - --fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> { -- let gil_enabled = interpreter_config -- .build_flags -- .0 -- .contains(&BuildFlag::Py_GIL_DISABLED) -- .not(); -- ensure!( -- gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"), -- "the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\ -- = help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\ -- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -- = help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python", -- std::env::var("CARGO_PKG_VERSION").unwrap() -- ); -- if !gil_enabled && interpreter_config.abi3 { -- warn!( -- "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." -- ) -- } -- -- Ok(()) --} -- - fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> { - if let Some(pointer_width) = interpreter_config.pointer_width { - // Try to check whether the target architecture matches the python library -@@ -187,7 +176,7 @@ fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> { - ); - - if let Some(lib_dir) = &interpreter_config.lib_dir { -- println!("cargo:rustc-link-search=native={}", lib_dir); -+ println!("cargo:rustc-link-search=native={lib_dir}"); - } - - Ok(()) -@@ -209,7 +198,6 @@ fn configure_pyo3() -> Result<()> { - - ensure_python_version(&interpreter_config)?; - ensure_target_pointer_width(&interpreter_config)?; -- ensure_gil_enabled(&interpreter_config)?; - - // Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var. - interpreter_config.to_cargo_dep_env()?; -@@ -219,12 +207,12 @@ fn configure_pyo3() -> Result<()> { - } - - for cfg in interpreter_config.build_script_outputs() { -- println!("{}", cfg) -+ println!("{cfg}") - } - - // Extra lines come last, to support last write wins. - for line in &interpreter_config.extra_build_script_lines { -- println!("{}", line); -+ println!("{line}"); - } - - // Emit cfgs like `invalid_from_utf8_lint` -diff --git a/include/pyo3/pyo3-ffi/src/abstract_.rs b/include/pyo3/pyo3-ffi/src/abstract_.rs -index ce6c9b94..84fb98a1 100644 ---- a/include/pyo3/pyo3-ffi/src/abstract_.rs -+++ b/include/pyo3/pyo3-ffi/src/abstract_.rs -@@ -1,15 +1,23 @@ - use crate::object::*; - use crate::pyport::Py_ssize_t; -+#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] -+use libc::size_t; - use std::os::raw::{c_char, c_int}; - - #[inline] --#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h -+#[cfg(all( -+ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h -+ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ -+))] - pub unsafe fn PyObject_DelAttrString(o: *mut PyObject, attr_name: *const c_char) -> c_int { - PyObject_SetAttrString(o, attr_name, std::ptr::null_mut()) - } - - #[inline] --#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h -+#[cfg(all( -+ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h -+ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ -+))] - pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_int { - PyObject_SetAttr(o, attr_name, std::ptr::null_mut()) - } -@@ -17,6 +25,7 @@ pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_ - extern "C" { - #[cfg(all( - not(PyPy), -+ not(GraalPy), - any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 - ))] - #[cfg_attr(PyPy, link_name = "PyPyObject_CallNoArgs")] -@@ -70,6 +79,28 @@ extern "C" { - method: *mut PyObject, - ... - ) -> *mut PyObject; -+} -+#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] -+pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = -+ 1 << (8 * std::mem::size_of::() as size_t - 1); -+ -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")] -+ #[cfg(any(Py_3_12, all(Py_3_11, not(Py_LIMITED_API))))] -+ pub fn PyObject_Vectorcall( -+ callable: *mut PyObject, -+ args: *const *mut PyObject, -+ nargsf: size_t, -+ kwnames: *mut PyObject, -+ ) -> *mut PyObject; -+ -+ #[cfg(any(Py_3_12, all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy)))))] -+ pub fn PyObject_VectorcallMethod( -+ name: *mut PyObject, -+ args: *const *mut PyObject, -+ nargsf: size_t, -+ kwnames: *mut PyObject, -+ ) -> *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyObject_Type")] - pub fn PyObject_Type(o: *mut PyObject) -> *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyObject_Size")] -@@ -119,7 +150,11 @@ extern "C" { - pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; - #[cfg(all(not(PyPy), Py_3_10))] - #[cfg_attr(PyPy, link_name = "PyPyIter_Send")] -- pub fn PyIter_Send(iter: *mut PyObject, arg: *mut PyObject, presult: *mut *mut PyObject); -+ pub fn PyIter_Send( -+ iter: *mut PyObject, -+ arg: *mut PyObject, -+ presult: *mut *mut PyObject, -+ ) -> PySendResult; - - #[cfg_attr(PyPy, link_name = "PyPyNumber_Check")] - pub fn PyNumber_Check(o: *mut PyObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -index 24a97bcc..d27dfa8b 100644 ---- a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -+++ b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -@@ -17,7 +17,7 @@ pub struct PyByteArrayObject { - } - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] --opaque_struct!(PyByteArrayObject); -+opaque_struct!(pub PyByteArrayObject); - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -diff --git a/include/pyo3/pyo3-ffi/src/code.rs b/include/pyo3/pyo3-ffi/src/code.rs -index d28f68cd..296b17f6 100644 ---- a/include/pyo3/pyo3-ffi/src/code.rs -+++ b/include/pyo3/pyo3-ffi/src/code.rs -@@ -1,4 +1,4 @@ - // This header doesn't exist in CPython, but Include/cpython/code.h does. We add - // this here so that PyCodeObject has a definition under the limited API. - --opaque_struct!(PyCodeObject); -+opaque_struct!(pub PyCodeObject); -diff --git a/include/pyo3/pyo3-ffi/src/compat/mod.rs b/include/pyo3/pyo3-ffi/src/compat/mod.rs -index 11f29128..044ea467 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/mod.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/mod.rs -@@ -52,8 +52,10 @@ macro_rules! compat_function { - - mod py_3_10; - mod py_3_13; -+mod py_3_14; - mod py_3_9; - - pub use self::py_3_10::*; - pub use self::py_3_13::*; -+pub use self::py_3_14::*; - pub use self::py_3_9::*; -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -index c6e8c2cb..d962fb3b 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -@@ -17,3 +17,29 @@ compat_function!( - obj - } - ); -+ -+compat_function!( -+ originally_defined_for(Py_3_10); -+ -+ #[inline] -+ pub unsafe fn PyModule_AddObjectRef( -+ module: *mut crate::PyObject, -+ name: *const std::os::raw::c_char, -+ value: *mut crate::PyObject, -+ ) -> std::os::raw::c_int { -+ if value.is_null() && crate::PyErr_Occurred().is_null() { -+ crate::PyErr_SetString( -+ crate::PyExc_SystemError, -+ c_str!("PyModule_AddObjectRef() must be called with an exception raised if value is NULL").as_ptr(), -+ ); -+ return -1; -+ } -+ -+ crate::Py_XINCREF(value); -+ let result = crate::PyModule_AddObject(module, name, value); -+ if result < 0 { -+ crate::Py_XDECREF(value); -+ } -+ result -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -index 59289cb7..96c90e7e 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -@@ -104,3 +104,18 @@ compat_function!( - crate::PyList_SetSlice(list, 0, crate::PY_SSIZE_T_MAX, std::ptr::null_mut()) - } - ); -+ -+compat_function!( -+ originally_defined_for(Py_3_13); -+ -+ #[inline] -+ pub unsafe fn PyModule_Add( -+ module: *mut crate::PyObject, -+ name: *const std::os::raw::c_char, -+ value: *mut crate::PyObject, -+ ) -> std::os::raw::c_int { -+ let result = crate::compat::PyModule_AddObjectRef(module, name, value); -+ crate::Py_XDECREF(value); -+ result -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs -new file mode 100644 -index 00000000..6fdaef17 ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs -@@ -0,0 +1,26 @@ -+compat_function!( -+ originally_defined_for(all(Py_3_14, not(Py_LIMITED_API))); -+ -+ #[inline] -+ pub unsafe fn Py_HashBuffer( -+ ptr: *const std::ffi::c_void, -+ len: crate::Py_ssize_t, -+ ) -> crate::Py_hash_t { -+ #[cfg(not(any(Py_LIMITED_API, PyPy)))] -+ { -+ crate::_Py_HashBytes(ptr, len) -+ } -+ -+ #[cfg(any(Py_LIMITED_API, PyPy))] -+ { -+ let bytes = crate::PyBytes_FromStringAndSize(ptr as *const std::os::raw::c_char, len); -+ if bytes.is_null() { -+ -1 -+ } else { -+ let result = crate::PyObject_Hash(bytes); -+ crate::Py_DECREF(bytes); -+ result -+ } -+ } -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -index 477ad02b..6ada1a75 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -@@ -1,5 +1,5 @@ - use crate::{PyObject, Py_ssize_t}; --#[cfg(not(all(Py_3_11, GraalPy)))] -+#[cfg(any(all(Py_3_8, not(any(PyPy, GraalPy))), not(Py_3_11)))] - use std::os::raw::c_char; - use std::os::raw::c_int; - -@@ -41,8 +41,8 @@ extern "C" { - ) -> *mut PyObject; - } - --#[cfg(Py_3_8)] --pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = -+#[cfg(Py_3_8)] // NB exported as public in abstract.rs from 3.12 -+const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = - 1 << (8 * std::mem::size_of::() as size_t - 1); - - #[cfg(Py_3_8)] -@@ -91,7 +91,7 @@ pub unsafe fn _PyObject_VectorcallTstate( - } - } - --#[cfg(all(Py_3_8, not(any(PyPy, GraalPy))))] -+#[cfg(all(Py_3_8, not(any(PyPy, GraalPy, Py_3_11))))] // exported as a function from 3.11, see abstract.rs - #[inline(always)] - pub unsafe fn PyObject_Vectorcall( - callable: *mut PyObject, -@@ -103,16 +103,6 @@ pub unsafe fn PyObject_Vectorcall( - } - - extern "C" { -- #[cfg(all(PyPy, Py_3_8))] -- #[cfg_attr(not(Py_3_9), link_name = "_PyPyObject_Vectorcall")] -- #[cfg_attr(Py_3_9, link_name = "PyPyObject_Vectorcall")] -- pub fn PyObject_Vectorcall( -- callable: *mut PyObject, -- args: *const *mut PyObject, -- nargsf: size_t, -- kwnames: *mut PyObject, -- ) -> *mut PyObject; -- - #[cfg(Py_3_8)] - #[cfg_attr( - all(not(any(PyPy, GraalPy)), not(Py_3_9)), -@@ -187,23 +177,13 @@ pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *m - _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) - } - --extern "C" { -- #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] -- pub fn PyObject_VectorcallMethod( -- name: *mut PyObject, -- args: *const *mut PyObject, -- nargsf: size_t, -- kwnames: *mut PyObject, -- ) -> *mut PyObject; --} -- - #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] - #[inline(always)] - pub unsafe fn PyObject_CallMethodNoArgs( - self_: *mut PyObject, - name: *mut PyObject, - ) -> *mut PyObject { -- PyObject_VectorcallMethod( -+ crate::PyObject_VectorcallMethod( - name, - &self_, - 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, -@@ -220,7 +200,7 @@ pub unsafe fn PyObject_CallMethodOneArg( - ) -> *mut PyObject { - let args = [self_, arg]; - assert!(!arg.is_null()); -- PyObject_VectorcallMethod( -+ crate::PyObject_VectorcallMethod( - name, - args.as_ptr(), - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -index 306702de..dd78e646 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -@@ -17,7 +17,7 @@ pub struct PyBytesObject { - } - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] --opaque_struct!(PyBytesObject); -+opaque_struct!(pub PyBytesObject); - - extern "C" { - #[cfg_attr(PyPy, link_name = "_PyPyBytes_Resize")] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/code.rs b/include/pyo3/pyo3-ffi/src/cpython/code.rs -index 230096ca..3d47a1bc 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/code.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/code.rs -@@ -1,207 +1,43 @@ - use crate::object::*; - use crate::pyport::Py_ssize_t; - --#[allow(unused_imports)] --use std::os::raw::{c_char, c_int, c_short, c_uchar, c_void}; -+#[cfg(not(GraalPy))] -+use std::os::raw::c_char; -+use std::os::raw::{c_int, c_void}; - #[cfg(not(any(PyPy, GraalPy)))] - use std::ptr::addr_of_mut; - --#[cfg(all(Py_3_8, not(any(PyPy, GraalPy)), not(Py_3_11)))] --opaque_struct!(_PyOpcache); -+// skipped private _PY_MONITORING_LOCAL_EVENTS -+// skipped private _PY_MONITORING_UNGROUPED_EVENTS -+// skipped private _PY_MONITORING_EVENTS - --#[cfg(Py_3_12)] --pub const _PY_MONITORING_LOCAL_EVENTS: usize = 10; --#[cfg(Py_3_12)] --pub const _PY_MONITORING_UNGROUPED_EVENTS: usize = 15; --#[cfg(Py_3_12)] --pub const _PY_MONITORING_EVENTS: usize = 17; -+// skipped private _PyLocalMonitors -+// skipped private _Py_GlobalMonitors - --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Clone, Copy)] --pub struct _Py_LocalMonitors { -- pub tools: [u8; if cfg!(Py_3_13) { -- _PY_MONITORING_LOCAL_EVENTS -- } else { -- _PY_MONITORING_UNGROUPED_EVENTS -- }], --} -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Clone, Copy)] --pub struct _Py_GlobalMonitors { -- pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS], --} -- --// skipped _Py_CODEUNIT -+// skipped private _Py_CODEUNIT - --// skipped _Py_OPCODE --// skipped _Py_OPARG -+// skipped private _Py_OPCODE -+// skipped private _Py_OPARG - --// skipped _py_make_codeunit -+// skipped private _py_make_codeunit - --// skipped _py_set_opcode -+// skipped private _py_set_opcode - --// skipped _Py_MAKE_CODEUNIT --// skipped _Py_SET_OPCODE -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoCached { -- pub _co_code: *mut PyObject, -- pub _co_varnames: *mut PyObject, -- pub _co_cellvars: *mut PyObject, -- pub _co_freevars: *mut PyObject, --} -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoLineInstrumentationData { -- pub original_opcode: u8, -- pub line_delta: i8, --} -+// skipped private _Py_MAKE_CODEUNIT -+// skipped private _Py_SET_OPCODE - --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoMonitoringData { -- pub local_monitors: _Py_LocalMonitors, -- pub active_monitors: _Py_LocalMonitors, -- pub tools: *mut u8, -- pub lines: *mut _PyCoLineInstrumentationData, -- pub line_tools: *mut u8, -- pub per_instruction_opcodes: *mut u8, -- pub per_instruction_tools: *mut u8, --} -- --#[cfg(all(not(any(PyPy, GraalPy)), not(Py_3_7)))] --opaque_struct!(PyCodeObject); -- --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_7, not(Py_3_8)))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_argcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_nlocals: c_int, -- pub co_stacksize: c_int, -- pub co_flags: c_int, -- pub co_firstlineno: c_int, -- pub co_code: *mut PyObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_varnames: *mut PyObject, -- pub co_freevars: *mut PyObject, -- pub co_cellvars: *mut PyObject, -- pub co_cell2arg: *mut Py_ssize_t, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- pub co_lnotab: *mut PyObject, -- pub co_zombieframe: *mut c_void, -- pub co_weakreflist: *mut PyObject, -- pub co_extra: *mut c_void, --} -- --#[cfg(Py_3_13)] --opaque_struct!(_PyExecutorArray); -- --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_8, not(Py_3_11)))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_argcount: c_int, -- pub co_posonlyargcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_nlocals: c_int, -- pub co_stacksize: c_int, -- pub co_flags: c_int, -- pub co_firstlineno: c_int, -- pub co_code: *mut PyObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_varnames: *mut PyObject, -- pub co_freevars: *mut PyObject, -- pub co_cellvars: *mut PyObject, -- pub co_cell2arg: *mut Py_ssize_t, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- #[cfg(not(Py_3_10))] -- pub co_lnotab: *mut PyObject, -- #[cfg(Py_3_10)] -- pub co_linetable: *mut PyObject, -- pub co_zombieframe: *mut c_void, -- pub co_weakreflist: *mut PyObject, -- pub co_extra: *mut c_void, -- pub co_opcache_map: *mut c_uchar, -- pub co_opcache: *mut _PyOpcache, -- pub co_opcache_flag: c_int, -- pub co_opcache_size: c_uchar, --} -+// skipped private _PyCoCached -+// skipped private _PyCoLineInstrumentationData -+// skipped private _PyCoMontoringData - --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_11))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyVarObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_exceptiontable: *mut PyObject, -- pub co_flags: c_int, -- #[cfg(not(Py_3_12))] -- pub co_warmup: c_int, -+// skipped private _PyExecutorArray - -- pub co_argcount: c_int, -- pub co_posonlyargcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_stacksize: c_int, -- pub co_firstlineno: c_int, -- -- pub co_nlocalsplus: c_int, -- #[cfg(Py_3_12)] -- pub co_framesize: c_int, -- pub co_nlocals: c_int, -- #[cfg(not(Py_3_12))] -- pub co_nplaincellvars: c_int, -- pub co_ncellvars: c_int, -- pub co_nfreevars: c_int, -- #[cfg(Py_3_12)] -- pub co_version: u32, -- -- pub co_localsplusnames: *mut PyObject, -- pub co_localspluskinds: *mut PyObject, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- pub co_qualname: *mut PyObject, -- pub co_linetable: *mut PyObject, -- pub co_weakreflist: *mut PyObject, -- #[cfg(not(Py_3_12))] -- pub _co_code: *mut PyObject, -- #[cfg(not(Py_3_12))] -- pub _co_linearray: *mut c_char, -- #[cfg(Py_3_13)] -- pub co_executors: *mut _PyExecutorArray, -- #[cfg(Py_3_12)] -- pub _co_cached: *mut _PyCoCached, -- #[cfg(Py_3_12)] -- pub _co_instrumentation_version: u64, -- #[cfg(Py_3_12)] -- pub _co_monitoring: *mut _PyCoMonitoringData, -- pub _co_firsttraceable: c_int, -- pub co_extra: *mut c_void, -- pub co_code_adaptive: [c_char; 1], --} -- --#[cfg(PyPy)] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_name: *mut PyObject, -- pub co_filename: *mut PyObject, -- pub co_argcount: c_int, -- pub co_flags: c_int, --} -+opaque_struct!( -+ #[doc = "A Python code object.\n"] -+ #[doc = "\n"] -+ #[doc = "`pyo3-ffi` does not expose the contents of this struct, as it has no stability guarantees."] -+ pub PyCodeObject -+); - - /* Masks for co_flags */ - pub const CO_OPTIMIZED: c_int = 0x0001; -@@ -247,28 +83,14 @@ pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == addr_of_mut!(PyCode_Type)) as c_int - } - --#[inline] --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_10, not(Py_3_11)))] --pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t { -- crate::PyTuple_GET_SIZE((*op).co_freevars) --} -- --#[inline] --#[cfg(all(not(Py_3_10), Py_3_11, not(any(PyPy, GraalPy))))] --pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> c_int { -- (*op).co_nfreevars --} -- - extern "C" { - #[cfg(PyPy)] - #[link_name = "PyPyCode_Check"] - pub fn PyCode_Check(op: *mut PyObject) -> c_int; -- -- #[cfg(PyPy)] -- #[link_name = "PyPyCode_GetNumFree"] -- pub fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t; - } - -+// skipped PyCode_GetNumFree (requires knowledge of code object layout) -+ - extern "C" { - #[cfg(not(GraalPy))] - #[cfg_attr(PyPy, link_name = "PyPyCode_New")] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/compile.rs b/include/pyo3/pyo3-ffi/src/cpython/compile.rs -index 79f06c92..078b5e0d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/compile.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/compile.rs -@@ -6,19 +6,21 @@ use crate::pyarena::*; - use crate::pythonrun::*; - #[cfg(not(any(PyPy, Py_3_10)))] - use crate::PyCodeObject; -+use crate::INT_MAX; - #[cfg(not(any(PyPy, Py_3_10)))] - use std::os::raw::c_char; - use std::os::raw::c_int; - --// skipped non-limited PyCF_MASK --// skipped non-limited PyCF_MASK_OBSOLETE --// skipped non-limited PyCF_SOURCE_IS_UTF8 --// skipped non-limited PyCF_DONT_IMPLY_DEDENT --// skipped non-limited PyCF_ONLY_AST --// skipped non-limited PyCF_IGNORE_COOKIE --// skipped non-limited PyCF_TYPE_COMMENTS --// skipped non-limited PyCF_ALLOW_TOP_LEVEL_AWAIT --// skipped non-limited PyCF_COMPILE_MASK -+// skipped PyCF_MASK -+// skipped PyCF_MASK_OBSOLETE -+// skipped PyCF_SOURCE_IS_UTF8 -+// skipped PyCF_DONT_IMPLY_DEDENT -+// skipped PyCF_ONLY_AST -+// skipped PyCF_IGNORE_COOKIE -+// skipped PyCF_TYPE_COMMENTS -+// skipped PyCF_ALLOW_TOP_LEVEL_AWAIT -+// skipped PyCF_OPTIMIZED_AST -+// skipped PyCF_COMPILE_MASK - - #[repr(C)] - #[derive(Copy, Clone)] -@@ -28,31 +30,23 @@ pub struct PyCompilerFlags { - pub cf_feature_version: c_int, - } - --// skipped non-limited _PyCompilerFlags_INIT -+// skipped _PyCompilerFlags_INIT - --#[cfg(all(Py_3_12, not(any(Py_3_13, PyPy, GraalPy))))] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCompilerSrcLocation { -- pub lineno: c_int, -- pub end_lineno: c_int, -- pub col_offset: c_int, -- pub end_col_offset: c_int, --} -- --// skipped SRC_LOCATION_FROM_AST -- --#[cfg(not(any(PyPy, GraalPy, Py_3_13)))] -+// NB this type technically existed in the header until 3.13, when it was -+// moved to the internal CPython headers. -+// -+// We choose not to expose it in the public API past 3.10, as it is -+// not used in the public API past that point. -+#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] - #[repr(C)] - #[derive(Copy, Clone)] - pub struct PyFutureFeatures { - pub ff_features: c_int, -- #[cfg(not(Py_3_12))] - pub ff_lineno: c_int, -- #[cfg(Py_3_12)] -- pub ff_location: _PyCompilerSrcLocation, - } - -+// FIXME: these constants should probably be &CStr, if they are used at all -+ - pub const FUTURE_NESTED_SCOPES: &str = "nested_scopes"; - pub const FUTURE_GENERATORS: &str = "generators"; - pub const FUTURE_DIVISION: &str = "division"; -@@ -62,13 +56,12 @@ pub const FUTURE_PRINT_FUNCTION: &str = "print_function"; - pub const FUTURE_UNICODE_LITERALS: &str = "unicode_literals"; - pub const FUTURE_BARRY_AS_BDFL: &str = "barry_as_FLUFL"; - pub const FUTURE_GENERATOR_STOP: &str = "generator_stop"; --// skipped non-limited FUTURE_ANNOTATIONS -+pub const FUTURE_ANNOTATIONS: &str = "annotations"; - -+#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] - extern "C" { -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyNode_Compile(arg1: *mut _node, arg2: *const c_char) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyAST_CompileEx( - _mod: *mut _mod, - filename: *const c_char, -@@ -77,7 +70,6 @@ extern "C" { - arena: *mut PyArena, - ) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyAST_CompileObject( - _mod: *mut _mod, - filename: *mut PyObject, -@@ -86,23 +78,20 @@ extern "C" { - arena: *mut PyArena, - ) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyFuture_FromAST(_mod: *mut _mod, filename: *const c_char) -> *mut PyFutureFeatures; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyFuture_FromASTObject( - _mod: *mut _mod, - filename: *mut PyObject, - ) -> *mut PyFutureFeatures; -+} - -- // skipped non-limited _Py_Mangle -- // skipped non-limited PY_INVALID_STACK_EFFECT -+pub const PY_INVALID_STACK_EFFECT: c_int = INT_MAX; -+ -+extern "C" { - - pub fn PyCompile_OpcodeStackEffect(opcode: c_int, oparg: c_int) -> c_int; - - #[cfg(Py_3_8)] - pub fn PyCompile_OpcodeStackEffectWithJump(opcode: c_int, oparg: c_int, jump: c_int) -> c_int; -- -- // skipped non-limited _PyASTOptimizeState -- // skipped non-limited _PyAST_Optimize - } -diff --git a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -index 1fc6591c..808dba87 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -@@ -9,16 +9,6 @@ pub struct PyCriticalSection { - _cs_mutex: *mut PyMutex, - } - --#[cfg(Py_GIL_DISABLED)] --impl Default for PyCriticalSection { -- fn default() -> Self { -- PyCriticalSection { -- _cs_prev: 0, -- _cs_mutex: core::ptr::null_mut(), -- } -- } --} -- - #[repr(C)] - #[cfg(Py_GIL_DISABLED)] - pub struct PyCriticalSection2 { -@@ -27,10 +17,10 @@ pub struct PyCriticalSection2 { - } - - #[cfg(not(Py_GIL_DISABLED))] --opaque_struct!(PyCriticalSection); -+opaque_struct!(pub PyCriticalSection); - - #[cfg(not(Py_GIL_DISABLED))] --opaque_struct!(PyCriticalSection2); -+opaque_struct!(pub PyCriticalSection2); - - extern "C" { - pub fn PyCriticalSection_Begin(c: *mut PyCriticalSection, op: *mut PyObject); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -index 1b5ee466..7cef9bdb 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -@@ -69,10 +69,7 @@ pub struct PyWrapperDescrObject { - pub d_wrapped: *mut c_void, - } - --#[cfg_attr(windows, link(name = "pythonXY"))] --extern "C" { -- pub static mut _PyMethodWrapper_Type: PyTypeObject; --} -+// skipped _PyMethodWrapper_Type - - // skipped non-limited PyDescr_NewWrapper - // skipped non-limited PyDescr_IsData -diff --git a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -index f67a7725..34b66a96 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -@@ -2,10 +2,10 @@ use crate::object::*; - use crate::pyport::Py_ssize_t; - use std::os::raw::c_int; - --opaque_struct!(PyDictKeysObject); -+opaque_struct!(pub PyDictKeysObject); - - #[cfg(Py_3_11)] --opaque_struct!(PyDictValues); -+opaque_struct!(pub PyDictValues); - - #[cfg(not(GraalPy))] - #[repr(C)] -@@ -17,7 +17,10 @@ pub struct PyDictObject { - Py_3_12, - deprecated(note = "Deprecated in Python 3.12 and will be removed in the future.") - )] -+ #[cfg(not(Py_3_14))] - pub ma_version_tag: u64, -+ #[cfg(Py_3_14)] -+ _ma_watcher_tag: u64, - pub ma_keys: *mut PyDictKeysObject, - #[cfg(not(Py_3_11))] - pub ma_values: *mut *mut PyObject, -@@ -36,15 +39,6 @@ extern "C" { - item: *mut PyObject, - hash: crate::Py_hash_t, - ) -> c_int; -- -- #[cfg(Py_3_13)] -- pub fn _PyDict_SetItem_KnownHash_LockHeld( -- mp: *mut PyDictObject, -- name: *mut PyObject, -- value: *mut PyObject, -- hash: crate::Py_hash_t, -- ) -> c_int; -- - // skipped _PyDict_DelItem_KnownHash - // skipped _PyDict_DelItemIf - // skipped _PyDict_NewKeysForClass -diff --git a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -index 6d2346f9..993e93c8 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -@@ -53,7 +53,7 @@ pub struct PyFrameObject { - } - - #[cfg(any(PyPy, GraalPy, Py_3_11))] --opaque_struct!(PyFrameObject); -+opaque_struct!(pub PyFrameObject); - - // skipped _PyFrame_IsRunnable - // skipped _PyFrame_IsExecuting -@@ -89,7 +89,8 @@ extern "C" { - pub fn PyFrame_FastToLocals(f: *mut PyFrameObject); - - // skipped _PyFrame_DebugMallocStats -- // skipped PyFrame_GetBack -+ #[cfg(all(Py_3_9, not(PyPy)))] -+ pub fn PyFrame_GetBack(f: *mut PyFrameObject) -> *mut PyFrameObject; - - #[cfg(not(Py_3_9))] - pub fn PyFrame_ClearFreeList() -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -index 25de30d5..cd2052de 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -@@ -41,6 +41,8 @@ pub struct PyFunctionObject { - pub func_weakreflist: *mut PyObject, - pub func_module: *mut PyObject, - pub func_annotations: *mut PyObject, -+ #[cfg(Py_3_14)] -+ pub func_annotate: *mut PyObject, - #[cfg(Py_3_12)] - pub func_typeparams: *mut PyObject, - pub vectorcall: Option, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -index 4be310a8..92f14d59 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -@@ -1,13 +1,11 @@ - use crate::object::*; - use crate::PyFrameObject; --#[cfg(not(any(PyPy, GraalPy)))] --use crate::_PyErr_StackItem; --#[cfg(all(Py_3_11, not(GraalPy)))] -+#[cfg(all(Py_3_11, not(any(PyPy, GraalPy, Py_3_14))))] - use std::os::raw::c_char; - use std::os::raw::c_int; - use std::ptr::addr_of_mut; - --#[cfg(not(any(PyPy, GraalPy)))] -+#[cfg(not(any(PyPy, GraalPy, Py_3_14)))] - #[repr(C)] - pub struct PyGenObject { - pub ob_base: PyObject, -@@ -20,7 +18,7 @@ pub struct PyGenObject { - pub gi_weakreflist: *mut PyObject, - pub gi_name: *mut PyObject, - pub gi_qualname: *mut PyObject, -- pub gi_exc_state: _PyErr_StackItem, -+ pub gi_exc_state: crate::cpython::pystate::_PyErr_StackItem, - #[cfg(Py_3_11)] - pub gi_origin_or_finalizer: *mut PyObject, - #[cfg(Py_3_11)] -@@ -35,6 +33,9 @@ pub struct PyGenObject { - pub gi_iframe: [*mut PyObject; 1], - } - -+#[cfg(all(Py_3_14, not(any(PyPy, GraalPy))))] -+opaque_struct!(pub PyGenObject); -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - pub static mut PyGen_Type: PyTypeObject; -@@ -67,9 +68,10 @@ extern "C" { - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - pub static mut PyCoro_Type: PyTypeObject; -- pub static mut _PyCoroWrapper_Type: PyTypeObject; - } - -+// skipped _PyCoroWrapper_Type -+ - #[inline] - pub unsafe fn PyCoro_CheckExact(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, addr_of_mut!(PyCoro_Type)) -diff --git a/include/pyo3/pyo3-ffi/src/cpython/import.rs b/include/pyo3/pyo3-ffi/src/cpython/import.rs -index 697d68a4..c8ef5ab4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/import.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/import.rs -@@ -65,10 +65,8 @@ pub struct _frozen { - extern "C" { - #[cfg(not(PyPy))] - pub static mut PyImport_FrozenModules: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenBootstrap: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenStdlib: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenTest: *const _frozen; - } -+ -+// skipped _PyImport_FrozenBootstrap -+// skipped _PyImport_FrozenStdlib -+// skipped _PyImport_FrozenTest -diff --git a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -index 321d200e..076981f1 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -@@ -93,6 +93,8 @@ pub struct PyConfig { - pub tracemalloc: c_int, - #[cfg(Py_3_12)] - pub perf_profiling: c_int, -+ #[cfg(Py_3_14)] -+ pub remote_debug: c_int, - pub import_time: c_int, - #[cfg(Py_3_11)] - pub code_debug_ranges: c_int, -@@ -141,10 +143,18 @@ pub struct PyConfig { - pub safe_path: c_int, - #[cfg(Py_3_12)] - pub int_max_str_digits: c_int, -+ #[cfg(Py_3_14)] -+ pub thread_inherit_context: c_int, -+ #[cfg(Py_3_14)] -+ pub context_aware_warnings: c_int, -+ #[cfg(all(Py_3_14, target_os = "macos"))] -+ pub use_system_logger: c_int, - #[cfg(Py_3_13)] - pub cpu_count: c_int, - #[cfg(Py_GIL_DISABLED)] - pub enable_gil: c_int, -+ #[cfg(all(Py_3_14, Py_GIL_DISABLED))] -+ pub tlbc_enabled: c_int, - pub pathconfig_warnings: c_int, - #[cfg(Py_3_10)] - pub program_name: *mut wchar_t, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/lock.rs b/include/pyo3/pyo3-ffi/src/cpython/lock.rs -index c451666e..6c80b00d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/lock.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/lock.rs -@@ -8,15 +8,6 @@ pub struct PyMutex { - pub(crate) _pin: PhantomPinned, - } - --impl PyMutex { -- pub const fn new() -> PyMutex { -- PyMutex { -- _bits: AtomicU8::new(0), -- _pin: PhantomPinned, -- } -- } --} -- - extern "C" { - pub fn PyMutex_Lock(m: *mut PyMutex); - pub fn PyMutex_Unlock(m: *mut PyMutex); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -index 6063575e..45acaae5 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -@@ -61,18 +61,6 @@ extern "C" { - is_signed: c_int, - ) -> *mut PyObject; - -- #[cfg(Py_3_13)] -- #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -- pub fn _PyLong_AsByteArray( -- v: *mut PyLongObject, -- bytes: *mut c_uchar, -- n: size_t, -- little_endian: c_int, -- is_signed: c_int, -- with_exceptions: c_int, -- ) -> c_int; -- -- #[cfg(not(Py_3_13))] - #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] - pub fn _PyLong_AsByteArray( - v: *mut PyLongObject, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/mod.rs b/include/pyo3/pyo3-ffi/src/cpython/mod.rs -index fe909f0c..adaf8bc8 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/mod.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/mod.rs -@@ -38,6 +38,7 @@ pub(crate) mod pythonrun; - // skipped sysmodule.h - pub(crate) mod floatobject; - pub(crate) mod pyframe; -+pub(crate) mod pyhash; - pub(crate) mod tupleobject; - pub(crate) mod unicodeobject; - pub(crate) mod weakrefobject; -@@ -71,8 +72,10 @@ pub use self::object::*; - pub use self::objimpl::*; - pub use self::pydebug::*; - pub use self::pyerrors::*; --#[cfg(Py_3_11)] -+#[cfg(all(Py_3_11, not(PyPy)))] - pub use self::pyframe::*; -+#[cfg(any(not(PyPy), Py_3_13))] -+pub use self::pyhash::*; - #[cfg(all(Py_3_8, not(PyPy)))] - pub use self::pylifecycle::*; - pub use self::pymem::*; -diff --git a/include/pyo3/pyo3-ffi/src/cpython/object.rs b/include/pyo3/pyo3-ffi/src/cpython/object.rs -index 75eef11a..26ef784d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/object.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/object.rs -@@ -310,10 +310,14 @@ pub struct PyHeapTypeObject { - pub ht_cached_keys: *mut c_void, - #[cfg(Py_3_9)] - pub ht_module: *mut object::PyObject, -- #[cfg(Py_3_11)] -+ #[cfg(all(Py_3_11, not(PyPy)))] - _ht_tpname: *mut c_char, -- #[cfg(Py_3_11)] -+ #[cfg(Py_3_14)] -+ pub ht_token: *mut c_void, -+ #[cfg(all(Py_3_11, not(PyPy)))] - _spec_cache: _specialization_cache, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ pub unique_id: Py_ssize_t, - } - - impl Default for PyHeapTypeObject { -diff --git a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -index 98a19abe..14f7121a 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -@@ -1,4 +1,4 @@ --#[cfg(not(all(Py_3_11, GraalPy)))] -+#[cfg(not(all(Py_3_11, any(PyPy, GraalPy))))] - use libc::size_t; - use std::os::raw::c_int; - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -index c6e10e5f..c9831669 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -@@ -46,6 +46,8 @@ pub struct PySyntaxErrorObject { - pub end_offset: *mut PyObject, - pub text: *mut PyObject, - pub print_file_and_line: *mut PyObject, -+ #[cfg(Py_3_14)] -+ pub metadata: *mut PyObject, - } - - #[cfg(not(any(PyPy, GraalPy)))] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -index d0cfa0a2..f0c38be4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -@@ -1,2 +1,3 @@ --#[cfg(Py_3_11)] --opaque_struct!(_PyInterpreterFrame); -+// NB used in `_PyEval_EvalFrameDefault`, maybe we remove this too. -+#[cfg(all(Py_3_11, not(PyPy)))] -+opaque_struct!(pub _PyInterpreterFrame); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs -new file mode 100644 -index 00000000..b746018a ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs -@@ -0,0 +1,38 @@ -+#[cfg(Py_3_14)] -+use crate::Py_ssize_t; -+#[cfg(Py_3_13)] -+use crate::{PyObject, Py_hash_t}; -+#[cfg(any(Py_3_13, not(PyPy)))] -+use std::os::raw::c_void; -+#[cfg(not(PyPy))] -+use std::os::raw::{c_char, c_int}; -+ -+#[cfg(not(PyPy))] -+#[repr(C)] -+#[derive(Copy, Clone)] -+pub struct PyHash_FuncDef { -+ pub hash: -+ Option crate::Py_hash_t>, -+ pub name: *const c_char, -+ pub hash_bits: c_int, -+ pub seed_bits: c_int, -+} -+ -+#[cfg(not(PyPy))] -+impl Default for PyHash_FuncDef { -+ #[inline] -+ fn default() -> Self { -+ unsafe { std::mem::zeroed() } -+ } -+} -+ -+extern "C" { -+ #[cfg(not(PyPy))] -+ pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; -+ #[cfg(Py_3_13)] -+ pub fn Py_HashPointer(ptr: *const c_void) -> Py_hash_t; -+ #[cfg(Py_3_13)] -+ pub fn PyObject_GenericHash(obj: *mut PyObject) -> Py_hash_t; -+ #[cfg(Py_3_14)] -+ pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; -+} -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -index 5481265b..b8f6fd66 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -@@ -27,16 +27,18 @@ pub const PyTrace_OPCODE: c_int = 7; - // skipped PyTraceInfo - // skipped CFrame - -+/// Private structure used inline in `PyGenObject` - #[cfg(not(PyPy))] - #[repr(C)] - #[derive(Clone, Copy)] -+#[doc(hidden)] // TODO should be able to make pub(crate) after MSRV 1.74 - pub struct _PyErr_StackItem { - #[cfg(not(Py_3_11))] -- pub exc_type: *mut PyObject, -- pub exc_value: *mut PyObject, -+ exc_type: *mut PyObject, -+ exc_value: *mut PyObject, - #[cfg(not(Py_3_11))] -- pub exc_traceback: *mut PyObject, -- pub previous_item: *mut _PyErr_StackItem, -+ exc_traceback: *mut PyObject, -+ previous_item: *mut _PyErr_StackItem, - } - - // skipped _PyStackChunk -@@ -69,21 +71,21 @@ extern "C" { - pub fn PyThreadState_DeleteCurrent(); - } - --#[cfg(all(Py_3_9, not(Py_3_11)))] -+#[cfg(all(Py_3_9, not(any(Py_3_11, PyPy))))] - pub type _PyFrameEvalFunction = extern "C" fn( - *mut crate::PyThreadState, - *mut crate::PyFrameObject, - c_int, - ) -> *mut crate::object::PyObject; - --#[cfg(Py_3_11)] -+#[cfg(all(Py_3_11, not(PyPy)))] - pub type _PyFrameEvalFunction = extern "C" fn( - *mut crate::PyThreadState, - *mut crate::_PyInterpreterFrame, - c_int, - ) -> *mut crate::object::PyObject; - --#[cfg(Py_3_9)] -+#[cfg(all(Py_3_9, not(PyPy)))] - extern "C" { - /// Get the frame evaluation function. - pub fn _PyInterpreterState_GetEvalFrameFunc( -diff --git a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -index c06609fb..dc1bf8e4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -@@ -1,4 +1,6 @@ - use crate::object::*; -+#[cfg(Py_3_14)] -+use crate::pyport::Py_hash_t; - #[cfg(not(PyPy))] - use crate::pyport::Py_ssize_t; - -@@ -6,7 +8,7 @@ use crate::pyport::Py_ssize_t; - pub struct PyTupleObject { - pub ob_base: PyVarObject, - #[cfg(Py_3_14)] -- pub ob_hash: crate::Py_hash_t, -+ pub ob_hash: Py_hash_t, - pub ob_item: [*mut PyObject; 1], - } - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -index 72da46c1..452c82e4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -@@ -1,4 +1,4 @@ --#[cfg(not(PyPy))] -+#[cfg(any(Py_3_11, not(PyPy)))] - use crate::Py_hash_t; - use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; - use libc::wchar_t; -@@ -31,11 +31,13 @@ use std::os::raw::{c_char, c_int, c_uint, c_void}; - // skipped Py_UNICODE_LOW_SURROGATE - - // generated by bindgen v0.63.0 (with small adaptations) -+#[cfg(not(Py_3_14))] - #[repr(C)] - struct BitfieldUnit { - storage: Storage, - } - -+#[cfg(not(Py_3_14))] - impl BitfieldUnit { - #[inline] - pub const fn new(storage: Storage) -> Self { -@@ -43,7 +45,7 @@ impl BitfieldUnit { - } - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - impl BitfieldUnit - where - Storage: AsRef<[u8]> + AsMut<[u8]>, -@@ -117,30 +119,33 @@ where - } - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_INTERNED_INDEX: usize = 0; --#[cfg(all(not(GraalPy), not(Py_3_14)))] -+#[cfg(not(any(GraalPy, 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))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_KIND_WIDTH: u8 = 3; - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_COMPACT_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH) as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_COMPACT_WIDTH: u8 = 1; - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_ASCII_INDEX: usize = - (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH) as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_ASCII_WIDTH: u8 = 1; - -+#[cfg(all(not(any(GraalPy, Py_3_14)), 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(any(GraalPy, Py_3_14)), Py_3_12))] -+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; -@@ -156,15 +161,15 @@ const STATE_READY_WIDTH: u8 = 1; - /// - /// Memory layout of C bitfields is implementation defined, so these functions are still - /// unsafe. Users must verify that they work as expected on the architectures they target. -+#[cfg(not(Py_3_14))] - #[repr(C)] --#[repr(align(4))] - struct PyASCIIObjectState { - bitfield_align: [u8; 0], - bitfield: BitfieldUnit<[u8; 4usize]>, - } - - // c_uint and u32 are not necessarily the same type on all targets / architectures --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[allow(clippy::useless_transmute)] - impl PyASCIIObjectState { - #[inline] -@@ -218,6 +223,26 @@ impl PyASCIIObjectState { - .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) - } - -+ #[cfg(Py_3_12)] -+ #[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(Py_3_12)] -+ #[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, -+ ) -+ } -+ - #[cfg(not(Py_3_12))] - #[inline] - unsafe fn ready(&self) -> c_uint { -@@ -233,6 +258,7 @@ impl PyASCIIObjectState { - } - } - -+#[cfg(not(Py_3_14))] - impl From for PyASCIIObjectState { - #[inline] - fn from(value: u32) -> Self { -@@ -243,6 +269,7 @@ impl From for PyASCIIObjectState { - } - } - -+#[cfg(not(Py_3_14))] - impl From for u32 { - #[inline] - fn from(value: PyASCIIObjectState) -> Self { -@@ -254,26 +281,36 @@ impl From for u32 { - pub struct PyASCIIObject { - pub ob_base: PyObject, - pub length: Py_ssize_t, -- #[cfg(not(PyPy))] -+ #[cfg(any(Py_3_11, not(PyPy)))] - pub hash: Py_hash_t, - /// A bit field with various properties. - /// - /// 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; -+ /// on 3.14 and higher PyO3 doesn't access the internal state - pub state: u32, - #[cfg(not(Py_3_12))] - pub wstr: *mut wchar_t, - } - - /// Interacting with the bitfield is not actually well-defined, so we mark these APIs unsafe. --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - impl PyASCIIObject { - #[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. -@@ -350,6 +387,7 @@ impl PyASCIIObject { - /// - /// Calling this function with an argument that is neither `0` nor `1` is invalid. - #[inline] -+ #[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] - pub unsafe fn set_ascii(&mut self, val: c_uint) { - let mut state = PyASCIIObjectState::from(self.state); - state.set_ascii(val); -@@ -375,6 +413,26 @@ 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(Py_3_12)] -+ 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(Py_3_12)] -+ 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); -+ } - } - - #[repr(C)] -@@ -416,7 +474,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(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { - debug_assert!(crate::PyUnicode_Check(op) != 0); -@@ -426,13 +484,13 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).ascii() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).compact() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { - ((*(op as *mut PyASCIIObject)).ascii() != 0 && PyUnicode_IS_COMPACT(op) != 0).into() -@@ -464,7 +522,13 @@ 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), Py_3_14))] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyUnicode_KIND")] -+ pub fn PyUnicode_KIND(op: *mut PyObject) -> c_uint; -+} -+ -+#[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); -@@ -474,7 +538,7 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).kind() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { - if PyUnicode_IS_ASCII(op) != 0 { -@@ -492,7 +556,7 @@ pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void { - (*(op as *mut PyUnicodeObject)).data.any - } - --#[cfg(not(any(GraalPy, PyPy)))] -+#[cfg(not(any(GraalPy, PyPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { - debug_assert!(crate::PyUnicode_Check(op) != 0); -@@ -504,6 +568,13 @@ pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { - } - } - -+#[cfg(Py_3_14)] -+#[cfg(all(not(GraalPy), Py_3_14))] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyUnicode_DATA")] -+ pub fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void; -+} -+ - // skipped PyUnicode_WRITE - // skipped PyUnicode_READ - // skipped PyUnicode_READ_CHAR -diff --git a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -index 88bb501b..1c50c7a7 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -@@ -1,3 +1,4 @@ -+// NB publicly re-exported in `src/weakrefobject.rs` - #[cfg(not(any(PyPy, GraalPy)))] - pub struct _PyWeakReference { - pub ob_base: crate::PyObject, -diff --git a/include/pyo3/pyo3-ffi/src/datetime.rs b/include/pyo3/pyo3-ffi/src/datetime.rs -index e529e0fc..4db1bdd1 100644 ---- a/include/pyo3/pyo3-ffi/src/datetime.rs -+++ b/include/pyo3/pyo3-ffi/src/datetime.rs -@@ -119,7 +119,7 @@ pub struct PyDateTime_DateTime { - pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { - // This should work for Date or DateTime - let data = (*(o as *mut PyDateTime_Date)).data; -- c_int::from(data[0]) << 8 | c_int::from(data[1]) -+ (c_int::from(data[0]) << 8) | c_int::from(data[1]) - } - - #[inline] -@@ -487,7 +487,9 @@ extern "C" { - pub fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int; - #[link_name = "PyPyDateTime_GET_FOLD"] - pub fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_int; -- // skipped PyDateTime_DATE_GET_TZINFO (not in PyPy) -+ #[link_name = "PyPyDateTime_DATE_GET_TZINFO"] -+ #[cfg(Py_3_10)] -+ pub fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; - - #[link_name = "PyPyDateTime_TIME_GET_HOUR"] - pub fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int; -@@ -499,7 +501,9 @@ extern "C" { - pub fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int; - #[link_name = "PyPyDateTime_TIME_GET_FOLD"] - pub fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_int; -- // skipped PyDateTime_TIME_GET_TZINFO (not in PyPy) -+ #[link_name = "PyPyDateTime_TIME_GET_TZINFO"] -+ #[cfg(Py_3_10)] -+ pub fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; - - #[link_name = "PyPyDateTime_DELTA_GET_DAYS"] - pub fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/dictobject.rs b/include/pyo3/pyo3-ffi/src/dictobject.rs -index 710be802..e609352a 100644 ---- a/include/pyo3/pyo3-ffi/src/dictobject.rs -+++ b/include/pyo3/pyo3-ffi/src/dictobject.rs -@@ -118,4 +118,4 @@ extern "C" { - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] - // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) --opaque_struct!(PyDictObject); -+opaque_struct!(pub PyDictObject); -diff --git a/include/pyo3/pyo3-ffi/src/floatobject.rs b/include/pyo3/pyo3-ffi/src/floatobject.rs -index 65fc1d4c..4e1d6476 100644 ---- a/include/pyo3/pyo3-ffi/src/floatobject.rs -+++ b/include/pyo3/pyo3-ffi/src/floatobject.rs -@@ -4,7 +4,7 @@ use std::ptr::addr_of_mut; - - #[cfg(Py_LIMITED_API)] - // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) --opaque_struct!(PyFloatObject); -+opaque_struct!(pub PyFloatObject); - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -diff --git a/include/pyo3/pyo3-ffi/src/genericaliasobject.rs b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs -new file mode 100644 -index 00000000..7979d7d8 ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs -@@ -0,0 +1,12 @@ -+#[cfg(Py_3_9)] -+use crate::object::{PyObject, PyTypeObject}; -+ -+#[cfg_attr(windows, link(name = "pythonXY"))] -+extern "C" { -+ #[cfg(Py_3_9)] -+ #[cfg_attr(PyPy, link_name = "PyPy_GenericAlias")] -+ pub fn Py_GenericAlias(origin: *mut PyObject, args: *mut PyObject) -> *mut PyObject; -+ -+ #[cfg(Py_3_9)] -+ pub static mut Py_GenericAliasType: PyTypeObject; -+} -diff --git a/include/pyo3/pyo3-ffi/src/lib.rs b/include/pyo3/pyo3-ffi/src/lib.rs -index 7bdba117..f78c918a 100644 ---- a/include/pyo3/pyo3-ffi/src/lib.rs -+++ b/include/pyo3/pyo3-ffi/src/lib.rs -@@ -129,7 +129,7 @@ - //! ``` - //! - //! **`src/lib.rs`** --//! ```rust -+//! ```rust,no_run - //! use std::os::raw::{c_char, c_long}; - //! use std::ptr; - //! -@@ -327,17 +327,24 @@ - non_snake_case, - non_upper_case_globals, - clippy::upper_case_acronyms, -- clippy::missing_safety_doc -+ clippy::missing_safety_doc, -+ clippy::ptr_eq - )] - #![warn(elided_lifetimes_in_paths, unused_lifetimes)] -+// This crate is a hand-maintained translation of CPython's headers, so requiring "unsafe" -+// blocks within those translations increases maintenance burden without providing any -+// additional safety. The safety of the functions in this crate is determined by the -+// original CPython headers -+#![allow(unsafe_op_in_unsafe_fn)] - - // Until `extern type` is stabilized, use the recommended approach to - // model opaque types: - // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs - macro_rules! opaque_struct { -- ($name:ident) => { -+ ($(#[$attrs:meta])* $pub:vis $name:ident) => { -+ $(#[$attrs])* - #[repr(C)] -- pub struct $name([u8; 0]); -+ $pub struct $name([u8; 0]); - }; - } - -@@ -351,7 +358,7 @@ macro_rules! opaque_struct { - /// - /// Examples: - /// --/// ```rust -+/// ```rust,no_run - /// use std::ffi::CStr; - /// - /// const HELLO: &CStr = pyo3_ffi::c_str!("hello"); -@@ -410,6 +417,8 @@ pub use self::enumobject::*; - pub use self::fileobject::*; - pub use self::fileutils::*; - pub use self::floatobject::*; -+#[cfg(Py_3_9)] -+pub use self::genericaliasobject::*; - pub use self::import::*; - pub use self::intrcheck::*; - pub use self::iterobject::*; -@@ -439,6 +448,7 @@ pub use self::pystate::*; - pub use self::pystrtod::*; - pub use self::pythonrun::*; - pub use self::rangeobject::*; -+pub use self::refcount::*; - pub use self::setobject::*; - pub use self::sliceobject::*; - pub use self::structseq::*; -@@ -479,7 +489,7 @@ mod fileobject; - mod fileutils; - mod floatobject; - // skipped empty frameobject.h --// skipped genericaliasobject.h -+mod genericaliasobject; - mod import; - // skipped interpreteridobject.h - mod intrcheck; -@@ -531,6 +541,7 @@ mod pystrtod; - // skipped pythread.h - // skipped pytime.h - mod rangeobject; -+mod refcount; - mod setobject; - mod sliceobject; - mod structseq; -diff --git a/include/pyo3/pyo3-ffi/src/longobject.rs b/include/pyo3/pyo3-ffi/src/longobject.rs -index 68b4ecba..eca0af3d 100644 ---- a/include/pyo3/pyo3-ffi/src/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/longobject.rs -@@ -4,7 +4,7 @@ use libc::size_t; - use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void}; - use std::ptr::addr_of_mut; - --opaque_struct!(PyLongObject); -+opaque_struct!(pub PyLongObject); - - #[inline] - pub unsafe fn PyLong_Check(op: *mut PyObject) -> c_int { -diff --git a/include/pyo3/pyo3-ffi/src/memoryobject.rs b/include/pyo3/pyo3-ffi/src/memoryobject.rs -index b7ef9e2e..4e1e50c6 100644 ---- a/include/pyo3/pyo3-ffi/src/memoryobject.rs -+++ b/include/pyo3/pyo3-ffi/src/memoryobject.rs -@@ -3,11 +3,10 @@ use crate::pyport::Py_ssize_t; - use std::os::raw::{c_char, c_int}; - use std::ptr::addr_of_mut; - -+// skipped _PyManagedBuffer_Type -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -- #[cfg(not(Py_LIMITED_API))] -- pub static mut _PyManagedBuffer_Type: PyTypeObject; -- - #[cfg_attr(PyPy, link_name = "PyPyMemoryView_Type")] - pub static mut PyMemoryView_Type: PyTypeObject; - } -diff --git a/include/pyo3/pyo3-ffi/src/methodobject.rs b/include/pyo3/pyo3-ffi/src/methodobject.rs -index 37e1e206..3dfbbb5a 100644 ---- a/include/pyo3/pyo3-ffi/src/methodobject.rs -+++ b/include/pyo3/pyo3-ffi/src/methodobject.rs -@@ -50,7 +50,7 @@ pub type PyCFunctionFast = unsafe extern "C" fn( - ) -> *mut PyObject; - - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] --#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] -+#[deprecated(note = "renamed to `PyCFunctionFast`")] - pub type _PyCFunctionFast = PyCFunctionFast; - - pub type PyCFunctionWithKeywords = unsafe extern "C" fn( -@@ -68,7 +68,7 @@ pub type PyCFunctionFastWithKeywords = unsafe extern "C" fn( - ) -> *mut PyObject; - - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] --#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] -+#[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] - pub type _PyCFunctionFastWithKeywords = PyCFunctionFastWithKeywords; - - #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] -@@ -153,7 +153,7 @@ pub union PyMethodDefPointer { - - /// This variant corresponds with [`METH_FASTCALL`]. - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] -+ #[deprecated(note = "renamed to `PyCFunctionFast`")] - pub _PyCFunctionFast: PyCFunctionFast, - - /// This variant corresponds with [`METH_FASTCALL`]. -@@ -162,7 +162,7 @@ pub union PyMethodDefPointer { - - /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] -+ #[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] - pub _PyCFunctionFastWithKeywords: PyCFunctionFastWithKeywords, - - /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. -diff --git a/include/pyo3/pyo3-ffi/src/modsupport.rs b/include/pyo3/pyo3-ffi/src/modsupport.rs -index 6da2795b..56a68fe2 100644 ---- a/include/pyo3/pyo3-ffi/src/modsupport.rs -+++ b/include/pyo3/pyo3-ffi/src/modsupport.rs -@@ -37,12 +37,7 @@ extern "C" { - // skipped Py_VaBuildValue - - #[cfg(Py_3_13)] -- pub fn PyModule_Add( -- module: *mut PyObject, -- name: *const c_char, -- value: *mut PyObject, -- ) -> core::ffi::c_int; -- -+ pub fn PyModule_Add(module: *mut PyObject, name: *const c_char, value: *mut PyObject) -> c_int; - #[cfg(Py_3_10)] - #[cfg_attr(PyPy, link_name = "PyPyModule_AddObjectRef")] - pub fn PyModule_AddObjectRef( -diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -index 51083a8e..5fbf45db 100644 ---- a/include/pyo3/pyo3-ffi/src/object.rs -+++ b/include/pyo3/pyo3-ffi/src/object.rs -@@ -1,5 +1,7 @@ - use crate::pyport::{Py_hash_t, Py_ssize_t}; - #[cfg(Py_GIL_DISABLED)] -+use crate::refcount; -+#[cfg(Py_GIL_DISABLED)] - use crate::PyMutex; - #[cfg(Py_GIL_DISABLED)] - use std::marker::PhantomPinned; -@@ -7,81 +9,50 @@ use std::mem; - use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void}; - use std::ptr; - #[cfg(Py_GIL_DISABLED)] --use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8, Ordering::Relaxed}; -+use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8}; - - #[cfg(Py_LIMITED_API)] --opaque_struct!(PyTypeObject); -+opaque_struct!(pub PyTypeObject); - - #[cfg(not(Py_LIMITED_API))] - pub use crate::cpython::object::PyTypeObject; - --#[cfg(Py_3_12)] --const _Py_IMMORTAL_REFCNT: Py_ssize_t = { -- if cfg!(target_pointer_width = "64") { -- c_uint::MAX as Py_ssize_t -- } else { -- // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) -- (c_uint::MAX >> 2) as Py_ssize_t -- } --}; -- --#[cfg(Py_GIL_DISABLED)] --const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; -- --#[allow(clippy::declare_interior_mutable_const)] --pub const PyObject_HEAD_INIT: PyObject = PyObject { -- #[cfg(py_sys_config = "Py_TRACE_REFS")] -- _ob_next: std::ptr::null_mut(), -- #[cfg(py_sys_config = "Py_TRACE_REFS")] -- _ob_prev: std::ptr::null_mut(), -- #[cfg(Py_GIL_DISABLED)] -- ob_tid: 0, -- #[cfg(Py_GIL_DISABLED)] -- _padding: 0, -- #[cfg(Py_GIL_DISABLED)] -- ob_mutex: PyMutex { -- _bits: AtomicU8::new(0), -- _pin: PhantomPinned, -- }, -- #[cfg(Py_GIL_DISABLED)] -- ob_gc_bits: 0, -- #[cfg(Py_GIL_DISABLED)] -- ob_ref_local: AtomicU32::new(_Py_IMMORTAL_REFCNT_LOCAL), -- #[cfg(Py_GIL_DISABLED)] -- ob_ref_shared: AtomicIsize::new(0), -- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -- ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, -- #[cfg(not(Py_3_12))] -- ob_refcnt: 1, -- #[cfg(PyPy)] -- ob_pypy_link: 0, -- ob_type: std::ptr::null_mut(), --}; -- --// skipped PyObject_VAR_HEAD --// skipped Py_INVALID_SIZE -- --// skipped private _Py_UNOWNED_TID -+// skip PyObject_HEAD - --#[cfg(Py_GIL_DISABLED)] --const _Py_REF_SHARED_SHIFT: isize = 2; --// skipped private _Py_REF_SHARED_FLAG_MASK -- --// skipped private _Py_REF_SHARED_INIT --// skipped private _Py_REF_MAYBE_WEAKREF --// skipped private _Py_REF_QUEUED --// skipped private _Py_REF_MERGED -+#[repr(C)] -+#[derive(Copy, Clone)] -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "big"))] -+/// This struct is anonymous in CPython, so the name was given by PyO3 because -+/// Rust structs need a name. -+pub struct PyObjectObFlagsAndRefcnt { -+ pub ob_flags: u16, -+ pub ob_overflow: u16, -+ pub ob_refcnt: u32, -+} - --// skipped private _Py_REF_SHARED -+#[repr(C)] -+#[derive(Copy, Clone)] -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "little"))] -+/// This struct is anonymous in CPython, so the name was given by PyO3 because -+/// Rust structs need a name. -+pub struct PyObjectObFlagsAndRefcnt { -+ pub ob_refcnt: u32, -+ pub ob_overflow: u16, -+ pub ob_flags: u16, -+} - - #[repr(C)] - #[derive(Copy, Clone)] - #[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] - /// This union is anonymous in CPython, so the name was given by PyO3 because --/// Rust unions need a name. -+/// Rust union need a name. - pub union PyObjectObRefcnt { -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub ob_refcnt_full: crate::PY_INT64_T, -+ #[cfg(Py_3_14)] -+ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, - pub ob_refcnt: Py_ssize_t, -- #[cfg(target_pointer_width = "64")] -+ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] - pub ob_refcnt_split: [crate::PY_UINT32_T; 2], - } - -@@ -95,6 +66,9 @@ impl std::fmt::Debug for PyObjectObRefcnt { - #[cfg(all(not(Py_3_12), not(Py_GIL_DISABLED)))] - pub type PyObjectObRefcnt = Py_ssize_t; - -+// PyObject_HEAD_INIT comes before the PyObject definition in object.h -+// but we put it after PyObject because HEAD_INIT uses PyObject -+ - #[repr(C)] - #[derive(Debug)] - pub struct PyObject { -@@ -104,8 +78,10 @@ pub struct PyObject { - pub _ob_prev: *mut PyObject, - #[cfg(Py_GIL_DISABLED)] - pub ob_tid: libc::uintptr_t, -- #[cfg(Py_GIL_DISABLED)] -+ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] - pub _padding: u16, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ pub ob_flags: u16, - #[cfg(Py_GIL_DISABLED)] - pub ob_mutex: PyMutex, // per-object lock - #[cfg(Py_GIL_DISABLED)] -@@ -121,7 +97,41 @@ pub struct PyObject { - pub ob_type: *mut PyTypeObject, - } - --// skipped private _PyObject_CAST -+#[allow(clippy::declare_interior_mutable_const)] -+pub const PyObject_HEAD_INIT: PyObject = PyObject { -+ #[cfg(py_sys_config = "Py_TRACE_REFS")] -+ _ob_next: std::ptr::null_mut(), -+ #[cfg(py_sys_config = "Py_TRACE_REFS")] -+ _ob_prev: std::ptr::null_mut(), -+ #[cfg(Py_GIL_DISABLED)] -+ ob_tid: 0, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ ob_flags: 0, -+ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] -+ _padding: 0, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_mutex: PyMutex { -+ _bits: AtomicU8::new(0), -+ _pin: PhantomPinned, -+ }, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_gc_bits: 0, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_ref_local: AtomicU32::new(refcount::_Py_IMMORTAL_REFCNT_LOCAL), -+ #[cfg(Py_GIL_DISABLED)] -+ ob_ref_shared: AtomicIsize::new(0), -+ #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -+ ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, -+ #[cfg(not(Py_3_12))] -+ ob_refcnt: 1, -+ #[cfg(PyPy)] -+ ob_pypy_link: 0, -+ ob_type: std::ptr::null_mut(), -+}; -+ -+// skipped _Py_UNOWNED_TID -+ -+// skipped _PyObject_CAST - - #[repr(C)] - #[derive(Debug)] -@@ -150,41 +160,23 @@ extern "C" { - pub fn Py_Is(x: *mut PyObject, y: *mut PyObject) -> c_int; - } - --// skipped private _Py_GetThreadLocal_Addr -+// skipped _Py_GetThreadLocal_Addr - --// skipped private _Py_ThreadId -+// skipped _Py_ThreadID - --// skipped private _Py_IsOwnedByCurrentThread -+// skipped _Py_IsOwnedByCurrentThread - --#[inline] --pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { -- #[cfg(Py_GIL_DISABLED)] -- { -- let local = (*ob).ob_ref_local.load(Relaxed); -- if local == _Py_IMMORTAL_REFCNT_LOCAL { -- return _Py_IMMORTAL_REFCNT; -- } -- let shared = (*ob).ob_ref_shared.load(Relaxed); -- local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) -- } -- -- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -- { -- (*ob).ob_refcnt.ob_refcnt -- } -- -- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] -- { -- (*ob).ob_refcnt -- } -+#[cfg(GraalPy)] -+extern "C" { -+ #[cfg(GraalPy)] -+ fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; - -- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] -- { -- _Py_REFCNT(ob) -- } -+ #[cfg(GraalPy)] -+ fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; - } - - #[inline] -+#[cfg(not(Py_3_14))] - pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { - #[cfg(not(GraalPy))] - return (*ob).ob_type; -@@ -192,6 +184,15 @@ pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { - return _Py_TYPE(ob); - } - -+#[cfg_attr(windows, link(name = "pythonXY"))] -+#[cfg(Py_3_14)] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPy_TYPE")] -+ pub fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject; -+} -+ -+// skip _Py_TYPE compat shim -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyLong_Type")] -@@ -212,29 +213,11 @@ pub unsafe fn Py_SIZE(ob: *mut PyObject) -> Py_ssize_t { - _Py_SIZE(ob) - } - --#[inline(always)] --#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] --pub unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { -- #[cfg(target_pointer_width = "64")] -- { -- (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int -- } -- -- #[cfg(target_pointer_width = "32")] -- { -- ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int -- } --} -- - #[inline] - pub unsafe fn Py_IS_TYPE(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { - (Py_TYPE(ob) == tp) as c_int - } - --// skipped _Py_SetRefCnt -- --// skipped Py_SET_REFCNT -- - // skipped Py_SET_TYPE - - // skipped Py_SET_SIZE -@@ -436,7 +419,7 @@ extern "C" { - arg2: *const c_char, - arg3: *mut PyObject, - ) -> c_int; -- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h -+ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h - #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttrString")] - pub fn PyObject_DelAttrString(arg1: *mut PyObject, arg2: *const c_char) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttrString")] -@@ -460,7 +443,7 @@ extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyObject_SetAttr")] - pub fn PyObject_SetAttr(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> c_int; -- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h -+ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h - #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttr")] - pub fn PyObject_DelAttr(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttr")] -@@ -586,222 +569,6 @@ pub const Py_TPFLAGS_DEFAULT: c_ulong = if cfg!(Py_3_10) { - pub const Py_TPFLAGS_HAVE_FINALIZE: c_ulong = 1; - pub const Py_TPFLAGS_HAVE_VERSION_TAG: c_ulong = 1 << 18; - --extern "C" { -- #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); -- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_INCREF_IncRefTotal(); -- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_DECREF_DecRefTotal(); -- -- #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] -- fn _Py_Dealloc(arg1: *mut PyObject); -- -- #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] -- #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] -- pub fn Py_IncRef(o: *mut PyObject); -- #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] -- #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] -- pub fn Py_DecRef(o: *mut PyObject); -- -- #[cfg(all(Py_3_10, not(PyPy)))] -- fn _Py_IncRef(o: *mut PyObject); -- #[cfg(all(Py_3_10, not(PyPy)))] -- fn _Py_DecRef(o: *mut PyObject); -- -- #[cfg(GraalPy)] -- fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; -- -- #[cfg(GraalPy)] -- fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; -- -- #[cfg(GraalPy)] -- fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; --} -- --#[inline(always)] --pub unsafe fn Py_INCREF(op: *mut PyObject) { -- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -- #[cfg(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- py_sys_config = "Py_REF_DEBUG", -- GraalPy -- ))] -- { -- // _Py_IncRef was added to the ABI in 3.10; skips null checks -- #[cfg(all(Py_3_10, not(PyPy)))] -- { -- _Py_IncRef(op); -- } -- -- #[cfg(any(not(Py_3_10), PyPy))] -- { -- Py_IncRef(op); -- } -- } -- -- // version-specific builds are allowed to directly manipulate the reference count -- #[cfg(not(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- py_sys_config = "Py_REF_DEBUG", -- GraalPy -- )))] -- { -- #[cfg(all(Py_3_12, target_pointer_width = "64"))] -- { -- let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; -- let new_refcnt = cur_refcnt.wrapping_add(1); -- if new_refcnt == 0 { -- return; -- } -- (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; -- } -- -- #[cfg(all(Py_3_12, target_pointer_width = "32"))] -- { -- if _Py_IsImmortal(op) != 0 { -- return; -- } -- (*op).ob_refcnt.ob_refcnt += 1 -- } -- -- #[cfg(not(Py_3_12))] -- { -- (*op).ob_refcnt += 1 -- } -- -- // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue -- // or submit a PR supporting Py_STATS build option and pystats.h -- } --} -- --#[inline(always)] --#[cfg_attr( -- all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), -- track_caller --)] --pub unsafe fn Py_DECREF(op: *mut PyObject) { -- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -- // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts -- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -- #[cfg(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -- GraalPy -- ))] -- { -- // _Py_DecRef was added to the ABI in 3.10; skips null checks -- #[cfg(all(Py_3_10, not(PyPy)))] -- { -- _Py_DecRef(op); -- } -- -- #[cfg(any(not(Py_3_10), PyPy))] -- { -- Py_DecRef(op); -- } -- } -- -- #[cfg(not(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -- GraalPy -- )))] -- { -- #[cfg(Py_3_12)] -- if _Py_IsImmortal(op) != 0 { -- return; -- } -- -- // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue -- // or submit a PR supporting Py_STATS build option and pystats.h -- -- #[cfg(py_sys_config = "Py_REF_DEBUG")] -- _Py_DECREF_DecRefTotal(); -- -- #[cfg(Py_3_12)] -- { -- (*op).ob_refcnt.ob_refcnt -= 1; -- -- #[cfg(py_sys_config = "Py_REF_DEBUG")] -- if (*op).ob_refcnt.ob_refcnt < 0 { -- let location = std::panic::Location::caller(); -- let filename = std::ffi::CString::new(location.file()).unwrap(); -- _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); -- } -- -- if (*op).ob_refcnt.ob_refcnt == 0 { -- _Py_Dealloc(op); -- } -- } -- -- #[cfg(not(Py_3_12))] -- { -- (*op).ob_refcnt -= 1; -- -- if (*op).ob_refcnt == 0 { -- _Py_Dealloc(op); -- } -- } -- } --} -- --#[inline] --pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { -- let tmp = *op; -- if !tmp.is_null() { -- *op = ptr::null_mut(); -- Py_DECREF(tmp); -- } --} -- --#[inline] --pub unsafe fn Py_XINCREF(op: *mut PyObject) { -- if !op.is_null() { -- Py_INCREF(op) -- } --} -- --#[inline] --pub unsafe fn Py_XDECREF(op: *mut PyObject) { -- if !op.is_null() { -- Py_DECREF(op) -- } --} -- --extern "C" { -- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -- pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; -- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -- pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; --} -- --// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here --// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here -- --#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] --#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] --#[inline] --pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { -- Py_INCREF(obj); -- obj --} -- --#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] --#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] --#[inline] --pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { -- Py_XINCREF(obj); -- obj --} -- - #[cfg(Py_3_13)] - pub const Py_CONSTANT_NONE: c_uint = 0; - #[cfg(Py_3_13)] -@@ -942,4 +709,7 @@ extern "C" { - arg1: *mut crate::PyTypeObject, - arg2: *mut crate::PyModuleDef, - ) -> *mut PyObject; -+ -+ #[cfg(Py_3_14)] -+ pub fn PyType_Freeze(tp: *mut crate::PyTypeObject) -> c_int; - } -diff --git a/include/pyo3/pyo3-ffi/src/pyarena.rs b/include/pyo3/pyo3-ffi/src/pyarena.rs -index 87d5f28a..1200de3d 100644 ---- a/include/pyo3/pyo3-ffi/src/pyarena.rs -+++ b/include/pyo3/pyo3-ffi/src/pyarena.rs -@@ -1 +1 @@ --opaque_struct!(PyArena); -+opaque_struct!(pub PyArena); -diff --git a/include/pyo3/pyo3-ffi/src/pybuffer.rs b/include/pyo3/pyo3-ffi/src/pybuffer.rs -index 50bf4e61..de706759 100644 ---- a/include/pyo3/pyo3-ffi/src/pybuffer.rs -+++ b/include/pyo3/pyo3-ffi/src/pybuffer.rs -@@ -103,7 +103,11 @@ extern "C" { - } - - /// Maximum number of dimensions --pub const PyBUF_MAX_NDIM: c_int = if cfg!(PyPy) { 36 } else { 64 }; -+pub const PyBUF_MAX_NDIM: usize = if cfg!(all(PyPy, not(Py_3_11))) { -+ 36 -+} else { -+ 64 -+}; - - /* Flags for getting buffers */ - pub const PyBUF_SIMPLE: c_int = 0; -diff --git a/include/pyo3/pyo3-ffi/src/pyerrors.rs b/include/pyo3/pyo3-ffi/src/pyerrors.rs -index 6c9313c4..d341239a 100644 ---- a/include/pyo3/pyo3-ffi/src/pyerrors.rs -+++ b/include/pyo3/pyo3-ffi/src/pyerrors.rs -@@ -116,6 +116,7 @@ extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyExc_BaseException")] - pub static mut PyExc_BaseException: *mut PyObject; - #[cfg(Py_3_11)] -+ #[cfg_attr(PyPy, link_name = "PyPyExc_BaseExceptionGroup")] - pub static mut PyExc_BaseExceptionGroup: *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyExc_Exception")] - pub static mut PyExc_Exception: *mut PyObject; -diff --git a/include/pyo3/pyo3-ffi/src/pyframe.rs b/include/pyo3/pyo3-ffi/src/pyframe.rs -index 4dd3d2b3..1693b20b 100644 ---- a/include/pyo3/pyo3-ffi/src/pyframe.rs -+++ b/include/pyo3/pyo3-ffi/src/pyframe.rs -@@ -6,7 +6,7 @@ use crate::PyFrameObject; - use std::os::raw::c_int; - - #[cfg(Py_LIMITED_API)] --opaque_struct!(PyFrameObject); -+opaque_struct!(pub PyFrameObject); - - extern "C" { - pub fn PyFrame_GetLineNumber(f: *mut PyFrameObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/pyhash.rs b/include/pyo3/pyo3-ffi/src/pyhash.rs -index 8b0b6771..074278dd 100644 ---- a/include/pyo3/pyo3-ffi/src/pyhash.rs -+++ b/include/pyo3/pyo3-ffi/src/pyhash.rs -@@ -1,7 +1,5 @@ - #[cfg(not(any(Py_LIMITED_API, PyPy)))] - use crate::pyport::{Py_hash_t, Py_ssize_t}; --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --use std::os::raw::c_char; - #[cfg(not(any(Py_LIMITED_API, PyPy)))] - use std::os::raw::c_void; - -@@ -12,11 +10,8 @@ extern "C" { - // skipped non-limited _Py_HashPointer - // skipped non-limited _Py_HashPointerRaw - -- #[cfg(not(all(Py_3_14, any(Py_LIMITED_API, PyPy))))] -+ #[cfg(not(any(Py_LIMITED_API, PyPy)))] - pub fn _Py_HashBytes(src: *const c_void, len: Py_ssize_t) -> Py_hash_t; -- -- #[cfg(Py_3_14)] -- pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; - } - - pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; -@@ -25,29 +20,6 @@ pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; - - // skipped non-limited _Py_HashSecret_t - --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct PyHash_FuncDef { -- pub hash: Option Py_hash_t>, -- pub name: *const c_char, -- pub hash_bits: c_int, -- pub seed_bits: c_int, --} -- --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --impl Default for PyHash_FuncDef { -- #[inline] -- fn default() -> Self { -- unsafe { std::mem::zeroed() } -- } --} -- --extern "C" { -- #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] -- pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; --} -- - // skipped Py_HASH_CUTOFF - - pub const Py_HASH_EXTERNAL: c_int = 0; -diff --git a/include/pyo3/pyo3-ffi/src/pyport.rs b/include/pyo3/pyo3-ffi/src/pyport.rs -index a144c67f..e524831d 100644 ---- a/include/pyo3/pyo3-ffi/src/pyport.rs -+++ b/include/pyo3/pyo3-ffi/src/pyport.rs -@@ -1,3 +1,8 @@ -+// NB libc does not define this constant on all platforms, so we hard code it -+// like CPython does. -+// https://github.com/python/cpython/blob/d8b9011702443bb57579f8834f3effe58e290dfc/Include/pyport.h#L372 -+pub const INT_MAX: std::os::raw::c_int = 2147483647; -+ - pub type PY_UINT32_T = u32; - pub type PY_UINT64_T = u64; - -@@ -11,8 +16,8 @@ pub type Py_ssize_t = ::libc::ssize_t; - pub type Py_hash_t = Py_ssize_t; - pub type Py_uhash_t = ::libc::size_t; - --pub const PY_SSIZE_T_MIN: Py_ssize_t = isize::MIN as Py_ssize_t; --pub const PY_SSIZE_T_MAX: Py_ssize_t = isize::MAX as Py_ssize_t; -+pub const PY_SSIZE_T_MIN: Py_ssize_t = Py_ssize_t::MIN; -+pub const PY_SSIZE_T_MAX: Py_ssize_t = Py_ssize_t::MAX; - - #[cfg(target_endian = "big")] - pub const PY_BIG_ENDIAN: usize = 1; -diff --git a/include/pyo3/pyo3-ffi/src/pystate.rs b/include/pyo3/pyo3-ffi/src/pystate.rs -index 23aeea3a..cc16e554 100644 ---- a/include/pyo3/pyo3-ffi/src/pystate.rs -+++ b/include/pyo3/pyo3-ffi/src/pystate.rs -@@ -1,3 +1,5 @@ -+#[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] -+use crate::frameobject::PyFrameObject; - use crate::moduleobject::PyModuleDef; - use crate::object::PyObject; - use std::os::raw::c_int; -@@ -7,8 +9,8 @@ use std::os::raw::c_long; - - pub const MAX_CO_EXTRA_USERS: c_int = 255; - --opaque_struct!(PyThreadState); --opaque_struct!(PyInterpreterState); -+opaque_struct!(pub PyThreadState); -+opaque_struct!(pub PyInterpreterState); - - extern "C" { - #[cfg(not(PyPy))] -@@ -63,9 +65,14 @@ extern "C" { - } - - // skipped non-limited / 3.9 PyThreadState_GetInterpreter --// skipped non-limited / 3.9 PyThreadState_GetFrame - // skipped non-limited / 3.9 PyThreadState_GetID - -+extern "C" { -+ // PyThreadState_GetFrame -+ #[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] -+ pub fn PyThreadState_GetFrame(arg1: *mut PyThreadState) -> *mut PyFrameObject; -+} -+ - #[repr(C)] - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum PyGILState_STATE { -@@ -73,9 +80,70 @@ pub enum PyGILState_STATE { - PyGILState_UNLOCKED, - } - -+#[cfg(not(Py_3_14))] -+struct HangThread; -+ -+#[cfg(not(Py_3_14))] -+impl Drop for HangThread { -+ fn drop(&mut self) { -+ loop { -+ std::thread::park(); // Block forever. -+ } -+ } -+} -+ -+// The PyGILState_Ensure function will call pthread_exit during interpreter shutdown, -+// which causes undefined behavior. Redirect to the "safe" version that hangs instead, -+// as Python 3.14 does. -+// -+// See https://github.com/rust-lang/rust/issues/135929 -+ -+// C-unwind only supported (and necessary) since 1.71. Python 3.14+ does not do -+// pthread_exit from PyGILState_Ensure (https://github.com/python/cpython/issues/87135). -+mod raw { -+ #[cfg(all(not(Py_3_14), rustc_has_extern_c_unwind))] -+ extern "C-unwind" { -+ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -+ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; -+ } -+ -+ #[cfg(not(all(not(Py_3_14), rustc_has_extern_c_unwind)))] -+ extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -+ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; -+ } -+} -+ -+#[cfg(not(Py_3_14))] -+pub unsafe extern "C" fn PyGILState_Ensure() -> PyGILState_STATE { -+ let guard = HangThread; -+ // If `PyGILState_Ensure` calls `pthread_exit`, which it does on Python < 3.14 -+ // when the interpreter is shutting down, this will cause a forced unwind. -+ // doing a forced unwind through a function with a Rust destructor is unspecified -+ // behavior. -+ // -+ // However, currently it runs the destructor, which will cause the thread to -+ // hang as it should. -+ // -+ // And if we don't catch the unwinding here, then one of our callers probably has a destructor, -+ // so it's unspecified behavior anyway, and on many configurations causes the process to abort. -+ // -+ // The alternative is for pyo3 to contain custom C or C++ code that catches the `pthread_exit`, -+ // but that's also annoying from a portability point of view. -+ // -+ // On Windows, `PyGILState_Ensure` calls `_endthreadex` instead, which AFAICT can't be caught -+ // and therefore will cause unsafety if there are pinned objects on the stack. AFAICT there's -+ // nothing we can do it other than waiting for Python 3.14 or not using Windows. At least, -+ // if there is nothing pinned on the stack, it won't cause the process to crash. -+ let ret: PyGILState_STATE = raw::PyGILState_Ensure(); -+ std::mem::forget(guard); -+ ret -+} -+ -+#[cfg(Py_3_14)] -+pub use self::raw::PyGILState_Ensure; -+ - extern "C" { -- #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -- pub fn PyGILState_Ensure() -> PyGILState_STATE; - #[cfg_attr(PyPy, link_name = "PyPyGILState_Release")] - pub fn PyGILState_Release(arg1: PyGILState_STATE); - #[cfg(not(PyPy))] -diff --git a/include/pyo3/pyo3-ffi/src/pythonrun.rs b/include/pyo3/pyo3-ffi/src/pythonrun.rs -index e7ea2d2e..80209b58 100644 ---- a/include/pyo3/pyo3-ffi/src/pythonrun.rs -+++ b/include/pyo3/pyo3-ffi/src/pythonrun.rs -@@ -49,12 +49,12 @@ pub const PYOS_STACK_MARGIN: c_int = 2048; - // skipped PyOS_CheckStack under Microsoft C - - #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] --opaque_struct!(_mod); -+opaque_struct!(pub _mod); - - #[cfg(not(any(PyPy, Py_3_10)))] --opaque_struct!(symtable); -+opaque_struct!(pub symtable); - #[cfg(not(any(PyPy, Py_3_10)))] --opaque_struct!(_node); -+opaque_struct!(pub _node); - - #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] - #[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))] -diff --git a/include/pyo3/pyo3-ffi/src/refcount.rs b/include/pyo3/pyo3-ffi/src/refcount.rs -new file mode 100644 -index 00000000..fcb5f45b ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/refcount.rs -@@ -0,0 +1,369 @@ -+use crate::pyport::Py_ssize_t; -+use crate::PyObject; -+#[cfg(py_sys_config = "Py_REF_DEBUG")] -+use std::os::raw::c_char; -+#[cfg(Py_3_12)] -+use std::os::raw::c_int; -+#[cfg(all(Py_3_14, any(not(Py_GIL_DISABLED), target_pointer_width = "32")))] -+use std::os::raw::c_long; -+#[cfg(any(Py_GIL_DISABLED, all(Py_3_12, not(Py_3_14))))] -+use std::os::raw::c_uint; -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+use std::os::raw::c_ulong; -+use std::ptr; -+#[cfg(Py_GIL_DISABLED)] -+use std::sync::atomic::Ordering::Relaxed; -+ -+#[cfg(Py_3_14)] -+const _Py_STATICALLY_ALLOCATED_FLAG: c_int = 1 << 7; -+ -+#[cfg(all(Py_3_12, not(Py_3_14)))] -+const _Py_IMMORTAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ c_uint::MAX as Py_ssize_t -+ } else { -+ // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) -+ (c_uint::MAX >> 2) as Py_ssize_t -+ } -+}; -+ -+// comments in Python.h about the choices for these constants -+ -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ ((3 as c_ulong) << (30 as c_ulong)) as Py_ssize_t -+ } else { -+ ((5 as c_long) << (28 as c_long)) as Py_ssize_t -+ } -+}; -+ -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ _Py_IMMORTAL_INITIAL_REFCNT -+ | ((_Py_STATICALLY_ALLOCATED_FLAG as Py_ssize_t) << (32 as Py_ssize_t)) -+ } else { -+ ((7 as c_long) << (28 as c_long)) as Py_ssize_t -+ } -+}; -+ -+#[cfg(all(Py_3_14, target_pointer_width = "32"))] -+const _Py_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = ((1 as c_long) << (30 as c_long)) as Py_ssize_t; -+ -+#[cfg(all(Py_3_14, target_pointer_width = "32"))] -+const _Py_STATIC_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = -+ ((6 as c_long) << (28 as c_long)) as Py_ssize_t; -+ -+#[cfg(all(Py_3_14, Py_GIL_DISABLED))] -+const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = c_uint::MAX as Py_ssize_t; -+ -+#[cfg(Py_GIL_DISABLED)] -+pub(crate) const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; -+ -+#[cfg(Py_GIL_DISABLED)] -+const _Py_REF_SHARED_SHIFT: isize = 2; -+// skipped private _Py_REF_SHARED_FLAG_MASK -+ -+// skipped private _Py_REF_SHARED_INIT -+// skipped private _Py_REF_MAYBE_WEAKREF -+// skipped private _Py_REF_QUEUED -+// skipped private _Py_REF_MERGED -+ -+// skipped private _Py_REF_SHARED -+ -+extern "C" { -+ #[cfg(all(Py_3_14, Py_LIMITED_API))] -+ pub fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t; -+} -+ -+#[cfg(not(all(Py_3_14, Py_LIMITED_API)))] -+#[inline] -+pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { -+ #[cfg(Py_GIL_DISABLED)] -+ { -+ let local = (*ob).ob_ref_local.load(Relaxed); -+ if local == _Py_IMMORTAL_REFCNT_LOCAL { -+ #[cfg(not(Py_3_14))] -+ return _Py_IMMORTAL_REFCNT; -+ #[cfg(Py_3_14)] -+ return _Py_IMMORTAL_INITIAL_REFCNT; -+ } -+ let shared = (*ob).ob_ref_shared.load(Relaxed); -+ local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) -+ } -+ -+ #[cfg(all(Py_LIMITED_API, Py_3_14))] -+ { -+ Py_REFCNT(ob) -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(all(Py_LIMITED_API, Py_3_14)), Py_3_12))] -+ { -+ (*ob).ob_refcnt.ob_refcnt -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] -+ { -+ (*ob).ob_refcnt -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] -+ { -+ _Py_REFCNT(ob) -+ } -+} -+ -+#[cfg(Py_3_12)] -+#[inline(always)] -+unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { -+ #[cfg(all(target_pointer_width = "64", not(Py_GIL_DISABLED)))] -+ { -+ (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int -+ } -+ -+ #[cfg(all(target_pointer_width = "32", not(Py_GIL_DISABLED)))] -+ { -+ #[cfg(not(Py_3_14))] -+ { -+ ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int -+ } -+ -+ #[cfg(Py_3_14)] -+ { -+ ((*op).ob_refcnt.ob_refcnt >= _Py_IMMORTAL_MINIMUM_REFCNT) as c_int -+ } -+ } -+ -+ #[cfg(Py_GIL_DISABLED)] -+ { -+ ((*op).ob_ref_local.load(Relaxed) == _Py_IMMORTAL_REFCNT_LOCAL) as c_int -+ } -+} -+ -+// skipped _Py_IsStaticImmortal -+ -+// TODO: Py_SET_REFCNT -+ -+extern "C" { -+ #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); -+ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_INCREF_IncRefTotal(); -+ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_DECREF_DecRefTotal(); -+ -+ #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] -+ fn _Py_Dealloc(arg1: *mut PyObject); -+ -+ #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] -+ #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] -+ pub fn Py_IncRef(o: *mut PyObject); -+ #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] -+ #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] -+ pub fn Py_DecRef(o: *mut PyObject); -+ -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ fn _Py_IncRef(o: *mut PyObject); -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ fn _Py_DecRef(o: *mut PyObject); -+ -+ #[cfg(GraalPy)] -+ fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; -+} -+ -+#[inline(always)] -+pub unsafe fn Py_INCREF(op: *mut PyObject) { -+ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -+ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -+ #[cfg(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ py_sys_config = "Py_REF_DEBUG", -+ GraalPy -+ ))] -+ { -+ // _Py_IncRef was added to the ABI in 3.10; skips null checks -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ { -+ _Py_IncRef(op); -+ } -+ -+ #[cfg(any(not(Py_3_10), PyPy))] -+ { -+ Py_IncRef(op); -+ } -+ } -+ -+ // version-specific builds are allowed to directly manipulate the reference count -+ #[cfg(not(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ py_sys_config = "Py_REF_DEBUG", -+ GraalPy -+ )))] -+ { -+ #[cfg(all(Py_3_14, target_pointer_width = "64"))] -+ { -+ let cur_refcnt = (*op).ob_refcnt.ob_refcnt; -+ if (cur_refcnt as i32) < 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt = cur_refcnt.wrapping_add(1); -+ } -+ -+ #[cfg(all(Py_3_12, not(Py_3_14), target_pointer_width = "64"))] -+ { -+ let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; -+ let new_refcnt = cur_refcnt.wrapping_add(1); -+ if new_refcnt == 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; -+ } -+ -+ #[cfg(all(Py_3_12, target_pointer_width = "32"))] -+ { -+ if _Py_IsImmortal(op) != 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt += 1 -+ } -+ -+ #[cfg(not(Py_3_12))] -+ { -+ (*op).ob_refcnt += 1 -+ } -+ -+ // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue -+ // or submit a PR supporting Py_STATS build option and pystats.h -+ } -+} -+ -+// skipped _Py_DecRefShared -+// skipped _Py_DecRefSharedDebug -+// skipped _Py_MergeZeroLocalRefcount -+ -+#[inline(always)] -+#[cfg_attr( -+ all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), -+ track_caller -+)] -+pub unsafe fn Py_DECREF(op: *mut PyObject) { -+ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -+ // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts -+ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -+ #[cfg(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -+ GraalPy -+ ))] -+ { -+ // _Py_DecRef was added to the ABI in 3.10; skips null checks -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ { -+ _Py_DecRef(op); -+ } -+ -+ #[cfg(any(not(Py_3_10), PyPy))] -+ { -+ Py_DecRef(op); -+ } -+ } -+ -+ #[cfg(not(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -+ GraalPy -+ )))] -+ { -+ #[cfg(Py_3_12)] -+ if _Py_IsImmortal(op) != 0 { -+ return; -+ } -+ -+ // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue -+ // or submit a PR supporting Py_STATS build option and pystats.h -+ -+ #[cfg(py_sys_config = "Py_REF_DEBUG")] -+ _Py_DECREF_DecRefTotal(); -+ -+ #[cfg(Py_3_12)] -+ { -+ (*op).ob_refcnt.ob_refcnt -= 1; -+ -+ #[cfg(py_sys_config = "Py_REF_DEBUG")] -+ if (*op).ob_refcnt.ob_refcnt < 0 { -+ let location = std::panic::Location::caller(); -+ let filename = std::ffi::CString::new(location.file()).unwrap(); -+ _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); -+ } -+ -+ if (*op).ob_refcnt.ob_refcnt == 0 { -+ _Py_Dealloc(op); -+ } -+ } -+ -+ #[cfg(not(Py_3_12))] -+ { -+ (*op).ob_refcnt -= 1; -+ -+ if (*op).ob_refcnt == 0 { -+ _Py_Dealloc(op); -+ } -+ } -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { -+ let tmp = *op; -+ if !tmp.is_null() { -+ *op = ptr::null_mut(); -+ Py_DECREF(tmp); -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_XINCREF(op: *mut PyObject) { -+ if !op.is_null() { -+ Py_INCREF(op) -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_XDECREF(op: *mut PyObject) { -+ if !op.is_null() { -+ Py_DECREF(op) -+ } -+} -+ -+extern "C" { -+ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -+ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+ pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; -+ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -+ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+ pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; -+} -+ -+// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here -+// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here -+ -+#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] -+#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+#[inline] -+pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { -+ Py_INCREF(obj); -+ obj -+} -+ -+#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] -+#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+#[inline] -+pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { -+ Py_XINCREF(obj); -+ obj -+} -diff --git a/include/pyo3/pyo3-ffi/src/setobject.rs b/include/pyo3/pyo3-ffi/src/setobject.rs -index 9d5351fc..87e33e80 100644 ---- a/include/pyo3/pyo3-ffi/src/setobject.rs -+++ b/include/pyo3/pyo3-ffi/src/setobject.rs -@@ -39,11 +39,7 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { - (*so).used - } - --#[cfg(not(Py_LIMITED_API))] --#[cfg_attr(windows, link(name = "pythonXY"))] --extern "C" { -- pub static mut _PySet_Dummy: *mut PyObject; --} -+// skipped _PySet_Dummy - - extern "C" { - #[cfg(not(Py_LIMITED_API))] -diff --git a/include/pyo3/pyo3-ffi/src/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/weakrefobject.rs -index 305dc290..88a1bf90 100644 ---- a/include/pyo3/pyo3-ffi/src/weakrefobject.rs -+++ b/include/pyo3/pyo3-ffi/src/weakrefobject.rs -@@ -4,16 +4,18 @@ use std::os::raw::c_int; - use std::ptr::addr_of_mut; - - #[cfg(all(not(PyPy), Py_LIMITED_API, not(GraalPy)))] --opaque_struct!(PyWeakReference); -+opaque_struct!(pub PyWeakReference); - - #[cfg(all(not(PyPy), not(Py_LIMITED_API), not(GraalPy)))] - pub use crate::_PyWeakReference as PyWeakReference; - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -+ // TODO: PyO3 is depending on this symbol in `reference.rs`, we should change this and -+ // remove the export as this is a private symbol. - pub static mut _PyWeakref_RefType: PyTypeObject; -- pub static mut _PyWeakref_ProxyType: PyTypeObject; -- pub static mut _PyWeakref_CallableProxyType: PyTypeObject; -+ static mut _PyWeakref_ProxyType: PyTypeObject; -+ static mut _PyWeakref_CallableProxyType: PyTypeObject; - - #[cfg(PyPy)] - #[link_name = "PyPyWeakref_CheckRef"] - -From d2570d2f25eba771468f41020a2ad69e0cbdbbc7 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 17:00:34 +0200 -Subject: [PATCH 2/8] Add pyo3-ffi patch for _PyLong_AsByteArray with - exceptions - -Co-authored-by: ijl ---- - ...i-pylong-asbytearray-with-exceptions.patch | 23 +++++++++++++++++++ - 1 file changed, 23 insertions(+) - create mode 100644 include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch - -diff --git a/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch -new file mode 100644 -index 00000000..20e5d7a3 ---- /dev/null -+++ b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch -@@ -0,0 +1,23 @@ -+diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+index 45acaae..6063575 100644 -+--- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -++++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+@@ -61,6 +61,18 @@ extern "C" { -+ is_signed: c_int, -+ ) -> *mut PyObject; -+ -++ #[cfg(Py_3_13)] -++ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -++ pub fn _PyLong_AsByteArray( -++ v: *mut PyLongObject, -++ bytes: *mut c_uchar, -++ n: size_t, -++ little_endian: c_int, -++ is_signed: c_int, -++ with_exceptions: c_int, -++ ) -> c_int; -++ -++ #[cfg(not(Py_3_13))] -+ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -+ pub fn _PyLong_AsByteArray( -+ v: *mut PyLongObject, - -From d01aea4541800dca412e3f4c3068e974f18c3218 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 18:26:19 +0200 -Subject: [PATCH 3/8] Apply pyo3-ffi patch - ---- - include/pyo3/pyo3-ffi/src/cpython/longobject.rs | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -index 45acaae5..6063575e 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -@@ -61,6 +61,18 @@ extern "C" { - is_signed: c_int, - ) -> *mut PyObject; - -+ #[cfg(Py_3_13)] -+ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -+ pub fn _PyLong_AsByteArray( -+ v: *mut PyLongObject, -+ bytes: *mut c_uchar, -+ n: size_t, -+ little_endian: c_int, -+ is_signed: c_int, -+ with_exceptions: c_int, -+ ) -> c_int; -+ -+ #[cfg(not(Py_3_13))] - #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] - pub fn _PyLong_AsByteArray( - v: *mut PyLongObject, - -From 6427dba13406b499bc2edc033c0eeeaafdfa4ad4 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:16:24 +0200 -Subject: [PATCH 4/8] Fix _PyDict_SetItem_KnownHash_LockHeld - ---- - src/util.rs | 10 ---------- - 1 file changed, 10 deletions(-) - -diff --git a/src/util.rs b/src/util.rs -index ccf425ce..0a255c3f 100644 ---- a/src/util.rs -+++ b/src/util.rs -@@ -258,19 +258,9 @@ macro_rules! pydict_setitem { - ($dict:expr, $pykey:expr, $pyval:expr) => { - debug_assert!(ffi!(Py_REFCNT($dict)) == 1); - debug_assert!(str_hash!($pykey) != -1); -- #[cfg(not(Py_3_13))] - unsafe { - let _ = pyo3_ffi::_PyDict_SetItem_KnownHash($dict, $pykey, $pyval, str_hash!($pykey)); - } -- #[cfg(Py_3_13)] -- unsafe { -- let _ = pyo3_ffi::_PyDict_SetItem_KnownHash_LockHeld( -- $dict.cast::(), -- $pykey, -- $pyval, -- str_hash!($pykey), -- ); -- } - reverse_pydict_incref!($pykey); - reverse_pydict_incref!($pyval); - }; - -From 80779519c9363a81884e77358e62fb443a584039 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:16:40 +0200 -Subject: [PATCH 5/8] Fix _Py_IsImmortal - ---- - src/util.rs | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/src/util.rs b/src/util.rs -index 0a255c3f..bbfb72ed 100644 ---- a/src/util.rs -+++ b/src/util.rs -@@ -120,10 +120,8 @@ macro_rules! str_from_slice { - macro_rules! reverse_pydict_incref { - ($op:expr) => { - unsafe { -- if pyo3_ffi::_Py_IsImmortal($op) == 0 { -- debug_assert!(ffi!(Py_REFCNT($op)) >= 2); -- (*$op).ob_refcnt.ob_refcnt -= 1; -- } -+ debug_assert!(ffi!(Py_REFCNT($op)) >= 2); -+ (*$op).ob_refcnt.ob_refcnt -= 1; - } - }; - } - -From b90995f44f80176d403b720d706dfe5a027855ff Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 17:12:44 +0200 -Subject: [PATCH 6/8] Use PyUnicode API - ---- - src/serialize/per_type/dict.rs | 4 ++-- - src/serialize/per_type/unicode.rs | 4 ++-- - src/str/ffi.rs | 29 +++++++++++++++-------------- - src/str/pyunicode_new.rs | 3 +++ - 4 files changed, 22 insertions(+), 18 deletions(-) - -diff --git a/src/serialize/per_type/dict.rs b/src/serialize/per_type/dict.rs -index e2e04f62..cf2c3a4b 100644 ---- a/src/serialize/per_type/dict.rs -+++ b/src/serialize/per_type/dict.rs -@@ -13,7 +13,7 @@ use crate::serialize::per_type::{ - }; - use crate::serialize::serializer::PyObjectSerializer; - use crate::serialize::state::SerializerState; --use crate::str::{unicode_to_str, unicode_to_str_via_ffi}; -+use crate::str::unicode_to_str; - use crate::typeref::{STR_TYPE, TRUE, VALUE_STR}; - use crate::util::isize_to_usize; - use compact_str::CompactString; -@@ -321,7 +321,7 @@ fn non_str_str(key: *mut pyo3_ffi::PyObject) -> Result Result { -- let uni = unicode_to_str_via_ffi(key); -+ let uni = unicode_to_str(key); - if unlikely!(uni.is_none()) { - Err(SerializeError::InvalidStr) - } else { -diff --git a/src/serialize/per_type/unicode.rs b/src/serialize/per_type/unicode.rs -index 23270916..7267b454 100644 ---- a/src/serialize/per_type/unicode.rs -+++ b/src/serialize/per_type/unicode.rs -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: (Apache-2.0 OR MIT) - - use crate::serialize::error::SerializeError; --use crate::str::{unicode_to_str, unicode_to_str_via_ffi}; -+use crate::str::unicode_to_str; - - use serde::ser::{Serialize, Serializer}; - -@@ -50,7 +50,7 @@ impl Serialize for StrSubclassSerializer { - where - S: Serializer, - { -- let uni = unicode_to_str_via_ffi(self.ptr); -+ let uni = unicode_to_str(self.ptr); - if unlikely!(uni.is_none()) { - err!(SerializeError::InvalidStr) - } -diff --git a/src/str/ffi.rs b/src/str/ffi.rs -index 4f1c76bd..6b57e7ae 100644 ---- a/src/str/ffi.rs -+++ b/src/str/ffi.rs -@@ -1,25 +1,19 @@ - // SPDX-License-Identifier: (Apache-2.0 OR MIT) - - use crate::util::isize_to_usize; --use core::ffi::c_void; --use pyo3_ffi::{PyASCIIObject, PyCompactUnicodeObject, PyObject, Py_hash_t}; -+#[cfg(not(Py_3_14))] -+use pyo3_ffi::PyCompactUnicodeObject; -+use pyo3_ffi::{ -+ PyASCIIObject, PyObject, PyUnicode_DATA, PyUnicode_GET_LENGTH, PyUnicode_KIND, Py_hash_t, -+}; - - // see unicodeobject.h for documentation - - #[inline] - pub fn hash_str(op: *mut PyObject) -> Py_hash_t { - unsafe { -- let data_ptr: *mut c_void = if (*op.cast::()).compact() == 1 -- && (*op.cast::()).ascii() == 1 -- { -- op.cast::().offset(1).cast::() -- } else { -- op.cast::() -- .offset(1) -- .cast::() -- }; -- let num_bytes = -- (*op.cast::()).length * ((*op.cast::()).kind()) as isize; -+ let data_ptr = PyUnicode_DATA(op); -+ let num_bytes = PyUnicode_GET_LENGTH(op) * PyUnicode_KIND(op) as isize; - #[cfg(Py_3_14)] - let hash = pyo3_ffi::Py_HashBuffer(data_ptr, num_bytes); - #[cfg(not(Py_3_14))] -@@ -30,7 +24,7 @@ pub fn hash_str(op: *mut PyObject) -> Py_hash_t { - } - - #[inline(never)] --pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { -+fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { - let mut str_size: pyo3_ffi::Py_ssize_t = 0; - let ptr = ffi!(PyUnicode_AsUTF8AndSize(op, &mut str_size)).cast::(); - if unlikely!(ptr.is_null()) { -@@ -40,6 +34,7 @@ pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { - } - } - -+#[cfg(not(Py_3_14))] - #[inline] - pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { - unsafe { -@@ -58,3 +53,9 @@ pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { - } - } - } -+ -+#[cfg(Py_3_14)] -+#[inline] -+pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { -+ unicode_to_str_via_ffi(op) -+} -diff --git a/src/str/pyunicode_new.rs b/src/str/pyunicode_new.rs -index 7b1c2df2..738b8489 100644 ---- a/src/str/pyunicode_new.rs -+++ b/src/str/pyunicode_new.rs -@@ -8,7 +8,9 @@ macro_rules! validate_str { - #[cfg(not(Py_3_12))] - debug_assert!((*($ptr.cast::())).ready() == 1); - -+ #[cfg(not(Py_3_14))] - debug_assert!((*($ptr.cast::())).compact() == 1); -+ #[cfg(not(Py_3_14))] - debug_assert!((*($ptr.cast::())).interned() == 0); - - debug_assert!(ffi!(_PyUnicode_CheckConsistency($ptr.cast::(), 1)) == 1); -@@ -22,6 +24,7 @@ pub fn pyunicode_ascii(buf: *const u8, num_chars: usize) -> *mut pyo3_ffi::PyObj - let data_ptr = ptr.cast::().offset(1).cast::(); - core::ptr::copy_nonoverlapping(buf, data_ptr, num_chars); - core::ptr::write(data_ptr.add(num_chars), 0); -+ #[cfg(not(Py_3_14))] - debug_assert!((*(ptr.cast::())).ascii() == 1); - validate_str!(ptr); - ptr.cast::() - -From 4eb6604607891c0f6aa00bbbb07ccfdcfd85b589 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Thu, 5 Jun 2025 13:52:28 +0200 -Subject: [PATCH 7/8] Add pyo3-ffi patch for PyObjectObRefcnt.refcnt_and_flags - -Co-authored-by: Benjamin A. Beasley ---- - include/pyo3-ffi-pyobjectrefcnt-64only.patch | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - create mode 100644 include/pyo3-ffi-pyobjectrefcnt-64only.patch - -diff --git a/include/pyo3-ffi-pyobjectrefcnt-64only.patch b/include/pyo3-ffi-pyobjectrefcnt-64only.patch -new file mode 100644 -index 00000000..dfc328a7 ---- /dev/null -+++ b/include/pyo3-ffi-pyobjectrefcnt-64only.patch -@@ -0,0 +1,13 @@ -+diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -+index c4c5abc361c..6d5dc781287 100644 -+--- a/include/pyo3/pyo3-ffi/src/object.rs -++++ b/include/pyo3/pyo3-ffi/src/object.rs -+@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { -+ pub union PyObjectObRefcnt { -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub ob_refcnt_full: crate::PY_INT64_T, -+- #[cfg(Py_3_14)] -++ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, -+ pub ob_refcnt: Py_ssize_t, -+ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] - -From ea5a7d83f4ff5262f131d37dd1d490a634ce2626 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Thu, 5 Jun 2025 13:54:27 +0200 -Subject: [PATCH 8/8] Apply second pyo3-ffi patch - ---- - include/pyo3/pyo3-ffi/src/object.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -index 5fbf45db..aeb97352 100644 ---- a/include/pyo3/pyo3-ffi/src/object.rs -+++ b/include/pyo3/pyo3-ffi/src/object.rs -@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { - pub union PyObjectObRefcnt { - #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub ob_refcnt_full: crate::PY_INT64_T, -- #[cfg(Py_3_14)] -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, - pub ob_refcnt: Py_ssize_t, - #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] diff --git a/python-orjson.spec b/python-orjson.spec index ab35204..22c3de9 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,17 +9,120 @@ # Not yet in EPEL10: https://bugzilla.redhat.com/show_bug.cgi?id=2356387 %bcond pendulum %{undefined el10} +# By default, orjson uses a bundled copy of the C library yyjson, +# https://github.com/ibireme/yyjson, as the JSON deserialization backend. It is +# forked (customized) and compiled with a particular set of options via +# preprocessor defines (see build.rs), so it is not a candidate for unbundling. +# +# We do have the option to disable the yyjson backend, in which case the json +# crate from the Rust standard library, https://docs.rs/json, is used instead. +%bcond yyjson 0 + Name: python-orjson -Version: 3.10.18 +Version: 3.11.0 Release: %autorelease Summary: Fast, correct Python JSON library -License: Apache-2.0 OR MIT +# The entire source is (Apache-2.0 OR MIT), except: +# +%if %{with yyjson} +# MIT: +# - include/yyjson/yyjson.c +# - include/yyjson/yyjson.h +# +%endif +# Apache-2.0: +# - src/serialize/writer/str/mod.rs +# - src/serialize/writer/str/sse2.rs +License: (Apache-2.0 OR MIT) AND Apache-2.0%{?with_yyjson: AND MIT} +# Additionally, the following are removed in %%prep and do not contribute to +# the licenses of the binary RPMs: +# +# Apache-2.0 WITH LLVM-exception: +# - include/cargo/target-lexicon-*/ +# +# BSL-1.0: +# - include/cargo/xxhash-rust-*/ +# +# MIT: +%if %{without yyjson} +# - include/yyjson/yyjson.c +# - include/yyjson/yyjson.h +%endif +# - include/cargo/crunchy-*/ +# - include/cargo/itoap-*/ +# +# MIT OR Apache-2.0: +# - include/cargo/associative-cache-*/ +# - include/cargo/bytecount-*/ +# - include/cargo/cc-*/ +# - include/cargo/cfg-if-*/ +# - include/cargo/gimli-*/ +# - include/cargo/half-*/ +# - include/cargo/itoa-*/ +# - include/cargo/libc-*/ +# - include/cargo/once_cell-*/ +# - include/cargo/portable-atomic-*/ +# - include/cargo/portable-atomic-util-*/ +# - include/cargo/proc-macro2-*/ +# - include/cargo/pyo3-build-config-*/ +# - include/cargo/pyo3-ffi-*/ +# - include/cargo/quote-*/ +# - include/cargo/serde-*/ +# - include/cargo/serde_derive-*/ +# - include/cargo/serde_json-*/ +# - include/cargo/shlex-*/ +# - include/cargo/simdutf8-*/ +# - include/cargo/smallvec-*/ +# - include/cargo/syn-*/ +# - include/cargo/unwinding-*/ +# - include/cargo/uuid-*/ +# - include/cargo/version_check-*/ +# +# Apache-2.0 OR BSL-1.0: +# - include/cargo/ryu-*/ +# +# (Apache-2.0 OR MIT) AND BSD-3-Clause: +# - include/cargo/encoding_rs-*/ +# +# (MIT OR Apache-2.0) AND Unicode-3.0: +# - include/cargo/unicode-ident-*/ +# +# Unlicense OR MIT: +# - include/cargo/jiff-*/ +# - include/cargo/jiff-static-*/ +# - include/cargo/memchr-*/ +SourceLicense: %{shrink: + (Apache-2.0 OR MIT) AND + Apache-2.0 AND + Apache-2.0 WITH LLVM-exception AND + BSD-3-Clause AND + BSL-1.0 AND + MIT AND + Unicode-3.0 AND + (Apache-2.0 OR BSL-1.0) + (Unlicense OR MIT) + } URL: https://github.com/ijl/orjson +# We must be careful about the source archive. +# +# The PyPI releases have a vendored Rust dependency bundle in include/cargo/, +# which we would remove in %%prep, but which we must still check to make sure +# everything has a license acceptable for distribution in Fedora before +# uploading to the lookaside cache. Source: %{pypi_source orjson} -# Support Python 3.14 -# Updates bundled/forked pyo3-build-config/pyo3-ffi to 0.25.0 -Patch: https://github.com/ijl/orjson/pull/570.patch +# The GitHub archives from +# %%{url}/archive/%%{version}/orjson-%%{version}.tar.gz do not have the +# vendored crates, but they contain benchmark data in data/, some of which is +# lacking its license text (e.g. data/blns.txt.xz, which is from +# https://github.com/minimaxir/big-list-of-naughty-strings and should carry the +# corresponding MIT license text), and some of which looks like it might have +# at best unclear license status. Since the benchmark data is potentially +# problematic, we would need to filter the GitHub archives with a script; this +# hasn’t been written yet, but could be similar to the one in python-cramjam. +# Source0: orjson-%%{version}-filtered.tar.gz +# ./get_source ${COMMIT} (or ${TAG}) +# Source1: get_source BuildRequires: tomcli BuildRequires: python3-devel @@ -48,34 +151,31 @@ datetimes, and numpy} %package -n python3-orjson Summary: %{summary} +# Output of %%{cargo_license_summary}: +# # (Apache-2.0 OR MIT) AND BSD-3-Clause # Apache-2.0 OR BSL-1.0 # Apache-2.0 OR MIT # BSL-1.0 # MIT -# MIT OR Apache-2.0 (duplicate) +# MIT OR Apache-2.0 # Unlicense OR MIT # -# Bundled PyO3 crates in include/pyo3/ are also (Apache-2.0 OR MIT). +# Note that this must include the terms of the base package License expression. License: %{shrink: (Apache-2.0 OR MIT) AND - BSD-3-Clause AND + Apache-2.0 AND (Apache-2.0 OR BSL-1.0) AND + BSD-3-Clause AND BSL-1.0 AND MIT AND (Unlicense OR MIT) } -# Path to using published versions of pyo3-ffi/pyo3-build-config again? -# https://github.com/ijl/orjson/issues/524 -# -# “You are welcome to work to upstream the diff if you find the vendoring -# unsuitable for your organization's own preferences.” -# -# Note that these crates are actually forked, not only bundled/vendored; see -# https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for details. -Provides: bundled(crate(pyo3-build-config)) = 0.25.0 -Provides: bundled(crate(pyo3-ffi)) = 0.25.0 +%if %{with yyjson} +# Version from YYJSON_VERSION_STRING in include/yyjson/yyjson.h +Provides: bundled(yyjson) = 0.9.0 +%endif %description -n python3-orjson %{_description} @@ -90,12 +190,10 @@ tomcli-set Cargo.toml del 'features.unwind' tomcli-set Cargo.toml del 'dependencies.unwinding' # Remove bundled rust crates rm -r include/cargo +%if %{without yyjson} # Remove bundled yyjson. rm -rv include/yyjson/ - -# Collect licenses for remaining vendored crates -mkdir -p LICENSES.vendored/pyo3 -cp -vp include/pyo3/LICENSE* LICENSES.vendored/pyo3/ +%endif %if %{without pendulum} sed -i '/^pendulum\b/d' test/requirements.txt @@ -110,23 +208,15 @@ sed -i '/pytest-random-order/d' test/requirements.txt %generate_buildrequires %pyproject_buildrequires %{?with_tests:test/requirements.txt} %cargo_generate_buildrequires -for dir in include/pyo3/*/ -do - pushd "${dir}" >/dev/null - %cargo_generate_buildrequires - popd >/dev/null -done %build export RUSTFLAGS='%{build_rustflags}' %cargo_license_summary %{cargo_license} > LICENSES.dependencies -# Fedora's pyo3 is patched to not check Python version when building RPM packages. -# However, this uses a bundled version without the patch. -# Rather than patching it, we set the environment variable, -# which allows us to test this package with development Python versions. -export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 +%if %{without yyjson} +export ORJSON_DISABLE_YYJSON=1 +%endif %pyproject_wheel @@ -145,7 +235,6 @@ export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 %files -n python3-orjson -f %{pyproject_files} -%license LICENSES.vendored/ %doc README.md diff --git a/sources b/sources index 73cee52..0704996 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.18.tar.gz) = 248a42f671e713bf9d11163fe7744f5d9b626f5db681913071957723f48d77cc35aa883eb695a16df76986ecc8d9e9e493cee4431c47103beabc3517ffcaa65f +SHA512 (orjson-3.11.0.tar.gz) = be823592bc5fb28b1d5e90c412c9ec32b5953db27e5dcc088e43e2369dad28586603d0a3e450f72b5761a819bcd68a1305dac60219e5719b24e0a410c6e22af2