Skip to content

ci: start testing on 3.13-dev #4184

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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
Loading