Skip to content
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
2c1fd27
Add 3.15 testing in ci.yml
clin1234 Oct 15, 2025
2b1bc17
Create 5518-packaging.md
clin1234 Oct 15, 2025
fdffee3
Update .github/workflows/ci.yml
clin1234 Oct 20, 2025
761db77
Update Python version compatibility in pyo3-ffi/Cargo.toml
clin1234 Oct 21, 2025
7439eea
Typo
clin1234 Oct 21, 2025
aa4c3d4
Add `abi3-py315` in pyo3-build-config/Cargo.toml
clin1234 Oct 21, 2025
57f95d1
Add abi3-py315 in Cargo.toml
clin1234 Oct 21, 2025
cf0bcac
Bump max supported CPython version in noxfile.py
clin1234 Oct 21, 2025
af8eb8c
Bump max CPython version in pyo3-ffi/build.rs
clin1234 Oct 21, 2025
23f680f
Lint noxfile.py encountered by PyLance
clin1234 Nov 11, 2025
9662b22
Closer match of PyObject's definition in 3.15t
clin1234 Nov 11, 2025
331b886
tmp
clin1234 Nov 27, 2025
78137d4
fix ffi-check on the free-threaded build
ngoldbaum Jan 9, 2026
c919a1a
fix test failures on the free-threaded build
ngoldbaum Jan 9, 2026
94fe4bb
Update ABI3_MAX_MINOR
ngoldbaum Jan 9, 2026
79546bd
revert most changes to the noxfile
ngoldbaum Jan 9, 2026
d72f1a3
Fix free-threaded build on <3.15
ngoldbaum Jan 9, 2026
215c416
revert changes to moduleobject bindings
ngoldbaum Jan 9, 2026
67ca99b
fix ffi-check on 3.13t and 3.14t
ngoldbaum Jan 9, 2026
be18bd8
fix 3.13t for real
ngoldbaum Jan 9, 2026
9ce3ce0
adjust ob_flags setup and fix conditional compilation
ngoldbaum Jan 9, 2026
b873ad9
disable abi3-py315 for now
ngoldbaum Jan 12, 2026
43fd6c7
rework PyObject 4-byte alignment to more closely follow upstream
ngoldbaum Jan 12, 2026
00441c4
disable abi3-py315 for now
ngoldbaum Jan 12, 2026
af4bb11
fix check-feature-powerset and test-version-limits
ngoldbaum Jan 12, 2026
2a33e2d
really fix test-version-limits
ngoldbaum Jan 12, 2026
34bd3d5
run ruff
ngoldbaum Jan 12, 2026
4cc0aff
Remove free-threaded versions from ABI3_PY_VERSIONS
ngoldbaum Jan 12, 2026
032d532
fix ffi-check on free-threaded build
ngoldbaum Jan 12, 2026
6c4ce7f
Allow experimental builds of alpha releases of CPython
ngoldbaum Jan 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ jobs:
"3.13t",
"3.14",
"3.14t",
"3.15-dev",
"3.15t-dev",
"pypy3.11",
"graalpy25.0",
]
Expand Down
1 change: 1 addition & 0 deletions newsfragments/5518-packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add 3.15 to CI for preliminary testing
16 changes: 11 additions & 5 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ def _supported_interpreter_versions(


PY_VERSIONS = _supported_interpreter_versions("cpython")
# We don't yet support abi3-py315 but do support cp315 and cp315t
# version-specific builds
ABI3_PY_VERSIONS = [p for p in PY_VERSIONS if not p.endswith("t")]
ABI3_PY_VERSIONS.remove("3.15")
PYPY_VERSIONS = _supported_interpreter_versions("pypy")


Expand Down Expand Up @@ -959,14 +963,16 @@ def test_version_limits(session: nox.Session):
config_file.set("CPython", "3.6")
_run_cargo(session, "check", env=env, expect_error=True)

assert "3.15" not in PY_VERSIONS
config_file.set("CPython", "3.15")
assert "3.16" not in PY_VERSIONS
config_file.set("CPython", "3.16")
_run_cargo(session, "check", env=env, expect_error=True)

# 3.15 CPython should build if abi3 is explicitly requested
# 3.16 CPython should build if abi3 is explicitly requested
_run_cargo(session, "check", "--features=pyo3/abi3", env=env)

# 3.15 CPython should build with forward compatibility
# TODO: check on 3.16 when adding abi3-py315 support
config_file.set("CPython", "3.15")
env["PYO3_USE_ABI3_FORWARD_COMPATIBILITY"] = "1"
_run_cargo(session, "check", env=env)

Expand All @@ -976,7 +982,7 @@ def test_version_limits(session: nox.Session):

# attempt to build with latest version and check that abi3 version
# configured matches the feature
max_minor_version = max(int(v.split(".")[1]) for v in PY_VERSIONS if "t" not in v)
max_minor_version = max(int(v.split(".")[1]) for v in ABI3_PY_VERSIONS)
with tempfile.TemporaryFile() as stderr:
env = os.environ.copy()
env["PYO3_PRINT_CONFIG"] = "1" # get diagnostics from the build
Expand Down Expand Up @@ -1010,7 +1016,7 @@ def check_feature_powerset(session: nox.Session):

# free-threaded builds do not support ABI3 (yet)
EXPECTED_ABI3_FEATURES = {
f"abi3-py3{ver.split('.')[1]}" for ver in PY_VERSIONS if not ver.endswith("t")
f"abi3-py3{ver.split('.')[1]}" for ver in ABI3_PY_VERSIONS
}

EXCLUDED_FROM_FULL = {
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ workspace = true

[package.metadata.cpython]
min-version = "3.7"
max-version = "3.14" # inclusive
max-version = "3.15" # inclusive

[package.metadata.pypy]
min-version = "3.11"
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
min: PythonVersion { major: 3, minor: 7 },
max: PythonVersion {
major: 3,
minor: 14,
minor: 15,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be worth briefly discussing what the alternatives to bumping this number are. Could we make a rule that max + 1 will always build and run with warnings (maybe with an env var to turn off the warnings)?

My worry is that packages built against PyO3 0.28 might not be using 3.15 yet, but in the future when 3.15's ABI has both changed and finalised, users might install packages built with PyO3 0.28 on 3.15.0, get no warnings, and then get an instant crash.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make a rule that max + 1 will always build and run with warnings (maybe with an env var to turn off the warnings)?

Let me see how painful this is to set up. Seems sensible.

By "build with warnings" are you talking about the same kind of warning we already generate when someone tries to build a stable ABI extension on the free-threaded build?

},
};

Expand Down
2 changes: 2 additions & 0 deletions pyo3-ffi/src/bytearrayobject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ pub struct PyByteArrayObject {
pub ob_exports: Py_ssize_t,
#[cfg(not(Py_3_9))]
pub ob_exports: c_int,
#[cfg(Py_3_15)]
pub ob_bytes_object: *mut PyObject,
}

#[cfg(any(PyPy, GraalPy, Py_LIMITED_API))]
Expand Down
24 changes: 22 additions & 2 deletions pyo3-ffi/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ pub struct PyObjectObFlagsAndRefcnt {
pub ob_flags: u16,
}

// 4-byte alignment comes from value of _PyObject_MIN_ALIGNMENT

#[cfg(all(not(Py_GIL_DISABLED), Py_3_15))]
#[repr(C, align(4))]
#[derive(Copy, Clone)]
struct Aligner(c_char);

#[repr(C)]
#[derive(Copy, Clone)]
#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))]
Expand All @@ -62,6 +69,8 @@ pub union PyObjectObRefcnt {
pub ob_refcnt: Py_ssize_t,
#[cfg(all(target_pointer_width = "64", not(Py_3_14)))]
pub ob_refcnt_split: [crate::PY_UINT32_T; 2],
#[cfg(all(not(Py_GIL_DISABLED), Py_3_15))]
_aligner: Aligner,
Comment thread
ngoldbaum marked this conversation as resolved.
}

#[cfg(all(Py_3_12, not(Py_GIL_DISABLED)))]
Expand All @@ -74,10 +83,17 @@ impl std::fmt::Debug for PyObjectObRefcnt {
#[cfg(all(not(Py_3_12), not(Py_GIL_DISABLED)))]
pub type PyObjectObRefcnt = Py_ssize_t;

const _PyObject_MIN_ALIGNMENT: usize = 4;

// PyObject_HEAD_INIT comes before the PyObject definition in object.h
// but we put it after PyObject because HEAD_INIT uses PyObject

#[repr(C)]
// repr(align(4)) corresponds to the use of _Py_ALIGNED_DEF in object.h. It is
// not currently possible to use constant variables with repr(align()), see
// https://github.com/rust-lang/rust/issues/52840

#[cfg_attr(not(all(Py_3_15, Py_GIL_DISABLED)), repr(C))]
#[cfg_attr(all(Py_3_15, Py_GIL_DISABLED), repr(C, align(4)))]
Comment thread
ngoldbaum marked this conversation as resolved.
#[derive(Debug)]
pub struct PyObject {
#[cfg(py_sys_config = "Py_TRACE_REFS")]
Expand Down Expand Up @@ -105,6 +121,8 @@ pub struct PyObject {
pub ob_type: *mut PyTypeObject,
}

const _: () = assert!(std::mem::align_of::<PyObject>() >= _PyObject_MIN_ALIGNMENT);

#[allow(
clippy::declare_interior_mutable_const,
reason = "contains atomic refcount on free-threaded builds"
Expand All @@ -116,7 +134,9 @@ pub const PyObject_HEAD_INIT: PyObject = PyObject {
_ob_prev: std::ptr::null_mut(),
#[cfg(Py_GIL_DISABLED)]
ob_tid: 0,
#[cfg(all(Py_GIL_DISABLED, Py_3_14))]
#[cfg(all(Py_GIL_DISABLED, Py_3_15))]
ob_flags: refcount::_Py_STATICALLY_ALLOCATED_FLAG as u16,
#[cfg(all(Py_GIL_DISABLED, all(Py_3_14, not(Py_3_15))))]
ob_flags: 0,
#[cfg(all(Py_GIL_DISABLED, not(Py_3_14)))]
_padding: 0,
Expand Down
4 changes: 3 additions & 1 deletion pyo3-ffi/src/refcount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ use std::ptr;
#[cfg(Py_GIL_DISABLED)]
use std::sync::atomic::Ordering::Relaxed;

#[cfg(Py_3_14)]
#[cfg(all(Py_3_14, not(Py_3_15)))]
const _Py_STATICALLY_ALLOCATED_FLAG: c_int = 1 << 7;
#[cfg(Py_3_15)]
pub(crate) const _Py_STATICALLY_ALLOCATED_FLAG: c_int = 1 << 2;

#[cfg(all(Py_3_12, not(Py_3_14)))]
const _Py_IMMORTAL_REFCNT: Py_ssize_t = {
Expand Down
Loading