Skip to content

Commit 388d176

Browse files
authored
ci: start testing on 3.13-dev (#4184)
* 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
1 parent d21045c commit 388d176

File tree

17 files changed

+302
-106
lines changed

17 files changed

+302
-106
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ jobs:
224224
"3.10",
225225
"3.11",
226226
"3.12",
227+
"3.13-dev",
227228
"pypy3.7",
228229
"pypy3.8",
229230
"pypy3.9",

newsfragments/4184.packaging.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support Python 3.13.

noxfile.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src"
3131
PYO3_GUIDE_TARGET = PYO3_TARGET / "guide"
3232
PYO3_DOCS_TARGET = PYO3_TARGET / "doc"
33-
PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12")
33+
PY_VERSIONS = ("3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13")
3434
PYPY_VERSIONS = ("3.7", "3.8", "3.9", "3.10")
3535

3636

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

634-
assert "3.13" not in PY_VERSIONS
635-
config_file.set("CPython", "3.13")
634+
assert "3.14" not in PY_VERSIONS
635+
config_file.set("CPython", "3.14")
636636
_run_cargo(session, "check", env=env, expect_error=True)
637637

638-
# 3.13 CPython should build with forward compatibility
638+
# 3.14 CPython should build with forward compatibility
639639
env["PYO3_USE_ABI3_FORWARD_COMPATIBILITY"] = "1"
640640
_run_cargo(session, "check", env=env)
641641

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

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

739741

740742
@lru_cache()

pyo3-ffi/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
1717
min: PythonVersion { major: 3, minor: 7 },
1818
max: PythonVersion {
1919
major: 3,
20-
minor: 12,
20+
minor: 13,
2121
},
2222
};
2323

pyo3-ffi/src/cpython/code.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ pub const _PY_MONITORING_EVENTS: usize = 17;
2020
#[repr(C)]
2121
#[derive(Clone, Copy)]
2222
pub struct _Py_LocalMonitors {
23-
pub tools: [u8; _PY_MONITORING_UNGROUPED_EVENTS],
23+
pub tools: [u8; if cfg!(Py_3_13) {
24+
_PY_MONITORING_LOCAL_EVENTS
25+
} else {
26+
_PY_MONITORING_UNGROUPED_EVENTS
27+
}],
2428
}
2529

2630
#[cfg(Py_3_12)]
@@ -102,6 +106,9 @@ pub struct PyCodeObject {
102106
pub co_extra: *mut c_void,
103107
}
104108

109+
#[cfg(Py_3_13)]
110+
opaque_struct!(_PyExecutorArray);
111+
105112
#[cfg(all(not(any(PyPy, GraalPy)), Py_3_8, not(Py_3_11)))]
106113
#[repr(C)]
107114
#[derive(Copy, Clone)]
@@ -176,6 +183,8 @@ pub struct PyCodeObject {
176183
pub _co_code: *mut PyObject,
177184
#[cfg(not(Py_3_12))]
178185
pub _co_linearray: *mut c_char,
186+
#[cfg(Py_3_13)]
187+
pub co_executors: *mut _PyExecutorArray,
179188
#[cfg(Py_3_12)]
180189
pub _co_cached: *mut _PyCoCached,
181190
#[cfg(Py_3_12)]

pyo3-ffi/src/cpython/compile.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct PyCompilerFlags {
3030

3131
// skipped non-limited _PyCompilerFlags_INIT
3232

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

4343
// skipped SRC_LOCATION_FROM_AST
4444

45-
#[cfg(not(any(PyPy, GraalPy)))]
45+
#[cfg(not(any(PyPy, GraalPy, Py_3_13)))]
4646
#[repr(C)]
4747
#[derive(Copy, Clone)]
4848
pub struct PyFutureFeatures {

pyo3-ffi/src/cpython/import.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub struct _frozen {
5757
pub size: c_int,
5858
#[cfg(Py_3_11)]
5959
pub is_package: c_int,
60-
#[cfg(Py_3_11)]
60+
#[cfg(all(Py_3_11, not(Py_3_13)))]
6161
pub get_code: Option<unsafe extern "C" fn() -> *mut PyObject>,
6262
}
6363

pyo3-ffi/src/cpython/initconfig.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ pub struct PyConfig {
141141
pub safe_path: c_int,
142142
#[cfg(Py_3_12)]
143143
pub int_max_str_digits: c_int,
144+
#[cfg(Py_3_13)]
145+
pub cpu_count: c_int,
144146
pub pathconfig_warnings: c_int,
145147
#[cfg(Py_3_10)]
146148
pub program_name: *mut wchar_t,
@@ -165,6 +167,8 @@ pub struct PyConfig {
165167
pub run_command: *mut wchar_t,
166168
pub run_module: *mut wchar_t,
167169
pub run_filename: *mut wchar_t,
170+
#[cfg(Py_3_13)]
171+
pub sys_path_0: *mut wchar_t,
168172
pub _install_importlib: c_int,
169173
pub _init_main: c_int,
170174
#[cfg(all(Py_3_9, not(Py_3_12)))]

pyo3-ffi/src/cpython/longobject.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use crate::longobject::*;
2+
use crate::object::*;
3+
#[cfg(Py_3_13)]
4+
use crate::pyport::Py_ssize_t;
5+
use libc::size_t;
6+
#[cfg(Py_3_13)]
7+
use std::os::raw::c_void;
8+
use std::os::raw::{c_int, c_uchar};
9+
10+
#[cfg(Py_3_13)]
11+
extern "C" {
12+
pub fn PyLong_FromUnicodeObject(u: *mut PyObject, base: c_int) -> *mut PyObject;
13+
}
14+
15+
#[cfg(Py_3_13)]
16+
pub const Py_ASNATIVEBYTES_DEFAULTS: c_int = -1;
17+
#[cfg(Py_3_13)]
18+
pub const Py_ASNATIVEBYTES_BIG_ENDIAN: c_int = 0;
19+
#[cfg(Py_3_13)]
20+
pub const Py_ASNATIVEBYTES_LITTLE_ENDIAN: c_int = 1;
21+
#[cfg(Py_3_13)]
22+
pub const Py_ASNATIVEBYTES_NATIVE_ENDIAN: c_int = 3;
23+
#[cfg(Py_3_13)]
24+
pub const Py_ASNATIVEBYTES_UNSIGNED_BUFFER: c_int = 4;
25+
#[cfg(Py_3_13)]
26+
pub const Py_ASNATIVEBYTES_REJECT_NEGATIVE: c_int = 8;
27+
28+
extern "C" {
29+
// skipped _PyLong_Sign
30+
31+
#[cfg(Py_3_13)]
32+
pub fn PyLong_AsNativeBytes(
33+
v: *mut PyObject,
34+
buffer: *mut c_void,
35+
n_bytes: Py_ssize_t,
36+
flags: c_int,
37+
) -> Py_ssize_t;
38+
39+
#[cfg(Py_3_13)]
40+
pub fn PyLong_FromNativeBytes(
41+
buffer: *const c_void,
42+
n_bytes: size_t,
43+
flags: c_int,
44+
) -> *mut PyObject;
45+
46+
#[cfg(Py_3_13)]
47+
pub fn PyLong_FromUnsignedNativeBytes(
48+
buffer: *const c_void,
49+
n_bytes: size_t,
50+
flags: c_int,
51+
) -> *mut PyObject;
52+
53+
// skipped PyUnstable_Long_IsCompact
54+
// skipped PyUnstable_Long_CompactValue
55+
56+
#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
57+
pub fn _PyLong_FromByteArray(
58+
bytes: *const c_uchar,
59+
n: size_t,
60+
little_endian: c_int,
61+
is_signed: c_int,
62+
) -> *mut PyObject;
63+
64+
#[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")]
65+
pub fn _PyLong_AsByteArray(
66+
v: *mut PyLongObject,
67+
bytes: *mut c_uchar,
68+
n: size_t,
69+
little_endian: c_int,
70+
is_signed: c_int,
71+
) -> c_int;
72+
73+
// skipped _PyLong_GCD
74+
}

pyo3-ffi/src/cpython/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(crate) mod import;
1818
pub(crate) mod initconfig;
1919
// skipped interpreteridobject.h
2020
pub(crate) mod listobject;
21+
pub(crate) mod longobject;
2122
#[cfg(all(Py_3_9, not(PyPy)))]
2223
pub(crate) mod methodobject;
2324
pub(crate) mod object;
@@ -53,6 +54,7 @@ pub use self::import::*;
5354
#[cfg(all(Py_3_8, not(PyPy)))]
5455
pub use self::initconfig::*;
5556
pub use self::listobject::*;
57+
pub use self::longobject::*;
5658
#[cfg(all(Py_3_9, not(PyPy)))]
5759
pub use self::methodobject::*;
5860
pub use self::object::*;

pyo3-ffi/src/cpython/object.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ pub struct _specialization_cache {
296296
pub getitem: *mut PyObject,
297297
#[cfg(Py_3_12)]
298298
pub getitem_version: u32,
299+
#[cfg(Py_3_13)]
300+
pub init: *mut PyObject,
299301
}
300302

301303
#[repr(C)]

pyo3-ffi/src/longobject.rs

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::object::*;
22
use crate::pyport::Py_ssize_t;
33
use libc::size_t;
4-
#[cfg(not(Py_LIMITED_API))]
5-
use std::os::raw::c_uchar;
64
use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
75
use std::ptr::addr_of_mut;
86

@@ -90,34 +88,12 @@ extern "C" {
9088
arg3: c_int,
9189
) -> *mut PyObject;
9290
}
93-
// skipped non-limited PyLong_FromUnicodeObject
94-
// skipped non-limited _PyLong_FromBytes
9591

9692
#[cfg(not(Py_LIMITED_API))]
9793
extern "C" {
98-
// skipped non-limited _PyLong_Sign
99-
10094
#[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")]
95+
#[cfg(not(Py_3_13))]
10196
pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t;
102-
103-
// skipped _PyLong_DivmodNear
104-
105-
#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
106-
pub fn _PyLong_FromByteArray(
107-
bytes: *const c_uchar,
108-
n: size_t,
109-
little_endian: c_int,
110-
is_signed: c_int,
111-
) -> *mut PyObject;
112-
113-
#[cfg_attr(PyPy, link_name = "_PyPyLong_AsByteArrayO")]
114-
pub fn _PyLong_AsByteArray(
115-
v: *mut PyLongObject,
116-
bytes: *mut c_uchar,
117-
n: size_t,
118-
little_endian: c_int,
119-
is_signed: c_int,
120-
) -> c_int;
12197
}
12298

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

133-
// skipped non-limited _PyLong_GCD
134109
// skipped non-limited _PyLong_Rshift
135110
// skipped non-limited _PyLong_Lshift

pytests/noxfile.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@
99
def test(session: nox.Session):
1010
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
1111
session.run_always("python", "-m", "pip", "install", "-v", ".[dev]")
12-
try:
13-
session.install("--only-binary=numpy", "numpy>=1.16")
14-
except CommandFailed:
15-
# No binary wheel for numpy available on this platform
16-
pass
12+
13+
def try_install_binary(package: str, constraint: str):
14+
try:
15+
session.install(f"--only-binary={package}", f"{package}{constraint}")
16+
except CommandFailed:
17+
# No binary wheel available on this platform
18+
pass
19+
20+
try_install_binary("numpy", ">=1.16")
21+
try_install_binary("gevent", ">=22.10.2")
1722
ignored_paths = []
1823
if sys.version_info < (3, 10):
1924
# Match syntax is only available in Python >= 3.10

pytests/pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ classifiers = [
2020

2121
[project.optional-dependencies]
2222
dev = [
23-
"gevent>=22.10.2; implementation_name == 'cpython'",
2423
"hypothesis>=3.55",
2524
"pytest-asyncio>=0.21",
2625
"pytest-benchmark>=3.4",

pytests/tests/test_misc.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
import pyo3_pytests.misc
66
import pytest
77

8+
if sys.version_info >= (3, 13):
9+
subinterpreters = pytest.importorskip("subinterpreters")
10+
else:
11+
subinterpreters = pytest.importorskip("_xxsubinterpreters")
12+
813

914
def test_issue_219():
1015
# Should not deadlock
@@ -31,23 +36,19 @@ def test_multiple_imports_same_interpreter_ok():
3136
reason="PyPy and GraalPy do not support subinterpreters",
3237
)
3338
def test_import_in_subinterpreter_forbidden():
34-
import _xxsubinterpreters
35-
3639
if sys.version_info < (3, 12):
3740
expected_error = "PyO3 modules do not yet support subinterpreters, see https://github.com/PyO3/pyo3/issues/576"
3841
else:
3942
expected_error = "module pyo3_pytests.pyo3_pytests does not support loading in subinterpreters"
4043

41-
sub_interpreter = _xxsubinterpreters.create()
44+
sub_interpreter = subinterpreters.create()
4245
with pytest.raises(
43-
_xxsubinterpreters.RunFailedError,
46+
subinterpreters.RunFailedError,
4447
match=expected_error,
4548
):
46-
_xxsubinterpreters.run_string(
47-
sub_interpreter, "import pyo3_pytests.pyo3_pytests"
48-
)
49+
subinterpreters.run_string(sub_interpreter, "import pyo3_pytests.pyo3_pytests")
4950

50-
_xxsubinterpreters.destroy(sub_interpreter)
51+
subinterpreters.destroy(sub_interpreter)
5152

5253

5354
def test_type_full_name_includes_module():

0 commit comments

Comments
 (0)