Skip to content

Commit

Permalink
ci: start testing on 3.13-dev (#4184)
Browse files Browse the repository at this point in the history
* ci: start testing on 3.13-dev

* ffi fixes for 3.13 beta 1

* support 3.13

* move gevent to be binary-only

* adjust for div_ceil

* fixup pytests
  • Loading branch information
davidhewitt authored May 25, 2024
1 parent d21045c commit 388d176
Show file tree
Hide file tree
Showing 17 changed files with 302 additions and 106 deletions.
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ jobs:
"3.10",
"3.11",
"3.12",
"3.13-dev",
"pypy3.7",
"pypy3.8",
"pypy3.9",
Expand Down
1 change: 1 addition & 0 deletions newsfragments/4184.packaging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support Python 3.13.
12 changes: 7 additions & 5 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src"
PYO3_GUIDE_TARGET = PYO3_TARGET / "guide"
PYO3_DOCS_TARGET = PYO3_TARGET / "doc"
PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12")
PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13")
PYPY_VERSIONS = ("3.7", "3.8", "3.9", "3.10")


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

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

# 3.13 CPython should build with forward compatibility
# 3.14 CPython should build with forward compatibility
env["PYO3_USE_ABI3_FORWARD_COMPATIBILITY"] = "1"
_run_cargo(session, "check", env=env)

Expand Down Expand Up @@ -734,7 +734,9 @@ def update_ui_tests(session: nox.Session):

def _build_docs_for_ffi_check(session: nox.Session) -> None:
# pyo3-ffi-check needs to scrape docs of pyo3-ffi
_run_cargo(session, "doc", _FFI_CHECK, "-p", "pyo3-ffi", "--no-deps")
env = os.environ.copy()
env["PYO3_PYTHON"] = sys.executable
_run_cargo(session, "doc", _FFI_CHECK, "-p", "pyo3-ffi", "--no-deps", env=env)


@lru_cache()
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
min: PythonVersion { major: 3, minor: 7 },
max: PythonVersion {
major: 3,
minor: 12,
minor: 13,
},
};

Expand Down
11 changes: 10 additions & 1 deletion pyo3-ffi/src/cpython/code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ pub const _PY_MONITORING_EVENTS: usize = 17;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct _Py_LocalMonitors {
pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS],
pub tools: [u8; if cfg!(Py_3_13) {
_PY_MONITORING_LOCAL_EVENTS
} else {
_PY_MONITORING_UNGROUPED_EVENTS
}],
}

#[cfg(Py_3_12)]
Expand Down Expand Up @@ -102,6 +106,9 @@ pub struct PyCodeObject {
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)]
#[derive(Copy, Clone)]
Expand Down Expand Up @@ -176,6 +183,8 @@ pub struct PyCodeObject {
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)]
Expand Down
4 changes: 2 additions & 2 deletions pyo3-ffi/src/cpython/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub struct PyCompilerFlags {

// skipped non-limited _PyCompilerFlags_INIT

#[cfg(all(Py_3_12, not(any(PyPy, GraalPy))))]
#[cfg(all(Py_3_12, not(any(Py_3_13, PyPy, GraalPy))))]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct _PyCompilerSrcLocation {
Expand All @@ -42,7 +42,7 @@ pub struct _PyCompilerSrcLocation {

// skipped SRC_LOCATION_FROM_AST

#[cfg(not(any(PyPy, GraalPy)))]
#[cfg(not(any(PyPy, GraalPy, Py_3_13)))]
#[repr(C)]
#[derive(Copy, Clone)]
pub struct PyFutureFeatures {
Expand Down
2 changes: 1 addition & 1 deletion pyo3-ffi/src/cpython/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ pub struct _frozen {
pub size: c_int,
#[cfg(Py_3_11)]
pub is_package: c_int,
#[cfg(Py_3_11)]
#[cfg(all(Py_3_11, not(Py_3_13)))]
pub get_code: Option<unsafe extern "C" fn() -> *mut PyObject>,
}

Expand Down
4 changes: 4 additions & 0 deletions pyo3-ffi/src/cpython/initconfig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ pub struct PyConfig {
pub safe_path: c_int,
#[cfg(Py_3_12)]
pub int_max_str_digits: c_int,
#[cfg(Py_3_13)]
pub cpu_count: c_int,
pub pathconfig_warnings: c_int,
#[cfg(Py_3_10)]
pub program_name: *mut wchar_t,
Expand All @@ -165,6 +167,8 @@ pub struct PyConfig {
pub run_command: *mut wchar_t,
pub run_module: *mut wchar_t,
pub run_filename: *mut wchar_t,
#[cfg(Py_3_13)]
pub sys_path_0: *mut wchar_t,
pub _install_importlib: c_int,
pub _init_main: c_int,
#[cfg(all(Py_3_9, not(Py_3_12)))]
Expand Down
74 changes: 74 additions & 0 deletions pyo3-ffi/src/cpython/longobject.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use crate::longobject::*;
use crate::object::*;
#[cfg(Py_3_13)]
use crate::pyport::Py_ssize_t;
use libc::size_t;
#[cfg(Py_3_13)]
use std::os::raw::c_void;
use std::os::raw::{c_int, c_uchar};

#[cfg(Py_3_13)]
extern "C" {
pub fn PyLong_FromUnicodeObject(u: *mut PyObject, base: c_int) -> *mut PyObject;
}

#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_DEFAULTS: c_int = -1;
#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_BIG_ENDIAN: c_int = 0;
#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_LITTLE_ENDIAN: c_int = 1;
#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_NATIVE_ENDIAN: c_int = 3;
#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_UNSIGNED_BUFFER: c_int = 4;
#[cfg(Py_3_13)]
pub const Py_ASNATIVEBYTES_REJECT_NEGATIVE: c_int = 8;

extern "C" {
// skipped _PyLong_Sign

#[cfg(Py_3_13)]
pub fn PyLong_AsNativeBytes(
v: *mut PyObject,
buffer: *mut c_void,
n_bytes: Py_ssize_t,
flags: c_int,
) -> Py_ssize_t;

#[cfg(Py_3_13)]
pub fn PyLong_FromNativeBytes(
buffer: *const c_void,
n_bytes: size_t,
flags: c_int,
) -> *mut PyObject;

#[cfg(Py_3_13)]
pub fn PyLong_FromUnsignedNativeBytes(
buffer: *const c_void,
n_bytes: size_t,
flags: c_int,
) -> *mut PyObject;

// skipped PyUnstable_Long_IsCompact
// skipped PyUnstable_Long_CompactValue

#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
pub fn _PyLong_FromByteArray(
bytes: *const c_uchar,
n: size_t,
little_endian: c_int,
is_signed: c_int,
) -> *mut PyObject;

#[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,
) -> c_int;

// skipped _PyLong_GCD
}
2 changes: 2 additions & 0 deletions pyo3-ffi/src/cpython/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub(crate) mod import;
pub(crate) mod initconfig;
// skipped interpreteridobject.h
pub(crate) mod listobject;
pub(crate) mod longobject;
#[cfg(all(Py_3_9, not(PyPy)))]
pub(crate) mod methodobject;
pub(crate) mod object;
Expand Down Expand Up @@ -53,6 +54,7 @@ pub use self::import::*;
#[cfg(all(Py_3_8, not(PyPy)))]
pub use self::initconfig::*;
pub use self::listobject::*;
pub use self::longobject::*;
#[cfg(all(Py_3_9, not(PyPy)))]
pub use self::methodobject::*;
pub use self::object::*;
Expand Down
2 changes: 2 additions & 0 deletions pyo3-ffi/src/cpython/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ pub struct _specialization_cache {
pub getitem: *mut PyObject,
#[cfg(Py_3_12)]
pub getitem_version: u32,
#[cfg(Py_3_13)]
pub init: *mut PyObject,
}

#[repr(C)]
Expand Down
27 changes: 1 addition & 26 deletions pyo3-ffi/src/longobject.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use crate::object::*;
use crate::pyport::Py_ssize_t;
use libc::size_t;
#[cfg(not(Py_LIMITED_API))]
use std::os::raw::c_uchar;
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;

Expand Down Expand Up @@ -90,34 +88,12 @@ extern "C" {
arg3: c_int,
) -> *mut PyObject;
}
// skipped non-limited PyLong_FromUnicodeObject
// skipped non-limited _PyLong_FromBytes

#[cfg(not(Py_LIMITED_API))]
extern "C" {
// skipped non-limited _PyLong_Sign

#[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")]
#[cfg(not(Py_3_13))]
pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t;

// skipped _PyLong_DivmodNear

#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
pub fn _PyLong_FromByteArray(
bytes: *const c_uchar,
n: size_t,
little_endian: c_int,
is_signed: c_int,
) -> *mut PyObject;

#[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,
) -> c_int;
}

// skipped non-limited _PyLong_Format
Expand All @@ -130,6 +106,5 @@ extern "C" {
pub fn PyOS_strtol(arg1: *const c_char, arg2: *mut *mut c_char, arg3: c_int) -> c_long;
}

// skipped non-limited _PyLong_GCD
// skipped non-limited _PyLong_Rshift
// skipped non-limited _PyLong_Lshift
15 changes: 10 additions & 5 deletions pytests/noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
def test(session: nox.Session):
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
session.run_always("python", "-m", "pip", "install", "-v", ".[dev]")
try:
session.install("--only-binary=numpy", "numpy>=1.16")
except CommandFailed:
# No binary wheel for numpy available on this platform
pass

def try_install_binary(package: str, constraint: str):
try:
session.install(f"--only-binary={package}", f"{package}{constraint}")
except CommandFailed:
# No binary wheel available on this platform
pass

try_install_binary("numpy", ">=1.16")
try_install_binary("gevent", ">=22.10.2")
ignored_paths = []
if sys.version_info < (3, 10):
# Match syntax is only available in Python >= 3.10
Expand Down
1 change: 0 additions & 1 deletion pytests/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ classifiers = [

[project.optional-dependencies]
dev = [
"gevent>=22.10.2; implementation_name == 'cpython'",
"hypothesis>=3.55",
"pytest-asyncio>=0.21",
"pytest-benchmark>=3.4",
Expand Down
17 changes: 9 additions & 8 deletions pytests/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
import pyo3_pytests.misc
import pytest

if sys.version_info >= (3, 13):
subinterpreters = pytest.importorskip("subinterpreters")
else:
subinterpreters = pytest.importorskip("_xxsubinterpreters")


def test_issue_219():
# Should not deadlock
Expand All @@ -31,23 +36,19 @@ def test_multiple_imports_same_interpreter_ok():
reason="PyPy and GraalPy do not support subinterpreters",
)
def test_import_in_subinterpreter_forbidden():
import _xxsubinterpreters

if sys.version_info < (3, 12):
expected_error = "PyO3 modules do not yet support subinterpreters, see https://github.com/PyO3/pyo3/issues/576"
else:
expected_error = "module pyo3_pytests.pyo3_pytests does not support loading in subinterpreters"

sub_interpreter = _xxsubinterpreters.create()
sub_interpreter = subinterpreters.create()
with pytest.raises(
_xxsubinterpreters.RunFailedError,
subinterpreters.RunFailedError,
match=expected_error,
):
_xxsubinterpreters.run_string(
sub_interpreter, "import pyo3_pytests.pyo3_pytests"
)
subinterpreters.run_string(sub_interpreter, "import pyo3_pytests.pyo3_pytests")

_xxsubinterpreters.destroy(sub_interpreter)
subinterpreters.destroy(sub_interpreter)


def test_type_full_name_includes_module():
Expand Down
Loading

0 comments on commit 388d176

Please sign in to comment.