python-orjson/570.patch
2025-05-26 07:12:21 -04:00

3802 lines
129 KiB
Diff

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/6] 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 <https://github.com/PyO3>"]
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<String> {
/// the variable changes.
pub fn env_var(var: &str) -> Option<OsString> {
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<PythonVersion> {
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<Path>) -> Result<Sysco
})?;
script += r#"
for key, val in build_time_vars.items():
+ # (ana)conda(-forge) built Pythons are statically linked but ship the shared library with them.
+ # We detect them based on the magic prefix directory they have encoded in their builds.
+ if key == "Py_ENABLE_SHARED" and "_h_env_placehold" in build_time_vars.get("prefix"):
+ val = 1
print(key, val)
"#;
@@ -1424,7 +1427,7 @@ pub fn find_all_sysconfigdata(cross: &CrossCompileConfig) -> Result<Vec<PathBuf>
fn is_pypy_lib_dir(path: &str, v: &Option<PythonVersion>) -> 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<PythonVersion>) -> bool {
fn is_graalpy_lib_dir(path: &str, v: &Option<PythonVersion>) -> 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<PythonVersion>) -> bool {
fn is_cpython_lib_dir(path: &str, v: &Option<PythonVersion>) -> 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<String> {
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<u8> {
}
}
- 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<PathBuf> {
})
}
+/// 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 <https://github.com/PyO3>"]
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::<size_t>() 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::<size_t>() 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<crate::vectorcallfunc>,
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<extern "C" fn(arg1: *const c_void, arg2: crate::Py_ssize_t) -> 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: Storage,
}
+#[cfg(not(Py_3_14))]
impl<Storage> BitfieldUnit<Storage> {
#[inline]
pub const fn new(storage: Storage) -> Self {
@@ -43,7 +45,7 @@ impl<Storage> BitfieldUnit<Storage> {
}
}
-#[cfg(not(GraalPy))]
+#[cfg(not(any(GraalPy, Py_3_14)))]
impl<Storage> BitfieldUnit<Storage>
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<u32> for PyASCIIObjectState {
#[inline]
fn from(value: u32) -> Self {
@@ -243,6 +269,7 @@ impl From<u32> for PyASCIIObjectState {
}
}
+#[cfg(not(Py_3_14))]
impl From<PyASCIIObjectState> for u32 {
#[inline]
fn from(value: PyASCIIObjectState) -> Self {
@@ -254,26 +281,36 @@ impl From<PyASCIIObjectState> 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<extern "C" fn(arg1: *const c_void, arg2: Py_ssize_t) -> 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/6] Add pyo3-ffi patch for _PyLong_AsByteArray with
exceptions
Co-authored-by: ijl <ijl@mailbox.org>
---
...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/6] 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/6] 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::<pyo3_ffi::PyDictObject>(),
- $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/6] 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/6] 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<CompactString, SerializeE
#[cold]
#[inline(never)]
fn non_str_str_subclass(key: *mut pyo3_ffi::PyObject) -> Result<CompactString, SerializeError> {
- 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::<PyASCIIObject>()).compact() == 1
- && (*op.cast::<PyASCIIObject>()).ascii() == 1
- {
- op.cast::<PyASCIIObject>().offset(1).cast::<c_void>()
- } else {
- op.cast::<PyCompactUnicodeObject>()
- .offset(1)
- .cast::<c_void>()
- };
- let num_bytes =
- (*op.cast::<PyASCIIObject>()).length * ((*op.cast::<PyASCIIObject>()).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::<u8>();
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::<PyASCIIObject>())).ready() == 1);
+ #[cfg(not(Py_3_14))]
debug_assert!((*($ptr.cast::<PyASCIIObject>())).compact() == 1);
+ #[cfg(not(Py_3_14))]
debug_assert!((*($ptr.cast::<PyASCIIObject>())).interned() == 0);
debug_assert!(ffi!(_PyUnicode_CheckConsistency($ptr.cast::<PyObject>(), 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::<PyASCIIObject>().offset(1).cast::<u8>();
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::<PyASCIIObject>())).ascii() == 1);
validate_str!(ptr);
ptr.cast::<PyObject>()