From c5fbacd3aac10087ae8f81aae653f8f4a0844516 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Tue, 27 Jun 2023 22:49:01 +0200 Subject: [PATCH 01/55] Rebuilt for Python 3.12 --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 6f0cc3b..26a56f7 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -10,7 +10,7 @@ Name: python-orjson Version: 3.8.12 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -108,6 +108,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Tue Jun 27 2023 Python Maint - 3.8.12-2 +- Rebuilt for Python 3.12 + * Thu May 18 2023 Maxwell G - 3.8.12-1 - Update to 3.8.12. - Use maturin as a build system when available From 94e5ae7e88d0b323230246de5f9d3c22f41de56e Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 21 Jul 2023 11:59:11 +0000 Subject: [PATCH 02/55] Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild Signed-off-by: Fedora Release Engineering --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 26a56f7..32865d1 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -10,7 +10,7 @@ Name: python-orjson Version: 3.8.12 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -108,6 +108,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri Jul 21 2023 Fedora Release Engineering - 3.8.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + * Tue Jun 27 2023 Python Maint - 3.8.12-2 - Rebuilt for Python 3.12 From 504e5ac66c3a1603812327d5c98e133d6fac3499 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 4 Jul 2023 15:01:20 -0500 Subject: [PATCH 03/55] Update to 3.8.14. --- .gitignore | 1 + python-orjson.spec | 7 +++++-- sources | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index a09bd3f..f2d86cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /orjson-3.8.10.tar.gz /orjson-3.8.11.tar.gz /orjson-3.8.12.tar.gz +/orjson-3.8.14.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index 32865d1..cc37b12 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,8 +9,8 @@ %bcond maturin %[ 0%{?fedora} >= 39 ] Name: python-orjson -Version: 3.8.12 -Release: 3%{?dist} +Version: 3.8.14 +Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -108,6 +108,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri Jul 21 2023 Maxwell G - 3.8.14-1 +- Update to 3.8.14. + * Fri Jul 21 2023 Fedora Release Engineering - 3.8.12-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild diff --git a/sources b/sources index b09abb7..f28ad5c 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.8.12.tar.gz) = c2a6a106721e5c84d52ccc255b60fb3da67d442604c5dd5e2dea63a6d52eece87fd3d1d81a2691c1f93b4c21a0f608a8c1ea6b19d8ba4995584a562780ff39e9 +SHA512 (orjson-3.8.14.tar.gz) = 6c737396f0b59c6c519fa38c401edad01e132c3031b346b1982a97a965a5f5a64fe28eaee51fc797d04998ac4016383bd79067d670ad3ed022cd2ab03bfb6b05 From 0ceb97925f49f4d2724dcc90bfb221e3fc68d0f3 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 4 Jul 2023 19:51:05 +0000 Subject: [PATCH 04/55] Remove setuptools_rust patch maturin is now packaged for all stable Fedora releases. --- Use-setuptools-rust-instead-of-maturin.patch | 81 -------------------- python-orjson.spec | 9 +-- 2 files changed, 1 insertion(+), 89 deletions(-) delete mode 100644 Use-setuptools-rust-instead-of-maturin.patch diff --git a/Use-setuptools-rust-instead-of-maturin.patch b/Use-setuptools-rust-instead-of-maturin.patch deleted file mode 100644 index 587982c..0000000 --- a/Use-setuptools-rust-instead-of-maturin.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 37abb055e3435406156c6e4462b2a58923c9f11a Mon Sep 17 00:00:00 2001 -From: Maxwell G -Date: Wed, 25 Jan 2023 13:47:35 -0600 -Subject: [PATCH] Use setuptools-rust instead of maturin - -Maturin is not packaged for Fedora. We can use setuptools-rust instead. - -This requires setutpools >= 61.0 which is available on F37+. The -metadata in the [project] table can be translated to a setup.cfg or -added to setup.py for greater compatibility. ---- - pyproject.toml | 9 ++++++--- - setup.py | 28 ++++++++++++++++++++++++++++ - 2 files changed, 34 insertions(+), 3 deletions(-) - create mode 100644 setup.py - -diff --git a/pyproject.toml b/pyproject.toml -index 8372fc8..1e48b88 100644 ---- a/pyproject.toml -+++ b/pyproject.toml -@@ -1,6 +1,5 @@ - [project] - name = "orjson" --repository = "https://github.com/ijl/orjson" - requires-python = ">=3.7" - classifiers = [ - "Development Status :: 5 - Production/Stable", -@@ -22,10 +21,14 @@ classifiers = [ - "Programming Language :: Rust", - "Typing :: Typed", - ] -+dynamic = [ -+ # version is dynamically defined in setup.py -+ "version", -+] - - [build-system] --build-backend = "maturin" --requires = ["maturin>=0.13,<2"] -+build-backend = "setuptools.build_meta" -+requires = ["setuptools>=61.0", "setuptools-rust", "tomli; python_version<'3.11'"] - - [tool.maturin] - python-source = "pysrc" -diff --git a/setup.py b/setup.py -new file mode 100644 -index 0000000..63f263d ---- /dev/null -+++ b/setup.py -@@ -0,0 +1,28 @@ -+import sys -+from pathlib import Path -+ -+if sys.version_info[:2] >= (3, 11): -+ import tomllib -+else: -+ import tomli as tomllib -+ -+from setuptools import setup -+from setuptools_rust import Binding, RustExtension -+ -+HERE = Path(__file__).resolve().parent -+ -+with (HERE / "Cargo.toml").open("rb") as fp: -+ DATA = tomllib.load(fp)["package"] -+ -+setup( -+ # Can be removed once we no longer need a minimal build for setuptools < 61. -+ # This is duplicated in pyproject.toml's [project] table -+ name="orjson", -+ packages=["orjson"], -+ # -+ rust_extensions=[ -+ RustExtension("orjson.orjson", binding=Binding.NoBinding, args=["--offline"]) -+ ], -+ package_dir={"": "pysrc"}, -+ version=DATA["version"], -+) --- -2.40.1 - diff --git a/python-orjson.spec b/python-orjson.spec index cc37b12..212005c 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -6,7 +6,6 @@ # Specfile compatability: EPEL >= 9 or Fedora >= 37 and RPM >= 4.16 %bcond tests 1 -%bcond maturin %[ 0%{?fedora} >= 39 ] Name: python-orjson Version: 3.8.14 @@ -19,10 +18,6 @@ Source: %{pypi_source orjson} # Ineligble for upstreaming Patch: Remove-unstable-simd-feature.patch -# Ineligble for upstreaming -# Used when maturin bcond is disabled -Patch5000: Use-setuptools-rust-instead-of-maturin.patch - BuildRequires: python3-devel BuildRequires: rust-packaging @@ -68,9 +63,7 @@ License: (Apache-2.0 OR MIT) AND (Apache-2.0 OR BSL-1.0) AND BSD-3-Clause %prep -%autosetup -p1 -n orjson-%{version} -N -%autopatch -M 4999 -%{!?with_maturin:%autopatch -m5000 -M5000 -p1} +%autosetup -p1 -n orjson-%{version} # Remove bundled yyjson. rm -rv include/yyjson/ From 88820338088a40042474187f8c7e81f6592b1d7e Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 4 Jul 2023 19:53:48 +0000 Subject: [PATCH 05/55] Use pytest-forked to run tests --- python-orjson.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 212005c..f87259e 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -19,6 +19,7 @@ Source: %{pypi_source orjson} Patch: Remove-unstable-simd-feature.patch BuildRequires: python3-devel +BuildRequires: %{py3_dist pytest-forked} BuildRequires: rust-packaging @@ -91,7 +92,8 @@ export RUSTFLAGS='%{build_rustflags}' %check %pyproject_check_import %if %{with tests} -%pytest -vv +# --forked: protect the pytest process against test segfaults +%pytest --forked %endif From d44e992892225d16cbc104b631aa6a728d69479a Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 4 Jul 2023 20:10:40 +0000 Subject: [PATCH 06/55] Update license summary Use the %{shrink:} macro to make the whole mess more readable. Use the short license summary instead of pasting all of LICENSE.dependencies. --- python-orjson.spec | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/python-orjson.spec b/python-orjson.spec index f87259e..667336f 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -32,32 +32,18 @@ datetimes, and numpy} %package -n python3-orjson Summary: %{summary} -# Apache-2.0 OR MIT: orjson itself -# (Apache-2.0 OR MIT) AND BSD-3-Clause: encoding_rs v0.8.32 -# Apache-2.0 OR BSL-1.0: ryu v1.0.13 -# Apache-2.0 OR MIT: bytecount v0.6.3 -# Apache-2.0 OR MIT: orjson v3.8.12 -# Apache-2.0: pyo3-ffi v0.18.3 -# MIT OR Apache-2.0: ahash v0.8.3 -# MIT OR Apache-2.0: arrayvec v0.7.2 -# MIT OR Apache-2.0: associative-cache v1.0.1 -# MIT OR Apache-2.0: beef v0.5.2 -# MIT OR Apache-2.0: cfg-if v1.0.0 -# MIT OR Apache-2.0: chrono v0.4.24 -# MIT OR Apache-2.0: itoa v1.0.6 -# MIT OR Apache-2.0: libc v0.2.144 -# MIT OR Apache-2.0: num-integer v0.1.45 -# MIT OR Apache-2.0: num-traits v0.2.15 -# MIT OR Apache-2.0: once_cell v1.17.1 -# MIT OR Apache-2.0: serde v1.0.162 -# MIT OR Apache-2.0: serde_json v1.0.96 -# MIT OR Apache-2.0: simdutf8 v0.1.4 -# MIT OR Apache-2.0: smallvec v1.10.0 -# MIT OR Apache-2.0: static_assertions v1.1.0 -# MIT: castaway v0.2.2 -# MIT: compact_str v0.7.0 -# MIT: itoap v1.0.1 -License: (Apache-2.0 OR MIT) AND (Apache-2.0 OR BSL-1.0) AND BSD-3-Clause AND Apache-2.0 +# (Apache-2.0 OR MIT) AND BSD-3-Clause +# Apache-2.0 +# Apache-2.0 OR BSL-1.0 +# Apache-2.0 OR MIT +# MIT +License: %{shrink: + (Apache-2.0 OR MIT) AND + BSD-3-Clause AND + Apache-2.0 AND + (Apache-2.0 OR BSL-1.0) AND + MIT + } %description -n python3-orjson %{_description} From c71bb9cc5f20a58031104e0415055fdf1315e4e9 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Fri, 7 Jul 2023 11:24:03 -0500 Subject: [PATCH 07/55] Update to 3.9.2. Fixes rhbz#2211703. --- .gitignore | 1 + python-orjson.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index f2d86cb..68fe46f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /orjson-3.8.11.tar.gz /orjson-3.8.12.tar.gz /orjson-3.8.14.tar.gz +/orjson-3.9.2.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index 667336f..12d39a2 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.8.14 +Version: 3.9.2 Release: 1%{?dist} Summary: Fast, correct Python JSON library @@ -89,6 +89,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri Jul 21 2023 Maxwell G - 3.9.2-1 +- Update to 3.9.2. Fixes rhbz#2211703. + * Fri Jul 21 2023 Maxwell G - 3.8.14-1 - Update to 3.8.14. diff --git a/sources b/sources index f28ad5c..ce12853 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.8.14.tar.gz) = 6c737396f0b59c6c519fa38c401edad01e132c3031b346b1982a97a965a5f5a64fe28eaee51fc797d04998ac4016383bd79067d670ad3ed022cd2ab03bfb6b05 +SHA512 (orjson-3.9.2.tar.gz) = 5ac8713c8f921f876e1c6d61daacde5d5b7a738d00f1a0c3f51df2fcb10815de0d73cc980bca43139ddd7fbfca5e800a6d31a35cc833ddb1fac7cdb181a1116f From 787b7fcbf4e698b5c347c4b2c911768df88cd166 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 11 Jul 2023 00:00:55 +0000 Subject: [PATCH 08/55] Remove bundled rust crates %cargo_prep should override the vendoring config, but better safe than sorry. --- python-orjson.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index 12d39a2..a9b899b 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -52,6 +52,8 @@ License: %{shrink: %prep %autosetup -p1 -n orjson-%{version} +# Remove bundled rust crates +rm -r include/cargo .cargo/config.toml # Remove bundled yyjson. rm -rv include/yyjson/ From 8c9ea3fe0696af686a3ba07d2c4b05afae553630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hrn=C4=8Diar?= Date: Tue, 25 Jul 2023 09:56:00 +0200 Subject: [PATCH 09/55] Backport patch to add PyType_GetDict for Python 3.12 --- 408.patch | 56 ++++++++++++++++++++++++++++++++++++++++++++++ python-orjson.spec | 10 ++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 408.patch diff --git a/408.patch b/408.patch new file mode 100644 index 0000000..1ff0d8f --- /dev/null +++ b/408.patch @@ -0,0 +1,56 @@ +From c0d62d041371970297c31ac86465cf8983386191 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Sun, 23 Jul 2023 17:15:18 +0200 +Subject: [PATCH] Use PyType_GetDict to safely access tp_dict [3.12] + +--- + src/util.rs | 15 ++++++++++++++- + test/test_default.py | 12 ++++++++++++ + 2 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/src/util.rs b/src/util.rs +index 3b451bd5..ba011c1a 100644 +--- a/src/util.rs ++++ b/src/util.rs +@@ -125,7 +125,20 @@ macro_rules! str_hash { + }; + } + +-#[cfg(Py_3_10)] ++#[cfg(Py_3_12)] ++macro_rules! pydict_contains { ++ ($obj1:expr, $obj2:expr) => { ++ unsafe { ++ pyo3_ffi::_PyDict_Contains_KnownHash( ++ pyo3_ffi::PyType_GetDict($obj1), ++ $obj2, ++ (*$obj2.cast::()).hash, ++ ) == 1 ++ } ++ }; ++} ++ ++#[cfg(all(Py_3_10, not(Py_3_12)))] + macro_rules! pydict_contains { + ($obj1:expr, $obj2:expr) => { + unsafe { +diff --git a/test/test_default.py b/test/test_default.py +index d1e95587..547fce8c 100644 +--- a/test/test_default.py ++++ b/test/test_default.py +@@ -275,3 +275,15 @@ def default(obj): + orjson.dumps(ref, default=default) + + assert sys.getrefcount(ref) == 2 # one for ref, one for default ++ ++ def test_default_set(self): ++ """ ++ dumps() default function with set ++ """ ++ ++ def default(obj): ++ if isinstance(obj, set): ++ return list(obj) ++ raise TypeError ++ ++ assert orjson.dumps({1, 2}, default=default) == b"[1,2]" diff --git a/python-orjson.spec b/python-orjson.spec index a9b899b..39a7965 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,12 +9,16 @@ Name: python-orjson Version: 3.9.2 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} + +# Compatibility with Python 3.12 +Patch: https://github.com/ijl/orjson/pull/408.patch + # Ineligble for upstreaming Patch: Remove-unstable-simd-feature.patch @@ -91,6 +95,10 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Tue Jul 25 2023 Tomáš Hrnčiar - 3.9.2-2 +- Backport patch to add PyType_GetDict for Python 3.12 +- Fixes: rhbz#2220383 + * Fri Jul 21 2023 Maxwell G - 3.9.2-1 - Update to 3.9.2. Fixes rhbz#2211703. From 4e24494280c8806952b3feb1bfdc30523d307141 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Fri, 8 Sep 2023 22:45:25 -0500 Subject: [PATCH 10/55] Update to 3.9.8. Fixes rhbz#2229530. * Replace Cargo.toml patch that no longer applies with tomcli-set. * Also remove no-panic feature. --- .gitignore | 2 ++ 408.patch | 56 ------------------------------ Remove-unstable-simd-feature.patch | 30 ---------------- python-orjson.spec | 20 ++++++----- sources | 2 +- 5 files changed, 14 insertions(+), 96 deletions(-) delete mode 100644 408.patch delete mode 100644 Remove-unstable-simd-feature.patch diff --git a/.gitignore b/.gitignore index 68fe46f..0c8ff1c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ /orjson-3.8.12.tar.gz /orjson-3.8.14.tar.gz /orjson-3.9.2.tar.gz +/orjson-3.9.7.tar.gz +/orjson-3.9.8.tar.gz diff --git a/408.patch b/408.patch deleted file mode 100644 index 1ff0d8f..0000000 --- a/408.patch +++ /dev/null @@ -1,56 +0,0 @@ -From c0d62d041371970297c31ac86465cf8983386191 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Sun, 23 Jul 2023 17:15:18 +0200 -Subject: [PATCH] Use PyType_GetDict to safely access tp_dict [3.12] - ---- - src/util.rs | 15 ++++++++++++++- - test/test_default.py | 12 ++++++++++++ - 2 files changed, 26 insertions(+), 1 deletion(-) - -diff --git a/src/util.rs b/src/util.rs -index 3b451bd5..ba011c1a 100644 ---- a/src/util.rs -+++ b/src/util.rs -@@ -125,7 +125,20 @@ macro_rules! str_hash { - }; - } - --#[cfg(Py_3_10)] -+#[cfg(Py_3_12)] -+macro_rules! pydict_contains { -+ ($obj1:expr, $obj2:expr) => { -+ unsafe { -+ pyo3_ffi::_PyDict_Contains_KnownHash( -+ pyo3_ffi::PyType_GetDict($obj1), -+ $obj2, -+ (*$obj2.cast::()).hash, -+ ) == 1 -+ } -+ }; -+} -+ -+#[cfg(all(Py_3_10, not(Py_3_12)))] - macro_rules! pydict_contains { - ($obj1:expr, $obj2:expr) => { - unsafe { -diff --git a/test/test_default.py b/test/test_default.py -index d1e95587..547fce8c 100644 ---- a/test/test_default.py -+++ b/test/test_default.py -@@ -275,3 +275,15 @@ def default(obj): - orjson.dumps(ref, default=default) - - assert sys.getrefcount(ref) == 2 # one for ref, one for default -+ -+ def test_default_set(self): -+ """ -+ dumps() default function with set -+ """ -+ -+ def default(obj): -+ if isinstance(obj, set): -+ return list(obj) -+ raise TypeError -+ -+ assert orjson.dumps({1, 2}, default=default) == b"[1,2]" diff --git a/Remove-unstable-simd-feature.patch b/Remove-unstable-simd-feature.patch deleted file mode 100644 index 307ac08..0000000 --- a/Remove-unstable-simd-feature.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 8cb9c84fadfc490ec88665f0ab4871c2d7255738 Mon Sep 17 00:00:00 2001 -From: Maxwell G -Date: Wed, 25 Jan 2023 20:30:13 -0600 -Subject: [PATCH] Remove unstable-simd feature - -This feature requires nightly Rust. ---- - Cargo.toml | 6 ------ - 1 file changed, 6 deletions(-) - -diff --git a/Cargo.toml b/Cargo.toml -index fbd45a4..09724ce 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -32,12 +32,6 @@ crate-type = ["cdylib"] - [features] - default = [] - --# Use SIMD intrinsics. This requires Rust on the nightly channel. --unstable-simd = [ -- "bytecount/generic-simd", -- "encoding_rs/simd-accel", --] -- - # Build yyjson as a backend and panic if it fails. The default is to attempt - # to build and on failure fall back to another backend. - yyjson = [] --- -2.40.1 - diff --git a/python-orjson.spec b/python-orjson.spec index 39a7965..f31b29a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,20 +8,15 @@ %bcond tests 1 Name: python-orjson -Version: 3.9.2 -Release: 2%{?dist} +Version: 3.9.8 +Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} -# Compatibility with Python 3.12 -Patch: https://github.com/ijl/orjson/pull/408.patch - -# Ineligble for upstreaming -Patch: Remove-unstable-simd-feature.patch - +BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} BuildRequires: rust-packaging @@ -55,13 +50,17 @@ License: %{shrink: %prep %autosetup -p1 -n orjson-%{version} +%cargo_prep +# Remove unstable feature that requires rust nightly +tomcli-set Cargo.toml del 'features.unstable-simd' +# Remove no-panic feature +tomcli-set Cargo.toml del 'features.no-panic' # Remove bundled rust crates rm -r include/cargo .cargo/config.toml # Remove bundled yyjson. rm -rv include/yyjson/ -%cargo_prep %generate_buildrequires @@ -95,6 +94,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Tue Oct 10 2023 Maxwell G - 3.9.8-1 +- Update to 3.9.8. Fixes rhbz#2229530. + * Tue Jul 25 2023 Tomáš Hrnčiar - 3.9.2-2 - Backport patch to add PyType_GetDict for Python 3.12 - Fixes: rhbz#2220383 diff --git a/sources b/sources index ce12853..2aca32d 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.9.2.tar.gz) = 5ac8713c8f921f876e1c6d61daacde5d5b7a738d00f1a0c3f51df2fcb10815de0d73cc980bca43139ddd7fbfca5e800a6d31a35cc833ddb1fac7cdb181a1116f +SHA512 (orjson-3.9.8.tar.gz) = 924810c7f37debdd63398c0f4f63c0543371c227002530d19003c3f37c16a87d8d6aa93267242523d9b6e6ae94e53e6e6a84874f8eeac8ce93a3d088e8e0a4df From 3ff334ed6b48e485003a0d1538f524b3e1113efd Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 10 Oct 2023 19:03:53 +0000 Subject: [PATCH 11/55] Let %cargo_prep handle removing .cargo/config.toml. --- python-orjson.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index f31b29a..2ecc5d9 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -57,7 +57,7 @@ tomcli-set Cargo.toml del 'features.unstable-simd' # Remove no-panic feature tomcli-set Cargo.toml del 'features.no-panic' # Remove bundled rust crates -rm -r include/cargo .cargo/config.toml +rm -r include/cargo # Remove bundled yyjson. rm -rv include/yyjson/ From 988fb0947594dd6cffadb3ba451af72921c52e2b Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sun, 29 Oct 2023 19:05:35 -0400 Subject: [PATCH 12/55] Fix a typo in the description --- python-orjson.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 2ecc5d9..a000412 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -23,7 +23,7 @@ BuildRequires: rust-packaging %global _description %{expand: -orjosn is a fast, correct Python JSON library supporting dataclasses, +orjson is a fast, correct Python JSON library supporting dataclasses, datetimes, and numpy} From a1d33d86fd031f48025730cf7e18e8832541f548 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 21 Nov 2023 18:59:28 -0600 Subject: [PATCH 13/55] Update to 3.9.10. Fixes rhbz#2243767. --- .gitignore | 1 + python-orjson.spec | 5 ++++- sources | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 0c8ff1c..59c9ea3 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ /orjson-3.9.2.tar.gz /orjson-3.9.7.tar.gz /orjson-3.9.8.tar.gz +/orjson-3.9.10.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index a000412..bcad5d6 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.9.8 +Version: 3.9.10 Release: 1%{?dist} Summary: Fast, correct Python JSON library @@ -94,6 +94,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Wed Nov 22 2023 Maxwell G - 3.9.10-1 +- Update to 3.9.10. Fixes rhbz#2243767. + * Tue Oct 10 2023 Maxwell G - 3.9.8-1 - Update to 3.9.8. Fixes rhbz#2229530. diff --git a/sources b/sources index 2aca32d..19aef9a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.9.8.tar.gz) = 924810c7f37debdd63398c0f4f63c0543371c227002530d19003c3f37c16a87d8d6aa93267242523d9b6e6ae94e53e6e6a84874f8eeac8ce93a3d088e8e0a4df +SHA512 (orjson-3.9.10.tar.gz) = 070bbee45ec08b1496b0f92d5b36d278000e33f00af6e45f2857b97c14682887558ca6413789cfebfc96a29d2366be0832a01e5e9a69868ab66d2907f86d999a From d22ecca1a5499745eb3758c5c5c00aa515bddb2d Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Thu, 18 Jan 2024 21:04:20 +0000 Subject: [PATCH 14/55] Update to 3.9.12. Fixes rhbz#2259025. --- .gitignore | 1 + Remove-strict-ahash-version-pin.patch | 25 +++++++++++++++++++++++++ python-orjson.spec | 6 +++++- sources | 2 +- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 Remove-strict-ahash-version-pin.patch diff --git a/.gitignore b/.gitignore index 59c9ea3..dab65bf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /orjson-3.9.7.tar.gz /orjson-3.9.8.tar.gz /orjson-3.9.10.tar.gz +/orjson-3.9.12.tar.gz diff --git a/Remove-strict-ahash-version-pin.patch b/Remove-strict-ahash-version-pin.patch new file mode 100644 index 0000000..569f70f --- /dev/null +++ b/Remove-strict-ahash-version-pin.patch @@ -0,0 +1,25 @@ +From db97d953cbe35e6a34372e4a9d4702d3fdd5ddb1 Mon Sep 17 00:00:00 2001 +From: Maxwell G +Date: Thu, 18 Jan 2024 21:07:59 +0000 +Subject: [PATCH] Remove strict ahash version pin + +--- + Cargo.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Cargo.toml b/Cargo.toml +index c2cf457..475a1c0 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -45,7 +45,7 @@ no-panic = [ + yyjson = [] + + [dependencies] +-ahash = { version = "=0.8.6", default_features = false, features = ["compile-time-rng"] } ++ahash = { version = "0.8.6", default_features = false, features = ["compile-time-rng"] } + arrayvec = { version = "0.7", default_features = false, features = ["std", "serde"] } + associative-cache = { version = "2", default_features = false } + beef = { version = "0.5", default_features = false, features = ["impl_serde"] } +-- +2.43.0 + diff --git a/python-orjson.spec b/python-orjson.spec index bcad5d6..dd209b8 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,13 +8,14 @@ %bcond tests 1 Name: python-orjson -Version: 3.9.10 +Version: 3.9.12 Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} +Patch: Remove-strict-ahash-version-pin.patch BuildRequires: tomcli BuildRequires: python3-devel @@ -94,6 +95,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Thu Jan 18 2024 Maxwell G - 3.9.12-1 +- Update to 3.9.12. Fixes rhbz#2259025. + * Wed Nov 22 2023 Maxwell G - 3.9.10-1 - Update to 3.9.10. Fixes rhbz#2243767. diff --git a/sources b/sources index 19aef9a..627365a 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.9.10.tar.gz) = 070bbee45ec08b1496b0f92d5b36d278000e33f00af6e45f2857b97c14682887558ca6413789cfebfc96a29d2366be0832a01e5e9a69868ab66d2907f86d999a +SHA512 (orjson-3.9.12.tar.gz) = b2689d92b34b6c818e5a798e6de08d693e3a6b04f205d943fb8960727153635faf7118d33ea78a79ce09aa0b066b07d431b27871a61a8de536f64a80f6bab387 From e70ad5bead4b82b1a0cb617d9853fd53ea105e54 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Mon, 22 Jan 2024 04:20:49 +0000 Subject: [PATCH 15/55] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index dd209b8..9cb7591 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.9.12 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -95,6 +95,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Mon Jan 22 2024 Fedora Release Engineering - 3.9.12-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Thu Jan 18 2024 Maxwell G - 3.9.12-1 - Update to 3.9.12. Fixes rhbz#2259025. From 9ecc5f91b6d4c42be77b965fd2e6c76e955ae36f Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 26 Jan 2024 07:40:47 +0000 Subject: [PATCH 16/55] Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 9cb7591..43adc8f 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.9.12 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -95,6 +95,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri Jan 26 2024 Fedora Release Engineering - 3.9.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + * Mon Jan 22 2024 Fedora Release Engineering - 3.9.12-2 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild From 3fe02c11c6694af2819cefc881171fcb64f7c93a Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 6 Feb 2024 21:20:28 +0000 Subject: [PATCH 17/55] Update to 3.9.13. Fixes rhbz#2262570. --- .gitignore | 1 + python-orjson.spec | 11 +++++++---- sources | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index dab65bf..87f5fcf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /orjson-3.9.8.tar.gz /orjson-3.9.10.tar.gz /orjson-3.9.12.tar.gz +/orjson-3.9.13.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index 43adc8f..539ae9c 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,8 +8,8 @@ %bcond tests 1 Name: python-orjson -Version: 3.9.12 -Release: 3%{?dist} +Version: 3.9.13 +Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -33,15 +33,15 @@ datetimes, and numpy} %package -n python3-orjson Summary: %{summary} # (Apache-2.0 OR MIT) AND BSD-3-Clause -# Apache-2.0 # Apache-2.0 OR BSL-1.0 # Apache-2.0 OR MIT +# BSD-2-Clause OR Apache-2.0 OR MIT # MIT License: %{shrink: (Apache-2.0 OR MIT) AND BSD-3-Clause AND - Apache-2.0 AND (Apache-2.0 OR BSL-1.0) AND + (BSD-2-Clause OR Apache-2.0 OR MIT) AND MIT } @@ -95,6 +95,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Tue Feb 6 2024 Maxwell G - 3.9.13-1 +- Update to 3.9.13. Fixes rhbz#2262570. + * Fri Jan 26 2024 Fedora Release Engineering - 3.9.12-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild diff --git a/sources b/sources index 627365a..b6c529d 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.9.12.tar.gz) = b2689d92b34b6c818e5a798e6de08d693e3a6b04f205d943fb8960727153635faf7118d33ea78a79ce09aa0b066b07d431b27871a61a8de536f64a80f6bab387 +SHA512 (orjson-3.9.13.tar.gz) = c3fc7ad42a2ea40fa996cec11609c3d72ad3ad42a2ef24d36472bb89e5231ae6288c4ded57ba69074a04244e2e8ab7cf878c26b79bb01d5f6883be18395cd219 From e7bd69981e02b23492a83ffe3566752a126b54a2 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Mon, 15 Apr 2024 23:31:00 +0000 Subject: [PATCH 18/55] Update to 3.10.1. Fixes rhbz#2264126. --- .gitignore | 1 + Remove-strict-ahash-version-pin.patch | 25 ------------------------- python-orjson.spec | 11 +++++++++-- sources | 2 +- 4 files changed, 11 insertions(+), 28 deletions(-) delete mode 100644 Remove-strict-ahash-version-pin.patch diff --git a/.gitignore b/.gitignore index 87f5fcf..5a5d521 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /orjson-3.9.10.tar.gz /orjson-3.9.12.tar.gz /orjson-3.9.13.tar.gz +/orjson-3.10.1.tar.gz diff --git a/Remove-strict-ahash-version-pin.patch b/Remove-strict-ahash-version-pin.patch deleted file mode 100644 index 569f70f..0000000 --- a/Remove-strict-ahash-version-pin.patch +++ /dev/null @@ -1,25 +0,0 @@ -From db97d953cbe35e6a34372e4a9d4702d3fdd5ddb1 Mon Sep 17 00:00:00 2001 -From: Maxwell G -Date: Thu, 18 Jan 2024 21:07:59 +0000 -Subject: [PATCH] Remove strict ahash version pin - ---- - Cargo.toml | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Cargo.toml b/Cargo.toml -index c2cf457..475a1c0 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -45,7 +45,7 @@ no-panic = [ - yyjson = [] - - [dependencies] --ahash = { version = "=0.8.6", default_features = false, features = ["compile-time-rng"] } -+ahash = { version = "0.8.6", default_features = false, features = ["compile-time-rng"] } - arrayvec = { version = "0.7", default_features = false, features = ["std", "serde"] } - associative-cache = { version = "2", default_features = false } - beef = { version = "0.5", default_features = false, features = ["impl_serde"] } --- -2.43.0 - diff --git a/python-orjson.spec b/python-orjson.spec index 539ae9c..1b346dc 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,14 +8,13 @@ %bcond tests 1 Name: python-orjson -Version: 3.9.13 +Version: 3.10.1 Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} -Patch: Remove-strict-ahash-version-pin.patch BuildRequires: tomcli BuildRequires: python3-devel @@ -57,11 +56,16 @@ License: %{shrink: tomcli-set Cargo.toml del 'features.unstable-simd' # Remove no-panic feature tomcli-set Cargo.toml del 'features.no-panic' +# Remove strict dependencies +sed -Ei 's|(version = ")=|\1|' Cargo.toml # Remove bundled rust crates rm -r include/cargo # Remove bundled yyjson. rm -rv include/yyjson/ +# Remove unpackaged PyPI plugin +sed -i '/pytest-random-order/d' test/requirements.txt + %generate_buildrequires @@ -95,6 +99,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Mon Apr 15 2024 Maxwell G - 3.10.1-1 +- Update to 3.10.1. Fixes rhbz#2264126. + * Tue Feb 6 2024 Maxwell G - 3.9.13-1 - Update to 3.9.13. Fixes rhbz#2262570. diff --git a/sources b/sources index b6c529d..fffc269 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.9.13.tar.gz) = c3fc7ad42a2ea40fa996cec11609c3d72ad3ad42a2ef24d36472bb89e5231ae6288c4ded57ba69074a04244e2e8ab7cf878c26b79bb01d5f6883be18395cd219 +SHA512 (orjson-3.10.1.tar.gz) = 54b927f407a584b0751403b3473bdb26fa3827e07895a28255b7ef938dc404d36fd388e47ddd0e724916786a3e52ef9fe5f245d8d2732d1cb6880bedb75135cf From 227caa6c802713258ea684d78ae5d1b25afb9238 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 24 May 2024 12:32:29 -0400 Subject: [PATCH 19/55] Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces From 9109069447b33644bc7d14fabe959422cee548ba Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 24 May 2024 12:41:07 -0400 Subject: [PATCH 20/55] Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 1b346dc..560ebed 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.10.1 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -99,6 +99,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri May 24 2024 Benjamin A. Beasley - 3.10.1-2 +- Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces + * Mon Apr 15 2024 Maxwell G - 3.10.1-1 - Update to 3.10.1. Fixes rhbz#2264126. From 35560182ee3fa093bd241a3e12808efde579f7d2 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 1 Jun 2024 08:32:49 -0400 Subject: [PATCH 21/55] Update to 3.10.3. Fixes rhbz#2278078. --- .gitignore | 1 + python-orjson.spec | 11 ++++++++--- sources | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 5a5d521..105354c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /orjson-3.9.12.tar.gz /orjson-3.9.13.tar.gz /orjson-3.10.1.tar.gz +/orjson-3.10.3.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index 560ebed..8fdd6c3 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,8 +8,8 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.1 -Release: 2%{?dist} +Version: 3.10.3 +Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -52,8 +52,10 @@ License: %{shrink: %autosetup -p1 -n orjson-%{version} %cargo_prep -# Remove unstable feature that requires rust nightly +# Remove unstable features that require rust nightly; the avx512 feature also +# requires the x86_64 architecture tomcli-set Cargo.toml del 'features.unstable-simd' +tomcli-set Cargo.toml del 'features.avx512' # Remove no-panic feature tomcli-set Cargo.toml del 'features.no-panic' # Remove strict dependencies @@ -99,6 +101,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Sat Jun 01 2024 Benjamin A. Beasley - 3.10.3-1 +- Update to 3.10.3. Fixes rhbz#2278078. + * Fri May 24 2024 Benjamin A. Beasley - 3.10.1-2 - Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces diff --git a/sources b/sources index fffc269..d30640f 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.1.tar.gz) = 54b927f407a584b0751403b3473bdb26fa3827e07895a28255b7ef938dc404d36fd388e47ddd0e724916786a3e52ef9fe5f245d8d2732d1cb6880bedb75135cf +SHA512 (orjson-3.10.3.tar.gz) = 7c29ca375e18170088bfbd0adf58be0291668c77bdae30f74568731350ac384f70430fe3e52ff29733959685cd02bac33ddaae035b9f35824c8daf3c80419506 From 26a62120e0d4452f2348f8a1d442fcd7a14878d9 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 1 Jun 2024 08:44:03 -0400 Subject: [PATCH 22/55] Print the reasons for skipped tests --- python-orjson.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 8fdd6c3..24dc797 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -91,7 +91,8 @@ export RUSTFLAGS='%{build_rustflags}' %pyproject_check_import %if %{with tests} # --forked: protect the pytest process against test segfaults -%pytest --forked +# -rs: print the reasons for skipped tests +%pytest --forked -rs %endif From f896f0f8197e5541870a5945535110e4c933c360 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 1 Jun 2024 15:50:44 -0400 Subject: [PATCH 23/55] Re-enable skipped numpy and pendulum integration tests --- python-orjson.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index 24dc797..e2e0d7d 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -19,6 +19,11 @@ Source: %{pypi_source orjson} BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} +# Upstream restricts these test dependencies to particular Python interpreter +# versions and architectures, but we would like to run the corresponding tests +# everywhere. +BuildRequires: %{py3_dist numpy} +BuildRequires: %{py3_dist pendulum} BuildRequires: rust-packaging From e6b46905b456b09ea364deef64e8c0ccb64390da Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 1 Jun 2024 15:52:28 -0400 Subject: [PATCH 24/55] Enable skipped pandas and psutil integration tests --- python-orjson.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index e2e0d7d..503431a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -24,6 +24,11 @@ BuildRequires: %{py3_dist pytest-forked} # everywhere. BuildRequires: %{py3_dist numpy} BuildRequires: %{py3_dist pendulum} +# These are not in tests/requirements.txt, but they enable additional tests +%ifnarch %{ix86} +BuildRequires: %{py3_dist pandas} +%endif +BuildRequires: %{py3_dist psutil} BuildRequires: rust-packaging From 36f0703a428e166a5ca347b8ca07422ae48d1c89 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Sat, 8 Jun 2024 13:30:06 +0200 Subject: [PATCH 25/55] Rebuilt for Python 3.13 --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 503431a..f3c2685 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.10.3 -Release: 1%{?dist} +Release: 2%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -112,6 +112,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Sat Jun 08 2024 Python Maint - 3.10.3-2 +- Rebuilt for Python 3.13 + * Sat Jun 01 2024 Benjamin A. Beasley - 3.10.3-1 - Update to 3.10.3. Fixes rhbz#2278078. From 00d4cf1090c50b53bb04fddc6dd3fc89dd9b4c09 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 19 Jul 2024 13:44:43 +0000 Subject: [PATCH 26/55] Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild --- python-orjson.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index f3c2685..305b75e 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.10.3 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -112,6 +112,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Fri Jul 19 2024 Fedora Release Engineering - 3.10.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + * Sat Jun 08 2024 Python Maint - 3.10.3-2 - Rebuilt for Python 3.13 From e5307353bbb3196755c63704a753da897fd06798 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 12 Jul 2024 14:38:10 -0400 Subject: [PATCH 27/55] Update to 3.10.6. Fixes rhbz#2291190 --- .gitignore | 1 + python-orjson.spec | 16 ++++++++++++---- sources | 2 +- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 105354c..f20809a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ /orjson-3.9.13.tar.gz /orjson-3.10.1.tar.gz /orjson-3.10.3.tar.gz +/orjson-3.10.6.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index 305b75e..9a0c3d1 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,8 +8,8 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.3 -Release: 3%{?dist} +Version: 3.10.6 +Release: 1%{?dist} Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -44,13 +44,14 @@ Summary: %{summary} # (Apache-2.0 OR MIT) AND BSD-3-Clause # Apache-2.0 OR BSL-1.0 # Apache-2.0 OR MIT -# BSD-2-Clause OR Apache-2.0 OR MIT +# BSL-1.0 # MIT +# MIT OR Apache-2.0 (duplicate) License: %{shrink: (Apache-2.0 OR MIT) AND BSD-3-Clause AND (Apache-2.0 OR BSL-1.0) AND - (BSD-2-Clause OR Apache-2.0 OR MIT) AND + BSL-1.0 AND MIT } @@ -68,6 +69,10 @@ tomcli-set Cargo.toml del 'features.unstable-simd' tomcli-set Cargo.toml del 'features.avx512' # Remove no-panic feature tomcli-set Cargo.toml del 'features.no-panic' +# Remove unwind feature, which is not useful here: the comment above it says +# “Avoid bundling libgcc on musl.” +tomcli-set Cargo.toml del 'features.unwind' +tomcli-set Cargo.toml del 'dependencies.unwinding' # Remove strict dependencies sed -Ei 's|(version = ")=|\1|' Cargo.toml # Remove bundled rust crates @@ -112,6 +117,9 @@ export RUSTFLAGS='%{build_rustflags}' %changelog +* Mon Jul 22 2024 Benjamin A. Beasley - 3.10.6-1 +- Update to 3.10.6. Fixes rhbz#2291190. + * Fri Jul 19 2024 Fedora Release Engineering - 3.10.3-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild diff --git a/sources b/sources index d30640f..76f47dc 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.3.tar.gz) = 7c29ca375e18170088bfbd0adf58be0291668c77bdae30f74568731350ac384f70430fe3e52ff29733959685cd02bac33ddaae035b9f35824c8daf3c80419506 +SHA512 (orjson-3.10.6.tar.gz) = a3b6bfe4c9611ae62476490ccf9378135df2a714743df2252d247c8bf5724943d5717d2821d22305a854426e2be38cb9d40067aa892035414e8db5b26f1d90fb From 91516413ec1293ae9c313d74a1c21ba43726dbc2 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 31 Jul 2024 14:18:23 -0400 Subject: [PATCH 28/55] Convert to %autorelease and %autochangelog [skip changelog] --- changelog | 61 +++++++++++++++++++++++++++++++++++++++++++ python-orjson.spec | 64 ++-------------------------------------------- 2 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 changelog diff --git a/changelog b/changelog new file mode 100644 index 0000000..a07230a --- /dev/null +++ b/changelog @@ -0,0 +1,61 @@ +* Mon Jul 22 2024 Benjamin A. Beasley - 3.10.6-1 +- Update to 3.10.6. Fixes rhbz#2291190. + +* Fri Jul 19 2024 Fedora Release Engineering - 3.10.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild + +* Sat Jun 08 2024 Python Maint - 3.10.3-2 +- Rebuilt for Python 3.13 + +* Sat Jun 01 2024 Benjamin A. Beasley - 3.10.3-1 +- Update to 3.10.3. Fixes rhbz#2278078. + +* Fri May 24 2024 Benjamin A. Beasley - 3.10.1-2 +- Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces + +* Mon Apr 15 2024 Maxwell G - 3.10.1-1 +- Update to 3.10.1. Fixes rhbz#2264126. + +* Tue Feb 6 2024 Maxwell G - 3.9.13-1 +- Update to 3.9.13. Fixes rhbz#2262570. + +* Fri Jan 26 2024 Fedora Release Engineering - 3.9.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Mon Jan 22 2024 Fedora Release Engineering - 3.9.12-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild + +* Thu Jan 18 2024 Maxwell G - 3.9.12-1 +- Update to 3.9.12. Fixes rhbz#2259025. + +* Wed Nov 22 2023 Maxwell G - 3.9.10-1 +- Update to 3.9.10. Fixes rhbz#2243767. + +* Tue Oct 10 2023 Maxwell G - 3.9.8-1 +- Update to 3.9.8. Fixes rhbz#2229530. + +* Tue Jul 25 2023 Tomáš Hrnčiar - 3.9.2-2 +- Backport patch to add PyType_GetDict for Python 3.12 +- Fixes: rhbz#2220383 + +* Fri Jul 21 2023 Maxwell G - 3.9.2-1 +- Update to 3.9.2. Fixes rhbz#2211703. + +* Fri Jul 21 2023 Maxwell G - 3.8.14-1 +- Update to 3.8.14. + +* Fri Jul 21 2023 Fedora Release Engineering - 3.8.12-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild + +* Tue Jun 27 2023 Python Maint - 3.8.12-2 +- Rebuilt for Python 3.12 + +* Thu May 18 2023 Maxwell G - 3.8.12-1 +- Update to 3.8.12. +- Use maturin as a build system when available + +* Fri May 5 2023 Maxwell G - 3.8.11-1 +- Update to 3.8.11. Fixes rhbz#2193468. + +* Wed Apr 12 2023 Maxwell G - 3.8.10-1 +- Initial package (rhbz#2184237). diff --git a/python-orjson.spec b/python-orjson.spec index 9a0c3d1..a9f8d6a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,7 +9,7 @@ Name: python-orjson Version: 3.10.6 -Release: 1%{?dist} +Release: %autorelease Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT @@ -117,64 +117,4 @@ export RUSTFLAGS='%{build_rustflags}' %changelog -* Mon Jul 22 2024 Benjamin A. Beasley - 3.10.6-1 -- Update to 3.10.6. Fixes rhbz#2291190. - -* Fri Jul 19 2024 Fedora Release Engineering - 3.10.3-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild - -* Sat Jun 08 2024 Python Maint - 3.10.3-2 -- Rebuilt for Python 3.13 - -* Sat Jun 01 2024 Benjamin A. Beasley - 3.10.3-1 -- Update to 3.10.3. Fixes rhbz#2278078. - -* Fri May 24 2024 Benjamin A. Beasley - 3.10.1-2 -- Rebuild with Rust 1.78 to fix incomplete debuginfo and backtraces - -* Mon Apr 15 2024 Maxwell G - 3.10.1-1 -- Update to 3.10.1. Fixes rhbz#2264126. - -* Tue Feb 6 2024 Maxwell G - 3.9.13-1 -- Update to 3.9.13. Fixes rhbz#2262570. - -* Fri Jan 26 2024 Fedora Release Engineering - 3.9.12-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Mon Jan 22 2024 Fedora Release Engineering - 3.9.12-2 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild - -* Thu Jan 18 2024 Maxwell G - 3.9.12-1 -- Update to 3.9.12. Fixes rhbz#2259025. - -* Wed Nov 22 2023 Maxwell G - 3.9.10-1 -- Update to 3.9.10. Fixes rhbz#2243767. - -* Tue Oct 10 2023 Maxwell G - 3.9.8-1 -- Update to 3.9.8. Fixes rhbz#2229530. - -* Tue Jul 25 2023 Tomáš Hrnčiar - 3.9.2-2 -- Backport patch to add PyType_GetDict for Python 3.12 -- Fixes: rhbz#2220383 - -* Fri Jul 21 2023 Maxwell G - 3.9.2-1 -- Update to 3.9.2. Fixes rhbz#2211703. - -* Fri Jul 21 2023 Maxwell G - 3.8.14-1 -- Update to 3.8.14. - -* Fri Jul 21 2023 Fedora Release Engineering - 3.8.12-3 -- Rebuilt for https://fedoraproject.org/wiki/Fedora_39_Mass_Rebuild - -* Tue Jun 27 2023 Python Maint - 3.8.12-2 -- Rebuilt for Python 3.12 - -* Thu May 18 2023 Maxwell G - 3.8.12-1 -- Update to 3.8.12. -- Use maturin as a build system when available - -* Fri May 5 2023 Maxwell G - 3.8.11-1 -- Update to 3.8.11. Fixes rhbz#2193468. - -* Wed Apr 12 2023 Maxwell G - 3.8.10-1 -- Initial package (rhbz#2184237). +%autochangelog From 87b8204a346ce16d3261cb5d68974e345541de68 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 9 Aug 2024 11:32:16 -0400 Subject: [PATCH 29/55] Update to 3.10.7. Fixes rhbz#2303811 --- .gitignore | 1 + python-orjson.spec | 6 ++++-- sources | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f20809a..62c1ca5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ /orjson-3.10.1.tar.gz /orjson-3.10.3.tar.gz /orjson-3.10.6.tar.gz +/orjson-3.10.7.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index a9f8d6a..ddddd97 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.6 +Version: 3.10.7 Release: %autorelease Summary: Fast, correct Python JSON library @@ -47,12 +47,14 @@ Summary: %{summary} # BSL-1.0 # MIT # MIT OR Apache-2.0 (duplicate) +# Unlicense OR MIT License: %{shrink: (Apache-2.0 OR MIT) AND BSD-3-Clause AND (Apache-2.0 OR BSL-1.0) AND BSL-1.0 AND - MIT + MIT AND + (Unlicense OR MIT) } diff --git a/sources b/sources index 76f47dc..ea98349 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.6.tar.gz) = a3b6bfe4c9611ae62476490ccf9378135df2a714743df2252d247c8bf5724943d5717d2821d22305a854426e2be38cb9d40067aa892035414e8db5b26f1d90fb +SHA512 (orjson-3.10.7.tar.gz) = 3940b76039d6494626e69f9a2a0373779827c6686ab1321411f3b3a5056e0df227b68c9555796ce45f3952fe597c50740934d764f30dcf8bc1c84b2baf4b6631 From 7fd8624716258f6d07d53a56a0ad4e9a595f145f Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 19 Oct 2024 14:15:01 -0400 Subject: [PATCH 30/55] Update to 3.10.11. Fixes rhbz#2319918 - Both pyo3-ffi and pyo3-build-config are now vendored and forked - Add license files for vendored PyO3 crates --- .gitignore | 2 ++ python-orjson.spec | 29 ++++++++++++++++++++++++++--- sources | 2 +- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 62c1ca5..8502800 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ /orjson-3.10.3.tar.gz /orjson-3.10.6.tar.gz /orjson-3.10.7.tar.gz +/orjson-3.10.10.tar.gz +/orjson-3.10.11.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index ddddd97..df4f430 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.7 +Version: 3.10.11 Release: %autorelease Summary: Fast, correct Python JSON library @@ -48,6 +48,8 @@ Summary: %{summary} # MIT # MIT OR Apache-2.0 (duplicate) # Unlicense OR MIT +# +# Bundled PyO3 crates in include/pyo3/ are also (Apache-2.0 OR MIT). License: %{shrink: (Apache-2.0 OR MIT) AND BSD-3-Clause AND @@ -57,6 +59,18 @@ License: %{shrink: (Unlicense OR MIT) } +# Path to using published versions of pyo3-ffi/pyo3-build-config again? +# https://github.com/ijl/orjson/issues/524 +# +# “You are welcome to work to upstream the diff if you find the vendoring +# unsuitable for your organization's own preferences.” +# +# Note that the vendored crates are actually *forked* from a snapshot, +# https://github.com/PyO3/pyo3/commit/dc415fae9f5eaa8ac0f6d0ea769b9ab28860898f; +# see https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for +# details. +Provides: bundled(crate(pyo3-build-config)) = 0.23.0~dev^20241018gitdc415fa +Provides: bundled(crate(pyo3-ffi)) = 0.23.0~dev^20241018gitdc415fa %description -n python3-orjson %{_description} @@ -82,14 +96,23 @@ rm -r include/cargo # Remove bundled yyjson. rm -rv include/yyjson/ +# Collect licenses for remaining vendored crates +mkdir -p LICENSES.vendored/pyo3 +cp -vp include/pyo3/LICENSE* LICENSES.vendored/pyo3/ + # Remove unpackaged PyPI plugin sed -i '/pytest-random-order/d' test/requirements.txt - %generate_buildrequires %pyproject_buildrequires %{?with_tests:test/requirements.txt} %cargo_generate_buildrequires +for dir in include/pyo3/*/ +do + pushd "${dir}" >/dev/null + %cargo_generate_buildrequires + popd >/dev/null +done %build @@ -114,7 +137,7 @@ export RUSTFLAGS='%{build_rustflags}' %files -n python3-orjson -f %{pyproject_files} -%license LICENSE-MIT LICENSE-APACHE LICENSES.dependencies +%license LICENSE-MIT LICENSE-APACHE LICENSES.dependencies LICENSES.vendored/ %doc README.md diff --git a/sources b/sources index ea98349..9460ae7 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.7.tar.gz) = 3940b76039d6494626e69f9a2a0373779827c6686ab1321411f3b3a5056e0df227b68c9555796ce45f3952fe597c50740934d764f30dcf8bc1c84b2baf4b6631 +SHA512 (orjson-3.10.11.tar.gz) = b7a960cd06c0176a194c7c9e9dd412c47000258fb79e2407182156e07a48bc02823118ac708c23f482bcb8f3b77dcdcfd95f61f408b45cbaabca8ed317ef63e4 From 2746b9bffbd78c842e4a6127ccdd6c4c4104adb2 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Mon, 25 Nov 2024 13:17:18 -0500 Subject: [PATCH 31/55] Update to 3.10.12. Fixes rhbz#2328508 --- .gitignore | 1 + python-orjson.spec | 4 +--- sources | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 8502800..9fe6224 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ /orjson-3.10.7.tar.gz /orjson-3.10.10.tar.gz /orjson-3.10.11.tar.gz +/orjson-3.10.12.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index df4f430..4af3e8a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.11 +Version: 3.10.12 Release: %autorelease Summary: Fast, correct Python JSON library @@ -83,8 +83,6 @@ Provides: bundled(crate(pyo3-ffi)) = 0.23.0~dev^20241018gitdc415fa # requires the x86_64 architecture tomcli-set Cargo.toml del 'features.unstable-simd' tomcli-set Cargo.toml del 'features.avx512' -# Remove no-panic feature -tomcli-set Cargo.toml del 'features.no-panic' # Remove unwind feature, which is not useful here: the comment above it says # “Avoid bundling libgcc on musl.” tomcli-set Cargo.toml del 'features.unwind' diff --git a/sources b/sources index 9460ae7..6bdca07 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.11.tar.gz) = b7a960cd06c0176a194c7c9e9dd412c47000258fb79e2407182156e07a48bc02823118ac708c23f482bcb8f3b77dcdcfd95f61f408b45cbaabca8ed317ef63e4 +SHA512 (orjson-3.10.12.tar.gz) = 089420645ef4a54674436ebc4f49ba67fda4fff716c949e03a4af402fdd817decd066c508b18c19ac24b634e7173b37924ac1c2180679a92551884957d4b11ae From d7ee4a1249e8d8f2a487f09804c4f18a9759d28f Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Mon, 30 Dec 2024 08:49:56 -0500 Subject: [PATCH 32/55] Update to 3.10.13 (close RHBZ#2334886) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Work around upstream’s maturin version pin to allow building with maturin 1.8.0 --- .gitignore | 1 + orjson-3.10.13-unpin-maturin.patch | 12 ++++++++++++ python-orjson.spec | 27 ++++++++++++++++++++++++++- sources | 2 +- 4 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 orjson-3.10.13-unpin-maturin.patch diff --git a/.gitignore b/.gitignore index 9fe6224..edc287e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /orjson-3.10.10.tar.gz /orjson-3.10.11.tar.gz /orjson-3.10.12.tar.gz +/orjson-3.10.13.tar.gz diff --git a/orjson-3.10.13-unpin-maturin.patch b/orjson-3.10.13-unpin-maturin.patch new file mode 100644 index 0000000..4de1017 --- /dev/null +++ b/orjson-3.10.13-unpin-maturin.patch @@ -0,0 +1,12 @@ +diff -Naur orjson-3.10.13-original/pyproject.toml orjson-3.10.13/pyproject.toml +--- orjson-3.10.13-original/pyproject.toml 2024-12-29 18:05:45.000000000 -0500 ++++ orjson-3.10.13/pyproject.toml 2024-12-30 08:39:38.935289119 -0500 +@@ -31,7 +31,7 @@ + + [build-system] + build-backend = "maturin" +-requires = ["maturin==1.7.8"] ++requires = ["maturin>=1,<2"] + + [tool.maturin] + python-source = "pysrc" diff --git a/python-orjson.spec b/python-orjson.spec index 4af3e8a..85d4f16 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.12 +Version: 3.10.13 Release: %autorelease Summary: Fast, correct Python JSON library @@ -16,6 +16,31 @@ License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} +# To fix compatibility with maturin 1.8.0 and later *in the GitHub source +# archive*, we would need: +# +# pyproject.toml: Specify version metadata as dynamic +# https://github.com/ijl/orjson/pull/542 +# +# Fixes: +# +# Build error with maturin 1.8.0 +# https://github.com/ijl/orjson/issues/541 +# +# See also: +# +# https://github.com/PyO3/maturin/issues/2390 +# +# Upstream rejected this PR and decided to pin an old version of maturin +# instead, but we cannot respect this; we must use the system version. As long +# as we are using the *PyPI sdist* as our source, pyproject.toml has a static +# version field, and we don’t need to patch in the rejected PR. However, we +# must still unpin maturin via a partial revert – just the part that matters to +# us, in pyproject.toml – of: +# +# https://github.com/ijl/orjson/commit/9a4ef46991ad205b9a92cb6fdcb123c7ff72b640 +Patch: orjson-3.10.13-unpin-maturin.patch + BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} diff --git a/sources b/sources index 6bdca07..e15eaa8 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.12.tar.gz) = 089420645ef4a54674436ebc4f49ba67fda4fff716c949e03a4af402fdd817decd066c508b18c19ac24b634e7173b37924ac1c2180679a92551884957d4b11ae +SHA512 (orjson-3.10.13.tar.gz) = 84ee3a42654e5576190dd115483db1e3a9a151b22de79b99b497c2e181adbd5e33a4aa77bb5f72394e0947887e657c5dc6380e852dddb4a5e95b47e72486b18c From 30b44e027bdef844d7e5829d6738d80948a96243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 6 Jan 2025 20:05:01 +0100 Subject: [PATCH 33/55] Allow the bundled pyo3 to build this with unsupported Python versions [skip changelog] --- python-orjson.spec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index 85d4f16..f9283be 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -142,6 +142,11 @@ done export RUSTFLAGS='%{build_rustflags}' %cargo_license_summary %{cargo_license} > LICENSES.dependencies +# Fedora's pyo3 is patched to not check Python version when building RPM packages. +# However, this uses a bundled version without the patch. +# Rather than patching it, we set the environment variable, +# which allows us to test this package with development Python versions. +export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 %pyproject_wheel From e0e16f61a9d45f6b8118e331bae4c9104c048991 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Thu, 9 Jan 2025 14:13:26 -0500 Subject: [PATCH 34/55] Update to 3.10.14 (close RHBZ#2336424) --- .gitignore | 1 + orjson-3.10.13-unpin-maturin.patch | 12 ---------- python-orjson.spec | 37 ++++-------------------------- sources | 2 +- 4 files changed, 7 insertions(+), 45 deletions(-) delete mode 100644 orjson-3.10.13-unpin-maturin.patch diff --git a/.gitignore b/.gitignore index edc287e..cc3f74a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ /orjson-3.10.11.tar.gz /orjson-3.10.12.tar.gz /orjson-3.10.13.tar.gz +/orjson-3.10.14.tar.gz diff --git a/orjson-3.10.13-unpin-maturin.patch b/orjson-3.10.13-unpin-maturin.patch deleted file mode 100644 index 4de1017..0000000 --- a/orjson-3.10.13-unpin-maturin.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur orjson-3.10.13-original/pyproject.toml orjson-3.10.13/pyproject.toml ---- orjson-3.10.13-original/pyproject.toml 2024-12-29 18:05:45.000000000 -0500 -+++ orjson-3.10.13/pyproject.toml 2024-12-30 08:39:38.935289119 -0500 -@@ -31,7 +31,7 @@ - - [build-system] - build-backend = "maturin" --requires = ["maturin==1.7.8"] -+requires = ["maturin>=1,<2"] - - [tool.maturin] - python-source = "pysrc" diff --git a/python-orjson.spec b/python-orjson.spec index f9283be..68d8a52 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -8,7 +8,7 @@ %bcond tests 1 Name: python-orjson -Version: 3.10.13 +Version: 3.10.14 Release: %autorelease Summary: Fast, correct Python JSON library @@ -16,31 +16,6 @@ License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} -# To fix compatibility with maturin 1.8.0 and later *in the GitHub source -# archive*, we would need: -# -# pyproject.toml: Specify version metadata as dynamic -# https://github.com/ijl/orjson/pull/542 -# -# Fixes: -# -# Build error with maturin 1.8.0 -# https://github.com/ijl/orjson/issues/541 -# -# See also: -# -# https://github.com/PyO3/maturin/issues/2390 -# -# Upstream rejected this PR and decided to pin an old version of maturin -# instead, but we cannot respect this; we must use the system version. As long -# as we are using the *PyPI sdist* as our source, pyproject.toml has a static -# version field, and we don’t need to patch in the rejected PR. However, we -# must still unpin maturin via a partial revert – just the part that matters to -# us, in pyproject.toml – of: -# -# https://github.com/ijl/orjson/commit/9a4ef46991ad205b9a92cb6fdcb123c7ff72b640 -Patch: orjson-3.10.13-unpin-maturin.patch - BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} @@ -90,12 +65,10 @@ License: %{shrink: # “You are welcome to work to upstream the diff if you find the vendoring # unsuitable for your organization's own preferences.” # -# Note that the vendored crates are actually *forked* from a snapshot, -# https://github.com/PyO3/pyo3/commit/dc415fae9f5eaa8ac0f6d0ea769b9ab28860898f; -# see https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for -# details. -Provides: bundled(crate(pyo3-build-config)) = 0.23.0~dev^20241018gitdc415fa -Provides: bundled(crate(pyo3-ffi)) = 0.23.0~dev^20241018gitdc415fa +# Note that these crates are actually forked, not only bundled/vendored; see +# https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for details. +Provides: bundled(crate(pyo3-build-config)) = 0.23.3 +Provides: bundled(crate(pyo3-ffi)) = 0.23.3 %description -n python3-orjson %{_description} diff --git a/sources b/sources index e15eaa8..21c9858 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.13.tar.gz) = 84ee3a42654e5576190dd115483db1e3a9a151b22de79b99b497c2e181adbd5e33a4aa77bb5f72394e0947887e657c5dc6380e852dddb4a5e95b47e72486b18c +SHA512 (orjson-3.10.14.tar.gz) = d186e95ac6750af9f2688744fe663e63973e06942fcbfc10deadc1d63a5ad59f437ea4e2485d1866bde6abcd59086fa5c1a0794d3ba975e693995d3ca58f0f76 From 4384132be9580ac179d2c25fee11d3659ea7bdfe Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 18 Jan 2025 16:52:10 +0000 Subject: [PATCH 35/55] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From ab0fdcf9ab8d591b4f90e8ef2fba1a51bc8f193e Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 2 Apr 2025 19:57:33 -0400 Subject: [PATCH 36/55] Replace rust-packaging BR with cargo-rpm-macros https://docs.fedoraproject.org/en-US/packaging-guidelines/Rust/#_mandatory_buildrequires [skip changelog] --- python-orjson.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python-orjson.spec b/python-orjson.spec index 68d8a52..f48eeb3 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -29,7 +29,7 @@ BuildRequires: %{py3_dist pendulum} BuildRequires: %{py3_dist pandas} %endif BuildRequires: %{py3_dist psutil} -BuildRequires: rust-packaging +BuildRequires: cargo-rpm-macros >= 24 %global _description %{expand: From caa0c29ec484c3d814d607f072a961fc0b9325ae Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 2 Apr 2025 20:25:53 -0400 Subject: [PATCH 37/55] Omit Pendulum integration tests in EPEL10 --- python-orjson.spec | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index f48eeb3..ccc4fbc 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -6,6 +6,8 @@ # Specfile compatability: EPEL >= 9 or Fedora >= 37 and RPM >= 4.16 %bcond tests 1 +# Not yet in EPEL10: https://bugzilla.redhat.com/show_bug.cgi?id=2356387 +%bcond pendulum %{undefined el10} Name: python-orjson Version: 3.10.14 @@ -23,7 +25,9 @@ BuildRequires: %{py3_dist pytest-forked} # versions and architectures, but we would like to run the corresponding tests # everywhere. BuildRequires: %{py3_dist numpy} +%if %{with pendulum} BuildRequires: %{py3_dist pendulum} +%endif # These are not in tests/requirements.txt, but they enable additional tests %ifnarch %{ix86} BuildRequires: %{py3_dist pandas} @@ -96,6 +100,9 @@ rm -rv include/yyjson/ mkdir -p LICENSES.vendored/pyo3 cp -vp include/pyo3/LICENSE* LICENSES.vendored/pyo3/ +%if %{without pendulum} +sed -i '/^pendulum\b/d' test/requirements.txt +%endif # Remove unpackaged PyPI plugin sed -i '/pytest-random-order/d' test/requirements.txt From 8355452df85d1786140bf098f549552e2795f53f Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Thu, 3 Apr 2025 06:57:53 -0400 Subject: [PATCH 38/55] Remove spurious test dependency on arrow --- python-orjson.spec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python-orjson.spec b/python-orjson.spec index ccc4fbc..99136a1 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -103,6 +103,9 @@ cp -vp include/pyo3/LICENSE* LICENSES.vendored/pyo3/ %if %{without pendulum} sed -i '/^pendulum\b/d' test/requirements.txt %endif +# Test dependency on arrow appears spurious +# https://github.com/ijl/orjson/issues/559 +sed -i '/^arrow\b/d' test/requirements.txt # Remove unpackaged PyPI plugin sed -i '/pytest-random-order/d' test/requirements.txt From c0306d6a1eda9449f2de0507e82ed76b5dbb7975 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 2 Apr 2025 19:58:44 -0400 Subject: [PATCH 39/55] Update to 3.10.6 (close RHBZ#2354600) - Fixes compatibility with CPython 3.14 alpha 6 (close RHBZ#2349447) --- .gitignore | 1 + orjson-fix-metadata.diff | 12 ++++++++++++ python-orjson.spec | 9 ++++++--- sources | 2 +- 4 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 orjson-fix-metadata.diff diff --git a/.gitignore b/.gitignore index cc3f74a..b2d5787 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ /orjson-3.10.12.tar.gz /orjson-3.10.13.tar.gz /orjson-3.10.14.tar.gz +/orjson-3.10.16.tar.gz diff --git a/orjson-fix-metadata.diff b/orjson-fix-metadata.diff new file mode 100644 index 0000000..b3116ec --- /dev/null +++ b/orjson-fix-metadata.diff @@ -0,0 +1,12 @@ +diff -Naur orjson-3.10.16-original/Cargo.toml orjson-3.10.16/Cargo.toml +--- orjson-3.10.16-original/Cargo.toml 1969-12-31 19:00:00.000000000 -0500 ++++ orjson-3.10.16/Cargo.toml 2025-04-02 20:09:24.874851253 -0400 +@@ -51,7 +51,7 @@ + [dependencies] + associative-cache = { version = "2", default-features = false } + bytecount = { version = "^0.6.7", default-features = false, features = ["runtime-dispatch-simd"] } +-compact_str = { version = "0.9", default-features = false, features = ["serde"] } ++compact_str = { version = ">=0.8, <0.10", default-features = false, features = ["serde"] } + encoding_rs = { version = "0.8", default-features = false } + half = { version = "2", default-features = false, features = ["std"] } + itoa = { version = "1", default-features = false } diff --git a/python-orjson.spec b/python-orjson.spec index 99136a1..31d96a3 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -10,7 +10,7 @@ %bcond pendulum %{undefined el10} Name: python-orjson -Version: 3.10.14 +Version: 3.10.16 Release: %autorelease Summary: Fast, correct Python JSON library @@ -18,6 +18,11 @@ License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} +# Manual patches for Cargo.toml +# - For now, allow compact_str 0.8; +# https://bugzilla.redhat.com/show_bug.cgi?id=2347456 +Patch: orjson-fix-metadata.diff + BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} @@ -89,8 +94,6 @@ tomcli-set Cargo.toml del 'features.avx512' # “Avoid bundling libgcc on musl.” tomcli-set Cargo.toml del 'features.unwind' tomcli-set Cargo.toml del 'dependencies.unwinding' -# Remove strict dependencies -sed -Ei 's|(version = ")=|\1|' Cargo.toml # Remove bundled rust crates rm -r include/cargo # Remove bundled yyjson. diff --git a/sources b/sources index 21c9858..e553587 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.14.tar.gz) = d186e95ac6750af9f2688744fe663e63973e06942fcbfc10deadc1d63a5ad59f437ea4e2485d1866bde6abcd59086fa5c1a0794d3ba975e693995d3ca58f0f76 +SHA512 (orjson-3.10.16.tar.gz) = ea71aa463206feae1a96bb604366af2f17ddb083a1a4b7cc87c1b8ad1f01b54bd5c4ef148e9472fed8d8308de37b2a4e4fe0b4a4290d19eaf4fc3ae5777791f9 From 90a0f4fa5ed7473be22ae6846959fa8a873a76ba Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 11 Apr 2025 08:08:42 -0400 Subject: [PATCH 40/55] Expect maturin to handle license files, except LICENSES.vendored/ See discussion in https://bugzilla.redhat.com/show_bug.cgi?id=2357473. --- python-orjson.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python-orjson.spec b/python-orjson.spec index 31d96a3..d2fb7ca 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -138,7 +138,7 @@ export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 %install %pyproject_install -%pyproject_save_files orjson +%pyproject_save_files -l orjson %check @@ -151,7 +151,7 @@ export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 %files -n python3-orjson -f %{pyproject_files} -%license LICENSE-MIT LICENSE-APACHE LICENSES.dependencies LICENSES.vendored/ +%license LICENSES.vendored/ %doc README.md From 66bfabbfc60a9767d92e60aa88847e493eb5f37c Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 12 Apr 2025 19:48:03 -0400 Subject: [PATCH 41/55] No longer allow compact_str 0.8 --- orjson-fix-metadata.diff | 12 ------------ python-orjson.spec | 5 ----- 2 files changed, 17 deletions(-) delete mode 100644 orjson-fix-metadata.diff diff --git a/orjson-fix-metadata.diff b/orjson-fix-metadata.diff deleted file mode 100644 index b3116ec..0000000 --- a/orjson-fix-metadata.diff +++ /dev/null @@ -1,12 +0,0 @@ -diff -Naur orjson-3.10.16-original/Cargo.toml orjson-3.10.16/Cargo.toml ---- orjson-3.10.16-original/Cargo.toml 1969-12-31 19:00:00.000000000 -0500 -+++ orjson-3.10.16/Cargo.toml 2025-04-02 20:09:24.874851253 -0400 -@@ -51,7 +51,7 @@ - [dependencies] - associative-cache = { version = "2", default-features = false } - bytecount = { version = "^0.6.7", default-features = false, features = ["runtime-dispatch-simd"] } --compact_str = { version = "0.9", default-features = false, features = ["serde"] } -+compact_str = { version = ">=0.8, <0.10", default-features = false, features = ["serde"] } - encoding_rs = { version = "0.8", default-features = false } - half = { version = "2", default-features = false, features = ["std"] } - itoa = { version = "1", default-features = false } diff --git a/python-orjson.spec b/python-orjson.spec index d2fb7ca..c48f102 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -18,11 +18,6 @@ License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} -# Manual patches for Cargo.toml -# - For now, allow compact_str 0.8; -# https://bugzilla.redhat.com/show_bug.cgi?id=2347456 -Patch: orjson-fix-metadata.diff - BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} From 4dc38f53d622486a01840880da2a66fd08d04b1b Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Tue, 29 Apr 2025 23:37:22 -0400 Subject: [PATCH 42/55] Update to 3.10.18; close RHBZ#2362920 --- .gitignore | 1 + python-orjson.spec | 6 +----- sources | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index b2d5787..808eb9d 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ /orjson-3.10.13.tar.gz /orjson-3.10.14.tar.gz /orjson-3.10.16.tar.gz +/orjson-3.10.18.tar.gz diff --git a/python-orjson.spec b/python-orjson.spec index c48f102..6fe904a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -10,7 +10,7 @@ %bcond pendulum %{undefined el10} Name: python-orjson -Version: 3.10.16 +Version: 3.10.18 Release: %autorelease Summary: Fast, correct Python JSON library @@ -81,10 +81,6 @@ Provides: bundled(crate(pyo3-ffi)) = 0.23.3 %autosetup -p1 -n orjson-%{version} %cargo_prep -# Remove unstable features that require rust nightly; the avx512 feature also -# requires the x86_64 architecture -tomcli-set Cargo.toml del 'features.unstable-simd' -tomcli-set Cargo.toml del 'features.avx512' # Remove unwind feature, which is not useful here: the comment above it says # “Avoid bundling libgcc on musl.” tomcli-set Cargo.toml del 'features.unwind' diff --git a/sources b/sources index e553587..73cee52 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.16.tar.gz) = ea71aa463206feae1a96bb604366af2f17ddb083a1a4b7cc87c1b8ad1f01b54bd5c4ef148e9472fed8d8308de37b2a4e4fe0b4a4290d19eaf4fc3ae5777791f9 +SHA512 (orjson-3.10.18.tar.gz) = 248a42f671e713bf9d11163fe7744f5d9b626f5db681913071957723f48d77cc35aa883eb695a16df76986ecc8d9e9e493cee4431c47103beabc3517ffcaa65f From 227ce2c258a8c6aa8f43e954675395e7ff66824c Mon Sep 17 00:00:00 2001 From: Karolina Surma Date: Mon, 26 May 2025 12:32:03 +0200 Subject: [PATCH 43/55] Support Python 3.14 --- 570.patch | 3802 ++++++++++++++++++++++++++++++++++++++++++++ python-orjson.spec | 7 +- 2 files changed, 3807 insertions(+), 2 deletions(-) create mode 100644 570.patch diff --git a/570.patch b/570.patch new file mode 100644 index 0000000..26c290d --- /dev/null +++ b/570.patch @@ -0,0 +1,3802 @@ +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 "] + keywords = ["pyo3", "python", "cpython", "ffi"] +@@ -36,7 +36,9 @@ abi3-py38 = ["abi3-py39"] + abi3-py39 = ["abi3-py310"] + abi3-py310 = ["abi3-py311"] + abi3-py311 = ["abi3-py312"] +-abi3-py312 = ["abi3"] ++abi3-py312 = ["abi3-py313"] ++abi3-py313 = ["abi3-py314"] ++abi3-py314 = ["abi3"] + + [package.metadata.docs.rs] + features = ["resolve-config"] +diff --git a/include/pyo3/pyo3-build-config/src/errors.rs b/include/pyo3/pyo3-build-config/src/errors.rs +index 87c59a99..b11d02fd 100644 +--- a/include/pyo3/pyo3-build-config/src/errors.rs ++++ b/include/pyo3/pyo3-build-config/src/errors.rs +@@ -68,7 +68,7 @@ impl std::fmt::Display for ErrorReport<'_> { + writeln!(f, "\ncaused by:")?; + let mut index = 0; + while let Some(some_source) = source { +- writeln!(f, " - {}: {}", index, some_source)?; ++ writeln!(f, " - {index}: {some_source}")?; + source = some_source.source(); + index += 1; + } +diff --git a/include/pyo3/pyo3-build-config/src/impl_.rs b/include/pyo3/pyo3-build-config/src/impl_.rs +index 0f90d573..9485b8a0 100644 +--- a/include/pyo3/pyo3-build-config/src/impl_.rs ++++ b/include/pyo3/pyo3-build-config/src/impl_.rs +@@ -40,7 +40,7 @@ const MINIMUM_SUPPORTED_VERSION_GRAALPY: PythonVersion = PythonVersion { + }; + + /// Maximum Python version that can be used as minimum required Python version with abi3. +-pub(crate) const ABI3_MAX_MINOR: u8 = 12; ++pub(crate) const ABI3_MAX_MINOR: u8 = 13; + + #[cfg(test)] + thread_local! { +@@ -58,7 +58,7 @@ pub fn cargo_env_var(var: &str) -> Option { + /// the variable changes. + pub fn env_var(var: &str) -> Option { + if cfg!(feature = "resolve-config") { +- println!("cargo:rerun-if-env-changed={}", var); ++ println!("cargo:rerun-if-env-changed={var}"); + } + #[cfg(test)] + { +@@ -180,9 +180,8 @@ impl InterpreterConfig { + let mut out = vec![]; + + for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { +- out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); ++ out.push(format!("cargo:rustc-cfg=Py_3_{i}")); + } +- println!("cargo::rustc-check-cfg=cfg(Py_3_14)"); + + match self.implementation { + PythonImplementation::CPython => {} +@@ -200,7 +199,7 @@ impl InterpreterConfig { + BuildFlag::Py_GIL_DISABLED => { + out.push("cargo:rustc-cfg=Py_GIL_DISABLED".to_owned()) + } +- flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag)), ++ flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{flag}\"")), + } + } + +@@ -339,7 +338,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) + + let lib_dir = if cfg!(windows) { + map.get("base_prefix") +- .map(|base_prefix| format!("{}\\libs", base_prefix)) ++ .map(|base_prefix| format!("{base_prefix}\\libs")) + } else { + map.get("libdir").cloned() + }; +@@ -667,7 +666,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) + write_option_line!(python_framework_prefix)?; + write_line!(suppress_build_script_link_lines)?; + for line in &self.extra_build_script_lines { +- writeln!(writer, "extra_build_script_line={}", line) ++ writeln!(writer, "extra_build_script_line={line}") + .context("failed to write extra_build_script_line")?; + } + Ok(()) +@@ -854,7 +853,7 @@ fn is_abi3() -> bool { + /// Must be called from a PyO3 crate build script. + pub fn get_abi3_version() -> Option { + let minor_version = (MINIMUM_SUPPORTED_VERSION.minor..=ABI3_MAX_MINOR) +- .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); ++ .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{i}")).is_some()); + minor_version.map(|minor| PythonVersion { major: 3, minor }) + } + +@@ -1122,8 +1121,8 @@ pub enum BuildFlag { + impl Display for BuildFlag { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { +- BuildFlag::Other(flag) => write!(f, "{}", flag), +- _ => write!(f, "{:?}", self), ++ BuildFlag::Other(flag) => write!(f, "{flag}"), ++ _ => write!(f, "{self:?}"), + } + } + } +@@ -1147,7 +1146,7 @@ impl FromStr for BuildFlag { + /// PyO3 will pick these up and pass to rustc via `--cfg=py_sys_config={varname}`; + /// this allows using them conditional cfg attributes in the .rs files, so + /// +-/// ```rust ++/// ```rust,no_run + /// #[cfg(py_sys_config="{varname}")] + /// # struct Foo; + /// ``` +@@ -1203,7 +1202,7 @@ impl BuildFlags { + + for k in &BuildFlags::ALL { + use std::fmt::Write; +- writeln!(&mut script, "print(config.get('{}', '0'))", k).unwrap(); ++ writeln!(&mut script, "print(config.get('{k}', '0'))").unwrap(); + } + + let stdout = run_python_script(interpreter.as_ref(), &script)?; +@@ -1241,7 +1240,7 @@ impl Display for BuildFlags { + } else { + write!(f, ",")?; + } +- write!(f, "{}", flag)?; ++ write!(f, "{flag}")?; + } + Ok(()) + } +@@ -1307,6 +1306,10 @@ pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result Result + + fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { + let pypy_version_pat = if let Some(v) = v { +- format!("pypy{}", v) ++ format!("pypy{v}") + } else { + "pypy3.".into() + }; +@@ -1433,7 +1436,7 @@ fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { + + fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { + let graalpy_version_pat = if let Some(v) = v { +- format!("graalpy{}", v) ++ format!("graalpy{v}") + } else { + "graalpy2".into() + }; +@@ -1442,7 +1445,7 @@ fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { + + fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { + let cpython_version_pat = if let Some(v) = v { +- format!("python{}", v) ++ format!("python{v}") + } else { + "python3.".into() + }; +@@ -1756,7 +1759,7 @@ fn default_lib_name_unix( + ) -> Result { + match implementation { + PythonImplementation::CPython => match ld_version { +- Some(ld_version) => Ok(format!("python{}", ld_version)), ++ Some(ld_version) => Ok(format!("python{ld_version}")), + None => { + if version > PythonVersion::PY37 { + // PEP 3149 ABI version tags are finally gone +@@ -1773,7 +1776,7 @@ fn default_lib_name_unix( + } + }, + PythonImplementation::PyPy => match ld_version { +- Some(ld_version) => Ok(format!("pypy{}-c", ld_version)), ++ Some(ld_version) => Ok(format!("pypy{ld_version}-c")), + None => Ok(format!("pypy{}.{}-c", version.major, version.minor)), + }, + +@@ -2002,7 +2005,7 @@ fn unescape(escaped: &str) -> Vec { + } + } + +- bytes.push(unhex(chunk[0]) << 4 | unhex(chunk[1])); ++ bytes.push((unhex(chunk[0]) << 4) | unhex(chunk[1])); + } + + bytes +diff --git a/include/pyo3/pyo3-build-config/src/lib.rs b/include/pyo3/pyo3-build-config/src/lib.rs +index 9070f6d7..f47c16f4 100644 +--- a/include/pyo3/pyo3-build-config/src/lib.rs ++++ b/include/pyo3/pyo3-build-config/src/lib.rs +@@ -46,7 +46,7 @@ use target_lexicon::OperatingSystem; + pub fn use_pyo3_cfgs() { + print_expected_cfgs(); + for cargo_command in get().build_script_outputs() { +- println!("{}", cargo_command) ++ println!("{cargo_command}") + } + } + +@@ -102,12 +102,7 @@ fn _add_python_framework_link_args( + ) { + if matches!(triple.operating_system, OperatingSystem::Darwin(_)) && link_libpython { + if let Some(framework_prefix) = interpreter_config.python_framework_prefix.as_ref() { +- writeln!( +- writer, +- "cargo:rustc-link-arg=-Wl,-rpath,{}", +- framework_prefix +- ) +- .unwrap(); ++ writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{framework_prefix}").unwrap(); + } + } + } +@@ -168,40 +163,36 @@ fn resolve_cross_compile_config_path() -> Option { + }) + } + ++/// Helper to print a feature cfg with a minimum rust version required. ++fn print_feature_cfg(minor_version_required: u32, cfg: &str) { ++ let minor_version = rustc_minor_version().unwrap_or(0); ++ ++ if minor_version >= minor_version_required { ++ println!("cargo:rustc-cfg={cfg}"); ++ } ++ ++ // rustc 1.80.0 stabilized `rustc-check-cfg` feature, don't emit before ++ if minor_version >= 80 { ++ println!("cargo:rustc-check-cfg=cfg({cfg})"); ++ } ++} ++ + /// Use certain features if we detect the compiler being used supports them. + /// + /// Features may be removed or added as MSRV gets bumped or new features become available, + /// so this function is unstable. + #[doc(hidden)] + pub fn print_feature_cfgs() { +- let rustc_minor_version = rustc_minor_version().unwrap_or(0); +- +- if rustc_minor_version >= 70 { +- println!("cargo:rustc-cfg=rustc_has_once_lock"); +- } +- +- // invalid_from_utf8 lint was added in Rust 1.74 +- if rustc_minor_version >= 74 { +- println!("cargo:rustc-cfg=invalid_from_utf8_lint"); +- } +- +- if rustc_minor_version >= 79 { +- println!("cargo:rustc-cfg=c_str_lit"); +- } +- ++ print_feature_cfg(70, "rustc_has_once_lock"); ++ print_feature_cfg(70, "cargo_toml_lints"); ++ print_feature_cfg(71, "rustc_has_extern_c_unwind"); ++ print_feature_cfg(74, "invalid_from_utf8_lint"); ++ print_feature_cfg(79, "c_str_lit"); + // Actually this is available on 1.78, but we should avoid + // https://github.com/rust-lang/rust/issues/124651 just in case +- if rustc_minor_version >= 79 { +- println!("cargo:rustc-cfg=diagnostic_namespace"); +- } +- +- if rustc_minor_version >= 83 { +- println!("cargo:rustc-cfg=io_error_more"); +- } +- +- if rustc_minor_version >= 85 { +- println!("cargo:rustc-cfg=fn_ptr_eq"); +- } ++ print_feature_cfg(79, "diagnostic_namespace"); ++ print_feature_cfg(83, "io_error_more"); ++ print_feature_cfg(85, "fn_ptr_eq"); + } + + /// Registers `pyo3`s config names as reachable cfg expressions +@@ -220,14 +211,8 @@ pub fn print_expected_cfgs() { + println!("cargo:rustc-check-cfg=cfg(PyPy)"); + println!("cargo:rustc-check-cfg=cfg(GraalPy)"); + println!("cargo:rustc-check-cfg=cfg(py_sys_config, values(\"Py_DEBUG\", \"Py_REF_DEBUG\", \"Py_TRACE_REFS\", \"COUNT_ALLOCS\"))"); +- println!("cargo:rustc-check-cfg=cfg(invalid_from_utf8_lint)"); + println!("cargo:rustc-check-cfg=cfg(pyo3_disable_reference_pool)"); + println!("cargo:rustc-check-cfg=cfg(pyo3_leak_on_drop_without_reference_pool)"); +- println!("cargo:rustc-check-cfg=cfg(diagnostic_namespace)"); +- println!("cargo:rustc-check-cfg=cfg(c_str_lit)"); +- println!("cargo:rustc-check-cfg=cfg(rustc_has_once_lock)"); +- println!("cargo:rustc-check-cfg=cfg(io_error_more)"); +- println!("cargo:rustc-check-cfg=cfg(fn_ptr_eq)"); + + // allow `Py_3_*` cfgs from the minimum supported version up to the + // maximum minor version (+1 for development for the next) +diff --git a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS +index 4502d777..8b20727d 100644 +--- a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS ++++ b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS +@@ -3,4 +3,4 @@ for binary compatibility, with additional metadata to support PyPy. + + For original implementations please see: + - https://github.com/python/cpython +- - https://foss.heptapod.net/pypy/pypy ++ - https://github.com/pypy/pypy +diff --git a/include/pyo3/pyo3-ffi/Cargo.toml b/include/pyo3/pyo3-ffi/Cargo.toml +index 14a4f958..73ea84ab 100644 +--- a/include/pyo3/pyo3-ffi/Cargo.toml ++++ b/include/pyo3/pyo3-ffi/Cargo.toml +@@ -1,6 +1,6 @@ + [package] + name = "pyo3-ffi" +-version = "0.23.3" ++version = "0.25.0" + description = "Python-API bindings for the PyO3 ecosystem" + authors = ["PyO3 Project and Contributors "] + keywords = ["pyo3", "python", "cpython", "ffi"] +@@ -12,7 +12,7 @@ edition = "2021" + links = "python" + + [dependencies] +-libc = "0.2" ++libc = "0.2.62" + + [features] + +@@ -32,7 +32,9 @@ abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"] + abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"] + abi3-py310 = ["abi3-py311", "pyo3-build-config/abi3-py310"] + abi3-py311 = ["abi3-py312", "pyo3-build-config/abi3-py311"] +-abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312"] ++abi3-py312 = ["abi3-py313", "pyo3-build-config/abi3-py312"] ++abi3-py313 = ["abi3-py314", "pyo3-build-config/abi3-py313"] ++abi3-py314 = ["abi3", "pyo3-build-config/abi3-py314"] + + # Automatically generates `python3.dll` import libraries for Windows targets. + generate-import-lib = ["pyo3-build-config/python3-dll-a"] +@@ -41,4 +43,4 @@ generate-import-lib = ["pyo3-build-config/python3-dll-a"] + paste = "1" + + [build-dependencies] +-pyo3-build-config = { path = "../pyo3-build-config", features = ["resolve-config"] } ++pyo3-build-config = { path = "../pyo3-build-config", version = "=0.25.0", features = ["resolve-config"] } +diff --git a/include/pyo3/pyo3-ffi/build.rs b/include/pyo3/pyo3-ffi/build.rs +index b0f1c28d..6776cd80 100644 +--- a/include/pyo3/pyo3-ffi/build.rs ++++ b/include/pyo3/pyo3-ffi/build.rs +@@ -4,9 +4,8 @@ use pyo3_build_config::{ + cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config, + InterpreterConfig, PythonVersion, + }, +- warn, BuildFlag, PythonImplementation, ++ warn, PythonImplementation, + }; +-use std::ops::Not; + + /// Minimum Python version PyO3 supports. + struct SupportedVersions { +@@ -18,15 +17,15 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions { + min: PythonVersion { major: 3, minor: 7 }, + max: PythonVersion { + major: 3, +- minor: 13, ++ minor: 14, + }, + }; + + const SUPPORTED_VERSIONS_PYPY: SupportedVersions = SupportedVersions { +- min: PythonVersion { major: 3, minor: 7 }, ++ min: PythonVersion { major: 3, minor: 9 }, + max: PythonVersion { + major: 3, +- minor: 10, ++ minor: 11, + }, + }; + +@@ -57,15 +56,22 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { + interpreter_config.version, + versions.min, + ); +- ensure!( +- interpreter_config.version <= versions.max || env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), +- "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ +- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ +- = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", +- interpreter_config.version, +- versions.max, +- std::env::var("CARGO_PKG_VERSION").unwrap(), +- ); ++ if interpreter_config.version > versions.max { ++ ensure!(!interpreter_config.is_free_threaded(), ++ "The configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ ++ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ ++ = help: The free-threaded build of CPython does not support the limited API so this check cannot be suppressed.", ++ interpreter_config.version, versions.max, std::env::var("CARGO_PKG_VERSION").unwrap() ++ ); ++ ensure!(env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), ++ "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ ++ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ ++ = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", ++ interpreter_config.version, ++ versions.max, ++ std::env::var("CARGO_PKG_VERSION").unwrap(), ++ ); ++ } + } + PythonImplementation::PyPy => { + let versions = SUPPORTED_VERSIONS_PYPY; +@@ -107,10 +113,16 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { + + if interpreter_config.abi3 { + match interpreter_config.implementation { +- PythonImplementation::CPython => {} ++ PythonImplementation::CPython => { ++ if interpreter_config.is_free_threaded() { ++ warn!( ++ "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." ++ ) ++ } ++ } + PythonImplementation::PyPy => warn!( + "PyPy does not yet support abi3 so the build artifacts will be version-specific. \ +- See https://foss.heptapod.net/pypy/pypy/-/issues/3397 for more information." ++ See https://github.com/pypy/pypy/issues/3397 for more information." + ), + PythonImplementation::GraalPy => warn!( + "GraalPy does not support abi3 so the build artifacts will be version-specific." +@@ -121,29 +133,6 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { + Ok(()) + } + +-fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> { +- let gil_enabled = interpreter_config +- .build_flags +- .0 +- .contains(&BuildFlag::Py_GIL_DISABLED) +- .not(); +- ensure!( +- gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"), +- "the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\ +- = help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\ +- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ +- = help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python", +- std::env::var("CARGO_PKG_VERSION").unwrap() +- ); +- if !gil_enabled && interpreter_config.abi3 { +- warn!( +- "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." +- ) +- } +- +- Ok(()) +-} +- + fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> { + if let Some(pointer_width) = interpreter_config.pointer_width { + // Try to check whether the target architecture matches the python library +@@ -187,7 +176,7 @@ fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> { + ); + + if let Some(lib_dir) = &interpreter_config.lib_dir { +- println!("cargo:rustc-link-search=native={}", lib_dir); ++ println!("cargo:rustc-link-search=native={lib_dir}"); + } + + Ok(()) +@@ -209,7 +198,6 @@ fn configure_pyo3() -> Result<()> { + + ensure_python_version(&interpreter_config)?; + ensure_target_pointer_width(&interpreter_config)?; +- ensure_gil_enabled(&interpreter_config)?; + + // Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var. + interpreter_config.to_cargo_dep_env()?; +@@ -219,12 +207,12 @@ fn configure_pyo3() -> Result<()> { + } + + for cfg in interpreter_config.build_script_outputs() { +- println!("{}", cfg) ++ println!("{cfg}") + } + + // Extra lines come last, to support last write wins. + for line in &interpreter_config.extra_build_script_lines { +- println!("{}", line); ++ println!("{line}"); + } + + // Emit cfgs like `invalid_from_utf8_lint` +diff --git a/include/pyo3/pyo3-ffi/src/abstract_.rs b/include/pyo3/pyo3-ffi/src/abstract_.rs +index ce6c9b94..84fb98a1 100644 +--- a/include/pyo3/pyo3-ffi/src/abstract_.rs ++++ b/include/pyo3/pyo3-ffi/src/abstract_.rs +@@ -1,15 +1,23 @@ + use crate::object::*; + use crate::pyport::Py_ssize_t; ++#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] ++use libc::size_t; + use std::os::raw::{c_char, c_int}; + + #[inline] +-#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h ++#[cfg(all( ++ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h ++ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ ++))] + pub unsafe fn PyObject_DelAttrString(o: *mut PyObject, attr_name: *const c_char) -> c_int { + PyObject_SetAttrString(o, attr_name, std::ptr::null_mut()) + } + + #[inline] +-#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h ++#[cfg(all( ++ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h ++ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ ++))] + pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_int { + PyObject_SetAttr(o, attr_name, std::ptr::null_mut()) + } +@@ -17,6 +25,7 @@ pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_ + extern "C" { + #[cfg(all( + not(PyPy), ++ not(GraalPy), + any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 + ))] + #[cfg_attr(PyPy, link_name = "PyPyObject_CallNoArgs")] +@@ -70,6 +79,28 @@ extern "C" { + method: *mut PyObject, + ... + ) -> *mut PyObject; ++} ++#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] ++pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = ++ 1 << (8 * std::mem::size_of::() as size_t - 1); ++ ++extern "C" { ++ #[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")] ++ #[cfg(any(Py_3_12, all(Py_3_11, not(Py_LIMITED_API))))] ++ pub fn PyObject_Vectorcall( ++ callable: *mut PyObject, ++ args: *const *mut PyObject, ++ nargsf: size_t, ++ kwnames: *mut PyObject, ++ ) -> *mut PyObject; ++ ++ #[cfg(any(Py_3_12, all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy)))))] ++ pub fn PyObject_VectorcallMethod( ++ name: *mut PyObject, ++ args: *const *mut PyObject, ++ nargsf: size_t, ++ kwnames: *mut PyObject, ++ ) -> *mut PyObject; + #[cfg_attr(PyPy, link_name = "PyPyObject_Type")] + pub fn PyObject_Type(o: *mut PyObject) -> *mut PyObject; + #[cfg_attr(PyPy, link_name = "PyPyObject_Size")] +@@ -119,7 +150,11 @@ extern "C" { + pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; + #[cfg(all(not(PyPy), Py_3_10))] + #[cfg_attr(PyPy, link_name = "PyPyIter_Send")] +- pub fn PyIter_Send(iter: *mut PyObject, arg: *mut PyObject, presult: *mut *mut PyObject); ++ pub fn PyIter_Send( ++ iter: *mut PyObject, ++ arg: *mut PyObject, ++ presult: *mut *mut PyObject, ++ ) -> PySendResult; + + #[cfg_attr(PyPy, link_name = "PyPyNumber_Check")] + pub fn PyNumber_Check(o: *mut PyObject) -> c_int; +diff --git a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs +index 24a97bcc..d27dfa8b 100644 +--- a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs ++++ b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs +@@ -17,7 +17,7 @@ pub struct PyByteArrayObject { + } + + #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +-opaque_struct!(PyByteArrayObject); ++opaque_struct!(pub PyByteArrayObject); + + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { +diff --git a/include/pyo3/pyo3-ffi/src/code.rs b/include/pyo3/pyo3-ffi/src/code.rs +index d28f68cd..296b17f6 100644 +--- a/include/pyo3/pyo3-ffi/src/code.rs ++++ b/include/pyo3/pyo3-ffi/src/code.rs +@@ -1,4 +1,4 @@ + // This header doesn't exist in CPython, but Include/cpython/code.h does. We add + // this here so that PyCodeObject has a definition under the limited API. + +-opaque_struct!(PyCodeObject); ++opaque_struct!(pub PyCodeObject); +diff --git a/include/pyo3/pyo3-ffi/src/compat/mod.rs b/include/pyo3/pyo3-ffi/src/compat/mod.rs +index 11f29128..044ea467 100644 +--- a/include/pyo3/pyo3-ffi/src/compat/mod.rs ++++ b/include/pyo3/pyo3-ffi/src/compat/mod.rs +@@ -52,8 +52,10 @@ macro_rules! compat_function { + + mod py_3_10; + mod py_3_13; ++mod py_3_14; + mod py_3_9; + + pub use self::py_3_10::*; + pub use self::py_3_13::*; ++pub use self::py_3_14::*; + pub use self::py_3_9::*; +diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs +index c6e8c2cb..d962fb3b 100644 +--- a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs ++++ b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs +@@ -17,3 +17,29 @@ compat_function!( + obj + } + ); ++ ++compat_function!( ++ originally_defined_for(Py_3_10); ++ ++ #[inline] ++ pub unsafe fn PyModule_AddObjectRef( ++ module: *mut crate::PyObject, ++ name: *const std::os::raw::c_char, ++ value: *mut crate::PyObject, ++ ) -> std::os::raw::c_int { ++ if value.is_null() && crate::PyErr_Occurred().is_null() { ++ crate::PyErr_SetString( ++ crate::PyExc_SystemError, ++ c_str!("PyModule_AddObjectRef() must be called with an exception raised if value is NULL").as_ptr(), ++ ); ++ return -1; ++ } ++ ++ crate::Py_XINCREF(value); ++ let result = crate::PyModule_AddObject(module, name, value); ++ if result < 0 { ++ crate::Py_XDECREF(value); ++ } ++ result ++ } ++); +diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs +index 59289cb7..96c90e7e 100644 +--- a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs ++++ b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs +@@ -104,3 +104,18 @@ compat_function!( + crate::PyList_SetSlice(list, 0, crate::PY_SSIZE_T_MAX, std::ptr::null_mut()) + } + ); ++ ++compat_function!( ++ originally_defined_for(Py_3_13); ++ ++ #[inline] ++ pub unsafe fn PyModule_Add( ++ module: *mut crate::PyObject, ++ name: *const std::os::raw::c_char, ++ value: *mut crate::PyObject, ++ ) -> std::os::raw::c_int { ++ let result = crate::compat::PyModule_AddObjectRef(module, name, value); ++ crate::Py_XDECREF(value); ++ result ++ } ++); +diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs +new file mode 100644 +index 00000000..6fdaef17 +--- /dev/null ++++ b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs +@@ -0,0 +1,26 @@ ++compat_function!( ++ originally_defined_for(all(Py_3_14, not(Py_LIMITED_API))); ++ ++ #[inline] ++ pub unsafe fn Py_HashBuffer( ++ ptr: *const std::ffi::c_void, ++ len: crate::Py_ssize_t, ++ ) -> crate::Py_hash_t { ++ #[cfg(not(any(Py_LIMITED_API, PyPy)))] ++ { ++ crate::_Py_HashBytes(ptr, len) ++ } ++ ++ #[cfg(any(Py_LIMITED_API, PyPy))] ++ { ++ let bytes = crate::PyBytes_FromStringAndSize(ptr as *const std::os::raw::c_char, len); ++ if bytes.is_null() { ++ -1 ++ } else { ++ let result = crate::PyObject_Hash(bytes); ++ crate::Py_DECREF(bytes); ++ result ++ } ++ } ++ } ++); +diff --git a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs +index 477ad02b..6ada1a75 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs +@@ -1,5 +1,5 @@ + use crate::{PyObject, Py_ssize_t}; +-#[cfg(not(all(Py_3_11, GraalPy)))] ++#[cfg(any(all(Py_3_8, not(any(PyPy, GraalPy))), not(Py_3_11)))] + use std::os::raw::c_char; + use std::os::raw::c_int; + +@@ -41,8 +41,8 @@ extern "C" { + ) -> *mut PyObject; + } + +-#[cfg(Py_3_8)] +-pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = ++#[cfg(Py_3_8)] // NB exported as public in abstract.rs from 3.12 ++const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = + 1 << (8 * std::mem::size_of::() as size_t - 1); + + #[cfg(Py_3_8)] +@@ -91,7 +91,7 @@ pub unsafe fn _PyObject_VectorcallTstate( + } + } + +-#[cfg(all(Py_3_8, not(any(PyPy, GraalPy))))] ++#[cfg(all(Py_3_8, not(any(PyPy, GraalPy, Py_3_11))))] // exported as a function from 3.11, see abstract.rs + #[inline(always)] + pub unsafe fn PyObject_Vectorcall( + callable: *mut PyObject, +@@ -103,16 +103,6 @@ pub unsafe fn PyObject_Vectorcall( + } + + extern "C" { +- #[cfg(all(PyPy, Py_3_8))] +- #[cfg_attr(not(Py_3_9), link_name = "_PyPyObject_Vectorcall")] +- #[cfg_attr(Py_3_9, link_name = "PyPyObject_Vectorcall")] +- pub fn PyObject_Vectorcall( +- callable: *mut PyObject, +- args: *const *mut PyObject, +- nargsf: size_t, +- kwnames: *mut PyObject, +- ) -> *mut PyObject; +- + #[cfg(Py_3_8)] + #[cfg_attr( + all(not(any(PyPy, GraalPy)), not(Py_3_9)), +@@ -187,23 +177,13 @@ pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *m + _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) + } + +-extern "C" { +- #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] +- pub fn PyObject_VectorcallMethod( +- name: *mut PyObject, +- args: *const *mut PyObject, +- nargsf: size_t, +- kwnames: *mut PyObject, +- ) -> *mut PyObject; +-} +- + #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] + #[inline(always)] + pub unsafe fn PyObject_CallMethodNoArgs( + self_: *mut PyObject, + name: *mut PyObject, + ) -> *mut PyObject { +- PyObject_VectorcallMethod( ++ crate::PyObject_VectorcallMethod( + name, + &self_, + 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, +@@ -220,7 +200,7 @@ pub unsafe fn PyObject_CallMethodOneArg( + ) -> *mut PyObject { + let args = [self_, arg]; + assert!(!arg.is_null()); +- PyObject_VectorcallMethod( ++ crate::PyObject_VectorcallMethod( + name, + args.as_ptr(), + 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, +diff --git a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs +index 306702de..dd78e646 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs +@@ -17,7 +17,7 @@ pub struct PyBytesObject { + } + + #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] +-opaque_struct!(PyBytesObject); ++opaque_struct!(pub PyBytesObject); + + extern "C" { + #[cfg_attr(PyPy, link_name = "_PyPyBytes_Resize")] +diff --git a/include/pyo3/pyo3-ffi/src/cpython/code.rs b/include/pyo3/pyo3-ffi/src/cpython/code.rs +index 230096ca..3d47a1bc 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/code.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/code.rs +@@ -1,207 +1,43 @@ + use crate::object::*; + use crate::pyport::Py_ssize_t; + +-#[allow(unused_imports)] +-use std::os::raw::{c_char, c_int, c_short, c_uchar, c_void}; ++#[cfg(not(GraalPy))] ++use std::os::raw::c_char; ++use std::os::raw::{c_int, c_void}; + #[cfg(not(any(PyPy, GraalPy)))] + use std::ptr::addr_of_mut; + +-#[cfg(all(Py_3_8, not(any(PyPy, GraalPy)), not(Py_3_11)))] +-opaque_struct!(_PyOpcache); ++// skipped private _PY_MONITORING_LOCAL_EVENTS ++// skipped private _PY_MONITORING_UNGROUPED_EVENTS ++// skipped private _PY_MONITORING_EVENTS + +-#[cfg(Py_3_12)] +-pub const _PY_MONITORING_LOCAL_EVENTS: usize = 10; +-#[cfg(Py_3_12)] +-pub const _PY_MONITORING_UNGROUPED_EVENTS: usize = 15; +-#[cfg(Py_3_12)] +-pub const _PY_MONITORING_EVENTS: usize = 17; ++// skipped private _PyLocalMonitors ++// skipped private _Py_GlobalMonitors + +-#[cfg(Py_3_12)] +-#[repr(C)] +-#[derive(Clone, Copy)] +-pub struct _Py_LocalMonitors { +- pub tools: [u8; if cfg!(Py_3_13) { +- _PY_MONITORING_LOCAL_EVENTS +- } else { +- _PY_MONITORING_UNGROUPED_EVENTS +- }], +-} +- +-#[cfg(Py_3_12)] +-#[repr(C)] +-#[derive(Clone, Copy)] +-pub struct _Py_GlobalMonitors { +- pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS], +-} +- +-// skipped _Py_CODEUNIT ++// skipped private _Py_CODEUNIT + +-// skipped _Py_OPCODE +-// skipped _Py_OPARG ++// skipped private _Py_OPCODE ++// skipped private _Py_OPARG + +-// skipped _py_make_codeunit ++// skipped private _py_make_codeunit + +-// skipped _py_set_opcode ++// skipped private _py_set_opcode + +-// skipped _Py_MAKE_CODEUNIT +-// skipped _Py_SET_OPCODE +- +-#[cfg(Py_3_12)] +-#[repr(C)] +-#[derive(Copy, Clone)] +-pub struct _PyCoCached { +- pub _co_code: *mut PyObject, +- pub _co_varnames: *mut PyObject, +- pub _co_cellvars: *mut PyObject, +- pub _co_freevars: *mut PyObject, +-} +- +-#[cfg(Py_3_12)] +-#[repr(C)] +-#[derive(Copy, Clone)] +-pub struct _PyCoLineInstrumentationData { +- pub original_opcode: u8, +- pub line_delta: i8, +-} ++// skipped private _Py_MAKE_CODEUNIT ++// skipped private _Py_SET_OPCODE + +-#[cfg(Py_3_12)] +-#[repr(C)] +-#[derive(Copy, Clone)] +-pub struct _PyCoMonitoringData { +- pub local_monitors: _Py_LocalMonitors, +- pub active_monitors: _Py_LocalMonitors, +- pub tools: *mut u8, +- pub lines: *mut _PyCoLineInstrumentationData, +- pub line_tools: *mut u8, +- pub per_instruction_opcodes: *mut u8, +- pub per_instruction_tools: *mut u8, +-} +- +-#[cfg(all(not(any(PyPy, GraalPy)), not(Py_3_7)))] +-opaque_struct!(PyCodeObject); +- +-#[cfg(all(not(any(PyPy, GraalPy)), Py_3_7, not(Py_3_8)))] +-#[repr(C)] +-pub struct PyCodeObject { +- pub ob_base: PyObject, +- pub co_argcount: c_int, +- pub co_kwonlyargcount: c_int, +- pub co_nlocals: c_int, +- pub co_stacksize: c_int, +- pub co_flags: c_int, +- pub co_firstlineno: c_int, +- pub co_code: *mut PyObject, +- pub co_consts: *mut PyObject, +- pub co_names: *mut PyObject, +- pub co_varnames: *mut PyObject, +- pub co_freevars: *mut PyObject, +- pub co_cellvars: *mut PyObject, +- pub co_cell2arg: *mut Py_ssize_t, +- pub co_filename: *mut PyObject, +- pub co_name: *mut PyObject, +- pub co_lnotab: *mut PyObject, +- pub co_zombieframe: *mut c_void, +- pub co_weakreflist: *mut PyObject, +- pub co_extra: *mut c_void, +-} +- +-#[cfg(Py_3_13)] +-opaque_struct!(_PyExecutorArray); +- +-#[cfg(all(not(any(PyPy, GraalPy)), Py_3_8, not(Py_3_11)))] +-#[repr(C)] +-pub struct PyCodeObject { +- pub ob_base: PyObject, +- pub co_argcount: c_int, +- pub co_posonlyargcount: c_int, +- pub co_kwonlyargcount: c_int, +- pub co_nlocals: c_int, +- pub co_stacksize: c_int, +- pub co_flags: c_int, +- pub co_firstlineno: c_int, +- pub co_code: *mut PyObject, +- pub co_consts: *mut PyObject, +- pub co_names: *mut PyObject, +- pub co_varnames: *mut PyObject, +- pub co_freevars: *mut PyObject, +- pub co_cellvars: *mut PyObject, +- pub co_cell2arg: *mut Py_ssize_t, +- pub co_filename: *mut PyObject, +- pub co_name: *mut PyObject, +- #[cfg(not(Py_3_10))] +- pub co_lnotab: *mut PyObject, +- #[cfg(Py_3_10)] +- pub co_linetable: *mut PyObject, +- pub co_zombieframe: *mut c_void, +- pub co_weakreflist: *mut PyObject, +- pub co_extra: *mut c_void, +- pub co_opcache_map: *mut c_uchar, +- pub co_opcache: *mut _PyOpcache, +- pub co_opcache_flag: c_int, +- pub co_opcache_size: c_uchar, +-} ++// skipped private _PyCoCached ++// skipped private _PyCoLineInstrumentationData ++// skipped private _PyCoMontoringData + +-#[cfg(all(not(any(PyPy, GraalPy)), Py_3_11))] +-#[repr(C)] +-pub struct PyCodeObject { +- pub ob_base: PyVarObject, +- pub co_consts: *mut PyObject, +- pub co_names: *mut PyObject, +- pub co_exceptiontable: *mut PyObject, +- pub co_flags: c_int, +- #[cfg(not(Py_3_12))] +- pub co_warmup: c_int, ++// skipped private _PyExecutorArray + +- pub co_argcount: c_int, +- pub co_posonlyargcount: c_int, +- pub co_kwonlyargcount: c_int, +- pub co_stacksize: c_int, +- pub co_firstlineno: c_int, +- +- pub co_nlocalsplus: c_int, +- #[cfg(Py_3_12)] +- pub co_framesize: c_int, +- pub co_nlocals: c_int, +- #[cfg(not(Py_3_12))] +- pub co_nplaincellvars: c_int, +- pub co_ncellvars: c_int, +- pub co_nfreevars: c_int, +- #[cfg(Py_3_12)] +- pub co_version: u32, +- +- pub co_localsplusnames: *mut PyObject, +- pub co_localspluskinds: *mut PyObject, +- pub co_filename: *mut PyObject, +- pub co_name: *mut PyObject, +- pub co_qualname: *mut PyObject, +- pub co_linetable: *mut PyObject, +- pub co_weakreflist: *mut PyObject, +- #[cfg(not(Py_3_12))] +- pub _co_code: *mut PyObject, +- #[cfg(not(Py_3_12))] +- pub _co_linearray: *mut c_char, +- #[cfg(Py_3_13)] +- pub co_executors: *mut _PyExecutorArray, +- #[cfg(Py_3_12)] +- pub _co_cached: *mut _PyCoCached, +- #[cfg(Py_3_12)] +- pub _co_instrumentation_version: u64, +- #[cfg(Py_3_12)] +- pub _co_monitoring: *mut _PyCoMonitoringData, +- pub _co_firsttraceable: c_int, +- pub co_extra: *mut c_void, +- pub co_code_adaptive: [c_char; 1], +-} +- +-#[cfg(PyPy)] +-#[repr(C)] +-pub struct PyCodeObject { +- pub ob_base: PyObject, +- pub co_name: *mut PyObject, +- pub co_filename: *mut PyObject, +- pub co_argcount: c_int, +- pub co_flags: c_int, +-} ++opaque_struct!( ++ #[doc = "A Python code object.\n"] ++ #[doc = "\n"] ++ #[doc = "`pyo3-ffi` does not expose the contents of this struct, as it has no stability guarantees."] ++ pub PyCodeObject ++); + + /* Masks for co_flags */ + pub const CO_OPTIMIZED: c_int = 0x0001; +@@ -247,28 +83,14 @@ pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == addr_of_mut!(PyCode_Type)) as c_int + } + +-#[inline] +-#[cfg(all(not(any(PyPy, GraalPy)), Py_3_10, not(Py_3_11)))] +-pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t { +- crate::PyTuple_GET_SIZE((*op).co_freevars) +-} +- +-#[inline] +-#[cfg(all(not(Py_3_10), Py_3_11, not(any(PyPy, GraalPy))))] +-pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> c_int { +- (*op).co_nfreevars +-} +- + extern "C" { + #[cfg(PyPy)] + #[link_name = "PyPyCode_Check"] + pub fn PyCode_Check(op: *mut PyObject) -> c_int; +- +- #[cfg(PyPy)] +- #[link_name = "PyPyCode_GetNumFree"] +- pub fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t; + } + ++// skipped PyCode_GetNumFree (requires knowledge of code object layout) ++ + extern "C" { + #[cfg(not(GraalPy))] + #[cfg_attr(PyPy, link_name = "PyPyCode_New")] +diff --git a/include/pyo3/pyo3-ffi/src/cpython/compile.rs b/include/pyo3/pyo3-ffi/src/cpython/compile.rs +index 79f06c92..078b5e0d 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/compile.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/compile.rs +@@ -6,19 +6,21 @@ use crate::pyarena::*; + use crate::pythonrun::*; + #[cfg(not(any(PyPy, Py_3_10)))] + use crate::PyCodeObject; ++use crate::INT_MAX; + #[cfg(not(any(PyPy, Py_3_10)))] + use std::os::raw::c_char; + use std::os::raw::c_int; + +-// skipped non-limited PyCF_MASK +-// skipped non-limited PyCF_MASK_OBSOLETE +-// skipped non-limited PyCF_SOURCE_IS_UTF8 +-// skipped non-limited PyCF_DONT_IMPLY_DEDENT +-// skipped non-limited PyCF_ONLY_AST +-// skipped non-limited PyCF_IGNORE_COOKIE +-// skipped non-limited PyCF_TYPE_COMMENTS +-// skipped non-limited PyCF_ALLOW_TOP_LEVEL_AWAIT +-// skipped non-limited PyCF_COMPILE_MASK ++// skipped PyCF_MASK ++// skipped PyCF_MASK_OBSOLETE ++// skipped PyCF_SOURCE_IS_UTF8 ++// skipped PyCF_DONT_IMPLY_DEDENT ++// skipped PyCF_ONLY_AST ++// skipped PyCF_IGNORE_COOKIE ++// skipped PyCF_TYPE_COMMENTS ++// skipped PyCF_ALLOW_TOP_LEVEL_AWAIT ++// skipped PyCF_OPTIMIZED_AST ++// skipped PyCF_COMPILE_MASK + + #[repr(C)] + #[derive(Copy, Clone)] +@@ -28,31 +30,23 @@ pub struct PyCompilerFlags { + pub cf_feature_version: c_int, + } + +-// skipped non-limited _PyCompilerFlags_INIT ++// skipped _PyCompilerFlags_INIT + +-#[cfg(all(Py_3_12, not(any(Py_3_13, PyPy, GraalPy))))] +-#[repr(C)] +-#[derive(Copy, Clone)] +-pub struct _PyCompilerSrcLocation { +- pub lineno: c_int, +- pub end_lineno: c_int, +- pub col_offset: c_int, +- pub end_col_offset: c_int, +-} +- +-// skipped SRC_LOCATION_FROM_AST +- +-#[cfg(not(any(PyPy, GraalPy, Py_3_13)))] ++// NB this type technically existed in the header until 3.13, when it was ++// moved to the internal CPython headers. ++// ++// We choose not to expose it in the public API past 3.10, as it is ++// not used in the public API past that point. ++#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] + #[repr(C)] + #[derive(Copy, Clone)] + pub struct PyFutureFeatures { + pub ff_features: c_int, +- #[cfg(not(Py_3_12))] + pub ff_lineno: c_int, +- #[cfg(Py_3_12)] +- pub ff_location: _PyCompilerSrcLocation, + } + ++// FIXME: these constants should probably be &CStr, if they are used at all ++ + pub const FUTURE_NESTED_SCOPES: &str = "nested_scopes"; + pub const FUTURE_GENERATORS: &str = "generators"; + pub const FUTURE_DIVISION: &str = "division"; +@@ -62,13 +56,12 @@ pub const FUTURE_PRINT_FUNCTION: &str = "print_function"; + pub const FUTURE_UNICODE_LITERALS: &str = "unicode_literals"; + pub const FUTURE_BARRY_AS_BDFL: &str = "barry_as_FLUFL"; + pub const FUTURE_GENERATOR_STOP: &str = "generator_stop"; +-// skipped non-limited FUTURE_ANNOTATIONS ++pub const FUTURE_ANNOTATIONS: &str = "annotations"; + ++#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] + extern "C" { +- #[cfg(not(any(PyPy, Py_3_10)))] + pub fn PyNode_Compile(arg1: *mut _node, arg2: *const c_char) -> *mut PyCodeObject; + +- #[cfg(not(any(PyPy, Py_3_10)))] + pub fn PyAST_CompileEx( + _mod: *mut _mod, + filename: *const c_char, +@@ -77,7 +70,6 @@ extern "C" { + arena: *mut PyArena, + ) -> *mut PyCodeObject; + +- #[cfg(not(any(PyPy, Py_3_10)))] + pub fn PyAST_CompileObject( + _mod: *mut _mod, + filename: *mut PyObject, +@@ -86,23 +78,20 @@ extern "C" { + arena: *mut PyArena, + ) -> *mut PyCodeObject; + +- #[cfg(not(any(PyPy, Py_3_10)))] + pub fn PyFuture_FromAST(_mod: *mut _mod, filename: *const c_char) -> *mut PyFutureFeatures; + +- #[cfg(not(any(PyPy, Py_3_10)))] + pub fn PyFuture_FromASTObject( + _mod: *mut _mod, + filename: *mut PyObject, + ) -> *mut PyFutureFeatures; ++} + +- // skipped non-limited _Py_Mangle +- // skipped non-limited PY_INVALID_STACK_EFFECT ++pub const PY_INVALID_STACK_EFFECT: c_int = INT_MAX; ++ ++extern "C" { + + pub fn PyCompile_OpcodeStackEffect(opcode: c_int, oparg: c_int) -> c_int; + + #[cfg(Py_3_8)] + pub fn PyCompile_OpcodeStackEffectWithJump(opcode: c_int, oparg: c_int, jump: c_int) -> c_int; +- +- // skipped non-limited _PyASTOptimizeState +- // skipped non-limited _PyAST_Optimize + } +diff --git a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs +index 1fc6591c..808dba87 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs +@@ -9,16 +9,6 @@ pub struct PyCriticalSection { + _cs_mutex: *mut PyMutex, + } + +-#[cfg(Py_GIL_DISABLED)] +-impl Default for PyCriticalSection { +- fn default() -> Self { +- PyCriticalSection { +- _cs_prev: 0, +- _cs_mutex: core::ptr::null_mut(), +- } +- } +-} +- + #[repr(C)] + #[cfg(Py_GIL_DISABLED)] + pub struct PyCriticalSection2 { +@@ -27,10 +17,10 @@ pub struct PyCriticalSection2 { + } + + #[cfg(not(Py_GIL_DISABLED))] +-opaque_struct!(PyCriticalSection); ++opaque_struct!(pub PyCriticalSection); + + #[cfg(not(Py_GIL_DISABLED))] +-opaque_struct!(PyCriticalSection2); ++opaque_struct!(pub PyCriticalSection2); + + extern "C" { + pub fn PyCriticalSection_Begin(c: *mut PyCriticalSection, op: *mut PyObject); +diff --git a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs +index 1b5ee466..7cef9bdb 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs +@@ -69,10 +69,7 @@ pub struct PyWrapperDescrObject { + pub d_wrapped: *mut c_void, + } + +-#[cfg_attr(windows, link(name = "pythonXY"))] +-extern "C" { +- pub static mut _PyMethodWrapper_Type: PyTypeObject; +-} ++// skipped _PyMethodWrapper_Type + + // skipped non-limited PyDescr_NewWrapper + // skipped non-limited PyDescr_IsData +diff --git a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs +index f67a7725..34b66a96 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs +@@ -2,10 +2,10 @@ use crate::object::*; + use crate::pyport::Py_ssize_t; + use std::os::raw::c_int; + +-opaque_struct!(PyDictKeysObject); ++opaque_struct!(pub PyDictKeysObject); + + #[cfg(Py_3_11)] +-opaque_struct!(PyDictValues); ++opaque_struct!(pub PyDictValues); + + #[cfg(not(GraalPy))] + #[repr(C)] +@@ -17,7 +17,10 @@ pub struct PyDictObject { + Py_3_12, + deprecated(note = "Deprecated in Python 3.12 and will be removed in the future.") + )] ++ #[cfg(not(Py_3_14))] + pub ma_version_tag: u64, ++ #[cfg(Py_3_14)] ++ _ma_watcher_tag: u64, + pub ma_keys: *mut PyDictKeysObject, + #[cfg(not(Py_3_11))] + pub ma_values: *mut *mut PyObject, +@@ -36,15 +39,6 @@ extern "C" { + item: *mut PyObject, + hash: crate::Py_hash_t, + ) -> c_int; +- +- #[cfg(Py_3_13)] +- pub fn _PyDict_SetItem_KnownHash_LockHeld( +- mp: *mut PyDictObject, +- name: *mut PyObject, +- value: *mut PyObject, +- hash: crate::Py_hash_t, +- ) -> c_int; +- + // skipped _PyDict_DelItem_KnownHash + // skipped _PyDict_DelItemIf + // skipped _PyDict_NewKeysForClass +diff --git a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs +index 6d2346f9..993e93c8 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs +@@ -53,7 +53,7 @@ pub struct PyFrameObject { + } + + #[cfg(any(PyPy, GraalPy, Py_3_11))] +-opaque_struct!(PyFrameObject); ++opaque_struct!(pub PyFrameObject); + + // skipped _PyFrame_IsRunnable + // skipped _PyFrame_IsExecuting +@@ -89,7 +89,8 @@ extern "C" { + pub fn PyFrame_FastToLocals(f: *mut PyFrameObject); + + // skipped _PyFrame_DebugMallocStats +- // skipped PyFrame_GetBack ++ #[cfg(all(Py_3_9, not(PyPy)))] ++ pub fn PyFrame_GetBack(f: *mut PyFrameObject) -> *mut PyFrameObject; + + #[cfg(not(Py_3_9))] + pub fn PyFrame_ClearFreeList() -> c_int; +diff --git a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs +index 25de30d5..cd2052de 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs +@@ -41,6 +41,8 @@ pub struct PyFunctionObject { + pub func_weakreflist: *mut PyObject, + pub func_module: *mut PyObject, + pub func_annotations: *mut PyObject, ++ #[cfg(Py_3_14)] ++ pub func_annotate: *mut PyObject, + #[cfg(Py_3_12)] + pub func_typeparams: *mut PyObject, + pub vectorcall: Option, +diff --git a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs +index 4be310a8..92f14d59 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs +@@ -1,13 +1,11 @@ + use crate::object::*; + use crate::PyFrameObject; +-#[cfg(not(any(PyPy, GraalPy)))] +-use crate::_PyErr_StackItem; +-#[cfg(all(Py_3_11, not(GraalPy)))] ++#[cfg(all(Py_3_11, not(any(PyPy, GraalPy, Py_3_14))))] + use std::os::raw::c_char; + use std::os::raw::c_int; + use std::ptr::addr_of_mut; + +-#[cfg(not(any(PyPy, GraalPy)))] ++#[cfg(not(any(PyPy, GraalPy, Py_3_14)))] + #[repr(C)] + pub struct PyGenObject { + pub ob_base: PyObject, +@@ -20,7 +18,7 @@ pub struct PyGenObject { + pub gi_weakreflist: *mut PyObject, + pub gi_name: *mut PyObject, + pub gi_qualname: *mut PyObject, +- pub gi_exc_state: _PyErr_StackItem, ++ pub gi_exc_state: crate::cpython::pystate::_PyErr_StackItem, + #[cfg(Py_3_11)] + pub gi_origin_or_finalizer: *mut PyObject, + #[cfg(Py_3_11)] +@@ -35,6 +33,9 @@ pub struct PyGenObject { + pub gi_iframe: [*mut PyObject; 1], + } + ++#[cfg(all(Py_3_14, not(any(PyPy, GraalPy))))] ++opaque_struct!(pub PyGenObject); ++ + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { + pub static mut PyGen_Type: PyTypeObject; +@@ -67,9 +68,10 @@ extern "C" { + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { + pub static mut PyCoro_Type: PyTypeObject; +- pub static mut _PyCoroWrapper_Type: PyTypeObject; + } + ++// skipped _PyCoroWrapper_Type ++ + #[inline] + pub unsafe fn PyCoro_CheckExact(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, addr_of_mut!(PyCoro_Type)) +diff --git a/include/pyo3/pyo3-ffi/src/cpython/import.rs b/include/pyo3/pyo3-ffi/src/cpython/import.rs +index 697d68a4..c8ef5ab4 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/import.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/import.rs +@@ -65,10 +65,8 @@ pub struct _frozen { + extern "C" { + #[cfg(not(PyPy))] + pub static mut PyImport_FrozenModules: *const _frozen; +- #[cfg(all(not(PyPy), Py_3_11))] +- pub static mut _PyImport_FrozenBootstrap: *const _frozen; +- #[cfg(all(not(PyPy), Py_3_11))] +- pub static mut _PyImport_FrozenStdlib: *const _frozen; +- #[cfg(all(not(PyPy), Py_3_11))] +- pub static mut _PyImport_FrozenTest: *const _frozen; + } ++ ++// skipped _PyImport_FrozenBootstrap ++// skipped _PyImport_FrozenStdlib ++// skipped _PyImport_FrozenTest +diff --git a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs +index 321d200e..076981f1 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs +@@ -93,6 +93,8 @@ pub struct PyConfig { + pub tracemalloc: c_int, + #[cfg(Py_3_12)] + pub perf_profiling: c_int, ++ #[cfg(Py_3_14)] ++ pub remote_debug: c_int, + pub import_time: c_int, + #[cfg(Py_3_11)] + pub code_debug_ranges: c_int, +@@ -141,10 +143,18 @@ pub struct PyConfig { + pub safe_path: c_int, + #[cfg(Py_3_12)] + pub int_max_str_digits: c_int, ++ #[cfg(Py_3_14)] ++ pub thread_inherit_context: c_int, ++ #[cfg(Py_3_14)] ++ pub context_aware_warnings: c_int, ++ #[cfg(all(Py_3_14, target_os = "macos"))] ++ pub use_system_logger: c_int, + #[cfg(Py_3_13)] + pub cpu_count: c_int, + #[cfg(Py_GIL_DISABLED)] + pub enable_gil: c_int, ++ #[cfg(all(Py_3_14, Py_GIL_DISABLED))] ++ pub tlbc_enabled: c_int, + pub pathconfig_warnings: c_int, + #[cfg(Py_3_10)] + pub program_name: *mut wchar_t, +diff --git a/include/pyo3/pyo3-ffi/src/cpython/lock.rs b/include/pyo3/pyo3-ffi/src/cpython/lock.rs +index c451666e..6c80b00d 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/lock.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/lock.rs +@@ -8,15 +8,6 @@ pub struct PyMutex { + pub(crate) _pin: PhantomPinned, + } + +-impl PyMutex { +- pub const fn new() -> PyMutex { +- PyMutex { +- _bits: AtomicU8::new(0), +- _pin: PhantomPinned, +- } +- } +-} +- + extern "C" { + pub fn PyMutex_Lock(m: *mut PyMutex); + pub fn PyMutex_Unlock(m: *mut PyMutex); +diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs +index 6063575e..45acaae5 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs +@@ -61,18 +61,6 @@ extern "C" { + is_signed: c_int, + ) -> *mut PyObject; + +- #[cfg(Py_3_13)] +- #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] +- pub fn _PyLong_AsByteArray( +- v: *mut PyLongObject, +- bytes: *mut c_uchar, +- n: size_t, +- little_endian: c_int, +- is_signed: c_int, +- with_exceptions: c_int, +- ) -> c_int; +- +- #[cfg(not(Py_3_13))] + #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] + pub fn _PyLong_AsByteArray( + v: *mut PyLongObject, +diff --git a/include/pyo3/pyo3-ffi/src/cpython/mod.rs b/include/pyo3/pyo3-ffi/src/cpython/mod.rs +index fe909f0c..adaf8bc8 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/mod.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/mod.rs +@@ -38,6 +38,7 @@ pub(crate) mod pythonrun; + // skipped sysmodule.h + pub(crate) mod floatobject; + pub(crate) mod pyframe; ++pub(crate) mod pyhash; + pub(crate) mod tupleobject; + pub(crate) mod unicodeobject; + pub(crate) mod weakrefobject; +@@ -71,8 +72,10 @@ pub use self::object::*; + pub use self::objimpl::*; + pub use self::pydebug::*; + pub use self::pyerrors::*; +-#[cfg(Py_3_11)] ++#[cfg(all(Py_3_11, not(PyPy)))] + pub use self::pyframe::*; ++#[cfg(any(not(PyPy), Py_3_13))] ++pub use self::pyhash::*; + #[cfg(all(Py_3_8, not(PyPy)))] + pub use self::pylifecycle::*; + pub use self::pymem::*; +diff --git a/include/pyo3/pyo3-ffi/src/cpython/object.rs b/include/pyo3/pyo3-ffi/src/cpython/object.rs +index 75eef11a..26ef784d 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/object.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/object.rs +@@ -310,10 +310,14 @@ pub struct PyHeapTypeObject { + pub ht_cached_keys: *mut c_void, + #[cfg(Py_3_9)] + pub ht_module: *mut object::PyObject, +- #[cfg(Py_3_11)] ++ #[cfg(all(Py_3_11, not(PyPy)))] + _ht_tpname: *mut c_char, +- #[cfg(Py_3_11)] ++ #[cfg(Py_3_14)] ++ pub ht_token: *mut c_void, ++ #[cfg(all(Py_3_11, not(PyPy)))] + _spec_cache: _specialization_cache, ++ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] ++ pub unique_id: Py_ssize_t, + } + + impl Default for PyHeapTypeObject { +diff --git a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs +index 98a19abe..14f7121a 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs +@@ -1,4 +1,4 @@ +-#[cfg(not(all(Py_3_11, GraalPy)))] ++#[cfg(not(all(Py_3_11, any(PyPy, GraalPy))))] + use libc::size_t; + use std::os::raw::c_int; + +diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs +index c6e10e5f..c9831669 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs +@@ -46,6 +46,8 @@ pub struct PySyntaxErrorObject { + pub end_offset: *mut PyObject, + pub text: *mut PyObject, + pub print_file_and_line: *mut PyObject, ++ #[cfg(Py_3_14)] ++ pub metadata: *mut PyObject, + } + + #[cfg(not(any(PyPy, GraalPy)))] +diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs +index d0cfa0a2..f0c38be4 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs +@@ -1,2 +1,3 @@ +-#[cfg(Py_3_11)] +-opaque_struct!(_PyInterpreterFrame); ++// NB used in `_PyEval_EvalFrameDefault`, maybe we remove this too. ++#[cfg(all(Py_3_11, not(PyPy)))] ++opaque_struct!(pub _PyInterpreterFrame); +diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs +new file mode 100644 +index 00000000..b746018a +--- /dev/null ++++ b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs +@@ -0,0 +1,38 @@ ++#[cfg(Py_3_14)] ++use crate::Py_ssize_t; ++#[cfg(Py_3_13)] ++use crate::{PyObject, Py_hash_t}; ++#[cfg(any(Py_3_13, not(PyPy)))] ++use std::os::raw::c_void; ++#[cfg(not(PyPy))] ++use std::os::raw::{c_char, c_int}; ++ ++#[cfg(not(PyPy))] ++#[repr(C)] ++#[derive(Copy, Clone)] ++pub struct PyHash_FuncDef { ++ pub hash: ++ Option crate::Py_hash_t>, ++ pub name: *const c_char, ++ pub hash_bits: c_int, ++ pub seed_bits: c_int, ++} ++ ++#[cfg(not(PyPy))] ++impl Default for PyHash_FuncDef { ++ #[inline] ++ fn default() -> Self { ++ unsafe { std::mem::zeroed() } ++ } ++} ++ ++extern "C" { ++ #[cfg(not(PyPy))] ++ pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; ++ #[cfg(Py_3_13)] ++ pub fn Py_HashPointer(ptr: *const c_void) -> Py_hash_t; ++ #[cfg(Py_3_13)] ++ pub fn PyObject_GenericHash(obj: *mut PyObject) -> Py_hash_t; ++ #[cfg(Py_3_14)] ++ pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; ++} +diff --git a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs +index 5481265b..b8f6fd66 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs +@@ -27,16 +27,18 @@ pub const PyTrace_OPCODE: c_int = 7; + // skipped PyTraceInfo + // skipped CFrame + ++/// Private structure used inline in `PyGenObject` + #[cfg(not(PyPy))] + #[repr(C)] + #[derive(Clone, Copy)] ++#[doc(hidden)] // TODO should be able to make pub(crate) after MSRV 1.74 + pub struct _PyErr_StackItem { + #[cfg(not(Py_3_11))] +- pub exc_type: *mut PyObject, +- pub exc_value: *mut PyObject, ++ exc_type: *mut PyObject, ++ exc_value: *mut PyObject, + #[cfg(not(Py_3_11))] +- pub exc_traceback: *mut PyObject, +- pub previous_item: *mut _PyErr_StackItem, ++ exc_traceback: *mut PyObject, ++ previous_item: *mut _PyErr_StackItem, + } + + // skipped _PyStackChunk +@@ -69,21 +71,21 @@ extern "C" { + pub fn PyThreadState_DeleteCurrent(); + } + +-#[cfg(all(Py_3_9, not(Py_3_11)))] ++#[cfg(all(Py_3_9, not(any(Py_3_11, PyPy))))] + pub type _PyFrameEvalFunction = extern "C" fn( + *mut crate::PyThreadState, + *mut crate::PyFrameObject, + c_int, + ) -> *mut crate::object::PyObject; + +-#[cfg(Py_3_11)] ++#[cfg(all(Py_3_11, not(PyPy)))] + pub type _PyFrameEvalFunction = extern "C" fn( + *mut crate::PyThreadState, + *mut crate::_PyInterpreterFrame, + c_int, + ) -> *mut crate::object::PyObject; + +-#[cfg(Py_3_9)] ++#[cfg(all(Py_3_9, not(PyPy)))] + extern "C" { + /// Get the frame evaluation function. + pub fn _PyInterpreterState_GetEvalFrameFunc( +diff --git a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs +index c06609fb..dc1bf8e4 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs +@@ -1,4 +1,6 @@ + use crate::object::*; ++#[cfg(Py_3_14)] ++use crate::pyport::Py_hash_t; + #[cfg(not(PyPy))] + use crate::pyport::Py_ssize_t; + +@@ -6,7 +8,7 @@ use crate::pyport::Py_ssize_t; + pub struct PyTupleObject { + pub ob_base: PyVarObject, + #[cfg(Py_3_14)] +- pub ob_hash: crate::Py_hash_t, ++ pub ob_hash: Py_hash_t, + pub ob_item: [*mut PyObject; 1], + } + +diff --git a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs +index 72da46c1..452c82e4 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs +@@ -1,4 +1,4 @@ +-#[cfg(not(PyPy))] ++#[cfg(any(Py_3_11, not(PyPy)))] + use crate::Py_hash_t; + use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; + use libc::wchar_t; +@@ -31,11 +31,13 @@ use std::os::raw::{c_char, c_int, c_uint, c_void}; + // skipped Py_UNICODE_LOW_SURROGATE + + // generated by bindgen v0.63.0 (with small adaptations) ++#[cfg(not(Py_3_14))] + #[repr(C)] + struct BitfieldUnit { + storage: Storage, + } + ++#[cfg(not(Py_3_14))] + impl BitfieldUnit { + #[inline] + pub const fn new(storage: Storage) -> Self { +@@ -43,7 +45,7 @@ impl BitfieldUnit { + } + } + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + impl BitfieldUnit + where + Storage: AsRef<[u8]> + AsMut<[u8]>, +@@ -117,30 +119,33 @@ where + } + } + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_INTERNED_INDEX: usize = 0; +-#[cfg(all(not(GraalPy), not(Py_3_14)))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_INTERNED_WIDTH: u8 = 2; + +-#[cfg(all(not(GraalPy), Py_3_14))] +-const STATE_INTERNED_WIDTH: u8 = 16; +- +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize; +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_KIND_WIDTH: u8 = 3; + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_COMPACT_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH) as usize; +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_COMPACT_WIDTH: u8 = 1; + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_ASCII_INDEX: usize = + (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH) as usize; +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + const STATE_ASCII_WIDTH: u8 = 1; + ++#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))] ++const STATE_STATICALLY_ALLOCATED_INDEX: usize = ++ (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; ++#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))] ++const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; ++ + #[cfg(not(any(Py_3_12, GraalPy)))] + const STATE_READY_INDEX: usize = + (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; +@@ -156,15 +161,15 @@ const STATE_READY_WIDTH: u8 = 1; + /// + /// Memory layout of C bitfields is implementation defined, so these functions are still + /// unsafe. Users must verify that they work as expected on the architectures they target. ++#[cfg(not(Py_3_14))] + #[repr(C)] +-#[repr(align(4))] + struct PyASCIIObjectState { + bitfield_align: [u8; 0], + bitfield: BitfieldUnit<[u8; 4usize]>, + } + + // c_uint and u32 are not necessarily the same type on all targets / architectures +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + #[allow(clippy::useless_transmute)] + impl PyASCIIObjectState { + #[inline] +@@ -218,6 +223,26 @@ impl PyASCIIObjectState { + .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) + } + ++ #[cfg(Py_3_12)] ++ #[inline] ++ unsafe fn statically_allocated(&self) -> c_uint { ++ std::mem::transmute(self.bitfield.get( ++ STATE_STATICALLY_ALLOCATED_INDEX, ++ STATE_STATICALLY_ALLOCATED_WIDTH, ++ ) as u32) ++ } ++ ++ #[cfg(Py_3_12)] ++ #[inline] ++ unsafe fn set_statically_allocated(&mut self, val: c_uint) { ++ let val: u32 = std::mem::transmute(val); ++ self.bitfield.set( ++ STATE_STATICALLY_ALLOCATED_INDEX, ++ STATE_STATICALLY_ALLOCATED_WIDTH, ++ val as u64, ++ ) ++ } ++ + #[cfg(not(Py_3_12))] + #[inline] + unsafe fn ready(&self) -> c_uint { +@@ -233,6 +258,7 @@ impl PyASCIIObjectState { + } + } + ++#[cfg(not(Py_3_14))] + impl From for PyASCIIObjectState { + #[inline] + fn from(value: u32) -> Self { +@@ -243,6 +269,7 @@ impl From for PyASCIIObjectState { + } + } + ++#[cfg(not(Py_3_14))] + impl From for u32 { + #[inline] + fn from(value: PyASCIIObjectState) -> Self { +@@ -254,26 +281,36 @@ impl From for u32 { + pub struct PyASCIIObject { + pub ob_base: PyObject, + pub length: Py_ssize_t, +- #[cfg(not(PyPy))] ++ #[cfg(any(Py_3_11, not(PyPy)))] + pub hash: Py_hash_t, + /// A bit field with various properties. + /// + /// Rust doesn't expose bitfields. So we have accessor functions for + /// retrieving values. + /// ++ /// Before 3.12: + /// unsigned int interned:2; // SSTATE_* constants. + /// unsigned int kind:3; // PyUnicode_*_KIND constants. + /// unsigned int compact:1; + /// unsigned int ascii:1; + /// unsigned int ready:1; + /// unsigned int :24; ++ /// ++ /// 3.12, and 3.13 ++ /// unsigned int interned:2; // SSTATE_* constants. ++ /// unsigned int kind:3; // PyUnicode_*_KIND constants. ++ /// unsigned int compact:1; ++ /// unsigned int ascii:1; ++ /// unsigned int statically_allocated:1; ++ /// unsigned int :24; ++ /// on 3.14 and higher PyO3 doesn't access the internal state + pub state: u32, + #[cfg(not(Py_3_12))] + pub wstr: *mut wchar_t, + } + + /// Interacting with the bitfield is not actually well-defined, so we mark these APIs unsafe. +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + impl PyASCIIObject { + #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 + /// Get the `interned` field of the [`PyASCIIObject`] state bitfield. +@@ -350,6 +387,7 @@ impl PyASCIIObject { + /// + /// Calling this function with an argument that is neither `0` nor `1` is invalid. + #[inline] ++ #[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] + pub unsafe fn set_ascii(&mut self, val: c_uint) { + let mut state = PyASCIIObjectState::from(self.state); + state.set_ascii(val); +@@ -375,6 +413,26 @@ impl PyASCIIObject { + state.set_ready(val); + self.state = u32::from(state); + } ++ ++ /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. ++ /// ++ /// Returns either `0` or `1`. ++ #[inline] ++ #[cfg(Py_3_12)] ++ pub unsafe fn statically_allocated(&self) -> c_uint { ++ PyASCIIObjectState::from(self.state).statically_allocated() ++ } ++ ++ /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. ++ /// ++ /// Calling this function with an argument that is neither `0` nor `1` is invalid. ++ #[inline] ++ #[cfg(Py_3_12)] ++ pub unsafe fn set_statically_allocated(&mut self, val: c_uint) { ++ let mut state = PyASCIIObjectState::from(self.state); ++ state.set_statically_allocated(val); ++ self.state = u32::from(state); ++ } + } + + #[repr(C)] +@@ -416,7 +474,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; + #[cfg(Py_3_12)] + pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + #[inline] + pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { + debug_assert!(crate::PyUnicode_Check(op) != 0); +@@ -426,13 +484,13 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { + (*(op as *mut PyASCIIObject)).ascii() + } + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + #[inline] + pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { + (*(op as *mut PyASCIIObject)).compact() + } + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + #[inline] + pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { + ((*(op as *mut PyASCIIObject)).ascii() != 0 && PyUnicode_IS_COMPACT(op) != 0).into() +@@ -464,7 +522,13 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { + PyUnicode_DATA(op) as *mut Py_UCS4 + } + +-#[cfg(not(GraalPy))] ++#[cfg(all(not(GraalPy), Py_3_14))] ++extern "C" { ++ #[cfg_attr(PyPy, link_name = "PyPyUnicode_KIND")] ++ pub fn PyUnicode_KIND(op: *mut PyObject) -> c_uint; ++} ++ ++#[cfg(all(not(GraalPy), not(Py_3_14)))] + #[inline] + pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { + debug_assert!(crate::PyUnicode_Check(op) != 0); +@@ -474,7 +538,7 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { + (*(op as *mut PyASCIIObject)).kind() + } + +-#[cfg(not(GraalPy))] ++#[cfg(not(any(GraalPy, Py_3_14)))] + #[inline] + pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { + if PyUnicode_IS_ASCII(op) != 0 { +@@ -492,7 +556,7 @@ pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void { + (*(op as *mut PyUnicodeObject)).data.any + } + +-#[cfg(not(any(GraalPy, PyPy)))] ++#[cfg(not(any(GraalPy, PyPy, Py_3_14)))] + #[inline] + pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { + debug_assert!(crate::PyUnicode_Check(op) != 0); +@@ -504,6 +568,13 @@ pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { + } + } + ++#[cfg(Py_3_14)] ++#[cfg(all(not(GraalPy), Py_3_14))] ++extern "C" { ++ #[cfg_attr(PyPy, link_name = "PyPyUnicode_DATA")] ++ pub fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void; ++} ++ + // skipped PyUnicode_WRITE + // skipped PyUnicode_READ + // skipped PyUnicode_READ_CHAR +diff --git a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs +index 88bb501b..1c50c7a7 100644 +--- a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs ++++ b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs +@@ -1,3 +1,4 @@ ++// NB publicly re-exported in `src/weakrefobject.rs` + #[cfg(not(any(PyPy, GraalPy)))] + pub struct _PyWeakReference { + pub ob_base: crate::PyObject, +diff --git a/include/pyo3/pyo3-ffi/src/datetime.rs b/include/pyo3/pyo3-ffi/src/datetime.rs +index e529e0fc..4db1bdd1 100644 +--- a/include/pyo3/pyo3-ffi/src/datetime.rs ++++ b/include/pyo3/pyo3-ffi/src/datetime.rs +@@ -119,7 +119,7 @@ pub struct PyDateTime_DateTime { + pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { + // This should work for Date or DateTime + let data = (*(o as *mut PyDateTime_Date)).data; +- c_int::from(data[0]) << 8 | c_int::from(data[1]) ++ (c_int::from(data[0]) << 8) | c_int::from(data[1]) + } + + #[inline] +@@ -487,7 +487,9 @@ extern "C" { + pub fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int; + #[link_name = "PyPyDateTime_GET_FOLD"] + pub fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_int; +- // skipped PyDateTime_DATE_GET_TZINFO (not in PyPy) ++ #[link_name = "PyPyDateTime_DATE_GET_TZINFO"] ++ #[cfg(Py_3_10)] ++ pub fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; + + #[link_name = "PyPyDateTime_TIME_GET_HOUR"] + pub fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int; +@@ -499,7 +501,9 @@ extern "C" { + pub fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int; + #[link_name = "PyPyDateTime_TIME_GET_FOLD"] + pub fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_int; +- // skipped PyDateTime_TIME_GET_TZINFO (not in PyPy) ++ #[link_name = "PyPyDateTime_TIME_GET_TZINFO"] ++ #[cfg(Py_3_10)] ++ pub fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; + + #[link_name = "PyPyDateTime_DELTA_GET_DAYS"] + pub fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int; +diff --git a/include/pyo3/pyo3-ffi/src/dictobject.rs b/include/pyo3/pyo3-ffi/src/dictobject.rs +index 710be802..e609352a 100644 +--- a/include/pyo3/pyo3-ffi/src/dictobject.rs ++++ b/include/pyo3/pyo3-ffi/src/dictobject.rs +@@ -118,4 +118,4 @@ extern "C" { + + #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] + // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) +-opaque_struct!(PyDictObject); ++opaque_struct!(pub PyDictObject); +diff --git a/include/pyo3/pyo3-ffi/src/floatobject.rs b/include/pyo3/pyo3-ffi/src/floatobject.rs +index 65fc1d4c..4e1d6476 100644 +--- a/include/pyo3/pyo3-ffi/src/floatobject.rs ++++ b/include/pyo3/pyo3-ffi/src/floatobject.rs +@@ -4,7 +4,7 @@ use std::ptr::addr_of_mut; + + #[cfg(Py_LIMITED_API)] + // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) +-opaque_struct!(PyFloatObject); ++opaque_struct!(pub PyFloatObject); + + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { +diff --git a/include/pyo3/pyo3-ffi/src/genericaliasobject.rs b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs +new file mode 100644 +index 00000000..7979d7d8 +--- /dev/null ++++ b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs +@@ -0,0 +1,12 @@ ++#[cfg(Py_3_9)] ++use crate::object::{PyObject, PyTypeObject}; ++ ++#[cfg_attr(windows, link(name = "pythonXY"))] ++extern "C" { ++ #[cfg(Py_3_9)] ++ #[cfg_attr(PyPy, link_name = "PyPy_GenericAlias")] ++ pub fn Py_GenericAlias(origin: *mut PyObject, args: *mut PyObject) -> *mut PyObject; ++ ++ #[cfg(Py_3_9)] ++ pub static mut Py_GenericAliasType: PyTypeObject; ++} +diff --git a/include/pyo3/pyo3-ffi/src/lib.rs b/include/pyo3/pyo3-ffi/src/lib.rs +index 7bdba117..f78c918a 100644 +--- a/include/pyo3/pyo3-ffi/src/lib.rs ++++ b/include/pyo3/pyo3-ffi/src/lib.rs +@@ -129,7 +129,7 @@ + //! ``` + //! + //! **`src/lib.rs`** +-//! ```rust ++//! ```rust,no_run + //! use std::os::raw::{c_char, c_long}; + //! use std::ptr; + //! +@@ -327,17 +327,24 @@ + non_snake_case, + non_upper_case_globals, + clippy::upper_case_acronyms, +- clippy::missing_safety_doc ++ clippy::missing_safety_doc, ++ clippy::ptr_eq + )] + #![warn(elided_lifetimes_in_paths, unused_lifetimes)] ++// This crate is a hand-maintained translation of CPython's headers, so requiring "unsafe" ++// blocks within those translations increases maintenance burden without providing any ++// additional safety. The safety of the functions in this crate is determined by the ++// original CPython headers ++#![allow(unsafe_op_in_unsafe_fn)] + + // Until `extern type` is stabilized, use the recommended approach to + // model opaque types: + // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs + macro_rules! opaque_struct { +- ($name:ident) => { ++ ($(#[$attrs:meta])* $pub:vis $name:ident) => { ++ $(#[$attrs])* + #[repr(C)] +- pub struct $name([u8; 0]); ++ $pub struct $name([u8; 0]); + }; + } + +@@ -351,7 +358,7 @@ macro_rules! opaque_struct { + /// + /// Examples: + /// +-/// ```rust ++/// ```rust,no_run + /// use std::ffi::CStr; + /// + /// const HELLO: &CStr = pyo3_ffi::c_str!("hello"); +@@ -410,6 +417,8 @@ pub use self::enumobject::*; + pub use self::fileobject::*; + pub use self::fileutils::*; + pub use self::floatobject::*; ++#[cfg(Py_3_9)] ++pub use self::genericaliasobject::*; + pub use self::import::*; + pub use self::intrcheck::*; + pub use self::iterobject::*; +@@ -439,6 +448,7 @@ pub use self::pystate::*; + pub use self::pystrtod::*; + pub use self::pythonrun::*; + pub use self::rangeobject::*; ++pub use self::refcount::*; + pub use self::setobject::*; + pub use self::sliceobject::*; + pub use self::structseq::*; +@@ -479,7 +489,7 @@ mod fileobject; + mod fileutils; + mod floatobject; + // skipped empty frameobject.h +-// skipped genericaliasobject.h ++mod genericaliasobject; + mod import; + // skipped interpreteridobject.h + mod intrcheck; +@@ -531,6 +541,7 @@ mod pystrtod; + // skipped pythread.h + // skipped pytime.h + mod rangeobject; ++mod refcount; + mod setobject; + mod sliceobject; + mod structseq; +diff --git a/include/pyo3/pyo3-ffi/src/longobject.rs b/include/pyo3/pyo3-ffi/src/longobject.rs +index 68b4ecba..eca0af3d 100644 +--- a/include/pyo3/pyo3-ffi/src/longobject.rs ++++ b/include/pyo3/pyo3-ffi/src/longobject.rs +@@ -4,7 +4,7 @@ use libc::size_t; + use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void}; + use std::ptr::addr_of_mut; + +-opaque_struct!(PyLongObject); ++opaque_struct!(pub PyLongObject); + + #[inline] + pub unsafe fn PyLong_Check(op: *mut PyObject) -> c_int { +diff --git a/include/pyo3/pyo3-ffi/src/memoryobject.rs b/include/pyo3/pyo3-ffi/src/memoryobject.rs +index b7ef9e2e..4e1e50c6 100644 +--- a/include/pyo3/pyo3-ffi/src/memoryobject.rs ++++ b/include/pyo3/pyo3-ffi/src/memoryobject.rs +@@ -3,11 +3,10 @@ use crate::pyport::Py_ssize_t; + use std::os::raw::{c_char, c_int}; + use std::ptr::addr_of_mut; + ++// skipped _PyManagedBuffer_Type ++ + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { +- #[cfg(not(Py_LIMITED_API))] +- pub static mut _PyManagedBuffer_Type: PyTypeObject; +- + #[cfg_attr(PyPy, link_name = "PyPyMemoryView_Type")] + pub static mut PyMemoryView_Type: PyTypeObject; + } +diff --git a/include/pyo3/pyo3-ffi/src/methodobject.rs b/include/pyo3/pyo3-ffi/src/methodobject.rs +index 37e1e206..3dfbbb5a 100644 +--- a/include/pyo3/pyo3-ffi/src/methodobject.rs ++++ b/include/pyo3/pyo3-ffi/src/methodobject.rs +@@ -50,7 +50,7 @@ pub type PyCFunctionFast = unsafe extern "C" fn( + ) -> *mut PyObject; + + #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] +-#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] ++#[deprecated(note = "renamed to `PyCFunctionFast`")] + pub type _PyCFunctionFast = PyCFunctionFast; + + pub type PyCFunctionWithKeywords = unsafe extern "C" fn( +@@ -68,7 +68,7 @@ pub type PyCFunctionFastWithKeywords = unsafe extern "C" fn( + ) -> *mut PyObject; + + #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] +-#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] ++#[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] + pub type _PyCFunctionFastWithKeywords = PyCFunctionFastWithKeywords; + + #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] +@@ -153,7 +153,7 @@ pub union PyMethodDefPointer { + + /// This variant corresponds with [`METH_FASTCALL`]. + #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] +- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] ++ #[deprecated(note = "renamed to `PyCFunctionFast`")] + pub _PyCFunctionFast: PyCFunctionFast, + + /// This variant corresponds with [`METH_FASTCALL`]. +@@ -162,7 +162,7 @@ pub union PyMethodDefPointer { + + /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. + #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] +- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] ++ #[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] + pub _PyCFunctionFastWithKeywords: PyCFunctionFastWithKeywords, + + /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. +diff --git a/include/pyo3/pyo3-ffi/src/modsupport.rs b/include/pyo3/pyo3-ffi/src/modsupport.rs +index 6da2795b..56a68fe2 100644 +--- a/include/pyo3/pyo3-ffi/src/modsupport.rs ++++ b/include/pyo3/pyo3-ffi/src/modsupport.rs +@@ -37,12 +37,7 @@ extern "C" { + // skipped Py_VaBuildValue + + #[cfg(Py_3_13)] +- pub fn PyModule_Add( +- module: *mut PyObject, +- name: *const c_char, +- value: *mut PyObject, +- ) -> core::ffi::c_int; +- ++ pub fn PyModule_Add(module: *mut PyObject, name: *const c_char, value: *mut PyObject) -> c_int; + #[cfg(Py_3_10)] + #[cfg_attr(PyPy, link_name = "PyPyModule_AddObjectRef")] + pub fn PyModule_AddObjectRef( +diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs +index 51083a8e..5fbf45db 100644 +--- a/include/pyo3/pyo3-ffi/src/object.rs ++++ b/include/pyo3/pyo3-ffi/src/object.rs +@@ -1,5 +1,7 @@ + use crate::pyport::{Py_hash_t, Py_ssize_t}; + #[cfg(Py_GIL_DISABLED)] ++use crate::refcount; ++#[cfg(Py_GIL_DISABLED)] + use crate::PyMutex; + #[cfg(Py_GIL_DISABLED)] + use std::marker::PhantomPinned; +@@ -7,81 +9,50 @@ use std::mem; + use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void}; + use std::ptr; + #[cfg(Py_GIL_DISABLED)] +-use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8, Ordering::Relaxed}; ++use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8}; + + #[cfg(Py_LIMITED_API)] +-opaque_struct!(PyTypeObject); ++opaque_struct!(pub PyTypeObject); + + #[cfg(not(Py_LIMITED_API))] + pub use crate::cpython::object::PyTypeObject; + +-#[cfg(Py_3_12)] +-const _Py_IMMORTAL_REFCNT: Py_ssize_t = { +- if cfg!(target_pointer_width = "64") { +- c_uint::MAX as Py_ssize_t +- } else { +- // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) +- (c_uint::MAX >> 2) as Py_ssize_t +- } +-}; +- +-#[cfg(Py_GIL_DISABLED)] +-const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; +- +-#[allow(clippy::declare_interior_mutable_const)] +-pub const PyObject_HEAD_INIT: PyObject = PyObject { +- #[cfg(py_sys_config = "Py_TRACE_REFS")] +- _ob_next: std::ptr::null_mut(), +- #[cfg(py_sys_config = "Py_TRACE_REFS")] +- _ob_prev: std::ptr::null_mut(), +- #[cfg(Py_GIL_DISABLED)] +- ob_tid: 0, +- #[cfg(Py_GIL_DISABLED)] +- _padding: 0, +- #[cfg(Py_GIL_DISABLED)] +- ob_mutex: PyMutex { +- _bits: AtomicU8::new(0), +- _pin: PhantomPinned, +- }, +- #[cfg(Py_GIL_DISABLED)] +- ob_gc_bits: 0, +- #[cfg(Py_GIL_DISABLED)] +- ob_ref_local: AtomicU32::new(_Py_IMMORTAL_REFCNT_LOCAL), +- #[cfg(Py_GIL_DISABLED)] +- ob_ref_shared: AtomicIsize::new(0), +- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] +- ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, +- #[cfg(not(Py_3_12))] +- ob_refcnt: 1, +- #[cfg(PyPy)] +- ob_pypy_link: 0, +- ob_type: std::ptr::null_mut(), +-}; +- +-// skipped PyObject_VAR_HEAD +-// skipped Py_INVALID_SIZE +- +-// skipped private _Py_UNOWNED_TID ++// skip PyObject_HEAD + +-#[cfg(Py_GIL_DISABLED)] +-const _Py_REF_SHARED_SHIFT: isize = 2; +-// skipped private _Py_REF_SHARED_FLAG_MASK +- +-// skipped private _Py_REF_SHARED_INIT +-// skipped private _Py_REF_MAYBE_WEAKREF +-// skipped private _Py_REF_QUEUED +-// skipped private _Py_REF_MERGED ++#[repr(C)] ++#[derive(Copy, Clone)] ++#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "big"))] ++/// This struct is anonymous in CPython, so the name was given by PyO3 because ++/// Rust structs need a name. ++pub struct PyObjectObFlagsAndRefcnt { ++ pub ob_flags: u16, ++ pub ob_overflow: u16, ++ pub ob_refcnt: u32, ++} + +-// skipped private _Py_REF_SHARED ++#[repr(C)] ++#[derive(Copy, Clone)] ++#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "little"))] ++/// This struct is anonymous in CPython, so the name was given by PyO3 because ++/// Rust structs need a name. ++pub struct PyObjectObFlagsAndRefcnt { ++ pub ob_refcnt: u32, ++ pub ob_overflow: u16, ++ pub ob_flags: u16, ++} + + #[repr(C)] + #[derive(Copy, Clone)] + #[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] + /// This union is anonymous in CPython, so the name was given by PyO3 because +-/// Rust unions need a name. ++/// Rust union need a name. + pub union PyObjectObRefcnt { ++ #[cfg(all(target_pointer_width = "64", Py_3_14))] ++ pub ob_refcnt_full: crate::PY_INT64_T, ++ #[cfg(Py_3_14)] ++ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, + pub ob_refcnt: Py_ssize_t, +- #[cfg(target_pointer_width = "64")] ++ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] + pub ob_refcnt_split: [crate::PY_UINT32_T; 2], + } + +@@ -95,6 +66,9 @@ impl std::fmt::Debug for PyObjectObRefcnt { + #[cfg(all(not(Py_3_12), not(Py_GIL_DISABLED)))] + pub type PyObjectObRefcnt = Py_ssize_t; + ++// PyObject_HEAD_INIT comes before the PyObject definition in object.h ++// but we put it after PyObject because HEAD_INIT uses PyObject ++ + #[repr(C)] + #[derive(Debug)] + pub struct PyObject { +@@ -104,8 +78,10 @@ pub struct PyObject { + pub _ob_prev: *mut PyObject, + #[cfg(Py_GIL_DISABLED)] + pub ob_tid: libc::uintptr_t, +- #[cfg(Py_GIL_DISABLED)] ++ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] + pub _padding: u16, ++ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] ++ pub ob_flags: u16, + #[cfg(Py_GIL_DISABLED)] + pub ob_mutex: PyMutex, // per-object lock + #[cfg(Py_GIL_DISABLED)] +@@ -121,7 +97,41 @@ pub struct PyObject { + pub ob_type: *mut PyTypeObject, + } + +-// skipped private _PyObject_CAST ++#[allow(clippy::declare_interior_mutable_const)] ++pub const PyObject_HEAD_INIT: PyObject = PyObject { ++ #[cfg(py_sys_config = "Py_TRACE_REFS")] ++ _ob_next: std::ptr::null_mut(), ++ #[cfg(py_sys_config = "Py_TRACE_REFS")] ++ _ob_prev: std::ptr::null_mut(), ++ #[cfg(Py_GIL_DISABLED)] ++ ob_tid: 0, ++ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] ++ ob_flags: 0, ++ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] ++ _padding: 0, ++ #[cfg(Py_GIL_DISABLED)] ++ ob_mutex: PyMutex { ++ _bits: AtomicU8::new(0), ++ _pin: PhantomPinned, ++ }, ++ #[cfg(Py_GIL_DISABLED)] ++ ob_gc_bits: 0, ++ #[cfg(Py_GIL_DISABLED)] ++ ob_ref_local: AtomicU32::new(refcount::_Py_IMMORTAL_REFCNT_LOCAL), ++ #[cfg(Py_GIL_DISABLED)] ++ ob_ref_shared: AtomicIsize::new(0), ++ #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] ++ ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, ++ #[cfg(not(Py_3_12))] ++ ob_refcnt: 1, ++ #[cfg(PyPy)] ++ ob_pypy_link: 0, ++ ob_type: std::ptr::null_mut(), ++}; ++ ++// skipped _Py_UNOWNED_TID ++ ++// skipped _PyObject_CAST + + #[repr(C)] + #[derive(Debug)] +@@ -150,41 +160,23 @@ extern "C" { + pub fn Py_Is(x: *mut PyObject, y: *mut PyObject) -> c_int; + } + +-// skipped private _Py_GetThreadLocal_Addr ++// skipped _Py_GetThreadLocal_Addr + +-// skipped private _Py_ThreadId ++// skipped _Py_ThreadID + +-// skipped private _Py_IsOwnedByCurrentThread ++// skipped _Py_IsOwnedByCurrentThread + +-#[inline] +-pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { +- #[cfg(Py_GIL_DISABLED)] +- { +- let local = (*ob).ob_ref_local.load(Relaxed); +- if local == _Py_IMMORTAL_REFCNT_LOCAL { +- return _Py_IMMORTAL_REFCNT; +- } +- let shared = (*ob).ob_ref_shared.load(Relaxed); +- local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) +- } +- +- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] +- { +- (*ob).ob_refcnt.ob_refcnt +- } +- +- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] +- { +- (*ob).ob_refcnt +- } ++#[cfg(GraalPy)] ++extern "C" { ++ #[cfg(GraalPy)] ++ fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; + +- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] +- { +- _Py_REFCNT(ob) +- } ++ #[cfg(GraalPy)] ++ fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; + } + + #[inline] ++#[cfg(not(Py_3_14))] + pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { + #[cfg(not(GraalPy))] + return (*ob).ob_type; +@@ -192,6 +184,15 @@ pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { + return _Py_TYPE(ob); + } + ++#[cfg_attr(windows, link(name = "pythonXY"))] ++#[cfg(Py_3_14)] ++extern "C" { ++ #[cfg_attr(PyPy, link_name = "PyPy_TYPE")] ++ pub fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject; ++} ++ ++// skip _Py_TYPE compat shim ++ + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { + #[cfg_attr(PyPy, link_name = "PyPyLong_Type")] +@@ -212,29 +213,11 @@ pub unsafe fn Py_SIZE(ob: *mut PyObject) -> Py_ssize_t { + _Py_SIZE(ob) + } + +-#[inline(always)] +-#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] +-pub unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { +- #[cfg(target_pointer_width = "64")] +- { +- (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int +- } +- +- #[cfg(target_pointer_width = "32")] +- { +- ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int +- } +-} +- + #[inline] + pub unsafe fn Py_IS_TYPE(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { + (Py_TYPE(ob) == tp) as c_int + } + +-// skipped _Py_SetRefCnt +- +-// skipped Py_SET_REFCNT +- + // skipped Py_SET_TYPE + + // skipped Py_SET_SIZE +@@ -436,7 +419,7 @@ extern "C" { + arg2: *const c_char, + arg3: *mut PyObject, + ) -> c_int; +- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h ++ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h + #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttrString")] + pub fn PyObject_DelAttrString(arg1: *mut PyObject, arg2: *const c_char) -> c_int; + #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttrString")] +@@ -460,7 +443,7 @@ extern "C" { + #[cfg_attr(PyPy, link_name = "PyPyObject_SetAttr")] + pub fn PyObject_SetAttr(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) + -> c_int; +- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h ++ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h + #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttr")] + pub fn PyObject_DelAttr(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int; + #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttr")] +@@ -586,222 +569,6 @@ pub const Py_TPFLAGS_DEFAULT: c_ulong = if cfg!(Py_3_10) { + pub const Py_TPFLAGS_HAVE_FINALIZE: c_ulong = 1; + pub const Py_TPFLAGS_HAVE_VERSION_TAG: c_ulong = 1 << 18; + +-extern "C" { +- #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] +- fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); +- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] +- fn _Py_INCREF_IncRefTotal(); +- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] +- fn _Py_DECREF_DecRefTotal(); +- +- #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] +- fn _Py_Dealloc(arg1: *mut PyObject); +- +- #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] +- #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] +- pub fn Py_IncRef(o: *mut PyObject); +- #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] +- #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] +- pub fn Py_DecRef(o: *mut PyObject); +- +- #[cfg(all(Py_3_10, not(PyPy)))] +- fn _Py_IncRef(o: *mut PyObject); +- #[cfg(all(Py_3_10, not(PyPy)))] +- fn _Py_DecRef(o: *mut PyObject); +- +- #[cfg(GraalPy)] +- fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; +- +- #[cfg(GraalPy)] +- fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; +- +- #[cfg(GraalPy)] +- fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; +-} +- +-#[inline(always)] +-pub unsafe fn Py_INCREF(op: *mut PyObject) { +- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting +- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. +- #[cfg(any( +- Py_GIL_DISABLED, +- Py_LIMITED_API, +- py_sys_config = "Py_REF_DEBUG", +- GraalPy +- ))] +- { +- // _Py_IncRef was added to the ABI in 3.10; skips null checks +- #[cfg(all(Py_3_10, not(PyPy)))] +- { +- _Py_IncRef(op); +- } +- +- #[cfg(any(not(Py_3_10), PyPy))] +- { +- Py_IncRef(op); +- } +- } +- +- // version-specific builds are allowed to directly manipulate the reference count +- #[cfg(not(any( +- Py_GIL_DISABLED, +- Py_LIMITED_API, +- py_sys_config = "Py_REF_DEBUG", +- GraalPy +- )))] +- { +- #[cfg(all(Py_3_12, target_pointer_width = "64"))] +- { +- let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; +- let new_refcnt = cur_refcnt.wrapping_add(1); +- if new_refcnt == 0 { +- return; +- } +- (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; +- } +- +- #[cfg(all(Py_3_12, target_pointer_width = "32"))] +- { +- if _Py_IsImmortal(op) != 0 { +- return; +- } +- (*op).ob_refcnt.ob_refcnt += 1 +- } +- +- #[cfg(not(Py_3_12))] +- { +- (*op).ob_refcnt += 1 +- } +- +- // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue +- // or submit a PR supporting Py_STATS build option and pystats.h +- } +-} +- +-#[inline(always)] +-#[cfg_attr( +- all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), +- track_caller +-)] +-pub unsafe fn Py_DECREF(op: *mut PyObject) { +- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting +- // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts +- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. +- #[cfg(any( +- Py_GIL_DISABLED, +- Py_LIMITED_API, +- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), +- GraalPy +- ))] +- { +- // _Py_DecRef was added to the ABI in 3.10; skips null checks +- #[cfg(all(Py_3_10, not(PyPy)))] +- { +- _Py_DecRef(op); +- } +- +- #[cfg(any(not(Py_3_10), PyPy))] +- { +- Py_DecRef(op); +- } +- } +- +- #[cfg(not(any( +- Py_GIL_DISABLED, +- Py_LIMITED_API, +- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), +- GraalPy +- )))] +- { +- #[cfg(Py_3_12)] +- if _Py_IsImmortal(op) != 0 { +- return; +- } +- +- // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue +- // or submit a PR supporting Py_STATS build option and pystats.h +- +- #[cfg(py_sys_config = "Py_REF_DEBUG")] +- _Py_DECREF_DecRefTotal(); +- +- #[cfg(Py_3_12)] +- { +- (*op).ob_refcnt.ob_refcnt -= 1; +- +- #[cfg(py_sys_config = "Py_REF_DEBUG")] +- if (*op).ob_refcnt.ob_refcnt < 0 { +- let location = std::panic::Location::caller(); +- let filename = std::ffi::CString::new(location.file()).unwrap(); +- _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); +- } +- +- if (*op).ob_refcnt.ob_refcnt == 0 { +- _Py_Dealloc(op); +- } +- } +- +- #[cfg(not(Py_3_12))] +- { +- (*op).ob_refcnt -= 1; +- +- if (*op).ob_refcnt == 0 { +- _Py_Dealloc(op); +- } +- } +- } +-} +- +-#[inline] +-pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { +- let tmp = *op; +- if !tmp.is_null() { +- *op = ptr::null_mut(); +- Py_DECREF(tmp); +- } +-} +- +-#[inline] +-pub unsafe fn Py_XINCREF(op: *mut PyObject) { +- if !op.is_null() { +- Py_INCREF(op) +- } +-} +- +-#[inline] +-pub unsafe fn Py_XDECREF(op: *mut PyObject) { +- if !op.is_null() { +- Py_DECREF(op) +- } +-} +- +-extern "C" { +- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] +- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] +- pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; +- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] +- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] +- pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; +-} +- +-// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here +-// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here +- +-#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] +-#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] +-#[inline] +-pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { +- Py_INCREF(obj); +- obj +-} +- +-#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] +-#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] +-#[inline] +-pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { +- Py_XINCREF(obj); +- obj +-} +- + #[cfg(Py_3_13)] + pub const Py_CONSTANT_NONE: c_uint = 0; + #[cfg(Py_3_13)] +@@ -942,4 +709,7 @@ extern "C" { + arg1: *mut crate::PyTypeObject, + arg2: *mut crate::PyModuleDef, + ) -> *mut PyObject; ++ ++ #[cfg(Py_3_14)] ++ pub fn PyType_Freeze(tp: *mut crate::PyTypeObject) -> c_int; + } +diff --git a/include/pyo3/pyo3-ffi/src/pyarena.rs b/include/pyo3/pyo3-ffi/src/pyarena.rs +index 87d5f28a..1200de3d 100644 +--- a/include/pyo3/pyo3-ffi/src/pyarena.rs ++++ b/include/pyo3/pyo3-ffi/src/pyarena.rs +@@ -1 +1 @@ +-opaque_struct!(PyArena); ++opaque_struct!(pub PyArena); +diff --git a/include/pyo3/pyo3-ffi/src/pybuffer.rs b/include/pyo3/pyo3-ffi/src/pybuffer.rs +index 50bf4e61..de706759 100644 +--- a/include/pyo3/pyo3-ffi/src/pybuffer.rs ++++ b/include/pyo3/pyo3-ffi/src/pybuffer.rs +@@ -103,7 +103,11 @@ extern "C" { + } + + /// Maximum number of dimensions +-pub const PyBUF_MAX_NDIM: c_int = if cfg!(PyPy) { 36 } else { 64 }; ++pub const PyBUF_MAX_NDIM: usize = if cfg!(all(PyPy, not(Py_3_11))) { ++ 36 ++} else { ++ 64 ++}; + + /* Flags for getting buffers */ + pub const PyBUF_SIMPLE: c_int = 0; +diff --git a/include/pyo3/pyo3-ffi/src/pyerrors.rs b/include/pyo3/pyo3-ffi/src/pyerrors.rs +index 6c9313c4..d341239a 100644 +--- a/include/pyo3/pyo3-ffi/src/pyerrors.rs ++++ b/include/pyo3/pyo3-ffi/src/pyerrors.rs +@@ -116,6 +116,7 @@ extern "C" { + #[cfg_attr(PyPy, link_name = "PyPyExc_BaseException")] + pub static mut PyExc_BaseException: *mut PyObject; + #[cfg(Py_3_11)] ++ #[cfg_attr(PyPy, link_name = "PyPyExc_BaseExceptionGroup")] + pub static mut PyExc_BaseExceptionGroup: *mut PyObject; + #[cfg_attr(PyPy, link_name = "PyPyExc_Exception")] + pub static mut PyExc_Exception: *mut PyObject; +diff --git a/include/pyo3/pyo3-ffi/src/pyframe.rs b/include/pyo3/pyo3-ffi/src/pyframe.rs +index 4dd3d2b3..1693b20b 100644 +--- a/include/pyo3/pyo3-ffi/src/pyframe.rs ++++ b/include/pyo3/pyo3-ffi/src/pyframe.rs +@@ -6,7 +6,7 @@ use crate::PyFrameObject; + use std::os::raw::c_int; + + #[cfg(Py_LIMITED_API)] +-opaque_struct!(PyFrameObject); ++opaque_struct!(pub PyFrameObject); + + extern "C" { + pub fn PyFrame_GetLineNumber(f: *mut PyFrameObject) -> c_int; +diff --git a/include/pyo3/pyo3-ffi/src/pyhash.rs b/include/pyo3/pyo3-ffi/src/pyhash.rs +index 8b0b6771..074278dd 100644 +--- a/include/pyo3/pyo3-ffi/src/pyhash.rs ++++ b/include/pyo3/pyo3-ffi/src/pyhash.rs +@@ -1,7 +1,5 @@ + #[cfg(not(any(Py_LIMITED_API, PyPy)))] + use crate::pyport::{Py_hash_t, Py_ssize_t}; +-#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] +-use std::os::raw::c_char; + #[cfg(not(any(Py_LIMITED_API, PyPy)))] + use std::os::raw::c_void; + +@@ -12,11 +10,8 @@ extern "C" { + // skipped non-limited _Py_HashPointer + // skipped non-limited _Py_HashPointerRaw + +- #[cfg(not(all(Py_3_14, any(Py_LIMITED_API, PyPy))))] ++ #[cfg(not(any(Py_LIMITED_API, PyPy)))] + pub fn _Py_HashBytes(src: *const c_void, len: Py_ssize_t) -> Py_hash_t; +- +- #[cfg(Py_3_14)] +- pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; + } + + pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; +@@ -25,29 +20,6 @@ pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; + + // skipped non-limited _Py_HashSecret_t + +-#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] +-#[repr(C)] +-#[derive(Copy, Clone)] +-pub struct PyHash_FuncDef { +- pub hash: Option Py_hash_t>, +- pub name: *const c_char, +- pub hash_bits: c_int, +- pub seed_bits: c_int, +-} +- +-#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] +-impl Default for PyHash_FuncDef { +- #[inline] +- fn default() -> Self { +- unsafe { std::mem::zeroed() } +- } +-} +- +-extern "C" { +- #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] +- pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; +-} +- + // skipped Py_HASH_CUTOFF + + pub const Py_HASH_EXTERNAL: c_int = 0; +diff --git a/include/pyo3/pyo3-ffi/src/pyport.rs b/include/pyo3/pyo3-ffi/src/pyport.rs +index a144c67f..e524831d 100644 +--- a/include/pyo3/pyo3-ffi/src/pyport.rs ++++ b/include/pyo3/pyo3-ffi/src/pyport.rs +@@ -1,3 +1,8 @@ ++// NB libc does not define this constant on all platforms, so we hard code it ++// like CPython does. ++// https://github.com/python/cpython/blob/d8b9011702443bb57579f8834f3effe58e290dfc/Include/pyport.h#L372 ++pub const INT_MAX: std::os::raw::c_int = 2147483647; ++ + pub type PY_UINT32_T = u32; + pub type PY_UINT64_T = u64; + +@@ -11,8 +16,8 @@ pub type Py_ssize_t = ::libc::ssize_t; + pub type Py_hash_t = Py_ssize_t; + pub type Py_uhash_t = ::libc::size_t; + +-pub const PY_SSIZE_T_MIN: Py_ssize_t = isize::MIN as Py_ssize_t; +-pub const PY_SSIZE_T_MAX: Py_ssize_t = isize::MAX as Py_ssize_t; ++pub const PY_SSIZE_T_MIN: Py_ssize_t = Py_ssize_t::MIN; ++pub const PY_SSIZE_T_MAX: Py_ssize_t = Py_ssize_t::MAX; + + #[cfg(target_endian = "big")] + pub const PY_BIG_ENDIAN: usize = 1; +diff --git a/include/pyo3/pyo3-ffi/src/pystate.rs b/include/pyo3/pyo3-ffi/src/pystate.rs +index 23aeea3a..cc16e554 100644 +--- a/include/pyo3/pyo3-ffi/src/pystate.rs ++++ b/include/pyo3/pyo3-ffi/src/pystate.rs +@@ -1,3 +1,5 @@ ++#[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] ++use crate::frameobject::PyFrameObject; + use crate::moduleobject::PyModuleDef; + use crate::object::PyObject; + use std::os::raw::c_int; +@@ -7,8 +9,8 @@ use std::os::raw::c_long; + + pub const MAX_CO_EXTRA_USERS: c_int = 255; + +-opaque_struct!(PyThreadState); +-opaque_struct!(PyInterpreterState); ++opaque_struct!(pub PyThreadState); ++opaque_struct!(pub PyInterpreterState); + + extern "C" { + #[cfg(not(PyPy))] +@@ -63,9 +65,14 @@ extern "C" { + } + + // skipped non-limited / 3.9 PyThreadState_GetInterpreter +-// skipped non-limited / 3.9 PyThreadState_GetFrame + // skipped non-limited / 3.9 PyThreadState_GetID + ++extern "C" { ++ // PyThreadState_GetFrame ++ #[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] ++ pub fn PyThreadState_GetFrame(arg1: *mut PyThreadState) -> *mut PyFrameObject; ++} ++ + #[repr(C)] + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub enum PyGILState_STATE { +@@ -73,9 +80,70 @@ pub enum PyGILState_STATE { + PyGILState_UNLOCKED, + } + ++#[cfg(not(Py_3_14))] ++struct HangThread; ++ ++#[cfg(not(Py_3_14))] ++impl Drop for HangThread { ++ fn drop(&mut self) { ++ loop { ++ std::thread::park(); // Block forever. ++ } ++ } ++} ++ ++// The PyGILState_Ensure function will call pthread_exit during interpreter shutdown, ++// which causes undefined behavior. Redirect to the "safe" version that hangs instead, ++// as Python 3.14 does. ++// ++// See https://github.com/rust-lang/rust/issues/135929 ++ ++// C-unwind only supported (and necessary) since 1.71. Python 3.14+ does not do ++// pthread_exit from PyGILState_Ensure (https://github.com/python/cpython/issues/87135). ++mod raw { ++ #[cfg(all(not(Py_3_14), rustc_has_extern_c_unwind))] ++ extern "C-unwind" { ++ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] ++ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; ++ } ++ ++ #[cfg(not(all(not(Py_3_14), rustc_has_extern_c_unwind)))] ++ extern "C" { ++ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] ++ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; ++ } ++} ++ ++#[cfg(not(Py_3_14))] ++pub unsafe extern "C" fn PyGILState_Ensure() -> PyGILState_STATE { ++ let guard = HangThread; ++ // If `PyGILState_Ensure` calls `pthread_exit`, which it does on Python < 3.14 ++ // when the interpreter is shutting down, this will cause a forced unwind. ++ // doing a forced unwind through a function with a Rust destructor is unspecified ++ // behavior. ++ // ++ // However, currently it runs the destructor, which will cause the thread to ++ // hang as it should. ++ // ++ // And if we don't catch the unwinding here, then one of our callers probably has a destructor, ++ // so it's unspecified behavior anyway, and on many configurations causes the process to abort. ++ // ++ // The alternative is for pyo3 to contain custom C or C++ code that catches the `pthread_exit`, ++ // but that's also annoying from a portability point of view. ++ // ++ // On Windows, `PyGILState_Ensure` calls `_endthreadex` instead, which AFAICT can't be caught ++ // and therefore will cause unsafety if there are pinned objects on the stack. AFAICT there's ++ // nothing we can do it other than waiting for Python 3.14 or not using Windows. At least, ++ // if there is nothing pinned on the stack, it won't cause the process to crash. ++ let ret: PyGILState_STATE = raw::PyGILState_Ensure(); ++ std::mem::forget(guard); ++ ret ++} ++ ++#[cfg(Py_3_14)] ++pub use self::raw::PyGILState_Ensure; ++ + extern "C" { +- #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] +- pub fn PyGILState_Ensure() -> PyGILState_STATE; + #[cfg_attr(PyPy, link_name = "PyPyGILState_Release")] + pub fn PyGILState_Release(arg1: PyGILState_STATE); + #[cfg(not(PyPy))] +diff --git a/include/pyo3/pyo3-ffi/src/pythonrun.rs b/include/pyo3/pyo3-ffi/src/pythonrun.rs +index e7ea2d2e..80209b58 100644 +--- a/include/pyo3/pyo3-ffi/src/pythonrun.rs ++++ b/include/pyo3/pyo3-ffi/src/pythonrun.rs +@@ -49,12 +49,12 @@ pub const PYOS_STACK_MARGIN: c_int = 2048; + // skipped PyOS_CheckStack under Microsoft C + + #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] +-opaque_struct!(_mod); ++opaque_struct!(pub _mod); + + #[cfg(not(any(PyPy, Py_3_10)))] +-opaque_struct!(symtable); ++opaque_struct!(pub symtable); + #[cfg(not(any(PyPy, Py_3_10)))] +-opaque_struct!(_node); ++opaque_struct!(pub _node); + + #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] + #[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))] +diff --git a/include/pyo3/pyo3-ffi/src/refcount.rs b/include/pyo3/pyo3-ffi/src/refcount.rs +new file mode 100644 +index 00000000..fcb5f45b +--- /dev/null ++++ b/include/pyo3/pyo3-ffi/src/refcount.rs +@@ -0,0 +1,369 @@ ++use crate::pyport::Py_ssize_t; ++use crate::PyObject; ++#[cfg(py_sys_config = "Py_REF_DEBUG")] ++use std::os::raw::c_char; ++#[cfg(Py_3_12)] ++use std::os::raw::c_int; ++#[cfg(all(Py_3_14, any(not(Py_GIL_DISABLED), target_pointer_width = "32")))] ++use std::os::raw::c_long; ++#[cfg(any(Py_GIL_DISABLED, all(Py_3_12, not(Py_3_14))))] ++use std::os::raw::c_uint; ++#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] ++use std::os::raw::c_ulong; ++use std::ptr; ++#[cfg(Py_GIL_DISABLED)] ++use std::sync::atomic::Ordering::Relaxed; ++ ++#[cfg(Py_3_14)] ++const _Py_STATICALLY_ALLOCATED_FLAG: c_int = 1 << 7; ++ ++#[cfg(all(Py_3_12, not(Py_3_14)))] ++const _Py_IMMORTAL_REFCNT: Py_ssize_t = { ++ if cfg!(target_pointer_width = "64") { ++ c_uint::MAX as Py_ssize_t ++ } else { ++ // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) ++ (c_uint::MAX >> 2) as Py_ssize_t ++ } ++}; ++ ++// comments in Python.h about the choices for these constants ++ ++#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] ++const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { ++ if cfg!(target_pointer_width = "64") { ++ ((3 as c_ulong) << (30 as c_ulong)) as Py_ssize_t ++ } else { ++ ((5 as c_long) << (28 as c_long)) as Py_ssize_t ++ } ++}; ++ ++#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] ++const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { ++ if cfg!(target_pointer_width = "64") { ++ _Py_IMMORTAL_INITIAL_REFCNT ++ | ((_Py_STATICALLY_ALLOCATED_FLAG as Py_ssize_t) << (32 as Py_ssize_t)) ++ } else { ++ ((7 as c_long) << (28 as c_long)) as Py_ssize_t ++ } ++}; ++ ++#[cfg(all(Py_3_14, target_pointer_width = "32"))] ++const _Py_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = ((1 as c_long) << (30 as c_long)) as Py_ssize_t; ++ ++#[cfg(all(Py_3_14, target_pointer_width = "32"))] ++const _Py_STATIC_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = ++ ((6 as c_long) << (28 as c_long)) as Py_ssize_t; ++ ++#[cfg(all(Py_3_14, Py_GIL_DISABLED))] ++const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = c_uint::MAX as Py_ssize_t; ++ ++#[cfg(Py_GIL_DISABLED)] ++pub(crate) const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; ++ ++#[cfg(Py_GIL_DISABLED)] ++const _Py_REF_SHARED_SHIFT: isize = 2; ++// skipped private _Py_REF_SHARED_FLAG_MASK ++ ++// skipped private _Py_REF_SHARED_INIT ++// skipped private _Py_REF_MAYBE_WEAKREF ++// skipped private _Py_REF_QUEUED ++// skipped private _Py_REF_MERGED ++ ++// skipped private _Py_REF_SHARED ++ ++extern "C" { ++ #[cfg(all(Py_3_14, Py_LIMITED_API))] ++ pub fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t; ++} ++ ++#[cfg(not(all(Py_3_14, Py_LIMITED_API)))] ++#[inline] ++pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { ++ #[cfg(Py_GIL_DISABLED)] ++ { ++ let local = (*ob).ob_ref_local.load(Relaxed); ++ if local == _Py_IMMORTAL_REFCNT_LOCAL { ++ #[cfg(not(Py_3_14))] ++ return _Py_IMMORTAL_REFCNT; ++ #[cfg(Py_3_14)] ++ return _Py_IMMORTAL_INITIAL_REFCNT; ++ } ++ let shared = (*ob).ob_ref_shared.load(Relaxed); ++ local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) ++ } ++ ++ #[cfg(all(Py_LIMITED_API, Py_3_14))] ++ { ++ Py_REFCNT(ob) ++ } ++ ++ #[cfg(all(not(Py_GIL_DISABLED), not(all(Py_LIMITED_API, Py_3_14)), Py_3_12))] ++ { ++ (*ob).ob_refcnt.ob_refcnt ++ } ++ ++ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] ++ { ++ (*ob).ob_refcnt ++ } ++ ++ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] ++ { ++ _Py_REFCNT(ob) ++ } ++} ++ ++#[cfg(Py_3_12)] ++#[inline(always)] ++unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { ++ #[cfg(all(target_pointer_width = "64", not(Py_GIL_DISABLED)))] ++ { ++ (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int ++ } ++ ++ #[cfg(all(target_pointer_width = "32", not(Py_GIL_DISABLED)))] ++ { ++ #[cfg(not(Py_3_14))] ++ { ++ ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int ++ } ++ ++ #[cfg(Py_3_14)] ++ { ++ ((*op).ob_refcnt.ob_refcnt >= _Py_IMMORTAL_MINIMUM_REFCNT) as c_int ++ } ++ } ++ ++ #[cfg(Py_GIL_DISABLED)] ++ { ++ ((*op).ob_ref_local.load(Relaxed) == _Py_IMMORTAL_REFCNT_LOCAL) as c_int ++ } ++} ++ ++// skipped _Py_IsStaticImmortal ++ ++// TODO: Py_SET_REFCNT ++ ++extern "C" { ++ #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] ++ fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); ++ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] ++ fn _Py_INCREF_IncRefTotal(); ++ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] ++ fn _Py_DECREF_DecRefTotal(); ++ ++ #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] ++ fn _Py_Dealloc(arg1: *mut PyObject); ++ ++ #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] ++ #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] ++ pub fn Py_IncRef(o: *mut PyObject); ++ #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] ++ #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] ++ pub fn Py_DecRef(o: *mut PyObject); ++ ++ #[cfg(all(Py_3_10, not(PyPy)))] ++ fn _Py_IncRef(o: *mut PyObject); ++ #[cfg(all(Py_3_10, not(PyPy)))] ++ fn _Py_DecRef(o: *mut PyObject); ++ ++ #[cfg(GraalPy)] ++ fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; ++} ++ ++#[inline(always)] ++pub unsafe fn Py_INCREF(op: *mut PyObject) { ++ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting ++ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. ++ #[cfg(any( ++ Py_GIL_DISABLED, ++ Py_LIMITED_API, ++ py_sys_config = "Py_REF_DEBUG", ++ GraalPy ++ ))] ++ { ++ // _Py_IncRef was added to the ABI in 3.10; skips null checks ++ #[cfg(all(Py_3_10, not(PyPy)))] ++ { ++ _Py_IncRef(op); ++ } ++ ++ #[cfg(any(not(Py_3_10), PyPy))] ++ { ++ Py_IncRef(op); ++ } ++ } ++ ++ // version-specific builds are allowed to directly manipulate the reference count ++ #[cfg(not(any( ++ Py_GIL_DISABLED, ++ Py_LIMITED_API, ++ py_sys_config = "Py_REF_DEBUG", ++ GraalPy ++ )))] ++ { ++ #[cfg(all(Py_3_14, target_pointer_width = "64"))] ++ { ++ let cur_refcnt = (*op).ob_refcnt.ob_refcnt; ++ if (cur_refcnt as i32) < 0 { ++ return; ++ } ++ (*op).ob_refcnt.ob_refcnt = cur_refcnt.wrapping_add(1); ++ } ++ ++ #[cfg(all(Py_3_12, not(Py_3_14), target_pointer_width = "64"))] ++ { ++ let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; ++ let new_refcnt = cur_refcnt.wrapping_add(1); ++ if new_refcnt == 0 { ++ return; ++ } ++ (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; ++ } ++ ++ #[cfg(all(Py_3_12, target_pointer_width = "32"))] ++ { ++ if _Py_IsImmortal(op) != 0 { ++ return; ++ } ++ (*op).ob_refcnt.ob_refcnt += 1 ++ } ++ ++ #[cfg(not(Py_3_12))] ++ { ++ (*op).ob_refcnt += 1 ++ } ++ ++ // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue ++ // or submit a PR supporting Py_STATS build option and pystats.h ++ } ++} ++ ++// skipped _Py_DecRefShared ++// skipped _Py_DecRefSharedDebug ++// skipped _Py_MergeZeroLocalRefcount ++ ++#[inline(always)] ++#[cfg_attr( ++ all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), ++ track_caller ++)] ++pub unsafe fn Py_DECREF(op: *mut PyObject) { ++ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting ++ // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts ++ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. ++ #[cfg(any( ++ Py_GIL_DISABLED, ++ Py_LIMITED_API, ++ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), ++ GraalPy ++ ))] ++ { ++ // _Py_DecRef was added to the ABI in 3.10; skips null checks ++ #[cfg(all(Py_3_10, not(PyPy)))] ++ { ++ _Py_DecRef(op); ++ } ++ ++ #[cfg(any(not(Py_3_10), PyPy))] ++ { ++ Py_DecRef(op); ++ } ++ } ++ ++ #[cfg(not(any( ++ Py_GIL_DISABLED, ++ Py_LIMITED_API, ++ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), ++ GraalPy ++ )))] ++ { ++ #[cfg(Py_3_12)] ++ if _Py_IsImmortal(op) != 0 { ++ return; ++ } ++ ++ // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue ++ // or submit a PR supporting Py_STATS build option and pystats.h ++ ++ #[cfg(py_sys_config = "Py_REF_DEBUG")] ++ _Py_DECREF_DecRefTotal(); ++ ++ #[cfg(Py_3_12)] ++ { ++ (*op).ob_refcnt.ob_refcnt -= 1; ++ ++ #[cfg(py_sys_config = "Py_REF_DEBUG")] ++ if (*op).ob_refcnt.ob_refcnt < 0 { ++ let location = std::panic::Location::caller(); ++ let filename = std::ffi::CString::new(location.file()).unwrap(); ++ _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); ++ } ++ ++ if (*op).ob_refcnt.ob_refcnt == 0 { ++ _Py_Dealloc(op); ++ } ++ } ++ ++ #[cfg(not(Py_3_12))] ++ { ++ (*op).ob_refcnt -= 1; ++ ++ if (*op).ob_refcnt == 0 { ++ _Py_Dealloc(op); ++ } ++ } ++ } ++} ++ ++#[inline] ++pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { ++ let tmp = *op; ++ if !tmp.is_null() { ++ *op = ptr::null_mut(); ++ Py_DECREF(tmp); ++ } ++} ++ ++#[inline] ++pub unsafe fn Py_XINCREF(op: *mut PyObject) { ++ if !op.is_null() { ++ Py_INCREF(op) ++ } ++} ++ ++#[inline] ++pub unsafe fn Py_XDECREF(op: *mut PyObject) { ++ if !op.is_null() { ++ Py_DECREF(op) ++ } ++} ++ ++extern "C" { ++ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] ++ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] ++ pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; ++ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] ++ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] ++ pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; ++} ++ ++// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here ++// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here ++ ++#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] ++#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] ++#[inline] ++pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { ++ Py_INCREF(obj); ++ obj ++} ++ ++#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] ++#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] ++#[inline] ++pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { ++ Py_XINCREF(obj); ++ obj ++} +diff --git a/include/pyo3/pyo3-ffi/src/setobject.rs b/include/pyo3/pyo3-ffi/src/setobject.rs +index 9d5351fc..87e33e80 100644 +--- a/include/pyo3/pyo3-ffi/src/setobject.rs ++++ b/include/pyo3/pyo3-ffi/src/setobject.rs +@@ -39,11 +39,7 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { + (*so).used + } + +-#[cfg(not(Py_LIMITED_API))] +-#[cfg_attr(windows, link(name = "pythonXY"))] +-extern "C" { +- pub static mut _PySet_Dummy: *mut PyObject; +-} ++// skipped _PySet_Dummy + + extern "C" { + #[cfg(not(Py_LIMITED_API))] +diff --git a/include/pyo3/pyo3-ffi/src/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/weakrefobject.rs +index 305dc290..88a1bf90 100644 +--- a/include/pyo3/pyo3-ffi/src/weakrefobject.rs ++++ b/include/pyo3/pyo3-ffi/src/weakrefobject.rs +@@ -4,16 +4,18 @@ use std::os::raw::c_int; + use std::ptr::addr_of_mut; + + #[cfg(all(not(PyPy), Py_LIMITED_API, not(GraalPy)))] +-opaque_struct!(PyWeakReference); ++opaque_struct!(pub PyWeakReference); + + #[cfg(all(not(PyPy), not(Py_LIMITED_API), not(GraalPy)))] + pub use crate::_PyWeakReference as PyWeakReference; + + #[cfg_attr(windows, link(name = "pythonXY"))] + extern "C" { ++ // TODO: PyO3 is depending on this symbol in `reference.rs`, we should change this and ++ // remove the export as this is a private symbol. + pub static mut _PyWeakref_RefType: PyTypeObject; +- pub static mut _PyWeakref_ProxyType: PyTypeObject; +- pub static mut _PyWeakref_CallableProxyType: PyTypeObject; ++ static mut _PyWeakref_ProxyType: PyTypeObject; ++ static mut _PyWeakref_CallableProxyType: PyTypeObject; + + #[cfg(PyPy)] + #[link_name = "PyPyWeakref_CheckRef"] + +From d2570d2f25eba771468f41020a2ad69e0cbdbbc7 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Fri, 23 May 2025 17:00:34 +0200 +Subject: [PATCH 2/6] Add pyo3-ffi patch for _PyLong_AsByteArray with + exceptions + +Co-authored-by: ijl +--- + ...i-pylong-asbytearray-with-exceptions.patch | 23 +++++++++++++++++++ + 1 file changed, 23 insertions(+) + create mode 100644 include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch + +diff --git a/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch +new file mode 100644 +index 00000000..20e5d7a3 +--- /dev/null ++++ b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch +@@ -0,0 +1,23 @@ ++diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs ++index 45acaae..6063575 100644 ++--- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs +++++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs ++@@ -61,6 +61,18 @@ extern "C" { ++ is_signed: c_int, ++ ) -> *mut PyObject; ++ +++ #[cfg(Py_3_13)] +++ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] +++ pub fn _PyLong_AsByteArray( +++ v: *mut PyLongObject, +++ bytes: *mut c_uchar, +++ n: size_t, +++ little_endian: c_int, +++ is_signed: c_int, +++ with_exceptions: c_int, +++ ) -> c_int; +++ +++ #[cfg(not(Py_3_13))] ++ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] ++ pub fn _PyLong_AsByteArray( ++ v: *mut PyLongObject, + +From d01aea4541800dca412e3f4c3068e974f18c3218 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Fri, 23 May 2025 18:26:19 +0200 +Subject: [PATCH 3/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::(), +- $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 Result { +- let uni = unicode_to_str_via_ffi(key); ++ let uni = unicode_to_str(key); + if unlikely!(uni.is_none()) { + Err(SerializeError::InvalidStr) + } else { +diff --git a/src/serialize/per_type/unicode.rs b/src/serialize/per_type/unicode.rs +index 23270916..7267b454 100644 +--- a/src/serialize/per_type/unicode.rs ++++ b/src/serialize/per_type/unicode.rs +@@ -1,7 +1,7 @@ + // SPDX-License-Identifier: (Apache-2.0 OR MIT) + + use crate::serialize::error::SerializeError; +-use crate::str::{unicode_to_str, unicode_to_str_via_ffi}; ++use crate::str::unicode_to_str; + + use serde::ser::{Serialize, Serializer}; + +@@ -50,7 +50,7 @@ impl Serialize for StrSubclassSerializer { + where + S: Serializer, + { +- let uni = unicode_to_str_via_ffi(self.ptr); ++ let uni = unicode_to_str(self.ptr); + if unlikely!(uni.is_none()) { + err!(SerializeError::InvalidStr) + } +diff --git a/src/str/ffi.rs b/src/str/ffi.rs +index 4f1c76bd..6b57e7ae 100644 +--- a/src/str/ffi.rs ++++ b/src/str/ffi.rs +@@ -1,25 +1,19 @@ + // SPDX-License-Identifier: (Apache-2.0 OR MIT) + + use crate::util::isize_to_usize; +-use core::ffi::c_void; +-use pyo3_ffi::{PyASCIIObject, PyCompactUnicodeObject, PyObject, Py_hash_t}; ++#[cfg(not(Py_3_14))] ++use pyo3_ffi::PyCompactUnicodeObject; ++use pyo3_ffi::{ ++ PyASCIIObject, PyObject, PyUnicode_DATA, PyUnicode_GET_LENGTH, PyUnicode_KIND, Py_hash_t, ++}; + + // see unicodeobject.h for documentation + + #[inline] + pub fn hash_str(op: *mut PyObject) -> Py_hash_t { + unsafe { +- let data_ptr: *mut c_void = if (*op.cast::()).compact() == 1 +- && (*op.cast::()).ascii() == 1 +- { +- op.cast::().offset(1).cast::() +- } else { +- op.cast::() +- .offset(1) +- .cast::() +- }; +- let num_bytes = +- (*op.cast::()).length * ((*op.cast::()).kind()) as isize; ++ let data_ptr = PyUnicode_DATA(op); ++ let num_bytes = PyUnicode_GET_LENGTH(op) * PyUnicode_KIND(op) as isize; + #[cfg(Py_3_14)] + let hash = pyo3_ffi::Py_HashBuffer(data_ptr, num_bytes); + #[cfg(not(Py_3_14))] +@@ -30,7 +24,7 @@ pub fn hash_str(op: *mut PyObject) -> Py_hash_t { + } + + #[inline(never)] +-pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { ++fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { + let mut str_size: pyo3_ffi::Py_ssize_t = 0; + let ptr = ffi!(PyUnicode_AsUTF8AndSize(op, &mut str_size)).cast::(); + if unlikely!(ptr.is_null()) { +@@ -40,6 +34,7 @@ pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { + } + } + ++#[cfg(not(Py_3_14))] + #[inline] + pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { + unsafe { +@@ -58,3 +53,9 @@ pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { + } + } + } ++ ++#[cfg(Py_3_14)] ++#[inline] ++pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { ++ unicode_to_str_via_ffi(op) ++} +diff --git a/src/str/pyunicode_new.rs b/src/str/pyunicode_new.rs +index 7b1c2df2..738b8489 100644 +--- a/src/str/pyunicode_new.rs ++++ b/src/str/pyunicode_new.rs +@@ -8,7 +8,9 @@ macro_rules! validate_str { + #[cfg(not(Py_3_12))] + debug_assert!((*($ptr.cast::())).ready() == 1); + ++ #[cfg(not(Py_3_14))] + debug_assert!((*($ptr.cast::())).compact() == 1); ++ #[cfg(not(Py_3_14))] + debug_assert!((*($ptr.cast::())).interned() == 0); + + debug_assert!(ffi!(_PyUnicode_CheckConsistency($ptr.cast::(), 1)) == 1); +@@ -22,6 +24,7 @@ pub fn pyunicode_ascii(buf: *const u8, num_chars: usize) -> *mut pyo3_ffi::PyObj + let data_ptr = ptr.cast::().offset(1).cast::(); + core::ptr::copy_nonoverlapping(buf, data_ptr, num_chars); + core::ptr::write(data_ptr.add(num_chars), 0); ++ #[cfg(not(Py_3_14))] + debug_assert!((*(ptr.cast::())).ascii() == 1); + validate_str!(ptr); + ptr.cast::() diff --git a/python-orjson.spec b/python-orjson.spec index 6fe904a..ab35204 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -17,6 +17,9 @@ Summary: Fast, correct Python JSON library License: Apache-2.0 OR MIT URL: https://github.com/ijl/orjson Source: %{pypi_source orjson} +# Support Python 3.14 +# Updates bundled/forked pyo3-build-config/pyo3-ffi to 0.25.0 +Patch: https://github.com/ijl/orjson/pull/570.patch BuildRequires: tomcli BuildRequires: python3-devel @@ -71,8 +74,8 @@ License: %{shrink: # # Note that these crates are actually forked, not only bundled/vendored; see # https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for details. -Provides: bundled(crate(pyo3-build-config)) = 0.23.3 -Provides: bundled(crate(pyo3-ffi)) = 0.23.3 +Provides: bundled(crate(pyo3-build-config)) = 0.25.0 +Provides: bundled(crate(pyo3-ffi)) = 0.25.0 %description -n python3-orjson %{_description} From 9b05a1a7442c310c5348ebce8c968b17afc47399 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Tue, 3 Jun 2025 18:24:47 +0200 Subject: [PATCH 44/55] Rebuilt for Python 3.14 From bb81ac5e1922115b815eda398c20fb94c66305ea Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Thu, 5 Jun 2025 07:41:17 -0400 Subject: [PATCH 45/55] Cherry-pick pyo3-ffi fix for Python 3.14 i686 segfaults --- ...bRefcnt.refcnt_and_flags-64-bit-only.patch | 27 +++++++++++++++++++ python-orjson.spec | 9 +++++++ 2 files changed, 36 insertions(+) create mode 100644 0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch diff --git a/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch b/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch new file mode 100644 index 0000000..6147734 --- /dev/null +++ b/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch @@ -0,0 +1,27 @@ +From c42f6b171f1be6961c7f599405a29147862642b7 Mon Sep 17 00:00:00 2001 +From: "Benjamin A. Beasley" +Date: Wed, 4 Jun 2025 08:58:27 -0400 +Subject: [PATCH] Make PyObjectObRefcnt.refcnt_and_flags 64-bit-only + +Fix #5175 as described in +https://github.com/PyO3/pyo3/issues/5175#issuecomment-2939918138. +--- + include/pyo3/pyo3-ffi/src/object.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs +index 5fbf45d..aeb9735 100644 +--- a/include/pyo3/pyo3-ffi/src/object.rs ++++ b/include/pyo3/pyo3-ffi/src/object.rs +@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { + pub union PyObjectObRefcnt { + #[cfg(all(target_pointer_width = "64", Py_3_14))] + pub ob_refcnt_full: crate::PY_INT64_T, +- #[cfg(Py_3_14)] ++ #[cfg(all(target_pointer_width = "64", Py_3_14))] + pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, + pub ob_refcnt: Py_ssize_t, + #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] +-- +2.49.0 + diff --git a/python-orjson.spec b/python-orjson.spec index ab35204..abc6289 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -20,6 +20,15 @@ Source: %{pypi_source orjson} # Support Python 3.14 # Updates bundled/forked pyo3-build-config/pyo3-ffi to 0.25.0 Patch: https://github.com/ijl/orjson/pull/570.patch +# Cherry-pick pyo3-ffi fix for Python 3.14 i686 segfaults: +# +# Make PyObjectObRefcnt.refcnt_and_flags 64-bit-only +# https://github.com/PyO3/pyo3/pull/5180 +# +# Cherry-picked on top of https://github.com/ijl/orjson/pull/570 (without the +# commit adding a news fragment); see +# https://github.com/ijl/orjson/pull/570#issuecomment-2943844083. +Patch: 0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch BuildRequires: tomcli BuildRequires: python3-devel From 6177d02f00a178dd07d817b1d434d485675c4874 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Thu, 5 Jun 2025 08:01:25 -0400 Subject: [PATCH 46/55] PyO3 32-bit fix is now included in upstream Python 3.14 PR --- ...bRefcnt.refcnt_and_flags-64-bit-only.patch | 27 -------- 570.patch | 66 +++++++++++++++++-- python-orjson.spec | 9 --- 3 files changed, 60 insertions(+), 42 deletions(-) delete mode 100644 0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch diff --git a/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch b/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch deleted file mode 100644 index 6147734..0000000 --- a/0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch +++ /dev/null @@ -1,27 +0,0 @@ -From c42f6b171f1be6961c7f599405a29147862642b7 Mon Sep 17 00:00:00 2001 -From: "Benjamin A. Beasley" -Date: Wed, 4 Jun 2025 08:58:27 -0400 -Subject: [PATCH] Make PyObjectObRefcnt.refcnt_and_flags 64-bit-only - -Fix #5175 as described in -https://github.com/PyO3/pyo3/issues/5175#issuecomment-2939918138. ---- - include/pyo3/pyo3-ffi/src/object.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -index 5fbf45d..aeb9735 100644 ---- a/include/pyo3/pyo3-ffi/src/object.rs -+++ b/include/pyo3/pyo3-ffi/src/object.rs -@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { - pub union PyObjectObRefcnt { - #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub ob_refcnt_full: crate::PY_INT64_T, -- #[cfg(Py_3_14)] -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, - pub ob_refcnt: Py_ssize_t, - #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] --- -2.49.0 - diff --git a/570.patch b/570.patch index 26c290d..f9e8e1b 100644 --- a/570.patch +++ b/570.patch @@ -1,7 +1,7 @@ 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 +Subject: [PATCH 1/8] Update pyo3 to 0.25.0 --- Cargo.lock | 4 +- @@ -3525,7 +3525,7 @@ index 305dc290..88a1bf90 100644 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 +Subject: [PATCH 2/8] Add pyo3-ffi patch for _PyLong_AsByteArray with exceptions Co-authored-by: ijl @@ -3567,7 +3567,7 @@ index 00000000..20e5d7a3 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 +Subject: [PATCH 3/8] Apply pyo3-ffi patch --- include/pyo3/pyo3-ffi/src/cpython/longobject.rs | 12 ++++++++++++ @@ -3600,7 +3600,7 @@ index 45acaae5..6063575e 100644 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 +Subject: [PATCH 4/8] Fix _PyDict_SetItem_KnownHash_LockHeld --- src/util.rs | 10 ---------- @@ -3634,7 +3634,7 @@ index ccf425ce..0a255c3f 100644 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 +Subject: [PATCH 5/8] Fix _Py_IsImmortal --- src/util.rs | 6 ++---- @@ -3661,7 +3661,7 @@ index 0a255c3f..bbfb72ed 100644 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 +Subject: [PATCH 6/8] Use PyUnicode API --- src/serialize/per_type/dict.rs | 4 ++-- @@ -3800,3 +3800,57 @@ index 7b1c2df2..738b8489 100644 debug_assert!((*(ptr.cast::())).ascii() == 1); validate_str!(ptr); ptr.cast::() + +From 4eb6604607891c0f6aa00bbbb07ccfdcfd85b589 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Thu, 5 Jun 2025 13:52:28 +0200 +Subject: [PATCH 7/8] Add pyo3-ffi patch for PyObjectObRefcnt.refcnt_and_flags + +Co-authored-by: Benjamin A. Beasley +--- + include/pyo3-ffi-pyobjectrefcnt-64only.patch | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + create mode 100644 include/pyo3-ffi-pyobjectrefcnt-64only.patch + +diff --git a/include/pyo3-ffi-pyobjectrefcnt-64only.patch b/include/pyo3-ffi-pyobjectrefcnt-64only.patch +new file mode 100644 +index 00000000..dfc328a7 +--- /dev/null ++++ b/include/pyo3-ffi-pyobjectrefcnt-64only.patch +@@ -0,0 +1,13 @@ ++diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs ++index c4c5abc361c..6d5dc781287 100644 ++--- a/include/pyo3/pyo3-ffi/src/object.rs +++++ b/include/pyo3/pyo3-ffi/src/object.rs ++@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { ++ pub union PyObjectObRefcnt { ++ #[cfg(all(target_pointer_width = "64", Py_3_14))] ++ pub ob_refcnt_full: crate::PY_INT64_T, ++- #[cfg(Py_3_14)] +++ #[cfg(all(target_pointer_width = "64", Py_3_14))] ++ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, ++ pub ob_refcnt: Py_ssize_t, ++ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] + +From ea5a7d83f4ff5262f131d37dd1d490a634ce2626 Mon Sep 17 00:00:00 2001 +From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> +Date: Thu, 5 Jun 2025 13:54:27 +0200 +Subject: [PATCH 8/8] Apply second pyo3-ffi patch + +--- + include/pyo3/pyo3-ffi/src/object.rs | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs +index 5fbf45db..aeb97352 100644 +--- a/include/pyo3/pyo3-ffi/src/object.rs ++++ b/include/pyo3/pyo3-ffi/src/object.rs +@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { + pub union PyObjectObRefcnt { + #[cfg(all(target_pointer_width = "64", Py_3_14))] + pub ob_refcnt_full: crate::PY_INT64_T, +- #[cfg(Py_3_14)] ++ #[cfg(all(target_pointer_width = "64", Py_3_14))] + pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, + pub ob_refcnt: Py_ssize_t, + #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] diff --git a/python-orjson.spec b/python-orjson.spec index abc6289..ab35204 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -20,15 +20,6 @@ Source: %{pypi_source orjson} # Support Python 3.14 # Updates bundled/forked pyo3-build-config/pyo3-ffi to 0.25.0 Patch: https://github.com/ijl/orjson/pull/570.patch -# Cherry-pick pyo3-ffi fix for Python 3.14 i686 segfaults: -# -# Make PyObjectObRefcnt.refcnt_and_flags 64-bit-only -# https://github.com/PyO3/pyo3/pull/5180 -# -# Cherry-picked on top of https://github.com/ijl/orjson/pull/570 (without the -# commit adding a news fragment); see -# https://github.com/ijl/orjson/pull/570#issuecomment-2943844083. -Patch: 0001-Make-PyObjectObRefcnt.refcnt_and_flags-64-bit-only.patch BuildRequires: tomcli BuildRequires: python3-devel From 49bc586dcea13d170b474ed7ebcf378e098acbb5 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Tue, 15 Jul 2025 19:52:46 -0400 Subject: [PATCH 47/55] Update to 3.11.0 (close RHBZ#2380202) - Upstream no longer bundles a forked copy of PyO3 - More carefully audit and account for all licenses in the source - Add a build conditional to allow building with the yyjson backend --- .gitignore | 1 + 570.patch | 3856 -------------------------------------------- python-orjson.spec | 157 +- sources | 2 +- 4 files changed, 125 insertions(+), 3891 deletions(-) delete mode 100644 570.patch diff --git a/.gitignore b/.gitignore index 808eb9d..eea9600 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ /orjson-3.10.14.tar.gz /orjson-3.10.16.tar.gz /orjson-3.10.18.tar.gz +/orjson-3.11.0.tar.gz diff --git a/570.patch b/570.patch deleted file mode 100644 index f9e8e1b..0000000 --- a/570.patch +++ /dev/null @@ -1,3856 +0,0 @@ -From 180f5886407e7c1dda34afa6fc7298b34d2925da Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:02:33 +0200 -Subject: [PATCH 1/8] Update pyo3 to 0.25.0 - ---- - Cargo.lock | 4 +- - include/pyo3/pyo3-build-config/Cargo.toml | 6 +- - include/pyo3/pyo3-build-config/src/errors.rs | 2 +- - include/pyo3/pyo3-build-config/src/impl_.rs | 41 +- - include/pyo3/pyo3-build-config/src/lib.rs | 63 +-- - include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS | 2 +- - include/pyo3/pyo3-ffi/Cargo.toml | 10 +- - include/pyo3/pyo3-ffi/build.rs | 74 ++- - include/pyo3/pyo3-ffi/src/abstract_.rs | 41 +- - include/pyo3/pyo3-ffi/src/bytearrayobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/code.rs | 2 +- - include/pyo3/pyo3-ffi/src/compat/mod.rs | 2 + - include/pyo3/pyo3-ffi/src/compat/py_3_10.rs | 26 ++ - include/pyo3/pyo3-ffi/src/compat/py_3_13.rs | 15 + - include/pyo3/pyo3-ffi/src/compat/py_3_14.rs | 26 ++ - .../pyo3/pyo3-ffi/src/cpython/abstract_.rs | 32 +- - .../pyo3/pyo3-ffi/src/cpython/bytesobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/cpython/code.rs | 232 ++-------- - include/pyo3/pyo3-ffi/src/cpython/compile.rs | 63 ++- - .../pyo3-ffi/src/cpython/critical_section.rs | 14 +- - .../pyo3/pyo3-ffi/src/cpython/descrobject.rs | 5 +- - .../pyo3/pyo3-ffi/src/cpython/dictobject.rs | 16 +- - .../pyo3/pyo3-ffi/src/cpython/frameobject.rs | 5 +- - .../pyo3/pyo3-ffi/src/cpython/funcobject.rs | 2 + - .../pyo3/pyo3-ffi/src/cpython/genobject.rs | 14 +- - include/pyo3/pyo3-ffi/src/cpython/import.rs | 10 +- - .../pyo3/pyo3-ffi/src/cpython/initconfig.rs | 10 + - include/pyo3/pyo3-ffi/src/cpython/lock.rs | 9 - - .../pyo3/pyo3-ffi/src/cpython/longobject.rs | 12 - - include/pyo3/pyo3-ffi/src/cpython/mod.rs | 5 +- - include/pyo3/pyo3-ffi/src/cpython/object.rs | 8 +- - include/pyo3/pyo3-ffi/src/cpython/objimpl.rs | 2 +- - include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs | 2 + - include/pyo3/pyo3-ffi/src/cpython/pyframe.rs | 5 +- - include/pyo3/pyo3-ffi/src/cpython/pyhash.rs | 38 ++ - include/pyo3/pyo3-ffi/src/cpython/pystate.rs | 16 +- - .../pyo3/pyo3-ffi/src/cpython/tupleobject.rs | 4 +- - .../pyo3-ffi/src/cpython/unicodeobject.rs | 117 ++++- - .../pyo3-ffi/src/cpython/weakrefobject.rs | 1 + - include/pyo3/pyo3-ffi/src/datetime.rs | 10 +- - include/pyo3/pyo3-ffi/src/dictobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/floatobject.rs | 2 +- - .../pyo3/pyo3-ffi/src/genericaliasobject.rs | 12 + - include/pyo3/pyo3-ffi/src/lib.rs | 23 +- - include/pyo3/pyo3-ffi/src/longobject.rs | 2 +- - include/pyo3/pyo3-ffi/src/memoryobject.rs | 5 +- - include/pyo3/pyo3-ffi/src/methodobject.rs | 8 +- - include/pyo3/pyo3-ffi/src/modsupport.rs | 7 +- - include/pyo3/pyo3-ffi/src/object.rs | 422 ++++-------------- - include/pyo3/pyo3-ffi/src/pyarena.rs | 2 +- - include/pyo3/pyo3-ffi/src/pybuffer.rs | 6 +- - include/pyo3/pyo3-ffi/src/pyerrors.rs | 1 + - include/pyo3/pyo3-ffi/src/pyframe.rs | 2 +- - include/pyo3/pyo3-ffi/src/pyhash.rs | 30 +- - include/pyo3/pyo3-ffi/src/pyport.rs | 9 +- - include/pyo3/pyo3-ffi/src/pystate.rs | 78 +++- - include/pyo3/pyo3-ffi/src/pythonrun.rs | 6 +- - include/pyo3/pyo3-ffi/src/refcount.rs | 369 +++++++++++++++ - include/pyo3/pyo3-ffi/src/setobject.rs | 6 +- - include/pyo3/pyo3-ffi/src/weakrefobject.rs | 8 +- - 60 files changed, 1065 insertions(+), 885 deletions(-) - create mode 100644 include/pyo3/pyo3-ffi/src/compat/py_3_14.rs - create mode 100644 include/pyo3/pyo3-ffi/src/cpython/pyhash.rs - create mode 100644 include/pyo3/pyo3-ffi/src/genericaliasobject.rs - create mode 100644 include/pyo3/pyo3-ffi/src/refcount.rs - -diff --git a/Cargo.lock b/Cargo.lock -index 03fdf346..c4b1eb1c 100644 ---- a/Cargo.lock -+++ b/Cargo.lock -@@ -190,7 +190,7 @@ dependencies = [ - - [[package]] - name = "pyo3-build-config" --version = "0.23.3" -+version = "0.25.0" - dependencies = [ - "once_cell", - "target-lexicon", -@@ -198,7 +198,7 @@ dependencies = [ - - [[package]] - name = "pyo3-ffi" --version = "0.23.3" -+version = "0.25.0" - dependencies = [ - "libc", - "pyo3-build-config", -diff --git a/include/pyo3/pyo3-build-config/Cargo.toml b/include/pyo3/pyo3-build-config/Cargo.toml -index 0facef81..54d5cb49 100644 ---- a/include/pyo3/pyo3-build-config/Cargo.toml -+++ b/include/pyo3/pyo3-build-config/Cargo.toml -@@ -1,6 +1,6 @@ - [package] - name = "pyo3-build-config" --version = "0.23.3" -+version = "0.25.0" - description = "Build configuration for the PyO3 ecosystem" - authors = ["PyO3 Project and Contributors "] - keywords = ["pyo3", "python", "cpython", "ffi"] -@@ -36,7 +36,9 @@ abi3-py38 = ["abi3-py39"] - abi3-py39 = ["abi3-py310"] - abi3-py310 = ["abi3-py311"] - abi3-py311 = ["abi3-py312"] --abi3-py312 = ["abi3"] -+abi3-py312 = ["abi3-py313"] -+abi3-py313 = ["abi3-py314"] -+abi3-py314 = ["abi3"] - - [package.metadata.docs.rs] - features = ["resolve-config"] -diff --git a/include/pyo3/pyo3-build-config/src/errors.rs b/include/pyo3/pyo3-build-config/src/errors.rs -index 87c59a99..b11d02fd 100644 ---- a/include/pyo3/pyo3-build-config/src/errors.rs -+++ b/include/pyo3/pyo3-build-config/src/errors.rs -@@ -68,7 +68,7 @@ impl std::fmt::Display for ErrorReport<'_> { - writeln!(f, "\ncaused by:")?; - let mut index = 0; - while let Some(some_source) = source { -- writeln!(f, " - {}: {}", index, some_source)?; -+ writeln!(f, " - {index}: {some_source}")?; - source = some_source.source(); - index += 1; - } -diff --git a/include/pyo3/pyo3-build-config/src/impl_.rs b/include/pyo3/pyo3-build-config/src/impl_.rs -index 0f90d573..9485b8a0 100644 ---- a/include/pyo3/pyo3-build-config/src/impl_.rs -+++ b/include/pyo3/pyo3-build-config/src/impl_.rs -@@ -40,7 +40,7 @@ const MINIMUM_SUPPORTED_VERSION_GRAALPY: PythonVersion = PythonVersion { - }; - - /// Maximum Python version that can be used as minimum required Python version with abi3. --pub(crate) const ABI3_MAX_MINOR: u8 = 12; -+pub(crate) const ABI3_MAX_MINOR: u8 = 13; - - #[cfg(test)] - thread_local! { -@@ -58,7 +58,7 @@ pub fn cargo_env_var(var: &str) -> Option { - /// the variable changes. - pub fn env_var(var: &str) -> Option { - if cfg!(feature = "resolve-config") { -- println!("cargo:rerun-if-env-changed={}", var); -+ println!("cargo:rerun-if-env-changed={var}"); - } - #[cfg(test)] - { -@@ -180,9 +180,8 @@ impl InterpreterConfig { - let mut out = vec![]; - - for i in MINIMUM_SUPPORTED_VERSION.minor..=self.version.minor { -- out.push(format!("cargo:rustc-cfg=Py_3_{}", i)); -+ out.push(format!("cargo:rustc-cfg=Py_3_{i}")); - } -- println!("cargo::rustc-check-cfg=cfg(Py_3_14)"); - - match self.implementation { - PythonImplementation::CPython => {} -@@ -200,7 +199,7 @@ impl InterpreterConfig { - BuildFlag::Py_GIL_DISABLED => { - out.push("cargo:rustc-cfg=Py_GIL_DISABLED".to_owned()) - } -- flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{}\"", flag)), -+ flag => out.push(format!("cargo:rustc-cfg=py_sys_config=\"{flag}\"")), - } - } - -@@ -339,7 +338,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) - - let lib_dir = if cfg!(windows) { - map.get("base_prefix") -- .map(|base_prefix| format!("{}\\libs", base_prefix)) -+ .map(|base_prefix| format!("{base_prefix}\\libs")) - } else { - map.get("libdir").cloned() - }; -@@ -667,7 +666,7 @@ print("gil_disabled", get_config_var("Py_GIL_DISABLED")) - write_option_line!(python_framework_prefix)?; - write_line!(suppress_build_script_link_lines)?; - for line in &self.extra_build_script_lines { -- writeln!(writer, "extra_build_script_line={}", line) -+ writeln!(writer, "extra_build_script_line={line}") - .context("failed to write extra_build_script_line")?; - } - Ok(()) -@@ -854,7 +853,7 @@ fn is_abi3() -> bool { - /// Must be called from a PyO3 crate build script. - pub fn get_abi3_version() -> Option { - let minor_version = (MINIMUM_SUPPORTED_VERSION.minor..=ABI3_MAX_MINOR) -- .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{}", i)).is_some()); -+ .find(|i| cargo_env_var(&format!("CARGO_FEATURE_ABI3_PY3{i}")).is_some()); - minor_version.map(|minor| PythonVersion { major: 3, minor }) - } - -@@ -1122,8 +1121,8 @@ pub enum BuildFlag { - impl Display for BuildFlag { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { -- BuildFlag::Other(flag) => write!(f, "{}", flag), -- _ => write!(f, "{:?}", self), -+ BuildFlag::Other(flag) => write!(f, "{flag}"), -+ _ => write!(f, "{self:?}"), - } - } - } -@@ -1147,7 +1146,7 @@ impl FromStr for BuildFlag { - /// PyO3 will pick these up and pass to rustc via `--cfg=py_sys_config={varname}`; - /// this allows using them conditional cfg attributes in the .rs files, so - /// --/// ```rust -+/// ```rust,no_run - /// #[cfg(py_sys_config="{varname}")] - /// # struct Foo; - /// ``` -@@ -1203,7 +1202,7 @@ impl BuildFlags { - - for k in &BuildFlags::ALL { - use std::fmt::Write; -- writeln!(&mut script, "print(config.get('{}', '0'))", k).unwrap(); -+ writeln!(&mut script, "print(config.get('{k}', '0'))").unwrap(); - } - - let stdout = run_python_script(interpreter.as_ref(), &script)?; -@@ -1241,7 +1240,7 @@ impl Display for BuildFlags { - } else { - write!(f, ",")?; - } -- write!(f, "{}", flag)?; -+ write!(f, "{flag}")?; - } - Ok(()) - } -@@ -1307,6 +1306,10 @@ pub fn parse_sysconfigdata(sysconfigdata_path: impl AsRef) -> Result Result - - fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { - let pypy_version_pat = if let Some(v) = v { -- format!("pypy{}", v) -+ format!("pypy{v}") - } else { - "pypy3.".into() - }; -@@ -1433,7 +1436,7 @@ fn is_pypy_lib_dir(path: &str, v: &Option) -> bool { - - fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { - let graalpy_version_pat = if let Some(v) = v { -- format!("graalpy{}", v) -+ format!("graalpy{v}") - } else { - "graalpy2".into() - }; -@@ -1442,7 +1445,7 @@ fn is_graalpy_lib_dir(path: &str, v: &Option) -> bool { - - fn is_cpython_lib_dir(path: &str, v: &Option) -> bool { - let cpython_version_pat = if let Some(v) = v { -- format!("python{}", v) -+ format!("python{v}") - } else { - "python3.".into() - }; -@@ -1756,7 +1759,7 @@ fn default_lib_name_unix( - ) -> Result { - match implementation { - PythonImplementation::CPython => match ld_version { -- Some(ld_version) => Ok(format!("python{}", ld_version)), -+ Some(ld_version) => Ok(format!("python{ld_version}")), - None => { - if version > PythonVersion::PY37 { - // PEP 3149 ABI version tags are finally gone -@@ -1773,7 +1776,7 @@ fn default_lib_name_unix( - } - }, - PythonImplementation::PyPy => match ld_version { -- Some(ld_version) => Ok(format!("pypy{}-c", ld_version)), -+ Some(ld_version) => Ok(format!("pypy{ld_version}-c")), - None => Ok(format!("pypy{}.{}-c", version.major, version.minor)), - }, - -@@ -2002,7 +2005,7 @@ fn unescape(escaped: &str) -> Vec { - } - } - -- bytes.push(unhex(chunk[0]) << 4 | unhex(chunk[1])); -+ bytes.push((unhex(chunk[0]) << 4) | unhex(chunk[1])); - } - - bytes -diff --git a/include/pyo3/pyo3-build-config/src/lib.rs b/include/pyo3/pyo3-build-config/src/lib.rs -index 9070f6d7..f47c16f4 100644 ---- a/include/pyo3/pyo3-build-config/src/lib.rs -+++ b/include/pyo3/pyo3-build-config/src/lib.rs -@@ -46,7 +46,7 @@ use target_lexicon::OperatingSystem; - pub fn use_pyo3_cfgs() { - print_expected_cfgs(); - for cargo_command in get().build_script_outputs() { -- println!("{}", cargo_command) -+ println!("{cargo_command}") - } - } - -@@ -102,12 +102,7 @@ fn _add_python_framework_link_args( - ) { - if matches!(triple.operating_system, OperatingSystem::Darwin(_)) && link_libpython { - if let Some(framework_prefix) = interpreter_config.python_framework_prefix.as_ref() { -- writeln!( -- writer, -- "cargo:rustc-link-arg=-Wl,-rpath,{}", -- framework_prefix -- ) -- .unwrap(); -+ writeln!(writer, "cargo:rustc-link-arg=-Wl,-rpath,{framework_prefix}").unwrap(); - } - } - } -@@ -168,40 +163,36 @@ fn resolve_cross_compile_config_path() -> Option { - }) - } - -+/// Helper to print a feature cfg with a minimum rust version required. -+fn print_feature_cfg(minor_version_required: u32, cfg: &str) { -+ let minor_version = rustc_minor_version().unwrap_or(0); -+ -+ if minor_version >= minor_version_required { -+ println!("cargo:rustc-cfg={cfg}"); -+ } -+ -+ // rustc 1.80.0 stabilized `rustc-check-cfg` feature, don't emit before -+ if minor_version >= 80 { -+ println!("cargo:rustc-check-cfg=cfg({cfg})"); -+ } -+} -+ - /// Use certain features if we detect the compiler being used supports them. - /// - /// Features may be removed or added as MSRV gets bumped or new features become available, - /// so this function is unstable. - #[doc(hidden)] - pub fn print_feature_cfgs() { -- let rustc_minor_version = rustc_minor_version().unwrap_or(0); -- -- if rustc_minor_version >= 70 { -- println!("cargo:rustc-cfg=rustc_has_once_lock"); -- } -- -- // invalid_from_utf8 lint was added in Rust 1.74 -- if rustc_minor_version >= 74 { -- println!("cargo:rustc-cfg=invalid_from_utf8_lint"); -- } -- -- if rustc_minor_version >= 79 { -- println!("cargo:rustc-cfg=c_str_lit"); -- } -- -+ print_feature_cfg(70, "rustc_has_once_lock"); -+ print_feature_cfg(70, "cargo_toml_lints"); -+ print_feature_cfg(71, "rustc_has_extern_c_unwind"); -+ print_feature_cfg(74, "invalid_from_utf8_lint"); -+ print_feature_cfg(79, "c_str_lit"); - // Actually this is available on 1.78, but we should avoid - // https://github.com/rust-lang/rust/issues/124651 just in case -- if rustc_minor_version >= 79 { -- println!("cargo:rustc-cfg=diagnostic_namespace"); -- } -- -- if rustc_minor_version >= 83 { -- println!("cargo:rustc-cfg=io_error_more"); -- } -- -- if rustc_minor_version >= 85 { -- println!("cargo:rustc-cfg=fn_ptr_eq"); -- } -+ print_feature_cfg(79, "diagnostic_namespace"); -+ print_feature_cfg(83, "io_error_more"); -+ print_feature_cfg(85, "fn_ptr_eq"); - } - - /// Registers `pyo3`s config names as reachable cfg expressions -@@ -220,14 +211,8 @@ pub fn print_expected_cfgs() { - println!("cargo:rustc-check-cfg=cfg(PyPy)"); - println!("cargo:rustc-check-cfg=cfg(GraalPy)"); - println!("cargo:rustc-check-cfg=cfg(py_sys_config, values(\"Py_DEBUG\", \"Py_REF_DEBUG\", \"Py_TRACE_REFS\", \"COUNT_ALLOCS\"))"); -- println!("cargo:rustc-check-cfg=cfg(invalid_from_utf8_lint)"); - println!("cargo:rustc-check-cfg=cfg(pyo3_disable_reference_pool)"); - println!("cargo:rustc-check-cfg=cfg(pyo3_leak_on_drop_without_reference_pool)"); -- println!("cargo:rustc-check-cfg=cfg(diagnostic_namespace)"); -- println!("cargo:rustc-check-cfg=cfg(c_str_lit)"); -- println!("cargo:rustc-check-cfg=cfg(rustc_has_once_lock)"); -- println!("cargo:rustc-check-cfg=cfg(io_error_more)"); -- println!("cargo:rustc-check-cfg=cfg(fn_ptr_eq)"); - - // allow `Py_3_*` cfgs from the minimum supported version up to the - // maximum minor version (+1 for development for the next) -diff --git a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -index 4502d777..8b20727d 100644 ---- a/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -+++ b/include/pyo3/pyo3-ffi/ACKNOWLEDGEMENTS -@@ -3,4 +3,4 @@ for binary compatibility, with additional metadata to support PyPy. - - For original implementations please see: - - https://github.com/python/cpython -- - https://foss.heptapod.net/pypy/pypy -+ - https://github.com/pypy/pypy -diff --git a/include/pyo3/pyo3-ffi/Cargo.toml b/include/pyo3/pyo3-ffi/Cargo.toml -index 14a4f958..73ea84ab 100644 ---- a/include/pyo3/pyo3-ffi/Cargo.toml -+++ b/include/pyo3/pyo3-ffi/Cargo.toml -@@ -1,6 +1,6 @@ - [package] - name = "pyo3-ffi" --version = "0.23.3" -+version = "0.25.0" - description = "Python-API bindings for the PyO3 ecosystem" - authors = ["PyO3 Project and Contributors "] - keywords = ["pyo3", "python", "cpython", "ffi"] -@@ -12,7 +12,7 @@ edition = "2021" - links = "python" - - [dependencies] --libc = "0.2" -+libc = "0.2.62" - - [features] - -@@ -32,7 +32,9 @@ abi3-py38 = ["abi3-py39", "pyo3-build-config/abi3-py38"] - abi3-py39 = ["abi3-py310", "pyo3-build-config/abi3-py39"] - abi3-py310 = ["abi3-py311", "pyo3-build-config/abi3-py310"] - abi3-py311 = ["abi3-py312", "pyo3-build-config/abi3-py311"] --abi3-py312 = ["abi3", "pyo3-build-config/abi3-py312"] -+abi3-py312 = ["abi3-py313", "pyo3-build-config/abi3-py312"] -+abi3-py313 = ["abi3-py314", "pyo3-build-config/abi3-py313"] -+abi3-py314 = ["abi3", "pyo3-build-config/abi3-py314"] - - # Automatically generates `python3.dll` import libraries for Windows targets. - generate-import-lib = ["pyo3-build-config/python3-dll-a"] -@@ -41,4 +43,4 @@ generate-import-lib = ["pyo3-build-config/python3-dll-a"] - paste = "1" - - [build-dependencies] --pyo3-build-config = { path = "../pyo3-build-config", features = ["resolve-config"] } -+pyo3-build-config = { path = "../pyo3-build-config", version = "=0.25.0", features = ["resolve-config"] } -diff --git a/include/pyo3/pyo3-ffi/build.rs b/include/pyo3/pyo3-ffi/build.rs -index b0f1c28d..6776cd80 100644 ---- a/include/pyo3/pyo3-ffi/build.rs -+++ b/include/pyo3/pyo3-ffi/build.rs -@@ -4,9 +4,8 @@ use pyo3_build_config::{ - cargo_env_var, env_var, errors::Result, is_linking_libpython, resolve_interpreter_config, - InterpreterConfig, PythonVersion, - }, -- warn, BuildFlag, PythonImplementation, -+ warn, PythonImplementation, - }; --use std::ops::Not; - - /// Minimum Python version PyO3 supports. - struct SupportedVersions { -@@ -18,15 +17,15 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions { - min: PythonVersion { major: 3, minor: 7 }, - max: PythonVersion { - major: 3, -- minor: 13, -+ minor: 14, - }, - }; - - const SUPPORTED_VERSIONS_PYPY: SupportedVersions = SupportedVersions { -- min: PythonVersion { major: 3, minor: 7 }, -+ min: PythonVersion { major: 3, minor: 9 }, - max: PythonVersion { - major: 3, -- minor: 10, -+ minor: 11, - }, - }; - -@@ -57,15 +56,22 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - interpreter_config.version, - versions.min, - ); -- ensure!( -- interpreter_config.version <= versions.max || env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), -- "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -- = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", -- interpreter_config.version, -- versions.max, -- std::env::var("CARGO_PKG_VERSION").unwrap(), -- ); -+ if interpreter_config.version > versions.max { -+ ensure!(!interpreter_config.is_free_threaded(), -+ "The configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -+ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -+ = help: The free-threaded build of CPython does not support the limited API so this check cannot be suppressed.", -+ interpreter_config.version, versions.max, std::env::var("CARGO_PKG_VERSION").unwrap() -+ ); -+ ensure!(env_var("PYO3_USE_ABI3_FORWARD_COMPATIBILITY").map_or(false, |os_str| os_str == "1"), -+ "the configured Python interpreter version ({}) is newer than PyO3's maximum supported version ({})\n\ -+ = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -+ = help: set PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1 to suppress this check and build anyway using the stable ABI", -+ interpreter_config.version, -+ versions.max, -+ std::env::var("CARGO_PKG_VERSION").unwrap(), -+ ); -+ } - } - PythonImplementation::PyPy => { - let versions = SUPPORTED_VERSIONS_PYPY; -@@ -107,10 +113,16 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - - if interpreter_config.abi3 { - match interpreter_config.implementation { -- PythonImplementation::CPython => {} -+ PythonImplementation::CPython => { -+ if interpreter_config.is_free_threaded() { -+ warn!( -+ "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." -+ ) -+ } -+ } - PythonImplementation::PyPy => warn!( - "PyPy does not yet support abi3 so the build artifacts will be version-specific. \ -- See https://foss.heptapod.net/pypy/pypy/-/issues/3397 for more information." -+ See https://github.com/pypy/pypy/issues/3397 for more information." - ), - PythonImplementation::GraalPy => warn!( - "GraalPy does not support abi3 so the build artifacts will be version-specific." -@@ -121,29 +133,6 @@ fn ensure_python_version(interpreter_config: &InterpreterConfig) -> Result<()> { - Ok(()) - } - --fn ensure_gil_enabled(interpreter_config: &InterpreterConfig) -> Result<()> { -- let gil_enabled = interpreter_config -- .build_flags -- .0 -- .contains(&BuildFlag::Py_GIL_DISABLED) -- .not(); -- ensure!( -- gil_enabled || std::env::var("UNSAFE_PYO3_BUILD_FREE_THREADED").map_or(false, |os_str| os_str == "1"), -- "the Python interpreter was built with the GIL disabled, which is not yet supported by PyO3\n\ -- = help: see https://github.com/PyO3/pyo3/issues/4265 for more information\n\ -- = help: please check if an updated version of PyO3 is available. Current version: {}\n\ -- = help: set UNSAFE_PYO3_BUILD_FREE_THREADED=1 to suppress this check and build anyway for free-threaded Python", -- std::env::var("CARGO_PKG_VERSION").unwrap() -- ); -- if !gil_enabled && interpreter_config.abi3 { -- warn!( -- "The free-threaded build of CPython does not yet support abi3 so the build artifacts will be version-specific." -- ) -- } -- -- Ok(()) --} -- - fn ensure_target_pointer_width(interpreter_config: &InterpreterConfig) -> Result<()> { - if let Some(pointer_width) = interpreter_config.pointer_width { - // Try to check whether the target architecture matches the python library -@@ -187,7 +176,7 @@ fn emit_link_config(interpreter_config: &InterpreterConfig) -> Result<()> { - ); - - if let Some(lib_dir) = &interpreter_config.lib_dir { -- println!("cargo:rustc-link-search=native={}", lib_dir); -+ println!("cargo:rustc-link-search=native={lib_dir}"); - } - - Ok(()) -@@ -209,7 +198,6 @@ fn configure_pyo3() -> Result<()> { - - ensure_python_version(&interpreter_config)?; - ensure_target_pointer_width(&interpreter_config)?; -- ensure_gil_enabled(&interpreter_config)?; - - // Serialize the whole interpreter config into DEP_PYTHON_PYO3_CONFIG env var. - interpreter_config.to_cargo_dep_env()?; -@@ -219,12 +207,12 @@ fn configure_pyo3() -> Result<()> { - } - - for cfg in interpreter_config.build_script_outputs() { -- println!("{}", cfg) -+ println!("{cfg}") - } - - // Extra lines come last, to support last write wins. - for line in &interpreter_config.extra_build_script_lines { -- println!("{}", line); -+ println!("{line}"); - } - - // Emit cfgs like `invalid_from_utf8_lint` -diff --git a/include/pyo3/pyo3-ffi/src/abstract_.rs b/include/pyo3/pyo3-ffi/src/abstract_.rs -index ce6c9b94..84fb98a1 100644 ---- a/include/pyo3/pyo3-ffi/src/abstract_.rs -+++ b/include/pyo3/pyo3-ffi/src/abstract_.rs -@@ -1,15 +1,23 @@ - use crate::object::*; - use crate::pyport::Py_ssize_t; -+#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] -+use libc::size_t; - use std::os::raw::{c_char, c_int}; - - #[inline] --#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h -+#[cfg(all( -+ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h -+ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ -+))] - pub unsafe fn PyObject_DelAttrString(o: *mut PyObject, attr_name: *const c_char) -> c_int { - PyObject_SetAttrString(o, attr_name, std::ptr::null_mut()) - } - - #[inline] --#[cfg(all(not(Py_3_13), not(PyPy)))] // CPython exposed as a function in 3.13, in object.h -+#[cfg(all( -+ not(Py_3_13), // CPython exposed as a function in 3.13, in object.h -+ not(all(PyPy, not(Py_3_11))) // PyPy exposed as a function until PyPy 3.10, macro in 3.11+ -+))] - pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_int { - PyObject_SetAttr(o, attr_name, std::ptr::null_mut()) - } -@@ -17,6 +25,7 @@ pub unsafe fn PyObject_DelAttr(o: *mut PyObject, attr_name: *mut PyObject) -> c_ - extern "C" { - #[cfg(all( - not(PyPy), -+ not(GraalPy), - any(Py_3_10, all(not(Py_LIMITED_API), Py_3_9)) // Added to python in 3.9 but to limited API in 3.10 - ))] - #[cfg_attr(PyPy, link_name = "PyPyObject_CallNoArgs")] -@@ -70,6 +79,28 @@ extern "C" { - method: *mut PyObject, - ... - ) -> *mut PyObject; -+} -+#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))] -+pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = -+ 1 << (8 * std::mem::size_of::() as size_t - 1); -+ -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")] -+ #[cfg(any(Py_3_12, all(Py_3_11, not(Py_LIMITED_API))))] -+ pub fn PyObject_Vectorcall( -+ callable: *mut PyObject, -+ args: *const *mut PyObject, -+ nargsf: size_t, -+ kwnames: *mut PyObject, -+ ) -> *mut PyObject; -+ -+ #[cfg(any(Py_3_12, all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy)))))] -+ pub fn PyObject_VectorcallMethod( -+ name: *mut PyObject, -+ args: *const *mut PyObject, -+ nargsf: size_t, -+ kwnames: *mut PyObject, -+ ) -> *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyObject_Type")] - pub fn PyObject_Type(o: *mut PyObject) -> *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyObject_Size")] -@@ -119,7 +150,11 @@ extern "C" { - pub fn PyIter_Next(arg1: *mut PyObject) -> *mut PyObject; - #[cfg(all(not(PyPy), Py_3_10))] - #[cfg_attr(PyPy, link_name = "PyPyIter_Send")] -- pub fn PyIter_Send(iter: *mut PyObject, arg: *mut PyObject, presult: *mut *mut PyObject); -+ pub fn PyIter_Send( -+ iter: *mut PyObject, -+ arg: *mut PyObject, -+ presult: *mut *mut PyObject, -+ ) -> PySendResult; - - #[cfg_attr(PyPy, link_name = "PyPyNumber_Check")] - pub fn PyNumber_Check(o: *mut PyObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -index 24a97bcc..d27dfa8b 100644 ---- a/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -+++ b/include/pyo3/pyo3-ffi/src/bytearrayobject.rs -@@ -17,7 +17,7 @@ pub struct PyByteArrayObject { - } - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] --opaque_struct!(PyByteArrayObject); -+opaque_struct!(pub PyByteArrayObject); - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -diff --git a/include/pyo3/pyo3-ffi/src/code.rs b/include/pyo3/pyo3-ffi/src/code.rs -index d28f68cd..296b17f6 100644 ---- a/include/pyo3/pyo3-ffi/src/code.rs -+++ b/include/pyo3/pyo3-ffi/src/code.rs -@@ -1,4 +1,4 @@ - // This header doesn't exist in CPython, but Include/cpython/code.h does. We add - // this here so that PyCodeObject has a definition under the limited API. - --opaque_struct!(PyCodeObject); -+opaque_struct!(pub PyCodeObject); -diff --git a/include/pyo3/pyo3-ffi/src/compat/mod.rs b/include/pyo3/pyo3-ffi/src/compat/mod.rs -index 11f29128..044ea467 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/mod.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/mod.rs -@@ -52,8 +52,10 @@ macro_rules! compat_function { - - mod py_3_10; - mod py_3_13; -+mod py_3_14; - mod py_3_9; - - pub use self::py_3_10::*; - pub use self::py_3_13::*; -+pub use self::py_3_14::*; - pub use self::py_3_9::*; -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -index c6e8c2cb..d962fb3b 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_10.rs -@@ -17,3 +17,29 @@ compat_function!( - obj - } - ); -+ -+compat_function!( -+ originally_defined_for(Py_3_10); -+ -+ #[inline] -+ pub unsafe fn PyModule_AddObjectRef( -+ module: *mut crate::PyObject, -+ name: *const std::os::raw::c_char, -+ value: *mut crate::PyObject, -+ ) -> std::os::raw::c_int { -+ if value.is_null() && crate::PyErr_Occurred().is_null() { -+ crate::PyErr_SetString( -+ crate::PyExc_SystemError, -+ c_str!("PyModule_AddObjectRef() must be called with an exception raised if value is NULL").as_ptr(), -+ ); -+ return -1; -+ } -+ -+ crate::Py_XINCREF(value); -+ let result = crate::PyModule_AddObject(module, name, value); -+ if result < 0 { -+ crate::Py_XDECREF(value); -+ } -+ result -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -index 59289cb7..96c90e7e 100644 ---- a/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_13.rs -@@ -104,3 +104,18 @@ compat_function!( - crate::PyList_SetSlice(list, 0, crate::PY_SSIZE_T_MAX, std::ptr::null_mut()) - } - ); -+ -+compat_function!( -+ originally_defined_for(Py_3_13); -+ -+ #[inline] -+ pub unsafe fn PyModule_Add( -+ module: *mut crate::PyObject, -+ name: *const std::os::raw::c_char, -+ value: *mut crate::PyObject, -+ ) -> std::os::raw::c_int { -+ let result = crate::compat::PyModule_AddObjectRef(module, name, value); -+ crate::Py_XDECREF(value); -+ result -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs -new file mode 100644 -index 00000000..6fdaef17 ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/compat/py_3_14.rs -@@ -0,0 +1,26 @@ -+compat_function!( -+ originally_defined_for(all(Py_3_14, not(Py_LIMITED_API))); -+ -+ #[inline] -+ pub unsafe fn Py_HashBuffer( -+ ptr: *const std::ffi::c_void, -+ len: crate::Py_ssize_t, -+ ) -> crate::Py_hash_t { -+ #[cfg(not(any(Py_LIMITED_API, PyPy)))] -+ { -+ crate::_Py_HashBytes(ptr, len) -+ } -+ -+ #[cfg(any(Py_LIMITED_API, PyPy))] -+ { -+ let bytes = crate::PyBytes_FromStringAndSize(ptr as *const std::os::raw::c_char, len); -+ if bytes.is_null() { -+ -1 -+ } else { -+ let result = crate::PyObject_Hash(bytes); -+ crate::Py_DECREF(bytes); -+ result -+ } -+ } -+ } -+); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -index 477ad02b..6ada1a75 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/abstract_.rs -@@ -1,5 +1,5 @@ - use crate::{PyObject, Py_ssize_t}; --#[cfg(not(all(Py_3_11, GraalPy)))] -+#[cfg(any(all(Py_3_8, not(any(PyPy, GraalPy))), not(Py_3_11)))] - use std::os::raw::c_char; - use std::os::raw::c_int; - -@@ -41,8 +41,8 @@ extern "C" { - ) -> *mut PyObject; - } - --#[cfg(Py_3_8)] --pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = -+#[cfg(Py_3_8)] // NB exported as public in abstract.rs from 3.12 -+const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t = - 1 << (8 * std::mem::size_of::() as size_t - 1); - - #[cfg(Py_3_8)] -@@ -91,7 +91,7 @@ pub unsafe fn _PyObject_VectorcallTstate( - } - } - --#[cfg(all(Py_3_8, not(any(PyPy, GraalPy))))] -+#[cfg(all(Py_3_8, not(any(PyPy, GraalPy, Py_3_11))))] // exported as a function from 3.11, see abstract.rs - #[inline(always)] - pub unsafe fn PyObject_Vectorcall( - callable: *mut PyObject, -@@ -103,16 +103,6 @@ pub unsafe fn PyObject_Vectorcall( - } - - extern "C" { -- #[cfg(all(PyPy, Py_3_8))] -- #[cfg_attr(not(Py_3_9), link_name = "_PyPyObject_Vectorcall")] -- #[cfg_attr(Py_3_9, link_name = "PyPyObject_Vectorcall")] -- pub fn PyObject_Vectorcall( -- callable: *mut PyObject, -- args: *const *mut PyObject, -- nargsf: size_t, -- kwnames: *mut PyObject, -- ) -> *mut PyObject; -- - #[cfg(Py_3_8)] - #[cfg_attr( - all(not(any(PyPy, GraalPy)), not(Py_3_9)), -@@ -187,23 +177,13 @@ pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *m - _PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut()) - } - --extern "C" { -- #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] -- pub fn PyObject_VectorcallMethod( -- name: *mut PyObject, -- args: *const *mut PyObject, -- nargsf: size_t, -- kwnames: *mut PyObject, -- ) -> *mut PyObject; --} -- - #[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))] - #[inline(always)] - pub unsafe fn PyObject_CallMethodNoArgs( - self_: *mut PyObject, - name: *mut PyObject, - ) -> *mut PyObject { -- PyObject_VectorcallMethod( -+ crate::PyObject_VectorcallMethod( - name, - &self_, - 1 | PY_VECTORCALL_ARGUMENTS_OFFSET, -@@ -220,7 +200,7 @@ pub unsafe fn PyObject_CallMethodOneArg( - ) -> *mut PyObject { - let args = [self_, arg]; - assert!(!arg.is_null()); -- PyObject_VectorcallMethod( -+ crate::PyObject_VectorcallMethod( - name, - args.as_ptr(), - 2 | PY_VECTORCALL_ARGUMENTS_OFFSET, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -index 306702de..dd78e646 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/bytesobject.rs -@@ -17,7 +17,7 @@ pub struct PyBytesObject { - } - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] --opaque_struct!(PyBytesObject); -+opaque_struct!(pub PyBytesObject); - - extern "C" { - #[cfg_attr(PyPy, link_name = "_PyPyBytes_Resize")] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/code.rs b/include/pyo3/pyo3-ffi/src/cpython/code.rs -index 230096ca..3d47a1bc 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/code.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/code.rs -@@ -1,207 +1,43 @@ - use crate::object::*; - use crate::pyport::Py_ssize_t; - --#[allow(unused_imports)] --use std::os::raw::{c_char, c_int, c_short, c_uchar, c_void}; -+#[cfg(not(GraalPy))] -+use std::os::raw::c_char; -+use std::os::raw::{c_int, c_void}; - #[cfg(not(any(PyPy, GraalPy)))] - use std::ptr::addr_of_mut; - --#[cfg(all(Py_3_8, not(any(PyPy, GraalPy)), not(Py_3_11)))] --opaque_struct!(_PyOpcache); -+// skipped private _PY_MONITORING_LOCAL_EVENTS -+// skipped private _PY_MONITORING_UNGROUPED_EVENTS -+// skipped private _PY_MONITORING_EVENTS - --#[cfg(Py_3_12)] --pub const _PY_MONITORING_LOCAL_EVENTS: usize = 10; --#[cfg(Py_3_12)] --pub const _PY_MONITORING_UNGROUPED_EVENTS: usize = 15; --#[cfg(Py_3_12)] --pub const _PY_MONITORING_EVENTS: usize = 17; -+// skipped private _PyLocalMonitors -+// skipped private _Py_GlobalMonitors - --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Clone, Copy)] --pub struct _Py_LocalMonitors { -- pub tools: [u8; if cfg!(Py_3_13) { -- _PY_MONITORING_LOCAL_EVENTS -- } else { -- _PY_MONITORING_UNGROUPED_EVENTS -- }], --} -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Clone, Copy)] --pub struct _Py_GlobalMonitors { -- pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS], --} -- --// skipped _Py_CODEUNIT -+// skipped private _Py_CODEUNIT - --// skipped _Py_OPCODE --// skipped _Py_OPARG -+// skipped private _Py_OPCODE -+// skipped private _Py_OPARG - --// skipped _py_make_codeunit -+// skipped private _py_make_codeunit - --// skipped _py_set_opcode -+// skipped private _py_set_opcode - --// skipped _Py_MAKE_CODEUNIT --// skipped _Py_SET_OPCODE -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoCached { -- pub _co_code: *mut PyObject, -- pub _co_varnames: *mut PyObject, -- pub _co_cellvars: *mut PyObject, -- pub _co_freevars: *mut PyObject, --} -- --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoLineInstrumentationData { -- pub original_opcode: u8, -- pub line_delta: i8, --} -+// skipped private _Py_MAKE_CODEUNIT -+// skipped private _Py_SET_OPCODE - --#[cfg(Py_3_12)] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCoMonitoringData { -- pub local_monitors: _Py_LocalMonitors, -- pub active_monitors: _Py_LocalMonitors, -- pub tools: *mut u8, -- pub lines: *mut _PyCoLineInstrumentationData, -- pub line_tools: *mut u8, -- pub per_instruction_opcodes: *mut u8, -- pub per_instruction_tools: *mut u8, --} -- --#[cfg(all(not(any(PyPy, GraalPy)), not(Py_3_7)))] --opaque_struct!(PyCodeObject); -- --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_7, not(Py_3_8)))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_argcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_nlocals: c_int, -- pub co_stacksize: c_int, -- pub co_flags: c_int, -- pub co_firstlineno: c_int, -- pub co_code: *mut PyObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_varnames: *mut PyObject, -- pub co_freevars: *mut PyObject, -- pub co_cellvars: *mut PyObject, -- pub co_cell2arg: *mut Py_ssize_t, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- pub co_lnotab: *mut PyObject, -- pub co_zombieframe: *mut c_void, -- pub co_weakreflist: *mut PyObject, -- pub co_extra: *mut c_void, --} -- --#[cfg(Py_3_13)] --opaque_struct!(_PyExecutorArray); -- --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_8, not(Py_3_11)))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_argcount: c_int, -- pub co_posonlyargcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_nlocals: c_int, -- pub co_stacksize: c_int, -- pub co_flags: c_int, -- pub co_firstlineno: c_int, -- pub co_code: *mut PyObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_varnames: *mut PyObject, -- pub co_freevars: *mut PyObject, -- pub co_cellvars: *mut PyObject, -- pub co_cell2arg: *mut Py_ssize_t, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- #[cfg(not(Py_3_10))] -- pub co_lnotab: *mut PyObject, -- #[cfg(Py_3_10)] -- pub co_linetable: *mut PyObject, -- pub co_zombieframe: *mut c_void, -- pub co_weakreflist: *mut PyObject, -- pub co_extra: *mut c_void, -- pub co_opcache_map: *mut c_uchar, -- pub co_opcache: *mut _PyOpcache, -- pub co_opcache_flag: c_int, -- pub co_opcache_size: c_uchar, --} -+// skipped private _PyCoCached -+// skipped private _PyCoLineInstrumentationData -+// skipped private _PyCoMontoringData - --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_11))] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyVarObject, -- pub co_consts: *mut PyObject, -- pub co_names: *mut PyObject, -- pub co_exceptiontable: *mut PyObject, -- pub co_flags: c_int, -- #[cfg(not(Py_3_12))] -- pub co_warmup: c_int, -+// skipped private _PyExecutorArray - -- pub co_argcount: c_int, -- pub co_posonlyargcount: c_int, -- pub co_kwonlyargcount: c_int, -- pub co_stacksize: c_int, -- pub co_firstlineno: c_int, -- -- pub co_nlocalsplus: c_int, -- #[cfg(Py_3_12)] -- pub co_framesize: c_int, -- pub co_nlocals: c_int, -- #[cfg(not(Py_3_12))] -- pub co_nplaincellvars: c_int, -- pub co_ncellvars: c_int, -- pub co_nfreevars: c_int, -- #[cfg(Py_3_12)] -- pub co_version: u32, -- -- pub co_localsplusnames: *mut PyObject, -- pub co_localspluskinds: *mut PyObject, -- pub co_filename: *mut PyObject, -- pub co_name: *mut PyObject, -- pub co_qualname: *mut PyObject, -- pub co_linetable: *mut PyObject, -- pub co_weakreflist: *mut PyObject, -- #[cfg(not(Py_3_12))] -- pub _co_code: *mut PyObject, -- #[cfg(not(Py_3_12))] -- pub _co_linearray: *mut c_char, -- #[cfg(Py_3_13)] -- pub co_executors: *mut _PyExecutorArray, -- #[cfg(Py_3_12)] -- pub _co_cached: *mut _PyCoCached, -- #[cfg(Py_3_12)] -- pub _co_instrumentation_version: u64, -- #[cfg(Py_3_12)] -- pub _co_monitoring: *mut _PyCoMonitoringData, -- pub _co_firsttraceable: c_int, -- pub co_extra: *mut c_void, -- pub co_code_adaptive: [c_char; 1], --} -- --#[cfg(PyPy)] --#[repr(C)] --pub struct PyCodeObject { -- pub ob_base: PyObject, -- pub co_name: *mut PyObject, -- pub co_filename: *mut PyObject, -- pub co_argcount: c_int, -- pub co_flags: c_int, --} -+opaque_struct!( -+ #[doc = "A Python code object.\n"] -+ #[doc = "\n"] -+ #[doc = "`pyo3-ffi` does not expose the contents of this struct, as it has no stability guarantees."] -+ pub PyCodeObject -+); - - /* Masks for co_flags */ - pub const CO_OPTIMIZED: c_int = 0x0001; -@@ -247,28 +83,14 @@ pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == addr_of_mut!(PyCode_Type)) as c_int - } - --#[inline] --#[cfg(all(not(any(PyPy, GraalPy)), Py_3_10, not(Py_3_11)))] --pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t { -- crate::PyTuple_GET_SIZE((*op).co_freevars) --} -- --#[inline] --#[cfg(all(not(Py_3_10), Py_3_11, not(any(PyPy, GraalPy))))] --pub unsafe fn PyCode_GetNumFree(op: *mut PyCodeObject) -> c_int { -- (*op).co_nfreevars --} -- - extern "C" { - #[cfg(PyPy)] - #[link_name = "PyPyCode_Check"] - pub fn PyCode_Check(op: *mut PyObject) -> c_int; -- -- #[cfg(PyPy)] -- #[link_name = "PyPyCode_GetNumFree"] -- pub fn PyCode_GetNumFree(op: *mut PyCodeObject) -> Py_ssize_t; - } - -+// skipped PyCode_GetNumFree (requires knowledge of code object layout) -+ - extern "C" { - #[cfg(not(GraalPy))] - #[cfg_attr(PyPy, link_name = "PyPyCode_New")] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/compile.rs b/include/pyo3/pyo3-ffi/src/cpython/compile.rs -index 79f06c92..078b5e0d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/compile.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/compile.rs -@@ -6,19 +6,21 @@ use crate::pyarena::*; - use crate::pythonrun::*; - #[cfg(not(any(PyPy, Py_3_10)))] - use crate::PyCodeObject; -+use crate::INT_MAX; - #[cfg(not(any(PyPy, Py_3_10)))] - use std::os::raw::c_char; - use std::os::raw::c_int; - --// skipped non-limited PyCF_MASK --// skipped non-limited PyCF_MASK_OBSOLETE --// skipped non-limited PyCF_SOURCE_IS_UTF8 --// skipped non-limited PyCF_DONT_IMPLY_DEDENT --// skipped non-limited PyCF_ONLY_AST --// skipped non-limited PyCF_IGNORE_COOKIE --// skipped non-limited PyCF_TYPE_COMMENTS --// skipped non-limited PyCF_ALLOW_TOP_LEVEL_AWAIT --// skipped non-limited PyCF_COMPILE_MASK -+// skipped PyCF_MASK -+// skipped PyCF_MASK_OBSOLETE -+// skipped PyCF_SOURCE_IS_UTF8 -+// skipped PyCF_DONT_IMPLY_DEDENT -+// skipped PyCF_ONLY_AST -+// skipped PyCF_IGNORE_COOKIE -+// skipped PyCF_TYPE_COMMENTS -+// skipped PyCF_ALLOW_TOP_LEVEL_AWAIT -+// skipped PyCF_OPTIMIZED_AST -+// skipped PyCF_COMPILE_MASK - - #[repr(C)] - #[derive(Copy, Clone)] -@@ -28,31 +30,23 @@ pub struct PyCompilerFlags { - pub cf_feature_version: c_int, - } - --// skipped non-limited _PyCompilerFlags_INIT -+// skipped _PyCompilerFlags_INIT - --#[cfg(all(Py_3_12, not(any(Py_3_13, PyPy, GraalPy))))] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct _PyCompilerSrcLocation { -- pub lineno: c_int, -- pub end_lineno: c_int, -- pub col_offset: c_int, -- pub end_col_offset: c_int, --} -- --// skipped SRC_LOCATION_FROM_AST -- --#[cfg(not(any(PyPy, GraalPy, Py_3_13)))] -+// NB this type technically existed in the header until 3.13, when it was -+// moved to the internal CPython headers. -+// -+// We choose not to expose it in the public API past 3.10, as it is -+// not used in the public API past that point. -+#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] - #[repr(C)] - #[derive(Copy, Clone)] - pub struct PyFutureFeatures { - pub ff_features: c_int, -- #[cfg(not(Py_3_12))] - pub ff_lineno: c_int, -- #[cfg(Py_3_12)] -- pub ff_location: _PyCompilerSrcLocation, - } - -+// FIXME: these constants should probably be &CStr, if they are used at all -+ - pub const FUTURE_NESTED_SCOPES: &str = "nested_scopes"; - pub const FUTURE_GENERATORS: &str = "generators"; - pub const FUTURE_DIVISION: &str = "division"; -@@ -62,13 +56,12 @@ pub const FUTURE_PRINT_FUNCTION: &str = "print_function"; - pub const FUTURE_UNICODE_LITERALS: &str = "unicode_literals"; - pub const FUTURE_BARRY_AS_BDFL: &str = "barry_as_FLUFL"; - pub const FUTURE_GENERATOR_STOP: &str = "generator_stop"; --// skipped non-limited FUTURE_ANNOTATIONS -+pub const FUTURE_ANNOTATIONS: &str = "annotations"; - -+#[cfg(not(any(PyPy, GraalPy, Py_3_10)))] - extern "C" { -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyNode_Compile(arg1: *mut _node, arg2: *const c_char) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyAST_CompileEx( - _mod: *mut _mod, - filename: *const c_char, -@@ -77,7 +70,6 @@ extern "C" { - arena: *mut PyArena, - ) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyAST_CompileObject( - _mod: *mut _mod, - filename: *mut PyObject, -@@ -86,23 +78,20 @@ extern "C" { - arena: *mut PyArena, - ) -> *mut PyCodeObject; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyFuture_FromAST(_mod: *mut _mod, filename: *const c_char) -> *mut PyFutureFeatures; - -- #[cfg(not(any(PyPy, Py_3_10)))] - pub fn PyFuture_FromASTObject( - _mod: *mut _mod, - filename: *mut PyObject, - ) -> *mut PyFutureFeatures; -+} - -- // skipped non-limited _Py_Mangle -- // skipped non-limited PY_INVALID_STACK_EFFECT -+pub const PY_INVALID_STACK_EFFECT: c_int = INT_MAX; -+ -+extern "C" { - - pub fn PyCompile_OpcodeStackEffect(opcode: c_int, oparg: c_int) -> c_int; - - #[cfg(Py_3_8)] - pub fn PyCompile_OpcodeStackEffectWithJump(opcode: c_int, oparg: c_int, jump: c_int) -> c_int; -- -- // skipped non-limited _PyASTOptimizeState -- // skipped non-limited _PyAST_Optimize - } -diff --git a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -index 1fc6591c..808dba87 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/critical_section.rs -@@ -9,16 +9,6 @@ pub struct PyCriticalSection { - _cs_mutex: *mut PyMutex, - } - --#[cfg(Py_GIL_DISABLED)] --impl Default for PyCriticalSection { -- fn default() -> Self { -- PyCriticalSection { -- _cs_prev: 0, -- _cs_mutex: core::ptr::null_mut(), -- } -- } --} -- - #[repr(C)] - #[cfg(Py_GIL_DISABLED)] - pub struct PyCriticalSection2 { -@@ -27,10 +17,10 @@ pub struct PyCriticalSection2 { - } - - #[cfg(not(Py_GIL_DISABLED))] --opaque_struct!(PyCriticalSection); -+opaque_struct!(pub PyCriticalSection); - - #[cfg(not(Py_GIL_DISABLED))] --opaque_struct!(PyCriticalSection2); -+opaque_struct!(pub PyCriticalSection2); - - extern "C" { - pub fn PyCriticalSection_Begin(c: *mut PyCriticalSection, op: *mut PyObject); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -index 1b5ee466..7cef9bdb 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/descrobject.rs -@@ -69,10 +69,7 @@ pub struct PyWrapperDescrObject { - pub d_wrapped: *mut c_void, - } - --#[cfg_attr(windows, link(name = "pythonXY"))] --extern "C" { -- pub static mut _PyMethodWrapper_Type: PyTypeObject; --} -+// skipped _PyMethodWrapper_Type - - // skipped non-limited PyDescr_NewWrapper - // skipped non-limited PyDescr_IsData -diff --git a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -index f67a7725..34b66a96 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/dictobject.rs -@@ -2,10 +2,10 @@ use crate::object::*; - use crate::pyport::Py_ssize_t; - use std::os::raw::c_int; - --opaque_struct!(PyDictKeysObject); -+opaque_struct!(pub PyDictKeysObject); - - #[cfg(Py_3_11)] --opaque_struct!(PyDictValues); -+opaque_struct!(pub PyDictValues); - - #[cfg(not(GraalPy))] - #[repr(C)] -@@ -17,7 +17,10 @@ pub struct PyDictObject { - Py_3_12, - deprecated(note = "Deprecated in Python 3.12 and will be removed in the future.") - )] -+ #[cfg(not(Py_3_14))] - pub ma_version_tag: u64, -+ #[cfg(Py_3_14)] -+ _ma_watcher_tag: u64, - pub ma_keys: *mut PyDictKeysObject, - #[cfg(not(Py_3_11))] - pub ma_values: *mut *mut PyObject, -@@ -36,15 +39,6 @@ extern "C" { - item: *mut PyObject, - hash: crate::Py_hash_t, - ) -> c_int; -- -- #[cfg(Py_3_13)] -- pub fn _PyDict_SetItem_KnownHash_LockHeld( -- mp: *mut PyDictObject, -- name: *mut PyObject, -- value: *mut PyObject, -- hash: crate::Py_hash_t, -- ) -> c_int; -- - // skipped _PyDict_DelItem_KnownHash - // skipped _PyDict_DelItemIf - // skipped _PyDict_NewKeysForClass -diff --git a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -index 6d2346f9..993e93c8 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/frameobject.rs -@@ -53,7 +53,7 @@ pub struct PyFrameObject { - } - - #[cfg(any(PyPy, GraalPy, Py_3_11))] --opaque_struct!(PyFrameObject); -+opaque_struct!(pub PyFrameObject); - - // skipped _PyFrame_IsRunnable - // skipped _PyFrame_IsExecuting -@@ -89,7 +89,8 @@ extern "C" { - pub fn PyFrame_FastToLocals(f: *mut PyFrameObject); - - // skipped _PyFrame_DebugMallocStats -- // skipped PyFrame_GetBack -+ #[cfg(all(Py_3_9, not(PyPy)))] -+ pub fn PyFrame_GetBack(f: *mut PyFrameObject) -> *mut PyFrameObject; - - #[cfg(not(Py_3_9))] - pub fn PyFrame_ClearFreeList() -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -index 25de30d5..cd2052de 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/funcobject.rs -@@ -41,6 +41,8 @@ pub struct PyFunctionObject { - pub func_weakreflist: *mut PyObject, - pub func_module: *mut PyObject, - pub func_annotations: *mut PyObject, -+ #[cfg(Py_3_14)] -+ pub func_annotate: *mut PyObject, - #[cfg(Py_3_12)] - pub func_typeparams: *mut PyObject, - pub vectorcall: Option, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -index 4be310a8..92f14d59 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/genobject.rs -@@ -1,13 +1,11 @@ - use crate::object::*; - use crate::PyFrameObject; --#[cfg(not(any(PyPy, GraalPy)))] --use crate::_PyErr_StackItem; --#[cfg(all(Py_3_11, not(GraalPy)))] -+#[cfg(all(Py_3_11, not(any(PyPy, GraalPy, Py_3_14))))] - use std::os::raw::c_char; - use std::os::raw::c_int; - use std::ptr::addr_of_mut; - --#[cfg(not(any(PyPy, GraalPy)))] -+#[cfg(not(any(PyPy, GraalPy, Py_3_14)))] - #[repr(C)] - pub struct PyGenObject { - pub ob_base: PyObject, -@@ -20,7 +18,7 @@ pub struct PyGenObject { - pub gi_weakreflist: *mut PyObject, - pub gi_name: *mut PyObject, - pub gi_qualname: *mut PyObject, -- pub gi_exc_state: _PyErr_StackItem, -+ pub gi_exc_state: crate::cpython::pystate::_PyErr_StackItem, - #[cfg(Py_3_11)] - pub gi_origin_or_finalizer: *mut PyObject, - #[cfg(Py_3_11)] -@@ -35,6 +33,9 @@ pub struct PyGenObject { - pub gi_iframe: [*mut PyObject; 1], - } - -+#[cfg(all(Py_3_14, not(any(PyPy, GraalPy))))] -+opaque_struct!(pub PyGenObject); -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - pub static mut PyGen_Type: PyTypeObject; -@@ -67,9 +68,10 @@ extern "C" { - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - pub static mut PyCoro_Type: PyTypeObject; -- pub static mut _PyCoroWrapper_Type: PyTypeObject; - } - -+// skipped _PyCoroWrapper_Type -+ - #[inline] - pub unsafe fn PyCoro_CheckExact(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, addr_of_mut!(PyCoro_Type)) -diff --git a/include/pyo3/pyo3-ffi/src/cpython/import.rs b/include/pyo3/pyo3-ffi/src/cpython/import.rs -index 697d68a4..c8ef5ab4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/import.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/import.rs -@@ -65,10 +65,8 @@ pub struct _frozen { - extern "C" { - #[cfg(not(PyPy))] - pub static mut PyImport_FrozenModules: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenBootstrap: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenStdlib: *const _frozen; -- #[cfg(all(not(PyPy), Py_3_11))] -- pub static mut _PyImport_FrozenTest: *const _frozen; - } -+ -+// skipped _PyImport_FrozenBootstrap -+// skipped _PyImport_FrozenStdlib -+// skipped _PyImport_FrozenTest -diff --git a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -index 321d200e..076981f1 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/initconfig.rs -@@ -93,6 +93,8 @@ pub struct PyConfig { - pub tracemalloc: c_int, - #[cfg(Py_3_12)] - pub perf_profiling: c_int, -+ #[cfg(Py_3_14)] -+ pub remote_debug: c_int, - pub import_time: c_int, - #[cfg(Py_3_11)] - pub code_debug_ranges: c_int, -@@ -141,10 +143,18 @@ pub struct PyConfig { - pub safe_path: c_int, - #[cfg(Py_3_12)] - pub int_max_str_digits: c_int, -+ #[cfg(Py_3_14)] -+ pub thread_inherit_context: c_int, -+ #[cfg(Py_3_14)] -+ pub context_aware_warnings: c_int, -+ #[cfg(all(Py_3_14, target_os = "macos"))] -+ pub use_system_logger: c_int, - #[cfg(Py_3_13)] - pub cpu_count: c_int, - #[cfg(Py_GIL_DISABLED)] - pub enable_gil: c_int, -+ #[cfg(all(Py_3_14, Py_GIL_DISABLED))] -+ pub tlbc_enabled: c_int, - pub pathconfig_warnings: c_int, - #[cfg(Py_3_10)] - pub program_name: *mut wchar_t, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/lock.rs b/include/pyo3/pyo3-ffi/src/cpython/lock.rs -index c451666e..6c80b00d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/lock.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/lock.rs -@@ -8,15 +8,6 @@ pub struct PyMutex { - pub(crate) _pin: PhantomPinned, - } - --impl PyMutex { -- pub const fn new() -> PyMutex { -- PyMutex { -- _bits: AtomicU8::new(0), -- _pin: PhantomPinned, -- } -- } --} -- - extern "C" { - pub fn PyMutex_Lock(m: *mut PyMutex); - pub fn PyMutex_Unlock(m: *mut PyMutex); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -index 6063575e..45acaae5 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -@@ -61,18 +61,6 @@ extern "C" { - is_signed: c_int, - ) -> *mut PyObject; - -- #[cfg(Py_3_13)] -- #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -- pub fn _PyLong_AsByteArray( -- v: *mut PyLongObject, -- bytes: *mut c_uchar, -- n: size_t, -- little_endian: c_int, -- is_signed: c_int, -- with_exceptions: c_int, -- ) -> c_int; -- -- #[cfg(not(Py_3_13))] - #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] - pub fn _PyLong_AsByteArray( - v: *mut PyLongObject, -diff --git a/include/pyo3/pyo3-ffi/src/cpython/mod.rs b/include/pyo3/pyo3-ffi/src/cpython/mod.rs -index fe909f0c..adaf8bc8 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/mod.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/mod.rs -@@ -38,6 +38,7 @@ pub(crate) mod pythonrun; - // skipped sysmodule.h - pub(crate) mod floatobject; - pub(crate) mod pyframe; -+pub(crate) mod pyhash; - pub(crate) mod tupleobject; - pub(crate) mod unicodeobject; - pub(crate) mod weakrefobject; -@@ -71,8 +72,10 @@ pub use self::object::*; - pub use self::objimpl::*; - pub use self::pydebug::*; - pub use self::pyerrors::*; --#[cfg(Py_3_11)] -+#[cfg(all(Py_3_11, not(PyPy)))] - pub use self::pyframe::*; -+#[cfg(any(not(PyPy), Py_3_13))] -+pub use self::pyhash::*; - #[cfg(all(Py_3_8, not(PyPy)))] - pub use self::pylifecycle::*; - pub use self::pymem::*; -diff --git a/include/pyo3/pyo3-ffi/src/cpython/object.rs b/include/pyo3/pyo3-ffi/src/cpython/object.rs -index 75eef11a..26ef784d 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/object.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/object.rs -@@ -310,10 +310,14 @@ pub struct PyHeapTypeObject { - pub ht_cached_keys: *mut c_void, - #[cfg(Py_3_9)] - pub ht_module: *mut object::PyObject, -- #[cfg(Py_3_11)] -+ #[cfg(all(Py_3_11, not(PyPy)))] - _ht_tpname: *mut c_char, -- #[cfg(Py_3_11)] -+ #[cfg(Py_3_14)] -+ pub ht_token: *mut c_void, -+ #[cfg(all(Py_3_11, not(PyPy)))] - _spec_cache: _specialization_cache, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ pub unique_id: Py_ssize_t, - } - - impl Default for PyHeapTypeObject { -diff --git a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -index 98a19abe..14f7121a 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/objimpl.rs -@@ -1,4 +1,4 @@ --#[cfg(not(all(Py_3_11, GraalPy)))] -+#[cfg(not(all(Py_3_11, any(PyPy, GraalPy))))] - use libc::size_t; - use std::os::raw::c_int; - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -index c6e10e5f..c9831669 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyerrors.rs -@@ -46,6 +46,8 @@ pub struct PySyntaxErrorObject { - pub end_offset: *mut PyObject, - pub text: *mut PyObject, - pub print_file_and_line: *mut PyObject, -+ #[cfg(Py_3_14)] -+ pub metadata: *mut PyObject, - } - - #[cfg(not(any(PyPy, GraalPy)))] -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -index d0cfa0a2..f0c38be4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyframe.rs -@@ -1,2 +1,3 @@ --#[cfg(Py_3_11)] --opaque_struct!(_PyInterpreterFrame); -+// NB used in `_PyEval_EvalFrameDefault`, maybe we remove this too. -+#[cfg(all(Py_3_11, not(PyPy)))] -+opaque_struct!(pub _PyInterpreterFrame); -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs -new file mode 100644 -index 00000000..b746018a ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/cpython/pyhash.rs -@@ -0,0 +1,38 @@ -+#[cfg(Py_3_14)] -+use crate::Py_ssize_t; -+#[cfg(Py_3_13)] -+use crate::{PyObject, Py_hash_t}; -+#[cfg(any(Py_3_13, not(PyPy)))] -+use std::os::raw::c_void; -+#[cfg(not(PyPy))] -+use std::os::raw::{c_char, c_int}; -+ -+#[cfg(not(PyPy))] -+#[repr(C)] -+#[derive(Copy, Clone)] -+pub struct PyHash_FuncDef { -+ pub hash: -+ Option crate::Py_hash_t>, -+ pub name: *const c_char, -+ pub hash_bits: c_int, -+ pub seed_bits: c_int, -+} -+ -+#[cfg(not(PyPy))] -+impl Default for PyHash_FuncDef { -+ #[inline] -+ fn default() -> Self { -+ unsafe { std::mem::zeroed() } -+ } -+} -+ -+extern "C" { -+ #[cfg(not(PyPy))] -+ pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; -+ #[cfg(Py_3_13)] -+ pub fn Py_HashPointer(ptr: *const c_void) -> Py_hash_t; -+ #[cfg(Py_3_13)] -+ pub fn PyObject_GenericHash(obj: *mut PyObject) -> Py_hash_t; -+ #[cfg(Py_3_14)] -+ pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; -+} -diff --git a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -index 5481265b..b8f6fd66 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/pystate.rs -@@ -27,16 +27,18 @@ pub const PyTrace_OPCODE: c_int = 7; - // skipped PyTraceInfo - // skipped CFrame - -+/// Private structure used inline in `PyGenObject` - #[cfg(not(PyPy))] - #[repr(C)] - #[derive(Clone, Copy)] -+#[doc(hidden)] // TODO should be able to make pub(crate) after MSRV 1.74 - pub struct _PyErr_StackItem { - #[cfg(not(Py_3_11))] -- pub exc_type: *mut PyObject, -- pub exc_value: *mut PyObject, -+ exc_type: *mut PyObject, -+ exc_value: *mut PyObject, - #[cfg(not(Py_3_11))] -- pub exc_traceback: *mut PyObject, -- pub previous_item: *mut _PyErr_StackItem, -+ exc_traceback: *mut PyObject, -+ previous_item: *mut _PyErr_StackItem, - } - - // skipped _PyStackChunk -@@ -69,21 +71,21 @@ extern "C" { - pub fn PyThreadState_DeleteCurrent(); - } - --#[cfg(all(Py_3_9, not(Py_3_11)))] -+#[cfg(all(Py_3_9, not(any(Py_3_11, PyPy))))] - pub type _PyFrameEvalFunction = extern "C" fn( - *mut crate::PyThreadState, - *mut crate::PyFrameObject, - c_int, - ) -> *mut crate::object::PyObject; - --#[cfg(Py_3_11)] -+#[cfg(all(Py_3_11, not(PyPy)))] - pub type _PyFrameEvalFunction = extern "C" fn( - *mut crate::PyThreadState, - *mut crate::_PyInterpreterFrame, - c_int, - ) -> *mut crate::object::PyObject; - --#[cfg(Py_3_9)] -+#[cfg(all(Py_3_9, not(PyPy)))] - extern "C" { - /// Get the frame evaluation function. - pub fn _PyInterpreterState_GetEvalFrameFunc( -diff --git a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -index c06609fb..dc1bf8e4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/tupleobject.rs -@@ -1,4 +1,6 @@ - use crate::object::*; -+#[cfg(Py_3_14)] -+use crate::pyport::Py_hash_t; - #[cfg(not(PyPy))] - use crate::pyport::Py_ssize_t; - -@@ -6,7 +8,7 @@ use crate::pyport::Py_ssize_t; - pub struct PyTupleObject { - pub ob_base: PyVarObject, - #[cfg(Py_3_14)] -- pub ob_hash: crate::Py_hash_t, -+ pub ob_hash: Py_hash_t, - pub ob_item: [*mut PyObject; 1], - } - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -index 72da46c1..452c82e4 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/unicodeobject.rs -@@ -1,4 +1,4 @@ --#[cfg(not(PyPy))] -+#[cfg(any(Py_3_11, not(PyPy)))] - use crate::Py_hash_t; - use crate::{PyObject, Py_UCS1, Py_UCS2, Py_UCS4, Py_ssize_t}; - use libc::wchar_t; -@@ -31,11 +31,13 @@ use std::os::raw::{c_char, c_int, c_uint, c_void}; - // skipped Py_UNICODE_LOW_SURROGATE - - // generated by bindgen v0.63.0 (with small adaptations) -+#[cfg(not(Py_3_14))] - #[repr(C)] - struct BitfieldUnit { - storage: Storage, - } - -+#[cfg(not(Py_3_14))] - impl BitfieldUnit { - #[inline] - pub const fn new(storage: Storage) -> Self { -@@ -43,7 +45,7 @@ impl BitfieldUnit { - } - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - impl BitfieldUnit - where - Storage: AsRef<[u8]> + AsMut<[u8]>, -@@ -117,30 +119,33 @@ where - } - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_INTERNED_INDEX: usize = 0; --#[cfg(all(not(GraalPy), not(Py_3_14)))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_INTERNED_WIDTH: u8 = 2; - --#[cfg(all(not(GraalPy), Py_3_14))] --const STATE_INTERNED_WIDTH: u8 = 16; -- --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_KIND_INDEX: usize = STATE_INTERNED_WIDTH as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_KIND_WIDTH: u8 = 3; - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_COMPACT_INDEX: usize = (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH) as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_COMPACT_WIDTH: u8 = 1; - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_ASCII_INDEX: usize = - (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH) as usize; --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - const STATE_ASCII_WIDTH: u8 = 1; - -+#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))] -+const STATE_STATICALLY_ALLOCATED_INDEX: usize = -+ (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; -+#[cfg(all(not(any(GraalPy, Py_3_14)), Py_3_12))] -+const STATE_STATICALLY_ALLOCATED_WIDTH: u8 = 1; -+ - #[cfg(not(any(Py_3_12, GraalPy)))] - const STATE_READY_INDEX: usize = - (STATE_INTERNED_WIDTH + STATE_KIND_WIDTH + STATE_COMPACT_WIDTH + STATE_ASCII_WIDTH) as usize; -@@ -156,15 +161,15 @@ const STATE_READY_WIDTH: u8 = 1; - /// - /// Memory layout of C bitfields is implementation defined, so these functions are still - /// unsafe. Users must verify that they work as expected on the architectures they target. -+#[cfg(not(Py_3_14))] - #[repr(C)] --#[repr(align(4))] - struct PyASCIIObjectState { - bitfield_align: [u8; 0], - bitfield: BitfieldUnit<[u8; 4usize]>, - } - - // c_uint and u32 are not necessarily the same type on all targets / architectures --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[allow(clippy::useless_transmute)] - impl PyASCIIObjectState { - #[inline] -@@ -218,6 +223,26 @@ impl PyASCIIObjectState { - .set(STATE_ASCII_INDEX, STATE_ASCII_WIDTH, val as u64) - } - -+ #[cfg(Py_3_12)] -+ #[inline] -+ unsafe fn statically_allocated(&self) -> c_uint { -+ std::mem::transmute(self.bitfield.get( -+ STATE_STATICALLY_ALLOCATED_INDEX, -+ STATE_STATICALLY_ALLOCATED_WIDTH, -+ ) as u32) -+ } -+ -+ #[cfg(Py_3_12)] -+ #[inline] -+ unsafe fn set_statically_allocated(&mut self, val: c_uint) { -+ let val: u32 = std::mem::transmute(val); -+ self.bitfield.set( -+ STATE_STATICALLY_ALLOCATED_INDEX, -+ STATE_STATICALLY_ALLOCATED_WIDTH, -+ val as u64, -+ ) -+ } -+ - #[cfg(not(Py_3_12))] - #[inline] - unsafe fn ready(&self) -> c_uint { -@@ -233,6 +258,7 @@ impl PyASCIIObjectState { - } - } - -+#[cfg(not(Py_3_14))] - impl From for PyASCIIObjectState { - #[inline] - fn from(value: u32) -> Self { -@@ -243,6 +269,7 @@ impl From for PyASCIIObjectState { - } - } - -+#[cfg(not(Py_3_14))] - impl From for u32 { - #[inline] - fn from(value: PyASCIIObjectState) -> Self { -@@ -254,26 +281,36 @@ impl From for u32 { - pub struct PyASCIIObject { - pub ob_base: PyObject, - pub length: Py_ssize_t, -- #[cfg(not(PyPy))] -+ #[cfg(any(Py_3_11, not(PyPy)))] - pub hash: Py_hash_t, - /// A bit field with various properties. - /// - /// Rust doesn't expose bitfields. So we have accessor functions for - /// retrieving values. - /// -+ /// Before 3.12: - /// unsigned int interned:2; // SSTATE_* constants. - /// unsigned int kind:3; // PyUnicode_*_KIND constants. - /// unsigned int compact:1; - /// unsigned int ascii:1; - /// unsigned int ready:1; - /// unsigned int :24; -+ /// -+ /// 3.12, and 3.13 -+ /// unsigned int interned:2; // SSTATE_* constants. -+ /// unsigned int kind:3; // PyUnicode_*_KIND constants. -+ /// unsigned int compact:1; -+ /// unsigned int ascii:1; -+ /// unsigned int statically_allocated:1; -+ /// unsigned int :24; -+ /// on 3.14 and higher PyO3 doesn't access the internal state - pub state: u32, - #[cfg(not(Py_3_12))] - pub wstr: *mut wchar_t, - } - - /// Interacting with the bitfield is not actually well-defined, so we mark these APIs unsafe. --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - impl PyASCIIObject { - #[cfg_attr(not(Py_3_12), allow(rustdoc::broken_intra_doc_links))] // SSTATE_INTERNED_IMMORTAL_STATIC requires 3.12 - /// Get the `interned` field of the [`PyASCIIObject`] state bitfield. -@@ -350,6 +387,7 @@ impl PyASCIIObject { - /// - /// Calling this function with an argument that is neither `0` nor `1` is invalid. - #[inline] -+ #[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] - pub unsafe fn set_ascii(&mut self, val: c_uint) { - let mut state = PyASCIIObjectState::from(self.state); - state.set_ascii(val); -@@ -375,6 +413,26 @@ impl PyASCIIObject { - state.set_ready(val); - self.state = u32::from(state); - } -+ -+ /// Get the `statically_allocated` field of the [`PyASCIIObject`] state bitfield. -+ /// -+ /// Returns either `0` or `1`. -+ #[inline] -+ #[cfg(Py_3_12)] -+ pub unsafe fn statically_allocated(&self) -> c_uint { -+ PyASCIIObjectState::from(self.state).statically_allocated() -+ } -+ -+ /// Set the `statically_allocated` flag of the [`PyASCIIObject`] state bitfield. -+ /// -+ /// Calling this function with an argument that is neither `0` nor `1` is invalid. -+ #[inline] -+ #[cfg(Py_3_12)] -+ pub unsafe fn set_statically_allocated(&mut self, val: c_uint) { -+ let mut state = PyASCIIObjectState::from(self.state); -+ state.set_statically_allocated(val); -+ self.state = u32::from(state); -+ } - } - - #[repr(C)] -@@ -416,7 +474,7 @@ pub const SSTATE_INTERNED_IMMORTAL: c_uint = 2; - #[cfg(Py_3_12)] - pub const SSTATE_INTERNED_IMMORTAL_STATIC: c_uint = 3; - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { - debug_assert!(crate::PyUnicode_Check(op) != 0); -@@ -426,13 +484,13 @@ pub unsafe fn PyUnicode_IS_ASCII(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).ascii() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_COMPACT(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).compact() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_IS_COMPACT_ASCII(op: *mut PyObject) -> c_uint { - ((*(op as *mut PyASCIIObject)).ascii() != 0 && PyUnicode_IS_COMPACT(op) != 0).into() -@@ -464,7 +522,13 @@ pub unsafe fn PyUnicode_4BYTE_DATA(op: *mut PyObject) -> *mut Py_UCS4 { - PyUnicode_DATA(op) as *mut Py_UCS4 - } - --#[cfg(not(GraalPy))] -+#[cfg(all(not(GraalPy), Py_3_14))] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyUnicode_KIND")] -+ pub fn PyUnicode_KIND(op: *mut PyObject) -> c_uint; -+} -+ -+#[cfg(all(not(GraalPy), not(Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { - debug_assert!(crate::PyUnicode_Check(op) != 0); -@@ -474,7 +538,7 @@ pub unsafe fn PyUnicode_KIND(op: *mut PyObject) -> c_uint { - (*(op as *mut PyASCIIObject)).kind() - } - --#[cfg(not(GraalPy))] -+#[cfg(not(any(GraalPy, Py_3_14)))] - #[inline] - pub unsafe fn _PyUnicode_COMPACT_DATA(op: *mut PyObject) -> *mut c_void { - if PyUnicode_IS_ASCII(op) != 0 { -@@ -492,7 +556,7 @@ pub unsafe fn _PyUnicode_NONCOMPACT_DATA(op: *mut PyObject) -> *mut c_void { - (*(op as *mut PyUnicodeObject)).data.any - } - --#[cfg(not(any(GraalPy, PyPy)))] -+#[cfg(not(any(GraalPy, PyPy, Py_3_14)))] - #[inline] - pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { - debug_assert!(crate::PyUnicode_Check(op) != 0); -@@ -504,6 +568,13 @@ pub unsafe fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void { - } - } - -+#[cfg(Py_3_14)] -+#[cfg(all(not(GraalPy), Py_3_14))] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyUnicode_DATA")] -+ pub fn PyUnicode_DATA(op: *mut PyObject) -> *mut c_void; -+} -+ - // skipped PyUnicode_WRITE - // skipped PyUnicode_READ - // skipped PyUnicode_READ_CHAR -diff --git a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -index 88bb501b..1c50c7a7 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/weakrefobject.rs -@@ -1,3 +1,4 @@ -+// NB publicly re-exported in `src/weakrefobject.rs` - #[cfg(not(any(PyPy, GraalPy)))] - pub struct _PyWeakReference { - pub ob_base: crate::PyObject, -diff --git a/include/pyo3/pyo3-ffi/src/datetime.rs b/include/pyo3/pyo3-ffi/src/datetime.rs -index e529e0fc..4db1bdd1 100644 ---- a/include/pyo3/pyo3-ffi/src/datetime.rs -+++ b/include/pyo3/pyo3-ffi/src/datetime.rs -@@ -119,7 +119,7 @@ pub struct PyDateTime_DateTime { - pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { - // This should work for Date or DateTime - let data = (*(o as *mut PyDateTime_Date)).data; -- c_int::from(data[0]) << 8 | c_int::from(data[1]) -+ (c_int::from(data[0]) << 8) | c_int::from(data[1]) - } - - #[inline] -@@ -487,7 +487,9 @@ extern "C" { - pub fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int; - #[link_name = "PyPyDateTime_GET_FOLD"] - pub fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_int; -- // skipped PyDateTime_DATE_GET_TZINFO (not in PyPy) -+ #[link_name = "PyPyDateTime_DATE_GET_TZINFO"] -+ #[cfg(Py_3_10)] -+ pub fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; - - #[link_name = "PyPyDateTime_TIME_GET_HOUR"] - pub fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int; -@@ -499,7 +501,9 @@ extern "C" { - pub fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int; - #[link_name = "PyPyDateTime_TIME_GET_FOLD"] - pub fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_int; -- // skipped PyDateTime_TIME_GET_TZINFO (not in PyPy) -+ #[link_name = "PyPyDateTime_TIME_GET_TZINFO"] -+ #[cfg(Py_3_10)] -+ pub fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject; - - #[link_name = "PyPyDateTime_DELTA_GET_DAYS"] - pub fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/dictobject.rs b/include/pyo3/pyo3-ffi/src/dictobject.rs -index 710be802..e609352a 100644 ---- a/include/pyo3/pyo3-ffi/src/dictobject.rs -+++ b/include/pyo3/pyo3-ffi/src/dictobject.rs -@@ -118,4 +118,4 @@ extern "C" { - - #[cfg(any(PyPy, GraalPy, Py_LIMITED_API))] - // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) --opaque_struct!(PyDictObject); -+opaque_struct!(pub PyDictObject); -diff --git a/include/pyo3/pyo3-ffi/src/floatobject.rs b/include/pyo3/pyo3-ffi/src/floatobject.rs -index 65fc1d4c..4e1d6476 100644 ---- a/include/pyo3/pyo3-ffi/src/floatobject.rs -+++ b/include/pyo3/pyo3-ffi/src/floatobject.rs -@@ -4,7 +4,7 @@ use std::ptr::addr_of_mut; - - #[cfg(Py_LIMITED_API)] - // TODO: remove (see https://github.com/PyO3/pyo3/pull/1341#issuecomment-751515985) --opaque_struct!(PyFloatObject); -+opaque_struct!(pub PyFloatObject); - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -diff --git a/include/pyo3/pyo3-ffi/src/genericaliasobject.rs b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs -new file mode 100644 -index 00000000..7979d7d8 ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/genericaliasobject.rs -@@ -0,0 +1,12 @@ -+#[cfg(Py_3_9)] -+use crate::object::{PyObject, PyTypeObject}; -+ -+#[cfg_attr(windows, link(name = "pythonXY"))] -+extern "C" { -+ #[cfg(Py_3_9)] -+ #[cfg_attr(PyPy, link_name = "PyPy_GenericAlias")] -+ pub fn Py_GenericAlias(origin: *mut PyObject, args: *mut PyObject) -> *mut PyObject; -+ -+ #[cfg(Py_3_9)] -+ pub static mut Py_GenericAliasType: PyTypeObject; -+} -diff --git a/include/pyo3/pyo3-ffi/src/lib.rs b/include/pyo3/pyo3-ffi/src/lib.rs -index 7bdba117..f78c918a 100644 ---- a/include/pyo3/pyo3-ffi/src/lib.rs -+++ b/include/pyo3/pyo3-ffi/src/lib.rs -@@ -129,7 +129,7 @@ - //! ``` - //! - //! **`src/lib.rs`** --//! ```rust -+//! ```rust,no_run - //! use std::os::raw::{c_char, c_long}; - //! use std::ptr; - //! -@@ -327,17 +327,24 @@ - non_snake_case, - non_upper_case_globals, - clippy::upper_case_acronyms, -- clippy::missing_safety_doc -+ clippy::missing_safety_doc, -+ clippy::ptr_eq - )] - #![warn(elided_lifetimes_in_paths, unused_lifetimes)] -+// This crate is a hand-maintained translation of CPython's headers, so requiring "unsafe" -+// blocks within those translations increases maintenance burden without providing any -+// additional safety. The safety of the functions in this crate is determined by the -+// original CPython headers -+#![allow(unsafe_op_in_unsafe_fn)] - - // Until `extern type` is stabilized, use the recommended approach to - // model opaque types: - // https://doc.rust-lang.org/nomicon/ffi.html#representing-opaque-structs - macro_rules! opaque_struct { -- ($name:ident) => { -+ ($(#[$attrs:meta])* $pub:vis $name:ident) => { -+ $(#[$attrs])* - #[repr(C)] -- pub struct $name([u8; 0]); -+ $pub struct $name([u8; 0]); - }; - } - -@@ -351,7 +358,7 @@ macro_rules! opaque_struct { - /// - /// Examples: - /// --/// ```rust -+/// ```rust,no_run - /// use std::ffi::CStr; - /// - /// const HELLO: &CStr = pyo3_ffi::c_str!("hello"); -@@ -410,6 +417,8 @@ pub use self::enumobject::*; - pub use self::fileobject::*; - pub use self::fileutils::*; - pub use self::floatobject::*; -+#[cfg(Py_3_9)] -+pub use self::genericaliasobject::*; - pub use self::import::*; - pub use self::intrcheck::*; - pub use self::iterobject::*; -@@ -439,6 +448,7 @@ pub use self::pystate::*; - pub use self::pystrtod::*; - pub use self::pythonrun::*; - pub use self::rangeobject::*; -+pub use self::refcount::*; - pub use self::setobject::*; - pub use self::sliceobject::*; - pub use self::structseq::*; -@@ -479,7 +489,7 @@ mod fileobject; - mod fileutils; - mod floatobject; - // skipped empty frameobject.h --// skipped genericaliasobject.h -+mod genericaliasobject; - mod import; - // skipped interpreteridobject.h - mod intrcheck; -@@ -531,6 +541,7 @@ mod pystrtod; - // skipped pythread.h - // skipped pytime.h - mod rangeobject; -+mod refcount; - mod setobject; - mod sliceobject; - mod structseq; -diff --git a/include/pyo3/pyo3-ffi/src/longobject.rs b/include/pyo3/pyo3-ffi/src/longobject.rs -index 68b4ecba..eca0af3d 100644 ---- a/include/pyo3/pyo3-ffi/src/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/longobject.rs -@@ -4,7 +4,7 @@ use libc::size_t; - use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void}; - use std::ptr::addr_of_mut; - --opaque_struct!(PyLongObject); -+opaque_struct!(pub PyLongObject); - - #[inline] - pub unsafe fn PyLong_Check(op: *mut PyObject) -> c_int { -diff --git a/include/pyo3/pyo3-ffi/src/memoryobject.rs b/include/pyo3/pyo3-ffi/src/memoryobject.rs -index b7ef9e2e..4e1e50c6 100644 ---- a/include/pyo3/pyo3-ffi/src/memoryobject.rs -+++ b/include/pyo3/pyo3-ffi/src/memoryobject.rs -@@ -3,11 +3,10 @@ use crate::pyport::Py_ssize_t; - use std::os::raw::{c_char, c_int}; - use std::ptr::addr_of_mut; - -+// skipped _PyManagedBuffer_Type -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -- #[cfg(not(Py_LIMITED_API))] -- pub static mut _PyManagedBuffer_Type: PyTypeObject; -- - #[cfg_attr(PyPy, link_name = "PyPyMemoryView_Type")] - pub static mut PyMemoryView_Type: PyTypeObject; - } -diff --git a/include/pyo3/pyo3-ffi/src/methodobject.rs b/include/pyo3/pyo3-ffi/src/methodobject.rs -index 37e1e206..3dfbbb5a 100644 ---- a/include/pyo3/pyo3-ffi/src/methodobject.rs -+++ b/include/pyo3/pyo3-ffi/src/methodobject.rs -@@ -50,7 +50,7 @@ pub type PyCFunctionFast = unsafe extern "C" fn( - ) -> *mut PyObject; - - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] --#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] -+#[deprecated(note = "renamed to `PyCFunctionFast`")] - pub type _PyCFunctionFast = PyCFunctionFast; - - pub type PyCFunctionWithKeywords = unsafe extern "C" fn( -@@ -68,7 +68,7 @@ pub type PyCFunctionFastWithKeywords = unsafe extern "C" fn( - ) -> *mut PyObject; - - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] --#[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] -+#[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] - pub type _PyCFunctionFastWithKeywords = PyCFunctionFastWithKeywords; - - #[cfg(all(Py_3_9, not(Py_LIMITED_API)))] -@@ -153,7 +153,7 @@ pub union PyMethodDefPointer { - - /// This variant corresponds with [`METH_FASTCALL`]. - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFast`"))] -+ #[deprecated(note = "renamed to `PyCFunctionFast`")] - pub _PyCFunctionFast: PyCFunctionFast, - - /// This variant corresponds with [`METH_FASTCALL`]. -@@ -162,7 +162,7 @@ pub union PyMethodDefPointer { - - /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. - #[cfg(any(Py_3_10, not(Py_LIMITED_API)))] -- #[cfg_attr(Py_3_10, deprecated(note = "renamed to `PyCFunctionFastWithKeywords`"))] -+ #[deprecated(note = "renamed to `PyCFunctionFastWithKeywords`")] - pub _PyCFunctionFastWithKeywords: PyCFunctionFastWithKeywords, - - /// This variant corresponds with [`METH_FASTCALL`] | [`METH_KEYWORDS`]. -diff --git a/include/pyo3/pyo3-ffi/src/modsupport.rs b/include/pyo3/pyo3-ffi/src/modsupport.rs -index 6da2795b..56a68fe2 100644 ---- a/include/pyo3/pyo3-ffi/src/modsupport.rs -+++ b/include/pyo3/pyo3-ffi/src/modsupport.rs -@@ -37,12 +37,7 @@ extern "C" { - // skipped Py_VaBuildValue - - #[cfg(Py_3_13)] -- pub fn PyModule_Add( -- module: *mut PyObject, -- name: *const c_char, -- value: *mut PyObject, -- ) -> core::ffi::c_int; -- -+ pub fn PyModule_Add(module: *mut PyObject, name: *const c_char, value: *mut PyObject) -> c_int; - #[cfg(Py_3_10)] - #[cfg_attr(PyPy, link_name = "PyPyModule_AddObjectRef")] - pub fn PyModule_AddObjectRef( -diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -index 51083a8e..5fbf45db 100644 ---- a/include/pyo3/pyo3-ffi/src/object.rs -+++ b/include/pyo3/pyo3-ffi/src/object.rs -@@ -1,5 +1,7 @@ - use crate::pyport::{Py_hash_t, Py_ssize_t}; - #[cfg(Py_GIL_DISABLED)] -+use crate::refcount; -+#[cfg(Py_GIL_DISABLED)] - use crate::PyMutex; - #[cfg(Py_GIL_DISABLED)] - use std::marker::PhantomPinned; -@@ -7,81 +9,50 @@ use std::mem; - use std::os::raw::{c_char, c_int, c_uint, c_ulong, c_void}; - use std::ptr; - #[cfg(Py_GIL_DISABLED)] --use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8, Ordering::Relaxed}; -+use std::sync::atomic::{AtomicIsize, AtomicU32, AtomicU8}; - - #[cfg(Py_LIMITED_API)] --opaque_struct!(PyTypeObject); -+opaque_struct!(pub PyTypeObject); - - #[cfg(not(Py_LIMITED_API))] - pub use crate::cpython::object::PyTypeObject; - --#[cfg(Py_3_12)] --const _Py_IMMORTAL_REFCNT: Py_ssize_t = { -- if cfg!(target_pointer_width = "64") { -- c_uint::MAX as Py_ssize_t -- } else { -- // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) -- (c_uint::MAX >> 2) as Py_ssize_t -- } --}; -- --#[cfg(Py_GIL_DISABLED)] --const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; -- --#[allow(clippy::declare_interior_mutable_const)] --pub const PyObject_HEAD_INIT: PyObject = PyObject { -- #[cfg(py_sys_config = "Py_TRACE_REFS")] -- _ob_next: std::ptr::null_mut(), -- #[cfg(py_sys_config = "Py_TRACE_REFS")] -- _ob_prev: std::ptr::null_mut(), -- #[cfg(Py_GIL_DISABLED)] -- ob_tid: 0, -- #[cfg(Py_GIL_DISABLED)] -- _padding: 0, -- #[cfg(Py_GIL_DISABLED)] -- ob_mutex: PyMutex { -- _bits: AtomicU8::new(0), -- _pin: PhantomPinned, -- }, -- #[cfg(Py_GIL_DISABLED)] -- ob_gc_bits: 0, -- #[cfg(Py_GIL_DISABLED)] -- ob_ref_local: AtomicU32::new(_Py_IMMORTAL_REFCNT_LOCAL), -- #[cfg(Py_GIL_DISABLED)] -- ob_ref_shared: AtomicIsize::new(0), -- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -- ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, -- #[cfg(not(Py_3_12))] -- ob_refcnt: 1, -- #[cfg(PyPy)] -- ob_pypy_link: 0, -- ob_type: std::ptr::null_mut(), --}; -- --// skipped PyObject_VAR_HEAD --// skipped Py_INVALID_SIZE -- --// skipped private _Py_UNOWNED_TID -+// skip PyObject_HEAD - --#[cfg(Py_GIL_DISABLED)] --const _Py_REF_SHARED_SHIFT: isize = 2; --// skipped private _Py_REF_SHARED_FLAG_MASK -- --// skipped private _Py_REF_SHARED_INIT --// skipped private _Py_REF_MAYBE_WEAKREF --// skipped private _Py_REF_QUEUED --// skipped private _Py_REF_MERGED -+#[repr(C)] -+#[derive(Copy, Clone)] -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "big"))] -+/// This struct is anonymous in CPython, so the name was given by PyO3 because -+/// Rust structs need a name. -+pub struct PyObjectObFlagsAndRefcnt { -+ pub ob_flags: u16, -+ pub ob_overflow: u16, -+ pub ob_refcnt: u32, -+} - --// skipped private _Py_REF_SHARED -+#[repr(C)] -+#[derive(Copy, Clone)] -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED), target_endian = "little"))] -+/// This struct is anonymous in CPython, so the name was given by PyO3 because -+/// Rust structs need a name. -+pub struct PyObjectObFlagsAndRefcnt { -+ pub ob_refcnt: u32, -+ pub ob_overflow: u16, -+ pub ob_flags: u16, -+} - - #[repr(C)] - #[derive(Copy, Clone)] - #[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] - /// This union is anonymous in CPython, so the name was given by PyO3 because --/// Rust unions need a name. -+/// Rust union need a name. - pub union PyObjectObRefcnt { -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub ob_refcnt_full: crate::PY_INT64_T, -+ #[cfg(Py_3_14)] -+ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, - pub ob_refcnt: Py_ssize_t, -- #[cfg(target_pointer_width = "64")] -+ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] - pub ob_refcnt_split: [crate::PY_UINT32_T; 2], - } - -@@ -95,6 +66,9 @@ impl std::fmt::Debug for PyObjectObRefcnt { - #[cfg(all(not(Py_3_12), not(Py_GIL_DISABLED)))] - pub type PyObjectObRefcnt = Py_ssize_t; - -+// PyObject_HEAD_INIT comes before the PyObject definition in object.h -+// but we put it after PyObject because HEAD_INIT uses PyObject -+ - #[repr(C)] - #[derive(Debug)] - pub struct PyObject { -@@ -104,8 +78,10 @@ pub struct PyObject { - pub _ob_prev: *mut PyObject, - #[cfg(Py_GIL_DISABLED)] - pub ob_tid: libc::uintptr_t, -- #[cfg(Py_GIL_DISABLED)] -+ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] - pub _padding: u16, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ pub ob_flags: u16, - #[cfg(Py_GIL_DISABLED)] - pub ob_mutex: PyMutex, // per-object lock - #[cfg(Py_GIL_DISABLED)] -@@ -121,7 +97,41 @@ pub struct PyObject { - pub ob_type: *mut PyTypeObject, - } - --// skipped private _PyObject_CAST -+#[allow(clippy::declare_interior_mutable_const)] -+pub const PyObject_HEAD_INIT: PyObject = PyObject { -+ #[cfg(py_sys_config = "Py_TRACE_REFS")] -+ _ob_next: std::ptr::null_mut(), -+ #[cfg(py_sys_config = "Py_TRACE_REFS")] -+ _ob_prev: std::ptr::null_mut(), -+ #[cfg(Py_GIL_DISABLED)] -+ ob_tid: 0, -+ #[cfg(all(Py_GIL_DISABLED, Py_3_14))] -+ ob_flags: 0, -+ #[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))] -+ _padding: 0, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_mutex: PyMutex { -+ _bits: AtomicU8::new(0), -+ _pin: PhantomPinned, -+ }, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_gc_bits: 0, -+ #[cfg(Py_GIL_DISABLED)] -+ ob_ref_local: AtomicU32::new(refcount::_Py_IMMORTAL_REFCNT_LOCAL), -+ #[cfg(Py_GIL_DISABLED)] -+ ob_ref_shared: AtomicIsize::new(0), -+ #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -+ ob_refcnt: PyObjectObRefcnt { ob_refcnt: 1 }, -+ #[cfg(not(Py_3_12))] -+ ob_refcnt: 1, -+ #[cfg(PyPy)] -+ ob_pypy_link: 0, -+ ob_type: std::ptr::null_mut(), -+}; -+ -+// skipped _Py_UNOWNED_TID -+ -+// skipped _PyObject_CAST - - #[repr(C)] - #[derive(Debug)] -@@ -150,41 +160,23 @@ extern "C" { - pub fn Py_Is(x: *mut PyObject, y: *mut PyObject) -> c_int; - } - --// skipped private _Py_GetThreadLocal_Addr -+// skipped _Py_GetThreadLocal_Addr - --// skipped private _Py_ThreadId -+// skipped _Py_ThreadID - --// skipped private _Py_IsOwnedByCurrentThread -+// skipped _Py_IsOwnedByCurrentThread - --#[inline] --pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { -- #[cfg(Py_GIL_DISABLED)] -- { -- let local = (*ob).ob_ref_local.load(Relaxed); -- if local == _Py_IMMORTAL_REFCNT_LOCAL { -- return _Py_IMMORTAL_REFCNT; -- } -- let shared = (*ob).ob_ref_shared.load(Relaxed); -- local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) -- } -- -- #[cfg(all(not(Py_GIL_DISABLED), Py_3_12))] -- { -- (*ob).ob_refcnt.ob_refcnt -- } -- -- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] -- { -- (*ob).ob_refcnt -- } -+#[cfg(GraalPy)] -+extern "C" { -+ #[cfg(GraalPy)] -+ fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; - -- #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] -- { -- _Py_REFCNT(ob) -- } -+ #[cfg(GraalPy)] -+ fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; - } - - #[inline] -+#[cfg(not(Py_3_14))] - pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { - #[cfg(not(GraalPy))] - return (*ob).ob_type; -@@ -192,6 +184,15 @@ pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { - return _Py_TYPE(ob); - } - -+#[cfg_attr(windows, link(name = "pythonXY"))] -+#[cfg(Py_3_14)] -+extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPy_TYPE")] -+ pub fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject; -+} -+ -+// skip _Py_TYPE compat shim -+ - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyLong_Type")] -@@ -212,29 +213,11 @@ pub unsafe fn Py_SIZE(ob: *mut PyObject) -> Py_ssize_t { - _Py_SIZE(ob) - } - --#[inline(always)] --#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))] --pub unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { -- #[cfg(target_pointer_width = "64")] -- { -- (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int -- } -- -- #[cfg(target_pointer_width = "32")] -- { -- ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int -- } --} -- - #[inline] - pub unsafe fn Py_IS_TYPE(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { - (Py_TYPE(ob) == tp) as c_int - } - --// skipped _Py_SetRefCnt -- --// skipped Py_SET_REFCNT -- - // skipped Py_SET_TYPE - - // skipped Py_SET_SIZE -@@ -436,7 +419,7 @@ extern "C" { - arg2: *const c_char, - arg3: *mut PyObject, - ) -> c_int; -- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h -+ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h - #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttrString")] - pub fn PyObject_DelAttrString(arg1: *mut PyObject, arg2: *const c_char) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttrString")] -@@ -460,7 +443,7 @@ extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyObject_SetAttr")] - pub fn PyObject_SetAttr(arg1: *mut PyObject, arg2: *mut PyObject, arg3: *mut PyObject) - -> c_int; -- #[cfg(any(Py_3_13, PyPy))] // CPython defined in 3.12 as an inline function in abstract.h -+ #[cfg(any(Py_3_13, all(PyPy, not(Py_3_11))))] // CPython defined in 3.12 as an inline function in abstract.h - #[cfg_attr(PyPy, link_name = "PyPyObject_DelAttr")] - pub fn PyObject_DelAttr(arg1: *mut PyObject, arg2: *mut PyObject) -> c_int; - #[cfg_attr(PyPy, link_name = "PyPyObject_HasAttr")] -@@ -586,222 +569,6 @@ pub const Py_TPFLAGS_DEFAULT: c_ulong = if cfg!(Py_3_10) { - pub const Py_TPFLAGS_HAVE_FINALIZE: c_ulong = 1; - pub const Py_TPFLAGS_HAVE_VERSION_TAG: c_ulong = 1 << 18; - --extern "C" { -- #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); -- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_INCREF_IncRefTotal(); -- #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -- fn _Py_DECREF_DecRefTotal(); -- -- #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] -- fn _Py_Dealloc(arg1: *mut PyObject); -- -- #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] -- #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] -- pub fn Py_IncRef(o: *mut PyObject); -- #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] -- #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] -- pub fn Py_DecRef(o: *mut PyObject); -- -- #[cfg(all(Py_3_10, not(PyPy)))] -- fn _Py_IncRef(o: *mut PyObject); -- #[cfg(all(Py_3_10, not(PyPy)))] -- fn _Py_DecRef(o: *mut PyObject); -- -- #[cfg(GraalPy)] -- fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; -- -- #[cfg(GraalPy)] -- fn _Py_TYPE(arg1: *const PyObject) -> *mut PyTypeObject; -- -- #[cfg(GraalPy)] -- fn _Py_SIZE(arg1: *const PyObject) -> Py_ssize_t; --} -- --#[inline(always)] --pub unsafe fn Py_INCREF(op: *mut PyObject) { -- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -- #[cfg(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- py_sys_config = "Py_REF_DEBUG", -- GraalPy -- ))] -- { -- // _Py_IncRef was added to the ABI in 3.10; skips null checks -- #[cfg(all(Py_3_10, not(PyPy)))] -- { -- _Py_IncRef(op); -- } -- -- #[cfg(any(not(Py_3_10), PyPy))] -- { -- Py_IncRef(op); -- } -- } -- -- // version-specific builds are allowed to directly manipulate the reference count -- #[cfg(not(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- py_sys_config = "Py_REF_DEBUG", -- GraalPy -- )))] -- { -- #[cfg(all(Py_3_12, target_pointer_width = "64"))] -- { -- let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; -- let new_refcnt = cur_refcnt.wrapping_add(1); -- if new_refcnt == 0 { -- return; -- } -- (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; -- } -- -- #[cfg(all(Py_3_12, target_pointer_width = "32"))] -- { -- if _Py_IsImmortal(op) != 0 { -- return; -- } -- (*op).ob_refcnt.ob_refcnt += 1 -- } -- -- #[cfg(not(Py_3_12))] -- { -- (*op).ob_refcnt += 1 -- } -- -- // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue -- // or submit a PR supporting Py_STATS build option and pystats.h -- } --} -- --#[inline(always)] --#[cfg_attr( -- all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), -- track_caller --)] --pub unsafe fn Py_DECREF(op: *mut PyObject) { -- // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -- // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts -- // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -- #[cfg(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -- GraalPy -- ))] -- { -- // _Py_DecRef was added to the ABI in 3.10; skips null checks -- #[cfg(all(Py_3_10, not(PyPy)))] -- { -- _Py_DecRef(op); -- } -- -- #[cfg(any(not(Py_3_10), PyPy))] -- { -- Py_DecRef(op); -- } -- } -- -- #[cfg(not(any( -- Py_GIL_DISABLED, -- Py_LIMITED_API, -- all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -- GraalPy -- )))] -- { -- #[cfg(Py_3_12)] -- if _Py_IsImmortal(op) != 0 { -- return; -- } -- -- // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue -- // or submit a PR supporting Py_STATS build option and pystats.h -- -- #[cfg(py_sys_config = "Py_REF_DEBUG")] -- _Py_DECREF_DecRefTotal(); -- -- #[cfg(Py_3_12)] -- { -- (*op).ob_refcnt.ob_refcnt -= 1; -- -- #[cfg(py_sys_config = "Py_REF_DEBUG")] -- if (*op).ob_refcnt.ob_refcnt < 0 { -- let location = std::panic::Location::caller(); -- let filename = std::ffi::CString::new(location.file()).unwrap(); -- _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); -- } -- -- if (*op).ob_refcnt.ob_refcnt == 0 { -- _Py_Dealloc(op); -- } -- } -- -- #[cfg(not(Py_3_12))] -- { -- (*op).ob_refcnt -= 1; -- -- if (*op).ob_refcnt == 0 { -- _Py_Dealloc(op); -- } -- } -- } --} -- --#[inline] --pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { -- let tmp = *op; -- if !tmp.is_null() { -- *op = ptr::null_mut(); -- Py_DECREF(tmp); -- } --} -- --#[inline] --pub unsafe fn Py_XINCREF(op: *mut PyObject) { -- if !op.is_null() { -- Py_INCREF(op) -- } --} -- --#[inline] --pub unsafe fn Py_XDECREF(op: *mut PyObject) { -- if !op.is_null() { -- Py_DECREF(op) -- } --} -- --extern "C" { -- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -- pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; -- #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -- #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -- pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; --} -- --// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here --// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here -- --#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] --#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] --#[inline] --pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { -- Py_INCREF(obj); -- obj --} -- --#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] --#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] --#[inline] --pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { -- Py_XINCREF(obj); -- obj --} -- - #[cfg(Py_3_13)] - pub const Py_CONSTANT_NONE: c_uint = 0; - #[cfg(Py_3_13)] -@@ -942,4 +709,7 @@ extern "C" { - arg1: *mut crate::PyTypeObject, - arg2: *mut crate::PyModuleDef, - ) -> *mut PyObject; -+ -+ #[cfg(Py_3_14)] -+ pub fn PyType_Freeze(tp: *mut crate::PyTypeObject) -> c_int; - } -diff --git a/include/pyo3/pyo3-ffi/src/pyarena.rs b/include/pyo3/pyo3-ffi/src/pyarena.rs -index 87d5f28a..1200de3d 100644 ---- a/include/pyo3/pyo3-ffi/src/pyarena.rs -+++ b/include/pyo3/pyo3-ffi/src/pyarena.rs -@@ -1 +1 @@ --opaque_struct!(PyArena); -+opaque_struct!(pub PyArena); -diff --git a/include/pyo3/pyo3-ffi/src/pybuffer.rs b/include/pyo3/pyo3-ffi/src/pybuffer.rs -index 50bf4e61..de706759 100644 ---- a/include/pyo3/pyo3-ffi/src/pybuffer.rs -+++ b/include/pyo3/pyo3-ffi/src/pybuffer.rs -@@ -103,7 +103,11 @@ extern "C" { - } - - /// Maximum number of dimensions --pub const PyBUF_MAX_NDIM: c_int = if cfg!(PyPy) { 36 } else { 64 }; -+pub const PyBUF_MAX_NDIM: usize = if cfg!(all(PyPy, not(Py_3_11))) { -+ 36 -+} else { -+ 64 -+}; - - /* Flags for getting buffers */ - pub const PyBUF_SIMPLE: c_int = 0; -diff --git a/include/pyo3/pyo3-ffi/src/pyerrors.rs b/include/pyo3/pyo3-ffi/src/pyerrors.rs -index 6c9313c4..d341239a 100644 ---- a/include/pyo3/pyo3-ffi/src/pyerrors.rs -+++ b/include/pyo3/pyo3-ffi/src/pyerrors.rs -@@ -116,6 +116,7 @@ extern "C" { - #[cfg_attr(PyPy, link_name = "PyPyExc_BaseException")] - pub static mut PyExc_BaseException: *mut PyObject; - #[cfg(Py_3_11)] -+ #[cfg_attr(PyPy, link_name = "PyPyExc_BaseExceptionGroup")] - pub static mut PyExc_BaseExceptionGroup: *mut PyObject; - #[cfg_attr(PyPy, link_name = "PyPyExc_Exception")] - pub static mut PyExc_Exception: *mut PyObject; -diff --git a/include/pyo3/pyo3-ffi/src/pyframe.rs b/include/pyo3/pyo3-ffi/src/pyframe.rs -index 4dd3d2b3..1693b20b 100644 ---- a/include/pyo3/pyo3-ffi/src/pyframe.rs -+++ b/include/pyo3/pyo3-ffi/src/pyframe.rs -@@ -6,7 +6,7 @@ use crate::PyFrameObject; - use std::os::raw::c_int; - - #[cfg(Py_LIMITED_API)] --opaque_struct!(PyFrameObject); -+opaque_struct!(pub PyFrameObject); - - extern "C" { - pub fn PyFrame_GetLineNumber(f: *mut PyFrameObject) -> c_int; -diff --git a/include/pyo3/pyo3-ffi/src/pyhash.rs b/include/pyo3/pyo3-ffi/src/pyhash.rs -index 8b0b6771..074278dd 100644 ---- a/include/pyo3/pyo3-ffi/src/pyhash.rs -+++ b/include/pyo3/pyo3-ffi/src/pyhash.rs -@@ -1,7 +1,5 @@ - #[cfg(not(any(Py_LIMITED_API, PyPy)))] - use crate::pyport::{Py_hash_t, Py_ssize_t}; --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --use std::os::raw::c_char; - #[cfg(not(any(Py_LIMITED_API, PyPy)))] - use std::os::raw::c_void; - -@@ -12,11 +10,8 @@ extern "C" { - // skipped non-limited _Py_HashPointer - // skipped non-limited _Py_HashPointerRaw - -- #[cfg(not(all(Py_3_14, any(Py_LIMITED_API, PyPy))))] -+ #[cfg(not(any(Py_LIMITED_API, PyPy)))] - pub fn _Py_HashBytes(src: *const c_void, len: Py_ssize_t) -> Py_hash_t; -- -- #[cfg(Py_3_14)] -- pub fn Py_HashBuffer(ptr: *const c_void, len: Py_ssize_t) -> Py_hash_t; - } - - pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; -@@ -25,29 +20,6 @@ pub const _PyHASH_MULTIPLIER: c_ulong = 1000003; - - // skipped non-limited _Py_HashSecret_t - --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --#[repr(C)] --#[derive(Copy, Clone)] --pub struct PyHash_FuncDef { -- pub hash: Option Py_hash_t>, -- pub name: *const c_char, -- pub hash_bits: c_int, -- pub seed_bits: c_int, --} -- --#[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] --impl Default for PyHash_FuncDef { -- #[inline] -- fn default() -> Self { -- unsafe { std::mem::zeroed() } -- } --} -- --extern "C" { -- #[cfg(not(any(Py_LIMITED_API, PyPy, GraalPy)))] -- pub fn PyHash_GetFuncDef() -> *mut PyHash_FuncDef; --} -- - // skipped Py_HASH_CUTOFF - - pub const Py_HASH_EXTERNAL: c_int = 0; -diff --git a/include/pyo3/pyo3-ffi/src/pyport.rs b/include/pyo3/pyo3-ffi/src/pyport.rs -index a144c67f..e524831d 100644 ---- a/include/pyo3/pyo3-ffi/src/pyport.rs -+++ b/include/pyo3/pyo3-ffi/src/pyport.rs -@@ -1,3 +1,8 @@ -+// NB libc does not define this constant on all platforms, so we hard code it -+// like CPython does. -+// https://github.com/python/cpython/blob/d8b9011702443bb57579f8834f3effe58e290dfc/Include/pyport.h#L372 -+pub const INT_MAX: std::os::raw::c_int = 2147483647; -+ - pub type PY_UINT32_T = u32; - pub type PY_UINT64_T = u64; - -@@ -11,8 +16,8 @@ pub type Py_ssize_t = ::libc::ssize_t; - pub type Py_hash_t = Py_ssize_t; - pub type Py_uhash_t = ::libc::size_t; - --pub const PY_SSIZE_T_MIN: Py_ssize_t = isize::MIN as Py_ssize_t; --pub const PY_SSIZE_T_MAX: Py_ssize_t = isize::MAX as Py_ssize_t; -+pub const PY_SSIZE_T_MIN: Py_ssize_t = Py_ssize_t::MIN; -+pub const PY_SSIZE_T_MAX: Py_ssize_t = Py_ssize_t::MAX; - - #[cfg(target_endian = "big")] - pub const PY_BIG_ENDIAN: usize = 1; -diff --git a/include/pyo3/pyo3-ffi/src/pystate.rs b/include/pyo3/pyo3-ffi/src/pystate.rs -index 23aeea3a..cc16e554 100644 ---- a/include/pyo3/pyo3-ffi/src/pystate.rs -+++ b/include/pyo3/pyo3-ffi/src/pystate.rs -@@ -1,3 +1,5 @@ -+#[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] -+use crate::frameobject::PyFrameObject; - use crate::moduleobject::PyModuleDef; - use crate::object::PyObject; - use std::os::raw::c_int; -@@ -7,8 +9,8 @@ use std::os::raw::c_long; - - pub const MAX_CO_EXTRA_USERS: c_int = 255; - --opaque_struct!(PyThreadState); --opaque_struct!(PyInterpreterState); -+opaque_struct!(pub PyThreadState); -+opaque_struct!(pub PyInterpreterState); - - extern "C" { - #[cfg(not(PyPy))] -@@ -63,9 +65,14 @@ extern "C" { - } - - // skipped non-limited / 3.9 PyThreadState_GetInterpreter --// skipped non-limited / 3.9 PyThreadState_GetFrame - // skipped non-limited / 3.9 PyThreadState_GetID - -+extern "C" { -+ // PyThreadState_GetFrame -+ #[cfg(all(Py_3_10, not(PyPy), not(Py_LIMITED_API)))] -+ pub fn PyThreadState_GetFrame(arg1: *mut PyThreadState) -> *mut PyFrameObject; -+} -+ - #[repr(C)] - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub enum PyGILState_STATE { -@@ -73,9 +80,70 @@ pub enum PyGILState_STATE { - PyGILState_UNLOCKED, - } - -+#[cfg(not(Py_3_14))] -+struct HangThread; -+ -+#[cfg(not(Py_3_14))] -+impl Drop for HangThread { -+ fn drop(&mut self) { -+ loop { -+ std::thread::park(); // Block forever. -+ } -+ } -+} -+ -+// The PyGILState_Ensure function will call pthread_exit during interpreter shutdown, -+// which causes undefined behavior. Redirect to the "safe" version that hangs instead, -+// as Python 3.14 does. -+// -+// See https://github.com/rust-lang/rust/issues/135929 -+ -+// C-unwind only supported (and necessary) since 1.71. Python 3.14+ does not do -+// pthread_exit from PyGILState_Ensure (https://github.com/python/cpython/issues/87135). -+mod raw { -+ #[cfg(all(not(Py_3_14), rustc_has_extern_c_unwind))] -+ extern "C-unwind" { -+ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -+ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; -+ } -+ -+ #[cfg(not(all(not(Py_3_14), rustc_has_extern_c_unwind)))] -+ extern "C" { -+ #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -+ pub fn PyGILState_Ensure() -> super::PyGILState_STATE; -+ } -+} -+ -+#[cfg(not(Py_3_14))] -+pub unsafe extern "C" fn PyGILState_Ensure() -> PyGILState_STATE { -+ let guard = HangThread; -+ // If `PyGILState_Ensure` calls `pthread_exit`, which it does on Python < 3.14 -+ // when the interpreter is shutting down, this will cause a forced unwind. -+ // doing a forced unwind through a function with a Rust destructor is unspecified -+ // behavior. -+ // -+ // However, currently it runs the destructor, which will cause the thread to -+ // hang as it should. -+ // -+ // And if we don't catch the unwinding here, then one of our callers probably has a destructor, -+ // so it's unspecified behavior anyway, and on many configurations causes the process to abort. -+ // -+ // The alternative is for pyo3 to contain custom C or C++ code that catches the `pthread_exit`, -+ // but that's also annoying from a portability point of view. -+ // -+ // On Windows, `PyGILState_Ensure` calls `_endthreadex` instead, which AFAICT can't be caught -+ // and therefore will cause unsafety if there are pinned objects on the stack. AFAICT there's -+ // nothing we can do it other than waiting for Python 3.14 or not using Windows. At least, -+ // if there is nothing pinned on the stack, it won't cause the process to crash. -+ let ret: PyGILState_STATE = raw::PyGILState_Ensure(); -+ std::mem::forget(guard); -+ ret -+} -+ -+#[cfg(Py_3_14)] -+pub use self::raw::PyGILState_Ensure; -+ - extern "C" { -- #[cfg_attr(PyPy, link_name = "PyPyGILState_Ensure")] -- pub fn PyGILState_Ensure() -> PyGILState_STATE; - #[cfg_attr(PyPy, link_name = "PyPyGILState_Release")] - pub fn PyGILState_Release(arg1: PyGILState_STATE); - #[cfg(not(PyPy))] -diff --git a/include/pyo3/pyo3-ffi/src/pythonrun.rs b/include/pyo3/pyo3-ffi/src/pythonrun.rs -index e7ea2d2e..80209b58 100644 ---- a/include/pyo3/pyo3-ffi/src/pythonrun.rs -+++ b/include/pyo3/pyo3-ffi/src/pythonrun.rs -@@ -49,12 +49,12 @@ pub const PYOS_STACK_MARGIN: c_int = 2048; - // skipped PyOS_CheckStack under Microsoft C - - #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] --opaque_struct!(_mod); -+opaque_struct!(pub _mod); - - #[cfg(not(any(PyPy, Py_3_10)))] --opaque_struct!(symtable); -+opaque_struct!(pub symtable); - #[cfg(not(any(PyPy, Py_3_10)))] --opaque_struct!(_node); -+opaque_struct!(pub _node); - - #[cfg(not(any(PyPy, Py_LIMITED_API, Py_3_10)))] - #[cfg_attr(Py_3_9, deprecated(note = "Python 3.9"))] -diff --git a/include/pyo3/pyo3-ffi/src/refcount.rs b/include/pyo3/pyo3-ffi/src/refcount.rs -new file mode 100644 -index 00000000..fcb5f45b ---- /dev/null -+++ b/include/pyo3/pyo3-ffi/src/refcount.rs -@@ -0,0 +1,369 @@ -+use crate::pyport::Py_ssize_t; -+use crate::PyObject; -+#[cfg(py_sys_config = "Py_REF_DEBUG")] -+use std::os::raw::c_char; -+#[cfg(Py_3_12)] -+use std::os::raw::c_int; -+#[cfg(all(Py_3_14, any(not(Py_GIL_DISABLED), target_pointer_width = "32")))] -+use std::os::raw::c_long; -+#[cfg(any(Py_GIL_DISABLED, all(Py_3_12, not(Py_3_14))))] -+use std::os::raw::c_uint; -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+use std::os::raw::c_ulong; -+use std::ptr; -+#[cfg(Py_GIL_DISABLED)] -+use std::sync::atomic::Ordering::Relaxed; -+ -+#[cfg(Py_3_14)] -+const _Py_STATICALLY_ALLOCATED_FLAG: c_int = 1 << 7; -+ -+#[cfg(all(Py_3_12, not(Py_3_14)))] -+const _Py_IMMORTAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ c_uint::MAX as Py_ssize_t -+ } else { -+ // for 32-bit systems, use the lower 30 bits (see comment in CPython's object.h) -+ (c_uint::MAX >> 2) as Py_ssize_t -+ } -+}; -+ -+// comments in Python.h about the choices for these constants -+ -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ ((3 as c_ulong) << (30 as c_ulong)) as Py_ssize_t -+ } else { -+ ((5 as c_long) << (28 as c_long)) as Py_ssize_t -+ } -+}; -+ -+#[cfg(all(Py_3_14, not(Py_GIL_DISABLED)))] -+const _Py_STATIC_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = { -+ if cfg!(target_pointer_width = "64") { -+ _Py_IMMORTAL_INITIAL_REFCNT -+ | ((_Py_STATICALLY_ALLOCATED_FLAG as Py_ssize_t) << (32 as Py_ssize_t)) -+ } else { -+ ((7 as c_long) << (28 as c_long)) as Py_ssize_t -+ } -+}; -+ -+#[cfg(all(Py_3_14, target_pointer_width = "32"))] -+const _Py_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = ((1 as c_long) << (30 as c_long)) as Py_ssize_t; -+ -+#[cfg(all(Py_3_14, target_pointer_width = "32"))] -+const _Py_STATIC_IMMORTAL_MINIMUM_REFCNT: Py_ssize_t = -+ ((6 as c_long) << (28 as c_long)) as Py_ssize_t; -+ -+#[cfg(all(Py_3_14, Py_GIL_DISABLED))] -+const _Py_IMMORTAL_INITIAL_REFCNT: Py_ssize_t = c_uint::MAX as Py_ssize_t; -+ -+#[cfg(Py_GIL_DISABLED)] -+pub(crate) const _Py_IMMORTAL_REFCNT_LOCAL: u32 = u32::MAX; -+ -+#[cfg(Py_GIL_DISABLED)] -+const _Py_REF_SHARED_SHIFT: isize = 2; -+// skipped private _Py_REF_SHARED_FLAG_MASK -+ -+// skipped private _Py_REF_SHARED_INIT -+// skipped private _Py_REF_MAYBE_WEAKREF -+// skipped private _Py_REF_QUEUED -+// skipped private _Py_REF_MERGED -+ -+// skipped private _Py_REF_SHARED -+ -+extern "C" { -+ #[cfg(all(Py_3_14, Py_LIMITED_API))] -+ pub fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t; -+} -+ -+#[cfg(not(all(Py_3_14, Py_LIMITED_API)))] -+#[inline] -+pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { -+ #[cfg(Py_GIL_DISABLED)] -+ { -+ let local = (*ob).ob_ref_local.load(Relaxed); -+ if local == _Py_IMMORTAL_REFCNT_LOCAL { -+ #[cfg(not(Py_3_14))] -+ return _Py_IMMORTAL_REFCNT; -+ #[cfg(Py_3_14)] -+ return _Py_IMMORTAL_INITIAL_REFCNT; -+ } -+ let shared = (*ob).ob_ref_shared.load(Relaxed); -+ local as Py_ssize_t + Py_ssize_t::from(shared >> _Py_REF_SHARED_SHIFT) -+ } -+ -+ #[cfg(all(Py_LIMITED_API, Py_3_14))] -+ { -+ Py_REFCNT(ob) -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(all(Py_LIMITED_API, Py_3_14)), Py_3_12))] -+ { -+ (*ob).ob_refcnt.ob_refcnt -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), not(GraalPy)))] -+ { -+ (*ob).ob_refcnt -+ } -+ -+ #[cfg(all(not(Py_GIL_DISABLED), not(Py_3_12), GraalPy))] -+ { -+ _Py_REFCNT(ob) -+ } -+} -+ -+#[cfg(Py_3_12)] -+#[inline(always)] -+unsafe fn _Py_IsImmortal(op: *mut PyObject) -> c_int { -+ #[cfg(all(target_pointer_width = "64", not(Py_GIL_DISABLED)))] -+ { -+ (((*op).ob_refcnt.ob_refcnt as crate::PY_INT32_T) < 0) as c_int -+ } -+ -+ #[cfg(all(target_pointer_width = "32", not(Py_GIL_DISABLED)))] -+ { -+ #[cfg(not(Py_3_14))] -+ { -+ ((*op).ob_refcnt.ob_refcnt == _Py_IMMORTAL_REFCNT) as c_int -+ } -+ -+ #[cfg(Py_3_14)] -+ { -+ ((*op).ob_refcnt.ob_refcnt >= _Py_IMMORTAL_MINIMUM_REFCNT) as c_int -+ } -+ } -+ -+ #[cfg(Py_GIL_DISABLED)] -+ { -+ ((*op).ob_ref_local.load(Relaxed) == _Py_IMMORTAL_REFCNT_LOCAL) as c_int -+ } -+} -+ -+// skipped _Py_IsStaticImmortal -+ -+// TODO: Py_SET_REFCNT -+ -+extern "C" { -+ #[cfg(all(py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_NegativeRefcount(filename: *const c_char, lineno: c_int, op: *mut PyObject); -+ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_INCREF_IncRefTotal(); -+ #[cfg(all(Py_3_12, py_sys_config = "Py_REF_DEBUG", not(Py_LIMITED_API)))] -+ fn _Py_DECREF_DecRefTotal(); -+ -+ #[cfg_attr(PyPy, link_name = "_PyPy_Dealloc")] -+ fn _Py_Dealloc(arg1: *mut PyObject); -+ -+ #[cfg_attr(PyPy, link_name = "PyPy_IncRef")] -+ #[cfg_attr(GraalPy, link_name = "_Py_IncRef")] -+ pub fn Py_IncRef(o: *mut PyObject); -+ #[cfg_attr(PyPy, link_name = "PyPy_DecRef")] -+ #[cfg_attr(GraalPy, link_name = "_Py_DecRef")] -+ pub fn Py_DecRef(o: *mut PyObject); -+ -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ fn _Py_IncRef(o: *mut PyObject); -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ fn _Py_DecRef(o: *mut PyObject); -+ -+ #[cfg(GraalPy)] -+ fn _Py_REFCNT(arg1: *const PyObject) -> Py_ssize_t; -+} -+ -+#[inline(always)] -+pub unsafe fn Py_INCREF(op: *mut PyObject) { -+ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -+ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -+ #[cfg(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ py_sys_config = "Py_REF_DEBUG", -+ GraalPy -+ ))] -+ { -+ // _Py_IncRef was added to the ABI in 3.10; skips null checks -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ { -+ _Py_IncRef(op); -+ } -+ -+ #[cfg(any(not(Py_3_10), PyPy))] -+ { -+ Py_IncRef(op); -+ } -+ } -+ -+ // version-specific builds are allowed to directly manipulate the reference count -+ #[cfg(not(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ py_sys_config = "Py_REF_DEBUG", -+ GraalPy -+ )))] -+ { -+ #[cfg(all(Py_3_14, target_pointer_width = "64"))] -+ { -+ let cur_refcnt = (*op).ob_refcnt.ob_refcnt; -+ if (cur_refcnt as i32) < 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt = cur_refcnt.wrapping_add(1); -+ } -+ -+ #[cfg(all(Py_3_12, not(Py_3_14), target_pointer_width = "64"))] -+ { -+ let cur_refcnt = (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN]; -+ let new_refcnt = cur_refcnt.wrapping_add(1); -+ if new_refcnt == 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt_split[crate::PY_BIG_ENDIAN] = new_refcnt; -+ } -+ -+ #[cfg(all(Py_3_12, target_pointer_width = "32"))] -+ { -+ if _Py_IsImmortal(op) != 0 { -+ return; -+ } -+ (*op).ob_refcnt.ob_refcnt += 1 -+ } -+ -+ #[cfg(not(Py_3_12))] -+ { -+ (*op).ob_refcnt += 1 -+ } -+ -+ // Skipped _Py_INCREF_STAT_INC - if anyone wants this, please file an issue -+ // or submit a PR supporting Py_STATS build option and pystats.h -+ } -+} -+ -+// skipped _Py_DecRefShared -+// skipped _Py_DecRefSharedDebug -+// skipped _Py_MergeZeroLocalRefcount -+ -+#[inline(always)] -+#[cfg_attr( -+ all(py_sys_config = "Py_REF_DEBUG", Py_3_12, not(Py_LIMITED_API)), -+ track_caller -+)] -+pub unsafe fn Py_DECREF(op: *mut PyObject) { -+ // On limited API, the free-threaded build, or with refcount debugging, let the interpreter do refcounting -+ // On 3.12+ we implement refcount debugging to get better assertion locations on negative refcounts -+ // TODO: reimplement the logic in the header in the free-threaded build, for a little bit of performance. -+ #[cfg(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -+ GraalPy -+ ))] -+ { -+ // _Py_DecRef was added to the ABI in 3.10; skips null checks -+ #[cfg(all(Py_3_10, not(PyPy)))] -+ { -+ _Py_DecRef(op); -+ } -+ -+ #[cfg(any(not(Py_3_10), PyPy))] -+ { -+ Py_DecRef(op); -+ } -+ } -+ -+ #[cfg(not(any( -+ Py_GIL_DISABLED, -+ Py_LIMITED_API, -+ all(py_sys_config = "Py_REF_DEBUG", not(Py_3_12)), -+ GraalPy -+ )))] -+ { -+ #[cfg(Py_3_12)] -+ if _Py_IsImmortal(op) != 0 { -+ return; -+ } -+ -+ // Skipped _Py_DECREF_STAT_INC - if anyone needs this, please file an issue -+ // or submit a PR supporting Py_STATS build option and pystats.h -+ -+ #[cfg(py_sys_config = "Py_REF_DEBUG")] -+ _Py_DECREF_DecRefTotal(); -+ -+ #[cfg(Py_3_12)] -+ { -+ (*op).ob_refcnt.ob_refcnt -= 1; -+ -+ #[cfg(py_sys_config = "Py_REF_DEBUG")] -+ if (*op).ob_refcnt.ob_refcnt < 0 { -+ let location = std::panic::Location::caller(); -+ let filename = std::ffi::CString::new(location.file()).unwrap(); -+ _Py_NegativeRefcount(filename.as_ptr(), location.line() as i32, op); -+ } -+ -+ if (*op).ob_refcnt.ob_refcnt == 0 { -+ _Py_Dealloc(op); -+ } -+ } -+ -+ #[cfg(not(Py_3_12))] -+ { -+ (*op).ob_refcnt -= 1; -+ -+ if (*op).ob_refcnt == 0 { -+ _Py_Dealloc(op); -+ } -+ } -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_CLEAR(op: *mut *mut PyObject) { -+ let tmp = *op; -+ if !tmp.is_null() { -+ *op = ptr::null_mut(); -+ Py_DECREF(tmp); -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_XINCREF(op: *mut PyObject) { -+ if !op.is_null() { -+ Py_INCREF(op) -+ } -+} -+ -+#[inline] -+pub unsafe fn Py_XDECREF(op: *mut PyObject) { -+ if !op.is_null() { -+ Py_DECREF(op) -+ } -+} -+ -+extern "C" { -+ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -+ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+ pub fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject; -+ #[cfg(all(Py_3_10, Py_LIMITED_API, not(PyPy)))] -+ #[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+ pub fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject; -+} -+ -+// macro _Py_NewRef not public; reimplemented directly inside Py_NewRef here -+// macro _Py_XNewRef not public; reimplemented directly inside Py_XNewRef here -+ -+#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] -+#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+#[inline] -+pub unsafe fn Py_NewRef(obj: *mut PyObject) -> *mut PyObject { -+ Py_INCREF(obj); -+ obj -+} -+ -+#[cfg(all(Py_3_10, any(not(Py_LIMITED_API), PyPy)))] -+#[cfg_attr(docsrs, doc(cfg(Py_3_10)))] -+#[inline] -+pub unsafe fn Py_XNewRef(obj: *mut PyObject) -> *mut PyObject { -+ Py_XINCREF(obj); -+ obj -+} -diff --git a/include/pyo3/pyo3-ffi/src/setobject.rs b/include/pyo3/pyo3-ffi/src/setobject.rs -index 9d5351fc..87e33e80 100644 ---- a/include/pyo3/pyo3-ffi/src/setobject.rs -+++ b/include/pyo3/pyo3-ffi/src/setobject.rs -@@ -39,11 +39,7 @@ pub unsafe fn PySet_GET_SIZE(so: *mut PyObject) -> Py_ssize_t { - (*so).used - } - --#[cfg(not(Py_LIMITED_API))] --#[cfg_attr(windows, link(name = "pythonXY"))] --extern "C" { -- pub static mut _PySet_Dummy: *mut PyObject; --} -+// skipped _PySet_Dummy - - extern "C" { - #[cfg(not(Py_LIMITED_API))] -diff --git a/include/pyo3/pyo3-ffi/src/weakrefobject.rs b/include/pyo3/pyo3-ffi/src/weakrefobject.rs -index 305dc290..88a1bf90 100644 ---- a/include/pyo3/pyo3-ffi/src/weakrefobject.rs -+++ b/include/pyo3/pyo3-ffi/src/weakrefobject.rs -@@ -4,16 +4,18 @@ use std::os::raw::c_int; - use std::ptr::addr_of_mut; - - #[cfg(all(not(PyPy), Py_LIMITED_API, not(GraalPy)))] --opaque_struct!(PyWeakReference); -+opaque_struct!(pub PyWeakReference); - - #[cfg(all(not(PyPy), not(Py_LIMITED_API), not(GraalPy)))] - pub use crate::_PyWeakReference as PyWeakReference; - - #[cfg_attr(windows, link(name = "pythonXY"))] - extern "C" { -+ // TODO: PyO3 is depending on this symbol in `reference.rs`, we should change this and -+ // remove the export as this is a private symbol. - pub static mut _PyWeakref_RefType: PyTypeObject; -- pub static mut _PyWeakref_ProxyType: PyTypeObject; -- pub static mut _PyWeakref_CallableProxyType: PyTypeObject; -+ static mut _PyWeakref_ProxyType: PyTypeObject; -+ static mut _PyWeakref_CallableProxyType: PyTypeObject; - - #[cfg(PyPy)] - #[link_name = "PyPyWeakref_CheckRef"] - -From d2570d2f25eba771468f41020a2ad69e0cbdbbc7 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 17:00:34 +0200 -Subject: [PATCH 2/8] Add pyo3-ffi patch for _PyLong_AsByteArray with - exceptions - -Co-authored-by: ijl ---- - ...i-pylong-asbytearray-with-exceptions.patch | 23 +++++++++++++++++++ - 1 file changed, 23 insertions(+) - create mode 100644 include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch - -diff --git a/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch -new file mode 100644 -index 00000000..20e5d7a3 ---- /dev/null -+++ b/include/pyo3-ffi-pylong-asbytearray-with-exceptions.patch -@@ -0,0 +1,23 @@ -+diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+index 45acaae..6063575 100644 -+--- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -++++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+@@ -61,6 +61,18 @@ extern "C" { -+ is_signed: c_int, -+ ) -> *mut PyObject; -+ -++ #[cfg(Py_3_13)] -++ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -++ pub fn _PyLong_AsByteArray( -++ v: *mut PyLongObject, -++ bytes: *mut c_uchar, -++ n: size_t, -++ little_endian: c_int, -++ is_signed: c_int, -++ with_exceptions: c_int, -++ ) -> c_int; -++ -++ #[cfg(not(Py_3_13))] -+ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -+ pub fn _PyLong_AsByteArray( -+ v: *mut PyLongObject, - -From d01aea4541800dca412e3f4c3068e974f18c3218 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 18:26:19 +0200 -Subject: [PATCH 3/8] Apply pyo3-ffi patch - ---- - include/pyo3/pyo3-ffi/src/cpython/longobject.rs | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -index 45acaae5..6063575e 100644 ---- a/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -+++ b/include/pyo3/pyo3-ffi/src/cpython/longobject.rs -@@ -61,6 +61,18 @@ extern "C" { - is_signed: c_int, - ) -> *mut PyObject; - -+ #[cfg(Py_3_13)] -+ #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] -+ pub fn _PyLong_AsByteArray( -+ v: *mut PyLongObject, -+ bytes: *mut c_uchar, -+ n: size_t, -+ little_endian: c_int, -+ is_signed: c_int, -+ with_exceptions: c_int, -+ ) -> c_int; -+ -+ #[cfg(not(Py_3_13))] - #[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")] - pub fn _PyLong_AsByteArray( - v: *mut PyLongObject, - -From 6427dba13406b499bc2edc033c0eeeaafdfa4ad4 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:16:24 +0200 -Subject: [PATCH 4/8] Fix _PyDict_SetItem_KnownHash_LockHeld - ---- - src/util.rs | 10 ---------- - 1 file changed, 10 deletions(-) - -diff --git a/src/util.rs b/src/util.rs -index ccf425ce..0a255c3f 100644 ---- a/src/util.rs -+++ b/src/util.rs -@@ -258,19 +258,9 @@ macro_rules! pydict_setitem { - ($dict:expr, $pykey:expr, $pyval:expr) => { - debug_assert!(ffi!(Py_REFCNT($dict)) == 1); - debug_assert!(str_hash!($pykey) != -1); -- #[cfg(not(Py_3_13))] - unsafe { - let _ = pyo3_ffi::_PyDict_SetItem_KnownHash($dict, $pykey, $pyval, str_hash!($pykey)); - } -- #[cfg(Py_3_13)] -- unsafe { -- let _ = pyo3_ffi::_PyDict_SetItem_KnownHash_LockHeld( -- $dict.cast::(), -- $pykey, -- $pyval, -- str_hash!($pykey), -- ); -- } - reverse_pydict_incref!($pykey); - reverse_pydict_incref!($pyval); - }; - -From 80779519c9363a81884e77358e62fb443a584039 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 15:16:40 +0200 -Subject: [PATCH 5/8] Fix _Py_IsImmortal - ---- - src/util.rs | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/src/util.rs b/src/util.rs -index 0a255c3f..bbfb72ed 100644 ---- a/src/util.rs -+++ b/src/util.rs -@@ -120,10 +120,8 @@ macro_rules! str_from_slice { - macro_rules! reverse_pydict_incref { - ($op:expr) => { - unsafe { -- if pyo3_ffi::_Py_IsImmortal($op) == 0 { -- debug_assert!(ffi!(Py_REFCNT($op)) >= 2); -- (*$op).ob_refcnt.ob_refcnt -= 1; -- } -+ debug_assert!(ffi!(Py_REFCNT($op)) >= 2); -+ (*$op).ob_refcnt.ob_refcnt -= 1; - } - }; - } - -From b90995f44f80176d403b720d706dfe5a027855ff Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Fri, 23 May 2025 17:12:44 +0200 -Subject: [PATCH 6/8] Use PyUnicode API - ---- - src/serialize/per_type/dict.rs | 4 ++-- - src/serialize/per_type/unicode.rs | 4 ++-- - src/str/ffi.rs | 29 +++++++++++++++-------------- - src/str/pyunicode_new.rs | 3 +++ - 4 files changed, 22 insertions(+), 18 deletions(-) - -diff --git a/src/serialize/per_type/dict.rs b/src/serialize/per_type/dict.rs -index e2e04f62..cf2c3a4b 100644 ---- a/src/serialize/per_type/dict.rs -+++ b/src/serialize/per_type/dict.rs -@@ -13,7 +13,7 @@ use crate::serialize::per_type::{ - }; - use crate::serialize::serializer::PyObjectSerializer; - use crate::serialize::state::SerializerState; --use crate::str::{unicode_to_str, unicode_to_str_via_ffi}; -+use crate::str::unicode_to_str; - use crate::typeref::{STR_TYPE, TRUE, VALUE_STR}; - use crate::util::isize_to_usize; - use compact_str::CompactString; -@@ -321,7 +321,7 @@ fn non_str_str(key: *mut pyo3_ffi::PyObject) -> Result Result { -- let uni = unicode_to_str_via_ffi(key); -+ let uni = unicode_to_str(key); - if unlikely!(uni.is_none()) { - Err(SerializeError::InvalidStr) - } else { -diff --git a/src/serialize/per_type/unicode.rs b/src/serialize/per_type/unicode.rs -index 23270916..7267b454 100644 ---- a/src/serialize/per_type/unicode.rs -+++ b/src/serialize/per_type/unicode.rs -@@ -1,7 +1,7 @@ - // SPDX-License-Identifier: (Apache-2.0 OR MIT) - - use crate::serialize::error::SerializeError; --use crate::str::{unicode_to_str, unicode_to_str_via_ffi}; -+use crate::str::unicode_to_str; - - use serde::ser::{Serialize, Serializer}; - -@@ -50,7 +50,7 @@ impl Serialize for StrSubclassSerializer { - where - S: Serializer, - { -- let uni = unicode_to_str_via_ffi(self.ptr); -+ let uni = unicode_to_str(self.ptr); - if unlikely!(uni.is_none()) { - err!(SerializeError::InvalidStr) - } -diff --git a/src/str/ffi.rs b/src/str/ffi.rs -index 4f1c76bd..6b57e7ae 100644 ---- a/src/str/ffi.rs -+++ b/src/str/ffi.rs -@@ -1,25 +1,19 @@ - // SPDX-License-Identifier: (Apache-2.0 OR MIT) - - use crate::util::isize_to_usize; --use core::ffi::c_void; --use pyo3_ffi::{PyASCIIObject, PyCompactUnicodeObject, PyObject, Py_hash_t}; -+#[cfg(not(Py_3_14))] -+use pyo3_ffi::PyCompactUnicodeObject; -+use pyo3_ffi::{ -+ PyASCIIObject, PyObject, PyUnicode_DATA, PyUnicode_GET_LENGTH, PyUnicode_KIND, Py_hash_t, -+}; - - // see unicodeobject.h for documentation - - #[inline] - pub fn hash_str(op: *mut PyObject) -> Py_hash_t { - unsafe { -- let data_ptr: *mut c_void = if (*op.cast::()).compact() == 1 -- && (*op.cast::()).ascii() == 1 -- { -- op.cast::().offset(1).cast::() -- } else { -- op.cast::() -- .offset(1) -- .cast::() -- }; -- let num_bytes = -- (*op.cast::()).length * ((*op.cast::()).kind()) as isize; -+ let data_ptr = PyUnicode_DATA(op); -+ let num_bytes = PyUnicode_GET_LENGTH(op) * PyUnicode_KIND(op) as isize; - #[cfg(Py_3_14)] - let hash = pyo3_ffi::Py_HashBuffer(data_ptr, num_bytes); - #[cfg(not(Py_3_14))] -@@ -30,7 +24,7 @@ pub fn hash_str(op: *mut PyObject) -> Py_hash_t { - } - - #[inline(never)] --pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { -+fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { - let mut str_size: pyo3_ffi::Py_ssize_t = 0; - let ptr = ffi!(PyUnicode_AsUTF8AndSize(op, &mut str_size)).cast::(); - if unlikely!(ptr.is_null()) { -@@ -40,6 +34,7 @@ pub fn unicode_to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { - } - } - -+#[cfg(not(Py_3_14))] - #[inline] - pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { - unsafe { -@@ -58,3 +53,9 @@ pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { - } - } - } -+ -+#[cfg(Py_3_14)] -+#[inline] -+pub fn unicode_to_str(op: *mut PyObject) -> Option<&'static str> { -+ unicode_to_str_via_ffi(op) -+} -diff --git a/src/str/pyunicode_new.rs b/src/str/pyunicode_new.rs -index 7b1c2df2..738b8489 100644 ---- a/src/str/pyunicode_new.rs -+++ b/src/str/pyunicode_new.rs -@@ -8,7 +8,9 @@ macro_rules! validate_str { - #[cfg(not(Py_3_12))] - debug_assert!((*($ptr.cast::())).ready() == 1); - -+ #[cfg(not(Py_3_14))] - debug_assert!((*($ptr.cast::())).compact() == 1); -+ #[cfg(not(Py_3_14))] - debug_assert!((*($ptr.cast::())).interned() == 0); - - debug_assert!(ffi!(_PyUnicode_CheckConsistency($ptr.cast::(), 1)) == 1); -@@ -22,6 +24,7 @@ pub fn pyunicode_ascii(buf: *const u8, num_chars: usize) -> *mut pyo3_ffi::PyObj - let data_ptr = ptr.cast::().offset(1).cast::(); - core::ptr::copy_nonoverlapping(buf, data_ptr, num_chars); - core::ptr::write(data_ptr.add(num_chars), 0); -+ #[cfg(not(Py_3_14))] - debug_assert!((*(ptr.cast::())).ascii() == 1); - validate_str!(ptr); - ptr.cast::() - -From 4eb6604607891c0f6aa00bbbb07ccfdcfd85b589 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Thu, 5 Jun 2025 13:52:28 +0200 -Subject: [PATCH 7/8] Add pyo3-ffi patch for PyObjectObRefcnt.refcnt_and_flags - -Co-authored-by: Benjamin A. Beasley ---- - include/pyo3-ffi-pyobjectrefcnt-64only.patch | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - create mode 100644 include/pyo3-ffi-pyobjectrefcnt-64only.patch - -diff --git a/include/pyo3-ffi-pyobjectrefcnt-64only.patch b/include/pyo3-ffi-pyobjectrefcnt-64only.patch -new file mode 100644 -index 00000000..dfc328a7 ---- /dev/null -+++ b/include/pyo3-ffi-pyobjectrefcnt-64only.patch -@@ -0,0 +1,13 @@ -+diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -+index c4c5abc361c..6d5dc781287 100644 -+--- a/include/pyo3/pyo3-ffi/src/object.rs -++++ b/include/pyo3/pyo3-ffi/src/object.rs -+@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { -+ pub union PyObjectObRefcnt { -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub ob_refcnt_full: crate::PY_INT64_T, -+- #[cfg(Py_3_14)] -++ #[cfg(all(target_pointer_width = "64", Py_3_14))] -+ pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, -+ pub ob_refcnt: Py_ssize_t, -+ #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] - -From ea5a7d83f4ff5262f131d37dd1d490a634ce2626 Mon Sep 17 00:00:00 2001 -From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> -Date: Thu, 5 Jun 2025 13:54:27 +0200 -Subject: [PATCH 8/8] Apply second pyo3-ffi patch - ---- - include/pyo3/pyo3-ffi/src/object.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/pyo3/pyo3-ffi/src/object.rs b/include/pyo3/pyo3-ffi/src/object.rs -index 5fbf45db..aeb97352 100644 ---- a/include/pyo3/pyo3-ffi/src/object.rs -+++ b/include/pyo3/pyo3-ffi/src/object.rs -@@ -49,7 +49,7 @@ pub struct PyObjectObFlagsAndRefcnt { - pub union PyObjectObRefcnt { - #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub ob_refcnt_full: crate::PY_INT64_T, -- #[cfg(Py_3_14)] -+ #[cfg(all(target_pointer_width = "64", Py_3_14))] - pub refcnt_and_flags: PyObjectObFlagsAndRefcnt, - pub ob_refcnt: Py_ssize_t, - #[cfg(all(target_pointer_width = "64", not(Py_3_14)))] diff --git a/python-orjson.spec b/python-orjson.spec index ab35204..22c3de9 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,17 +9,120 @@ # Not yet in EPEL10: https://bugzilla.redhat.com/show_bug.cgi?id=2356387 %bcond pendulum %{undefined el10} +# By default, orjson uses a bundled copy of the C library yyjson, +# https://github.com/ibireme/yyjson, as the JSON deserialization backend. It is +# forked (customized) and compiled with a particular set of options via +# preprocessor defines (see build.rs), so it is not a candidate for unbundling. +# +# We do have the option to disable the yyjson backend, in which case the json +# crate from the Rust standard library, https://docs.rs/json, is used instead. +%bcond yyjson 0 + Name: python-orjson -Version: 3.10.18 +Version: 3.11.0 Release: %autorelease Summary: Fast, correct Python JSON library -License: Apache-2.0 OR MIT +# The entire source is (Apache-2.0 OR MIT), except: +# +%if %{with yyjson} +# MIT: +# - include/yyjson/yyjson.c +# - include/yyjson/yyjson.h +# +%endif +# Apache-2.0: +# - src/serialize/writer/str/mod.rs +# - src/serialize/writer/str/sse2.rs +License: (Apache-2.0 OR MIT) AND Apache-2.0%{?with_yyjson: AND MIT} +# Additionally, the following are removed in %%prep and do not contribute to +# the licenses of the binary RPMs: +# +# Apache-2.0 WITH LLVM-exception: +# - include/cargo/target-lexicon-*/ +# +# BSL-1.0: +# - include/cargo/xxhash-rust-*/ +# +# MIT: +%if %{without yyjson} +# - include/yyjson/yyjson.c +# - include/yyjson/yyjson.h +%endif +# - include/cargo/crunchy-*/ +# - include/cargo/itoap-*/ +# +# MIT OR Apache-2.0: +# - include/cargo/associative-cache-*/ +# - include/cargo/bytecount-*/ +# - include/cargo/cc-*/ +# - include/cargo/cfg-if-*/ +# - include/cargo/gimli-*/ +# - include/cargo/half-*/ +# - include/cargo/itoa-*/ +# - include/cargo/libc-*/ +# - include/cargo/once_cell-*/ +# - include/cargo/portable-atomic-*/ +# - include/cargo/portable-atomic-util-*/ +# - include/cargo/proc-macro2-*/ +# - include/cargo/pyo3-build-config-*/ +# - include/cargo/pyo3-ffi-*/ +# - include/cargo/quote-*/ +# - include/cargo/serde-*/ +# - include/cargo/serde_derive-*/ +# - include/cargo/serde_json-*/ +# - include/cargo/shlex-*/ +# - include/cargo/simdutf8-*/ +# - include/cargo/smallvec-*/ +# - include/cargo/syn-*/ +# - include/cargo/unwinding-*/ +# - include/cargo/uuid-*/ +# - include/cargo/version_check-*/ +# +# Apache-2.0 OR BSL-1.0: +# - include/cargo/ryu-*/ +# +# (Apache-2.0 OR MIT) AND BSD-3-Clause: +# - include/cargo/encoding_rs-*/ +# +# (MIT OR Apache-2.0) AND Unicode-3.0: +# - include/cargo/unicode-ident-*/ +# +# Unlicense OR MIT: +# - include/cargo/jiff-*/ +# - include/cargo/jiff-static-*/ +# - include/cargo/memchr-*/ +SourceLicense: %{shrink: + (Apache-2.0 OR MIT) AND + Apache-2.0 AND + Apache-2.0 WITH LLVM-exception AND + BSD-3-Clause AND + BSL-1.0 AND + MIT AND + Unicode-3.0 AND + (Apache-2.0 OR BSL-1.0) + (Unlicense OR MIT) + } URL: https://github.com/ijl/orjson +# We must be careful about the source archive. +# +# The PyPI releases have a vendored Rust dependency bundle in include/cargo/, +# which we would remove in %%prep, but which we must still check to make sure +# everything has a license acceptable for distribution in Fedora before +# uploading to the lookaside cache. Source: %{pypi_source orjson} -# Support Python 3.14 -# Updates bundled/forked pyo3-build-config/pyo3-ffi to 0.25.0 -Patch: https://github.com/ijl/orjson/pull/570.patch +# The GitHub archives from +# %%{url}/archive/%%{version}/orjson-%%{version}.tar.gz do not have the +# vendored crates, but they contain benchmark data in data/, some of which is +# lacking its license text (e.g. data/blns.txt.xz, which is from +# https://github.com/minimaxir/big-list-of-naughty-strings and should carry the +# corresponding MIT license text), and some of which looks like it might have +# at best unclear license status. Since the benchmark data is potentially +# problematic, we would need to filter the GitHub archives with a script; this +# hasn’t been written yet, but could be similar to the one in python-cramjam. +# Source0: orjson-%%{version}-filtered.tar.gz +# ./get_source ${COMMIT} (or ${TAG}) +# Source1: get_source BuildRequires: tomcli BuildRequires: python3-devel @@ -48,34 +151,31 @@ datetimes, and numpy} %package -n python3-orjson Summary: %{summary} +# Output of %%{cargo_license_summary}: +# # (Apache-2.0 OR MIT) AND BSD-3-Clause # Apache-2.0 OR BSL-1.0 # Apache-2.0 OR MIT # BSL-1.0 # MIT -# MIT OR Apache-2.0 (duplicate) +# MIT OR Apache-2.0 # Unlicense OR MIT # -# Bundled PyO3 crates in include/pyo3/ are also (Apache-2.0 OR MIT). +# Note that this must include the terms of the base package License expression. License: %{shrink: (Apache-2.0 OR MIT) AND - BSD-3-Clause AND + Apache-2.0 AND (Apache-2.0 OR BSL-1.0) AND + BSD-3-Clause AND BSL-1.0 AND MIT AND (Unlicense OR MIT) } -# Path to using published versions of pyo3-ffi/pyo3-build-config again? -# https://github.com/ijl/orjson/issues/524 -# -# “You are welcome to work to upstream the diff if you find the vendoring -# unsuitable for your organization's own preferences.” -# -# Note that these crates are actually forked, not only bundled/vendored; see -# https://github.com/ijl/orjson/issues/524#issuecomment-2424170405 for details. -Provides: bundled(crate(pyo3-build-config)) = 0.25.0 -Provides: bundled(crate(pyo3-ffi)) = 0.25.0 +%if %{with yyjson} +# Version from YYJSON_VERSION_STRING in include/yyjson/yyjson.h +Provides: bundled(yyjson) = 0.9.0 +%endif %description -n python3-orjson %{_description} @@ -90,12 +190,10 @@ tomcli-set Cargo.toml del 'features.unwind' tomcli-set Cargo.toml del 'dependencies.unwinding' # Remove bundled rust crates rm -r include/cargo +%if %{without yyjson} # Remove bundled yyjson. rm -rv include/yyjson/ - -# Collect licenses for remaining vendored crates -mkdir -p LICENSES.vendored/pyo3 -cp -vp include/pyo3/LICENSE* LICENSES.vendored/pyo3/ +%endif %if %{without pendulum} sed -i '/^pendulum\b/d' test/requirements.txt @@ -110,23 +208,15 @@ sed -i '/pytest-random-order/d' test/requirements.txt %generate_buildrequires %pyproject_buildrequires %{?with_tests:test/requirements.txt} %cargo_generate_buildrequires -for dir in include/pyo3/*/ -do - pushd "${dir}" >/dev/null - %cargo_generate_buildrequires - popd >/dev/null -done %build export RUSTFLAGS='%{build_rustflags}' %cargo_license_summary %{cargo_license} > LICENSES.dependencies -# Fedora's pyo3 is patched to not check Python version when building RPM packages. -# However, this uses a bundled version without the patch. -# Rather than patching it, we set the environment variable, -# which allows us to test this package with development Python versions. -export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 +%if %{without yyjson} +export ORJSON_DISABLE_YYJSON=1 +%endif %pyproject_wheel @@ -145,7 +235,6 @@ export UNSAFE_PYO3_SKIP_VERSION_CHECK=1 %files -n python3-orjson -f %{pyproject_files} -%license LICENSES.vendored/ %doc README.md diff --git a/sources b/sources index 73cee52..0704996 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.10.18.tar.gz) = 248a42f671e713bf9d11163fe7744f5d9b626f5db681913071957723f48d77cc35aa883eb695a16df76986ecc8d9e9e493cee4431c47103beabc3517ffcaa65f +SHA512 (orjson-3.11.0.tar.gz) = be823592bc5fb28b1d5e90c412c9ec32b5953db27e5dcc088e43e2369dad28586603d0a3e450f72b5761a819bcd68a1305dac60219e5719b24e0a410c6e22af2 From bcfbf082b2e9705614b66dda17e5176458f21bd6 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 19 Jul 2025 08:00:28 -0400 Subject: [PATCH 48/55] Package from a GitHub archive, with data/ filtered out --- .gitignore | 1 + get_source | 49 ++++++++++++++++++++++++++++++ python-orjson.spec | 74 ++++------------------------------------------ sources | 2 +- 4 files changed, 57 insertions(+), 69 deletions(-) create mode 100755 get_source diff --git a/.gitignore b/.gitignore index eea9600..34c3a43 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ /orjson-3.10.16.tar.gz /orjson-3.10.18.tar.gz /orjson-3.11.0.tar.gz +/orjson-3.11.0-filtered.tar.xz diff --git a/get_source b/get_source new file mode 100755 index 0000000..5067989 --- /dev/null +++ b/get_source @@ -0,0 +1,49 @@ +#!/bin/sh +set -o errexit +set -o nounset + +if [ "$#" != '1' ] +then + cat 1>&2 < Downloading: ${URL}" 1>&2 +curl -L -O "${URL}" + +ARCHIVE="$(find . -mindepth 1 -maxdepth 1 -type f -name '*.tar.gz' -print -quit)" +echo "--> Extracting: $(basename "${ARCHIVE}")" 1>&2 +tar -xzf "${ARCHIVE}" +echo '--> Removing data/ due to licensing issues' 1>&2 +TARDIR="$(basename "${ARCHIVE}" '.tar.gz')" +MTIME="$(stat -c '%Y' "${TARDIR}")" +rm -rvf "${TARDIR}/data/" +# Make sure the original mtime is preserved even though we modified the base +# directory by removing something at the top level. +touch -d @"${MTIME}" "${TARDIR}" +FILTERED="$(basename "${ARCHIVE}" .tar.gz)-filtered.tar.xz" +echo "--> Re-archiving: ${FILTERED}" 1>&2 +# https://www.gnu.org/software/tar/manual/html_section/Reproducibility.html +TZ=UTC LC_ALL=C tar \ + --create --verbose \ + --sort=name \ + --format=posix \ + --numeric-owner --owner=0 --group=0 \ + --mode=go+u,go-w \ + --pax-option='delete=atime,delete=ctime' \ + "${TARDIR}/" | + xz -9e > "${FILTERED}" +mv -v "${FILTERED}" "${OUTDIR}" +echo 'Done.' 1>&2 diff --git a/python-orjson.spec b/python-orjson.spec index 22c3de9..d186bdf 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -35,74 +35,15 @@ Summary: Fast, correct Python JSON library # - src/serialize/writer/str/mod.rs # - src/serialize/writer/str/sse2.rs License: (Apache-2.0 OR MIT) AND Apache-2.0%{?with_yyjson: AND MIT} +%if %{without yyjson} # Additionally, the following are removed in %%prep and do not contribute to # the licenses of the binary RPMs: # -# Apache-2.0 WITH LLVM-exception: -# - include/cargo/target-lexicon-*/ -# -# BSL-1.0: -# - include/cargo/xxhash-rust-*/ -# # MIT: -%if %{without yyjson} # - include/yyjson/yyjson.c # - include/yyjson/yyjson.h %endif -# - include/cargo/crunchy-*/ -# - include/cargo/itoap-*/ -# -# MIT OR Apache-2.0: -# - include/cargo/associative-cache-*/ -# - include/cargo/bytecount-*/ -# - include/cargo/cc-*/ -# - include/cargo/cfg-if-*/ -# - include/cargo/gimli-*/ -# - include/cargo/half-*/ -# - include/cargo/itoa-*/ -# - include/cargo/libc-*/ -# - include/cargo/once_cell-*/ -# - include/cargo/portable-atomic-*/ -# - include/cargo/portable-atomic-util-*/ -# - include/cargo/proc-macro2-*/ -# - include/cargo/pyo3-build-config-*/ -# - include/cargo/pyo3-ffi-*/ -# - include/cargo/quote-*/ -# - include/cargo/serde-*/ -# - include/cargo/serde_derive-*/ -# - include/cargo/serde_json-*/ -# - include/cargo/shlex-*/ -# - include/cargo/simdutf8-*/ -# - include/cargo/smallvec-*/ -# - include/cargo/syn-*/ -# - include/cargo/unwinding-*/ -# - include/cargo/uuid-*/ -# - include/cargo/version_check-*/ -# -# Apache-2.0 OR BSL-1.0: -# - include/cargo/ryu-*/ -# -# (Apache-2.0 OR MIT) AND BSD-3-Clause: -# - include/cargo/encoding_rs-*/ -# -# (MIT OR Apache-2.0) AND Unicode-3.0: -# - include/cargo/unicode-ident-*/ -# -# Unlicense OR MIT: -# - include/cargo/jiff-*/ -# - include/cargo/jiff-static-*/ -# - include/cargo/memchr-*/ -SourceLicense: %{shrink: - (Apache-2.0 OR MIT) AND - Apache-2.0 AND - Apache-2.0 WITH LLVM-exception AND - BSD-3-Clause AND - BSL-1.0 AND - MIT AND - Unicode-3.0 AND - (Apache-2.0 OR BSL-1.0) - (Unlicense OR MIT) - } +SourceLicense: (Apache-2.0 OR MIT) AND Apache-2.0 AND MIT URL: https://github.com/ijl/orjson # We must be careful about the source archive. # @@ -110,7 +51,7 @@ URL: https://github.com/ijl/orjson # which we would remove in %%prep, but which we must still check to make sure # everything has a license acceptable for distribution in Fedora before # uploading to the lookaside cache. -Source: %{pypi_source orjson} +# Source: %%{pypi_source orjson} # The GitHub archives from # %%{url}/archive/%%{version}/orjson-%%{version}.tar.gz do not have the # vendored crates, but they contain benchmark data in data/, some of which is @@ -118,11 +59,10 @@ Source: %{pypi_source orjson} # https://github.com/minimaxir/big-list-of-naughty-strings and should carry the # corresponding MIT license text), and some of which looks like it might have # at best unclear license status. Since the benchmark data is potentially -# problematic, we would need to filter the GitHub archives with a script; this -# hasn’t been written yet, but could be similar to the one in python-cramjam. -# Source0: orjson-%%{version}-filtered.tar.gz +# problematic, we would need to filter the GitHub archives with a script. +Source0: orjson-%{version}-filtered.tar.xz # ./get_source ${COMMIT} (or ${TAG}) -# Source1: get_source +Source1: get_source BuildRequires: tomcli BuildRequires: python3-devel @@ -188,8 +128,6 @@ Provides: bundled(yyjson) = 0.9.0 # “Avoid bundling libgcc on musl.” tomcli-set Cargo.toml del 'features.unwind' tomcli-set Cargo.toml del 'dependencies.unwinding' -# Remove bundled rust crates -rm -r include/cargo %if %{without yyjson} # Remove bundled yyjson. rm -rv include/yyjson/ diff --git a/sources b/sources index 0704996..0c52415 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.11.0.tar.gz) = be823592bc5fb28b1d5e90c412c9ec32b5953db27e5dcc088e43e2369dad28586603d0a3e450f72b5761a819bcd68a1305dac60219e5719b24e0a410c6e22af2 +SHA512 (orjson-3.11.0-filtered.tar.xz) = cd0bfbe61d3c77fbed8d293babe4eb9cee0f4cc2801a33e5003041a426fd48d2817644b92697cbf0f57c2548b236e4a32f4efd9eccad6ba272107d1a801c402f From 804fc1ddf27eb458d995f95841965fb01033e228 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Wed, 23 Jul 2025 15:53:01 -0400 Subject: [PATCH 49/55] Patch for PyStr regression on big-endian architectures --- ...ff9df61b3b9d71527acf4fb60a735d841025.patch | 175 ++++++++++++++++++ python-orjson.spec | 9 + 2 files changed, 184 insertions(+) create mode 100644 9c33ff9df61b3b9d71527acf4fb60a735d841025.patch diff --git a/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch b/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch new file mode 100644 index 0000000..fd5153d --- /dev/null +++ b/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch @@ -0,0 +1,175 @@ +From 9c33ff9df61b3b9d71527acf4fb60a735d841025 Mon Sep 17 00:00:00 2001 +From: ijl +Date: Wed, 23 Jul 2025 13:49:55 +0000 +Subject: [PATCH] Fix big-endian PyStr + +--- + .github/workflows/artifact.yaml | 29 ++++++++++++++++ + README.md | 5 +-- + src/str/pystr.rs | 61 +++++++++++++++++++++------------ + 3 files changed, 72 insertions(+), 23 deletions(-) + +diff --git a/.github/workflows/artifact.yaml b/.github/workflows/artifact.yaml +index fa628c03..d7283562 100644 +--- a/.github/workflows/artifact.yaml ++++ b/.github/workflows/artifact.yaml +@@ -269,6 +269,35 @@ jobs: + if-no-files-found: "error" + compression-level: 0 + ++ - name: setup-qemu-container ++ if: "matrix.target.arch == 's390x'" ++ uses: sandervocke/setup-qemu-container@v1 ++ with: ++ container: registry.fedoraproject.org/fedora:42 ++ arch: ${{ matrix.target.arch }} ++ podman_args: "-v .:/orjson -v /tmp:/tmp --workdir /orjson" ++ ++ - name: setup-shell-wrapper ++ uses: sandervocke/setup-shell-wrapper@v1 ++ ++ - name: Emulated Test ++ if: "matrix.target.arch == 's390x'" ++ shell: wrap-shell {0} ++ env: ++ WRAP_SHELL: run-in-container.sh ++ run: | ++ set -eou pipefail ++ ++ dnf install --setopt=install_weak_deps=false -y ${{ matrix.python.interpreter }} python3-uv ++ ++ uv venv --python ${{ matrix.python.interpreter }} ++ source .venv/bin/activate ++ ++ uv pip install -r test/requirements.txt ++ uv pip install dist/orjson*.whl ++ ++ pytest -v test ++ + musllinux_amd64: + runs-on: ubuntu-24.04 + timeout-minutes: 10 +diff --git a/README.md b/README.md +index f0b9a6a0..3b7b642f 100644 +--- a/README.md ++++ b/README.md +@@ -1088,8 +1088,9 @@ is prudent to pin the nightly version because that channel can introduce + breaking changes. There is a significant performance benefit to using + nightly. + +-orjson is tested for amd64, aarch64, and i686 on Linux and cross-compiles for +-arm7, ppc64le, and s390x. It is tested for either aarch64 or amd64 on macOS and ++orjson is tested on native hardware for amd64, aarch64, and i686 on Linux and ++for arm7, ppc64le, and s390x is cross-compiled and may be tested via ++emulation. It is tested for either aarch64 or amd64 on macOS and + cross-compiles for the other, depending on version. For Windows it is + tested on amd64 and i686. + +diff --git a/src/str/pystr.rs b/src/str/pystr.rs +index a856b13a..186a0e50 100644 +--- a/src/str/pystr.rs ++++ b/src/str/pystr.rs +@@ -1,11 +1,14 @@ + // SPDX-License-Identifier: (Apache-2.0 OR MIT) + + use crate::typeref::{EMPTY_UNICODE, STR_TYPE}; ++#[cfg(target_endian = "little")] + use crate::util::isize_to_usize; +- ++#[cfg(target_endian = "little")] + use core::ffi::c_void; + use core::ptr::NonNull; +-use pyo3_ffi::{PyASCIIObject, PyCompactUnicodeObject, PyObject}; ++#[cfg(target_endian = "little")] ++use pyo3_ffi::PyCompactUnicodeObject; ++use pyo3_ffi::{PyASCIIObject, PyObject}; + + fn to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { + let mut str_size: pyo3_ffi::Py_ssize_t = 0; +@@ -32,29 +35,21 @@ pub fn set_str_create_fn() { + } + } + +-#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +-const STATE_COMPACT_ASCII: u32 = u32::from_le(0b000000000000000000_0_1_1_001_00000000); +- +-#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] +-const STATE_COMPACT_ASCII: u32 = u32::from_le(0b000000000000000000000000_0_1_1_001_00); +- +-#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +-const STATE_COMPACT: u32 = u32::from_le(0b000000000000000000_0_0_1_000_00000000); +- +-#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] +-const STATE_COMPACT: u32 = u32::from_le(0b000000000000000000000000_0_0_1_000_00); ++#[cfg(all(target_endian = "little", Py_3_14, Py_GIL_DISABLED))] ++const STATE_KIND_SHIFT: usize = 8; + +-#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +-const STATE_KIND_MASK: u32 = u32::from_le(0b111_00000000); ++#[cfg(all(target_endian = "little", not(all(Py_3_14, Py_GIL_DISABLED))))] ++const STATE_KIND_SHIFT: usize = 2; + +-#[cfg(all(Py_3_14, Py_GIL_DISABLED))] +-const STATE_KIND_SHIFT: usize = 8; ++#[cfg(target_endian = "little")] ++const STATE_KIND_MASK: u32 = 7 << STATE_KIND_SHIFT; + +-#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] +-const STATE_KIND_MASK: u32 = u32::from_le(0b111_00); ++#[cfg(target_endian = "little")] ++const STATE_COMPACT_ASCII: u32 = ++ 1 << STATE_KIND_SHIFT | 1 << (STATE_KIND_SHIFT + 3) | 1 << (STATE_KIND_SHIFT + 4); + +-#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] +-const STATE_KIND_SHIFT: usize = 2; ++#[cfg(target_endian = "little")] ++const STATE_COMPACT: u32 = 1 << (STATE_KIND_SHIFT + 3); + + #[repr(transparent)] + #[derive(Copy, Clone)] +@@ -96,6 +91,7 @@ impl PyStr { + } + } + ++ #[cfg(target_endian = "little")] + pub fn hash(&mut self) { + unsafe { + let ptr = self.ptr.as_ptr().cast::(); +@@ -118,7 +114,24 @@ impl PyStr { + } + } + ++ #[cfg(not(target_endian = "little"))] ++ pub fn hash(&mut self) { ++ unsafe { ++ let data_ptr = ffi!(PyUnicode_DATA(self.ptr.as_ptr())); ++ #[allow(clippy::cast_possible_wrap)] ++ let num_bytes = ++ ffi!(PyUnicode_KIND(self.ptr.as_ptr())) as isize * ffi!(Py_SIZE(self.ptr.as_ptr())); ++ #[cfg(Py_3_14)] ++ let hash = pyo3_ffi::Py_HashBuffer(data_ptr, num_bytes); ++ #[cfg(not(Py_3_14))] ++ let hash = pyo3_ffi::_Py_HashBytes(data_ptr, num_bytes); ++ (*self.ptr.as_ptr().cast::()).hash = hash; ++ debug_assert!((*self.ptr.as_ptr().cast::()).hash != -1); ++ } ++ } ++ + #[inline(always)] ++ #[cfg(target_endian = "little")] + pub fn to_str(self) -> Option<&'static str> { + unsafe { + let op = self.ptr.as_ptr(); +@@ -140,6 +153,12 @@ impl PyStr { + } + } + ++ #[inline(always)] ++ #[cfg(not(target_endian = "little"))] ++ pub fn to_str(self) -> Option<&'static str> { ++ to_str_via_ffi(self.ptr.as_ptr()) ++ } ++ + pub fn as_ptr(self) -> *mut PyObject { + self.ptr.as_ptr() + } diff --git a/python-orjson.spec b/python-orjson.spec index d186bdf..cd1fa1c 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -64,6 +64,15 @@ Source0: orjson-%{version}-filtered.tar.xz # ./get_source ${COMMIT} (or ${TAG}) Source1: get_source +# Fix big-endian PyStr +# https://github.com/ijl/orjson/commit/9c33ff9df61b3b9d71527acf4fb60a735d841025 +# +# Fixes: +# +# Test regressions (segfaults) on s390x architecture in 3.11.0 +# https://github.com/ijl/orjson/issues/584 +Patch: %{url}/commit/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch + BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} From cab2d3ef66983fc9042389c5101b40f73d25b8ab Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Sat, 26 Jul 2025 07:36:45 -0400 Subject: [PATCH 50/55] Update to 3.11.1 (close RHBZ#2383473) --- .gitignore | 1 + ...ff9df61b3b9d71527acf4fb60a735d841025.patch | 175 ------------------ python-orjson.spec | 11 +- sources | 2 +- 4 files changed, 3 insertions(+), 186 deletions(-) delete mode 100644 9c33ff9df61b3b9d71527acf4fb60a735d841025.patch diff --git a/.gitignore b/.gitignore index 34c3a43..e143901 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ /orjson-3.10.18.tar.gz /orjson-3.11.0.tar.gz /orjson-3.11.0-filtered.tar.xz +/orjson-3.11.1-filtered.tar.xz diff --git a/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch b/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch deleted file mode 100644 index fd5153d..0000000 --- a/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch +++ /dev/null @@ -1,175 +0,0 @@ -From 9c33ff9df61b3b9d71527acf4fb60a735d841025 Mon Sep 17 00:00:00 2001 -From: ijl -Date: Wed, 23 Jul 2025 13:49:55 +0000 -Subject: [PATCH] Fix big-endian PyStr - ---- - .github/workflows/artifact.yaml | 29 ++++++++++++++++ - README.md | 5 +-- - src/str/pystr.rs | 61 +++++++++++++++++++++------------ - 3 files changed, 72 insertions(+), 23 deletions(-) - -diff --git a/.github/workflows/artifact.yaml b/.github/workflows/artifact.yaml -index fa628c03..d7283562 100644 ---- a/.github/workflows/artifact.yaml -+++ b/.github/workflows/artifact.yaml -@@ -269,6 +269,35 @@ jobs: - if-no-files-found: "error" - compression-level: 0 - -+ - name: setup-qemu-container -+ if: "matrix.target.arch == 's390x'" -+ uses: sandervocke/setup-qemu-container@v1 -+ with: -+ container: registry.fedoraproject.org/fedora:42 -+ arch: ${{ matrix.target.arch }} -+ podman_args: "-v .:/orjson -v /tmp:/tmp --workdir /orjson" -+ -+ - name: setup-shell-wrapper -+ uses: sandervocke/setup-shell-wrapper@v1 -+ -+ - name: Emulated Test -+ if: "matrix.target.arch == 's390x'" -+ shell: wrap-shell {0} -+ env: -+ WRAP_SHELL: run-in-container.sh -+ run: | -+ set -eou pipefail -+ -+ dnf install --setopt=install_weak_deps=false -y ${{ matrix.python.interpreter }} python3-uv -+ -+ uv venv --python ${{ matrix.python.interpreter }} -+ source .venv/bin/activate -+ -+ uv pip install -r test/requirements.txt -+ uv pip install dist/orjson*.whl -+ -+ pytest -v test -+ - musllinux_amd64: - runs-on: ubuntu-24.04 - timeout-minutes: 10 -diff --git a/README.md b/README.md -index f0b9a6a0..3b7b642f 100644 ---- a/README.md -+++ b/README.md -@@ -1088,8 +1088,9 @@ is prudent to pin the nightly version because that channel can introduce - breaking changes. There is a significant performance benefit to using - nightly. - --orjson is tested for amd64, aarch64, and i686 on Linux and cross-compiles for --arm7, ppc64le, and s390x. It is tested for either aarch64 or amd64 on macOS and -+orjson is tested on native hardware for amd64, aarch64, and i686 on Linux and -+for arm7, ppc64le, and s390x is cross-compiled and may be tested via -+emulation. It is tested for either aarch64 or amd64 on macOS and - cross-compiles for the other, depending on version. For Windows it is - tested on amd64 and i686. - -diff --git a/src/str/pystr.rs b/src/str/pystr.rs -index a856b13a..186a0e50 100644 ---- a/src/str/pystr.rs -+++ b/src/str/pystr.rs -@@ -1,11 +1,14 @@ - // SPDX-License-Identifier: (Apache-2.0 OR MIT) - - use crate::typeref::{EMPTY_UNICODE, STR_TYPE}; -+#[cfg(target_endian = "little")] - use crate::util::isize_to_usize; -- -+#[cfg(target_endian = "little")] - use core::ffi::c_void; - use core::ptr::NonNull; --use pyo3_ffi::{PyASCIIObject, PyCompactUnicodeObject, PyObject}; -+#[cfg(target_endian = "little")] -+use pyo3_ffi::PyCompactUnicodeObject; -+use pyo3_ffi::{PyASCIIObject, PyObject}; - - fn to_str_via_ffi(op: *mut PyObject) -> Option<&'static str> { - let mut str_size: pyo3_ffi::Py_ssize_t = 0; -@@ -32,29 +35,21 @@ pub fn set_str_create_fn() { - } - } - --#[cfg(all(Py_3_14, Py_GIL_DISABLED))] --const STATE_COMPACT_ASCII: u32 = u32::from_le(0b000000000000000000_0_1_1_001_00000000); -- --#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] --const STATE_COMPACT_ASCII: u32 = u32::from_le(0b000000000000000000000000_0_1_1_001_00); -- --#[cfg(all(Py_3_14, Py_GIL_DISABLED))] --const STATE_COMPACT: u32 = u32::from_le(0b000000000000000000_0_0_1_000_00000000); -- --#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] --const STATE_COMPACT: u32 = u32::from_le(0b000000000000000000000000_0_0_1_000_00); -+#[cfg(all(target_endian = "little", Py_3_14, Py_GIL_DISABLED))] -+const STATE_KIND_SHIFT: usize = 8; - --#[cfg(all(Py_3_14, Py_GIL_DISABLED))] --const STATE_KIND_MASK: u32 = u32::from_le(0b111_00000000); -+#[cfg(all(target_endian = "little", not(all(Py_3_14, Py_GIL_DISABLED))))] -+const STATE_KIND_SHIFT: usize = 2; - --#[cfg(all(Py_3_14, Py_GIL_DISABLED))] --const STATE_KIND_SHIFT: usize = 8; -+#[cfg(target_endian = "little")] -+const STATE_KIND_MASK: u32 = 7 << STATE_KIND_SHIFT; - --#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] --const STATE_KIND_MASK: u32 = u32::from_le(0b111_00); -+#[cfg(target_endian = "little")] -+const STATE_COMPACT_ASCII: u32 = -+ 1 << STATE_KIND_SHIFT | 1 << (STATE_KIND_SHIFT + 3) | 1 << (STATE_KIND_SHIFT + 4); - --#[cfg(not(all(Py_3_14, Py_GIL_DISABLED)))] --const STATE_KIND_SHIFT: usize = 2; -+#[cfg(target_endian = "little")] -+const STATE_COMPACT: u32 = 1 << (STATE_KIND_SHIFT + 3); - - #[repr(transparent)] - #[derive(Copy, Clone)] -@@ -96,6 +91,7 @@ impl PyStr { - } - } - -+ #[cfg(target_endian = "little")] - pub fn hash(&mut self) { - unsafe { - let ptr = self.ptr.as_ptr().cast::(); -@@ -118,7 +114,24 @@ impl PyStr { - } - } - -+ #[cfg(not(target_endian = "little"))] -+ pub fn hash(&mut self) { -+ unsafe { -+ let data_ptr = ffi!(PyUnicode_DATA(self.ptr.as_ptr())); -+ #[allow(clippy::cast_possible_wrap)] -+ let num_bytes = -+ ffi!(PyUnicode_KIND(self.ptr.as_ptr())) as isize * ffi!(Py_SIZE(self.ptr.as_ptr())); -+ #[cfg(Py_3_14)] -+ let hash = pyo3_ffi::Py_HashBuffer(data_ptr, num_bytes); -+ #[cfg(not(Py_3_14))] -+ let hash = pyo3_ffi::_Py_HashBytes(data_ptr, num_bytes); -+ (*self.ptr.as_ptr().cast::()).hash = hash; -+ debug_assert!((*self.ptr.as_ptr().cast::()).hash != -1); -+ } -+ } -+ - #[inline(always)] -+ #[cfg(target_endian = "little")] - pub fn to_str(self) -> Option<&'static str> { - unsafe { - let op = self.ptr.as_ptr(); -@@ -140,6 +153,12 @@ impl PyStr { - } - } - -+ #[inline(always)] -+ #[cfg(not(target_endian = "little"))] -+ pub fn to_str(self) -> Option<&'static str> { -+ to_str_via_ffi(self.ptr.as_ptr()) -+ } -+ - pub fn as_ptr(self) -> *mut PyObject { - self.ptr.as_ptr() - } diff --git a/python-orjson.spec b/python-orjson.spec index cd1fa1c..4cf773a 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -19,7 +19,7 @@ %bcond yyjson 0 Name: python-orjson -Version: 3.11.0 +Version: 3.11.1 Release: %autorelease Summary: Fast, correct Python JSON library @@ -64,15 +64,6 @@ Source0: orjson-%{version}-filtered.tar.xz # ./get_source ${COMMIT} (or ${TAG}) Source1: get_source -# Fix big-endian PyStr -# https://github.com/ijl/orjson/commit/9c33ff9df61b3b9d71527acf4fb60a735d841025 -# -# Fixes: -# -# Test regressions (segfaults) on s390x architecture in 3.11.0 -# https://github.com/ijl/orjson/issues/584 -Patch: %{url}/commit/9c33ff9df61b3b9d71527acf4fb60a735d841025.patch - BuildRequires: tomcli BuildRequires: python3-devel BuildRequires: %{py3_dist pytest-forked} diff --git a/sources b/sources index 0c52415..c508619 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.11.0-filtered.tar.xz) = cd0bfbe61d3c77fbed8d293babe4eb9cee0f4cc2801a33e5003041a426fd48d2817644b92697cbf0f57c2548b236e4a32f4efd9eccad6ba272107d1a801c402f +SHA512 (orjson-3.11.1-filtered.tar.xz) = e8d47c8925d483f24bdef8ce715bf796e4718ee4117b780a8d54c3b96b6d51086b957c387182b40e8b5b7ed04e35a289a5a99085f0190a34382de7e49a6ad5b9 From c5b71bd4ae95b3acbe8307a64de801ed9eef5b33 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Tue, 12 Aug 2025 17:41:32 -0400 Subject: [PATCH 51/55] Update to 3.11.2 (close RHBZ#2387852) --- .gitignore | 1 + python-orjson.spec | 2 +- sources | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e143901..7f31cc5 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ /orjson-3.11.0.tar.gz /orjson-3.11.0-filtered.tar.xz /orjson-3.11.1-filtered.tar.xz +/orjson-3.11.2-filtered.tar.xz diff --git a/python-orjson.spec b/python-orjson.spec index 4cf773a..dcf50da 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -19,7 +19,7 @@ %bcond yyjson 0 Name: python-orjson -Version: 3.11.1 +Version: 3.11.2 Release: %autorelease Summary: Fast, correct Python JSON library diff --git a/sources b/sources index c508619..67def6d 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.11.1-filtered.tar.xz) = e8d47c8925d483f24bdef8ce715bf796e4718ee4117b780a8d54c3b96b6d51086b957c387182b40e8b5b7ed04e35a289a5a99085f0190a34382de7e49a6ad5b9 +SHA512 (orjson-3.11.2-filtered.tar.xz) = 5231153d88893d9219245314f99711b154b4151a11381f78fc4722bc4c180ae45a0bf0ec6d9e1a168a1f246e0291ccd716f8bb57a9464fc8632eaadcb83df490 From 5dd31d44056fe187901bd00d780792a00244326e Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 15 Aug 2025 14:17:59 +0200 Subject: [PATCH 52/55] Rebuilt for Python 3.14.0rc2 bytecode From 3a6a32f1ec5e0804450b1c2f296799dac8a458fd Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Mon, 1 Sep 2025 00:15:04 +0100 Subject: [PATCH 53/55] Update to 3.11.3 (close RHBZ#2391102) --- .gitignore | 1 + python-orjson.spec | 2 +- sources | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7f31cc5..96dc1e2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ /orjson-3.11.0-filtered.tar.xz /orjson-3.11.1-filtered.tar.xz /orjson-3.11.2-filtered.tar.xz +/orjson-3.11.3-filtered.tar.xz diff --git a/python-orjson.spec b/python-orjson.spec index dcf50da..67c680f 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -19,7 +19,7 @@ %bcond yyjson 0 Name: python-orjson -Version: 3.11.2 +Version: 3.11.3 Release: %autorelease Summary: Fast, correct Python JSON library diff --git a/sources b/sources index 67def6d..9caac9e 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.11.2-filtered.tar.xz) = 5231153d88893d9219245314f99711b154b4151a11381f78fc4722bc4c180ae45a0bf0ec6d9e1a168a1f246e0291ccd716f8bb57a9464fc8632eaadcb83df490 +SHA512 (orjson-3.11.3-filtered.tar.xz) = 1a1447046743469c20b43c1ab99a752e48f91828d0a13e0587fad1174640c6355f5fd973e25c0bc8950852992020852d202184cfe0b7aeb309e2a0c3d1b5feae From a9cf430503dfdca74cef3a3cf3c38960f8c8fee1 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 19 Sep 2025 13:54:26 +0200 Subject: [PATCH 54/55] Rebuilt for Python 3.14.0rc3 bytecode From 1d2ec9221eaff43b15bde18868e07504af0e8431 Mon Sep 17 00:00:00 2001 From: "Benjamin A. Beasley" Date: Fri, 24 Oct 2025 20:51:33 +0100 Subject: [PATCH 55/55] Update to 3.11.4 (close RHBZ#2406238) - Bundling yyjson is no longer optional --- .gitignore | 1 + python-orjson.spec | 44 +++++++++++++------------------------------- sources | 2 +- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/.gitignore b/.gitignore index 96dc1e2..8d7ae02 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ /orjson-3.11.1-filtered.tar.xz /orjson-3.11.2-filtered.tar.xz /orjson-3.11.3-filtered.tar.xz +/orjson-3.11.4-filtered.tar.xz diff --git a/python-orjson.spec b/python-orjson.spec index 67c680f..33e673e 100644 --- a/python-orjson.spec +++ b/python-orjson.spec @@ -9,41 +9,21 @@ # Not yet in EPEL10: https://bugzilla.redhat.com/show_bug.cgi?id=2356387 %bcond pendulum %{undefined el10} -# By default, orjson uses a bundled copy of the C library yyjson, -# https://github.com/ibireme/yyjson, as the JSON deserialization backend. It is -# forked (customized) and compiled with a particular set of options via -# preprocessor defines (see build.rs), so it is not a candidate for unbundling. -# -# We do have the option to disable the yyjson backend, in which case the json -# crate from the Rust standard library, https://docs.rs/json, is used instead. -%bcond yyjson 0 - Name: python-orjson -Version: 3.11.3 +Version: 3.11.4 Release: %autorelease Summary: Fast, correct Python JSON library # The entire source is (Apache-2.0 OR MIT), except: # -%if %{with yyjson} # MIT: # - include/yyjson/yyjson.c # - include/yyjson/yyjson.h # -%endif # Apache-2.0: # - src/serialize/writer/str/mod.rs # - src/serialize/writer/str/sse2.rs -License: (Apache-2.0 OR MIT) AND Apache-2.0%{?with_yyjson: AND MIT} -%if %{without yyjson} -# Additionally, the following are removed in %%prep and do not contribute to -# the licenses of the binary RPMs: -# -# MIT: -# - include/yyjson/yyjson.c -# - include/yyjson/yyjson.h -%endif -SourceLicense: (Apache-2.0 OR MIT) AND Apache-2.0 AND MIT +License: (Apache-2.0 OR MIT) AND Apache-2.0 AND MIT URL: https://github.com/ijl/orjson # We must be careful about the source archive. # @@ -96,6 +76,7 @@ Summary: %{summary} # (Apache-2.0 OR MIT) AND BSD-3-Clause # Apache-2.0 OR BSL-1.0 # Apache-2.0 OR MIT +# BSD-2-Clause OR Apache-2.0 OR MIT # BSL-1.0 # MIT # MIT OR Apache-2.0 @@ -106,16 +87,24 @@ License: %{shrink: (Apache-2.0 OR MIT) AND Apache-2.0 AND (Apache-2.0 OR BSL-1.0) AND + (Apache-2.0 OR BSD-2-Clause OR MIT) AND BSD-3-Clause AND BSL-1.0 AND MIT AND (Unlicense OR MIT) } -%if %{with yyjson} # Version from YYJSON_VERSION_STRING in include/yyjson/yyjson.h +# +# Since version 3.11.4, orjson unconditionally uses a bundled copy of the C +# library yyjson, https://github.com/ibireme/yyjson, as the JSON +# deserialization backend. It is forked (customized) and compiled with a +# particular set of options via preprocessor defines (see build.rs), so it is +# not a candidate for unbundling. (Prior to version 3.11.4, this could be +# disabled, and the json crate from the Rust standard library, +# https://docs.rs/json, would be used instead, but this is no longer +# supported.) Provides: bundled(yyjson) = 0.9.0 -%endif %description -n python3-orjson %{_description} @@ -128,10 +117,6 @@ Provides: bundled(yyjson) = 0.9.0 # “Avoid bundling libgcc on musl.” tomcli-set Cargo.toml del 'features.unwind' tomcli-set Cargo.toml del 'dependencies.unwinding' -%if %{without yyjson} -# Remove bundled yyjson. -rm -rv include/yyjson/ -%endif %if %{without pendulum} sed -i '/^pendulum\b/d' test/requirements.txt @@ -152,9 +137,6 @@ sed -i '/pytest-random-order/d' test/requirements.txt export RUSTFLAGS='%{build_rustflags}' %cargo_license_summary %{cargo_license} > LICENSES.dependencies -%if %{without yyjson} -export ORJSON_DISABLE_YYJSON=1 -%endif %pyproject_wheel diff --git a/sources b/sources index 9caac9e..18ca5d3 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (orjson-3.11.3-filtered.tar.xz) = 1a1447046743469c20b43c1ab99a752e48f91828d0a13e0587fad1174640c6355f5fd973e25c0bc8950852992020852d202184cfe0b7aeb309e2a0c3d1b5feae +SHA512 (orjson-3.11.4-filtered.tar.xz) = 1a457a91dfa1918341acfebcdb86ddf92b5776f166be9dbd16a4253f55140ae0bc90f911ceb3a9b33abd963be22f089aa2764b25e1701691298c0e8840c8225a