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
This commit is contained in:
parent
d21045cbc1
commit
388d1760b5
|
@ -224,6 +224,7 @@ jobs:
|
||||||
"3.10",
|
"3.10",
|
||||||
"3.11",
|
"3.11",
|
||||||
"3.12",
|
"3.12",
|
||||||
|
"3.13-dev",
|
||||||
"pypy3.7",
|
"pypy3.7",
|
||||||
"pypy3.8",
|
"pypy3.8",
|
||||||
"pypy3.9",
|
"pypy3.9",
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Support Python 3.13.
|
12
noxfile.py
12
noxfile.py
|
@ -30,7 +30,7 @@ PYO3_TARGET = Path(os.environ.get("CARGO_TARGET_DIR", PYO3_DIR / "target")).abso
|
||||||
PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src"
|
PYO3_GUIDE_SRC = PYO3_DIR / "guide" / "src"
|
||||||
PYO3_GUIDE_TARGET = PYO3_TARGET / "guide"
|
PYO3_GUIDE_TARGET = PYO3_TARGET / "guide"
|
||||||
PYO3_DOCS_TARGET = PYO3_TARGET / "doc"
|
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")
|
PYPY_VERSIONS = ("3.7", "3.8", "3.9", "3.10")
|
||||||
|
|
||||||
|
|
||||||
|
@ -631,11 +631,11 @@ def test_version_limits(session: nox.Session):
|
||||||
config_file.set("CPython", "3.6")
|
config_file.set("CPython", "3.6")
|
||||||
_run_cargo(session, "check", env=env, expect_error=True)
|
_run_cargo(session, "check", env=env, expect_error=True)
|
||||||
|
|
||||||
assert "3.13" not in PY_VERSIONS
|
assert "3.14" not in PY_VERSIONS
|
||||||
config_file.set("CPython", "3.13")
|
config_file.set("CPython", "3.14")
|
||||||
_run_cargo(session, "check", env=env, expect_error=True)
|
_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"
|
env["PYO3_USE_ABI3_FORWARD_COMPATIBILITY"] = "1"
|
||||||
_run_cargo(session, "check", env=env)
|
_run_cargo(session, "check", env=env)
|
||||||
|
|
||||||
|
@ -734,7 +734,9 @@ def update_ui_tests(session: nox.Session):
|
||||||
|
|
||||||
def _build_docs_for_ffi_check(session: nox.Session) -> None:
|
def _build_docs_for_ffi_check(session: nox.Session) -> None:
|
||||||
# pyo3-ffi-check needs to scrape docs of pyo3-ffi
|
# 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()
|
@lru_cache()
|
||||||
|
|
|
@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
|
||||||
min: PythonVersion { major: 3, minor: 7 },
|
min: PythonVersion { major: 3, minor: 7 },
|
||||||
max: PythonVersion {
|
max: PythonVersion {
|
||||||
major: 3,
|
major: 3,
|
||||||
minor: 12,
|
minor: 13,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,11 @@ pub const _PY_MONITORING_EVENTS: usize = 17;
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct _Py_LocalMonitors {
|
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)]
|
#[cfg(Py_3_12)]
|
||||||
|
@ -102,6 +106,9 @@ pub struct PyCodeObject {
|
||||||
pub co_extra: *mut c_void,
|
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)))]
|
#[cfg(all(not(any(PyPy, GraalPy)), Py_3_8, not(Py_3_11)))]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -176,6 +183,8 @@ pub struct PyCodeObject {
|
||||||
pub _co_code: *mut PyObject,
|
pub _co_code: *mut PyObject,
|
||||||
#[cfg(not(Py_3_12))]
|
#[cfg(not(Py_3_12))]
|
||||||
pub _co_linearray: *mut c_char,
|
pub _co_linearray: *mut c_char,
|
||||||
|
#[cfg(Py_3_13)]
|
||||||
|
pub co_executors: *mut _PyExecutorArray,
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
pub _co_cached: *mut _PyCoCached,
|
pub _co_cached: *mut _PyCoCached,
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
|
|
|
@ -30,7 +30,7 @@ pub struct PyCompilerFlags {
|
||||||
|
|
||||||
// skipped non-limited _PyCompilerFlags_INIT
|
// 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)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct _PyCompilerSrcLocation {
|
pub struct _PyCompilerSrcLocation {
|
||||||
|
@ -42,7 +42,7 @@ pub struct _PyCompilerSrcLocation {
|
||||||
|
|
||||||
// skipped SRC_LOCATION_FROM_AST
|
// skipped SRC_LOCATION_FROM_AST
|
||||||
|
|
||||||
#[cfg(not(any(PyPy, GraalPy)))]
|
#[cfg(not(any(PyPy, GraalPy, Py_3_13)))]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct PyFutureFeatures {
|
pub struct PyFutureFeatures {
|
||||||
|
|
|
@ -57,7 +57,7 @@ pub struct _frozen {
|
||||||
pub size: c_int,
|
pub size: c_int,
|
||||||
#[cfg(Py_3_11)]
|
#[cfg(Py_3_11)]
|
||||||
pub is_package: c_int,
|
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>,
|
pub get_code: Option<unsafe extern "C" fn() -> *mut PyObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,8 @@ pub struct PyConfig {
|
||||||
pub safe_path: c_int,
|
pub safe_path: c_int,
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
pub int_max_str_digits: c_int,
|
pub int_max_str_digits: c_int,
|
||||||
|
#[cfg(Py_3_13)]
|
||||||
|
pub cpu_count: c_int,
|
||||||
pub pathconfig_warnings: c_int,
|
pub pathconfig_warnings: c_int,
|
||||||
#[cfg(Py_3_10)]
|
#[cfg(Py_3_10)]
|
||||||
pub program_name: *mut wchar_t,
|
pub program_name: *mut wchar_t,
|
||||||
|
@ -165,6 +167,8 @@ pub struct PyConfig {
|
||||||
pub run_command: *mut wchar_t,
|
pub run_command: *mut wchar_t,
|
||||||
pub run_module: *mut wchar_t,
|
pub run_module: *mut wchar_t,
|
||||||
pub run_filename: *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 _install_importlib: c_int,
|
||||||
pub _init_main: c_int,
|
pub _init_main: c_int,
|
||||||
#[cfg(all(Py_3_9, not(Py_3_12)))]
|
#[cfg(all(Py_3_9, not(Py_3_12)))]
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ pub(crate) mod import;
|
||||||
pub(crate) mod initconfig;
|
pub(crate) mod initconfig;
|
||||||
// skipped interpreteridobject.h
|
// skipped interpreteridobject.h
|
||||||
pub(crate) mod listobject;
|
pub(crate) mod listobject;
|
||||||
|
pub(crate) mod longobject;
|
||||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||||
pub(crate) mod methodobject;
|
pub(crate) mod methodobject;
|
||||||
pub(crate) mod object;
|
pub(crate) mod object;
|
||||||
|
@ -53,6 +54,7 @@ pub use self::import::*;
|
||||||
#[cfg(all(Py_3_8, not(PyPy)))]
|
#[cfg(all(Py_3_8, not(PyPy)))]
|
||||||
pub use self::initconfig::*;
|
pub use self::initconfig::*;
|
||||||
pub use self::listobject::*;
|
pub use self::listobject::*;
|
||||||
|
pub use self::longobject::*;
|
||||||
#[cfg(all(Py_3_9, not(PyPy)))]
|
#[cfg(all(Py_3_9, not(PyPy)))]
|
||||||
pub use self::methodobject::*;
|
pub use self::methodobject::*;
|
||||||
pub use self::object::*;
|
pub use self::object::*;
|
||||||
|
|
|
@ -296,6 +296,8 @@ pub struct _specialization_cache {
|
||||||
pub getitem: *mut PyObject,
|
pub getitem: *mut PyObject,
|
||||||
#[cfg(Py_3_12)]
|
#[cfg(Py_3_12)]
|
||||||
pub getitem_version: u32,
|
pub getitem_version: u32,
|
||||||
|
#[cfg(Py_3_13)]
|
||||||
|
pub init: *mut PyObject,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use crate::object::*;
|
use crate::object::*;
|
||||||
use crate::pyport::Py_ssize_t;
|
use crate::pyport::Py_ssize_t;
|
||||||
use libc::size_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::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_ulong, c_ulonglong, c_void};
|
||||||
use std::ptr::addr_of_mut;
|
use std::ptr::addr_of_mut;
|
||||||
|
|
||||||
|
@ -90,34 +88,12 @@ extern "C" {
|
||||||
arg3: c_int,
|
arg3: c_int,
|
||||||
) -> *mut PyObject;
|
) -> *mut PyObject;
|
||||||
}
|
}
|
||||||
// skipped non-limited PyLong_FromUnicodeObject
|
|
||||||
// skipped non-limited _PyLong_FromBytes
|
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// skipped non-limited _PyLong_Sign
|
|
||||||
|
|
||||||
#[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")]
|
#[cfg_attr(PyPy, link_name = "_PyPyLong_NumBits")]
|
||||||
|
#[cfg(not(Py_3_13))]
|
||||||
pub fn _PyLong_NumBits(obj: *mut PyObject) -> size_t;
|
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
|
// skipped non-limited _PyLong_Format
|
||||||
|
@ -130,6 +106,5 @@ extern "C" {
|
||||||
pub fn PyOS_strtol(arg1: *const c_char, arg2: *mut *mut c_char, arg3: c_int) -> c_long;
|
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_Rshift
|
||||||
// skipped non-limited _PyLong_Lshift
|
// skipped non-limited _PyLong_Lshift
|
||||||
|
|
|
@ -9,11 +9,16 @@ nox.options.sessions = ["test"]
|
||||||
def test(session: nox.Session):
|
def test(session: nox.Session):
|
||||||
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
|
session.env["MATURIN_PEP517_ARGS"] = "--profile=dev"
|
||||||
session.run_always("python", "-m", "pip", "install", "-v", ".[dev]")
|
session.run_always("python", "-m", "pip", "install", "-v", ".[dev]")
|
||||||
|
|
||||||
|
def try_install_binary(package: str, constraint: str):
|
||||||
try:
|
try:
|
||||||
session.install("--only-binary=numpy", "numpy>=1.16")
|
session.install(f"--only-binary={package}", f"{package}{constraint}")
|
||||||
except CommandFailed:
|
except CommandFailed:
|
||||||
# No binary wheel for numpy available on this platform
|
# No binary wheel available on this platform
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
try_install_binary("numpy", ">=1.16")
|
||||||
|
try_install_binary("gevent", ">=22.10.2")
|
||||||
ignored_paths = []
|
ignored_paths = []
|
||||||
if sys.version_info < (3, 10):
|
if sys.version_info < (3, 10):
|
||||||
# Match syntax is only available in Python >= 3.10
|
# Match syntax is only available in Python >= 3.10
|
||||||
|
|
|
@ -20,7 +20,6 @@ classifiers = [
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
dev = [
|
dev = [
|
||||||
"gevent>=22.10.2; implementation_name == 'cpython'",
|
|
||||||
"hypothesis>=3.55",
|
"hypothesis>=3.55",
|
||||||
"pytest-asyncio>=0.21",
|
"pytest-asyncio>=0.21",
|
||||||
"pytest-benchmark>=3.4",
|
"pytest-benchmark>=3.4",
|
||||||
|
|
|
@ -5,6 +5,11 @@ import sys
|
||||||
import pyo3_pytests.misc
|
import pyo3_pytests.misc
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 13):
|
||||||
|
subinterpreters = pytest.importorskip("subinterpreters")
|
||||||
|
else:
|
||||||
|
subinterpreters = pytest.importorskip("_xxsubinterpreters")
|
||||||
|
|
||||||
|
|
||||||
def test_issue_219():
|
def test_issue_219():
|
||||||
# Should not deadlock
|
# Should not deadlock
|
||||||
|
@ -31,23 +36,19 @@ def test_multiple_imports_same_interpreter_ok():
|
||||||
reason="PyPy and GraalPy do not support subinterpreters",
|
reason="PyPy and GraalPy do not support subinterpreters",
|
||||||
)
|
)
|
||||||
def test_import_in_subinterpreter_forbidden():
|
def test_import_in_subinterpreter_forbidden():
|
||||||
import _xxsubinterpreters
|
|
||||||
|
|
||||||
if sys.version_info < (3, 12):
|
if sys.version_info < (3, 12):
|
||||||
expected_error = "PyO3 modules do not yet support subinterpreters, see https://github.com/PyO3/pyo3/issues/576"
|
expected_error = "PyO3 modules do not yet support subinterpreters, see https://github.com/PyO3/pyo3/issues/576"
|
||||||
else:
|
else:
|
||||||
expected_error = "module pyo3_pytests.pyo3_pytests does not support loading in subinterpreters"
|
expected_error = "module pyo3_pytests.pyo3_pytests does not support loading in subinterpreters"
|
||||||
|
|
||||||
sub_interpreter = _xxsubinterpreters.create()
|
sub_interpreter = subinterpreters.create()
|
||||||
with pytest.raises(
|
with pytest.raises(
|
||||||
_xxsubinterpreters.RunFailedError,
|
subinterpreters.RunFailedError,
|
||||||
match=expected_error,
|
match=expected_error,
|
||||||
):
|
):
|
||||||
_xxsubinterpreters.run_string(
|
subinterpreters.run_string(sub_interpreter, "import pyo3_pytests.pyo3_pytests")
|
||||||
sub_interpreter, "import pyo3_pytests.pyo3_pytests"
|
|
||||||
)
|
|
||||||
|
|
||||||
_xxsubinterpreters.destroy(sub_interpreter)
|
subinterpreters.destroy(sub_interpreter)
|
||||||
|
|
||||||
|
|
||||||
def test_type_full_name_includes_module():
|
def test_type_full_name_includes_module():
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
//! assert n + 1 == value
|
//! assert n + 1 == value
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
use crate::types::{bytes::PyBytesMethods, PyBytes};
|
use crate::types::{bytes::PyBytesMethods, PyBytes};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -63,20 +65,47 @@ use num_bigint::Sign;
|
||||||
|
|
||||||
// for identical functionality between BigInt and BigUint
|
// for identical functionality between BigInt and BigUint
|
||||||
macro_rules! bigint_conversion {
|
macro_rules! bigint_conversion {
|
||||||
($rust_ty: ty, $is_signed: expr, $to_bytes: path) => {
|
($rust_ty: ty, $is_signed: literal, $to_bytes: path) => {
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
||||||
impl ToPyObject for $rust_ty {
|
impl ToPyObject for $rust_ty {
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
let bytes = $to_bytes(self);
|
let bytes = $to_bytes(self);
|
||||||
|
#[cfg(not(Py_3_13))]
|
||||||
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let obj = ffi::_PyLong_FromByteArray(
|
ffi::_PyLong_FromByteArray(
|
||||||
bytes.as_ptr().cast(),
|
bytes.as_ptr().cast(),
|
||||||
bytes.len(),
|
bytes.len(),
|
||||||
1,
|
1,
|
||||||
$is_signed,
|
$is_signed.into(),
|
||||||
);
|
)
|
||||||
PyObject::from_owned_ptr(py, obj)
|
.assume_owned(py)
|
||||||
|
.unbind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(Py_3_13)]
|
||||||
|
{
|
||||||
|
if $is_signed {
|
||||||
|
unsafe {
|
||||||
|
ffi::PyLong_FromNativeBytes(
|
||||||
|
bytes.as_ptr().cast(),
|
||||||
|
bytes.len(),
|
||||||
|
ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN,
|
||||||
|
)
|
||||||
|
.assume_owned(py)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
ffi::PyLong_FromUnsignedNativeBytes(
|
||||||
|
bytes.as_ptr().cast(),
|
||||||
|
bytes.len(),
|
||||||
|
ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN,
|
||||||
|
)
|
||||||
|
.assume_owned(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unbind()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +113,7 @@ macro_rules! bigint_conversion {
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
let bytes = $to_bytes(self);
|
let bytes = $to_bytes(self);
|
||||||
let bytes_obj = PyBytes::new_bound(py, &bytes);
|
let bytes_obj = PyBytes::new_bound(py, &bytes);
|
||||||
let kwargs = if $is_signed > 0 {
|
let kwargs = if $is_signed {
|
||||||
let kwargs = crate::types::PyDict::new_bound(py);
|
let kwargs = crate::types::PyDict::new_bound(py);
|
||||||
kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
|
kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
|
||||||
Some(kwargs)
|
Some(kwargs)
|
||||||
|
@ -107,8 +136,8 @@ macro_rules! bigint_conversion {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bigint_conversion!(BigUint, 0, BigUint::to_bytes_le);
|
bigint_conversion!(BigUint, false, BigUint::to_bytes_le);
|
||||||
bigint_conversion!(BigInt, 1, BigInt::to_signed_bytes_le);
|
bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le);
|
||||||
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "num-bigint")))]
|
||||||
impl<'py> FromPyObject<'py> for BigInt {
|
impl<'py> FromPyObject<'py> for BigInt {
|
||||||
|
@ -122,13 +151,9 @@ impl<'py> FromPyObject<'py> for BigInt {
|
||||||
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
||||||
num_owned.bind(py)
|
num_owned.bind(py)
|
||||||
};
|
};
|
||||||
let n_bits = int_n_bits(num)?;
|
|
||||||
if n_bits == 0 {
|
|
||||||
return Ok(BigInt::from(0isize));
|
|
||||||
}
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
let mut buffer = int_to_u32_vec(num, (n_bits + 32) / 32, true)?;
|
let mut buffer = int_to_u32_vec::<true>(num)?;
|
||||||
let sign = if buffer.last().copied().map_or(false, |last| last >> 31 != 0) {
|
let sign = if buffer.last().copied().map_or(false, |last| last >> 31 != 0) {
|
||||||
// BigInt::new takes an unsigned array, so need to convert from two's complement
|
// BigInt::new takes an unsigned array, so need to convert from two's complement
|
||||||
// flip all bits, 'subtract' 1 (by adding one to the unsigned array)
|
// flip all bits, 'subtract' 1 (by adding one to the unsigned array)
|
||||||
|
@ -152,6 +177,10 @@ impl<'py> FromPyObject<'py> for BigInt {
|
||||||
}
|
}
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
{
|
{
|
||||||
|
let n_bits = int_n_bits(num)?;
|
||||||
|
if n_bits == 0 {
|
||||||
|
return Ok(BigInt::from(0isize));
|
||||||
|
}
|
||||||
let bytes = int_to_py_bytes(num, (n_bits + 8) / 8, true)?;
|
let bytes = int_to_py_bytes(num, (n_bits + 8) / 8, true)?;
|
||||||
Ok(BigInt::from_signed_bytes_le(bytes.as_bytes()))
|
Ok(BigInt::from_signed_bytes_le(bytes.as_bytes()))
|
||||||
}
|
}
|
||||||
|
@ -170,31 +199,37 @@ impl<'py> FromPyObject<'py> for BigUint {
|
||||||
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
num_owned = unsafe { Py::from_owned_ptr_or_err(py, ffi::PyNumber_Index(ob.as_ptr()))? };
|
||||||
num_owned.bind(py)
|
num_owned.bind(py)
|
||||||
};
|
};
|
||||||
let n_bits = int_n_bits(num)?;
|
|
||||||
if n_bits == 0 {
|
|
||||||
return Ok(BigUint::from(0usize));
|
|
||||||
}
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
{
|
{
|
||||||
let buffer = int_to_u32_vec(num, (n_bits + 31) / 32, false)?;
|
let buffer = int_to_u32_vec::<false>(num)?;
|
||||||
Ok(BigUint::new(buffer))
|
Ok(BigUint::new(buffer))
|
||||||
}
|
}
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
{
|
{
|
||||||
|
let n_bits = int_n_bits(num)?;
|
||||||
|
if n_bits == 0 {
|
||||||
|
return Ok(BigUint::from(0usize));
|
||||||
|
}
|
||||||
let bytes = int_to_py_bytes(num, (n_bits + 7) / 8, false)?;
|
let bytes = int_to_py_bytes(num, (n_bits + 7) / 8, false)?;
|
||||||
Ok(BigUint::from_bytes_le(bytes.as_bytes()))
|
Ok(BigUint::from_bytes_le(bytes.as_bytes()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(any(Py_LIMITED_API, Py_3_13)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn int_to_u32_vec(
|
fn int_to_u32_vec<const SIGNED: bool>(long: &Bound<'_, PyLong>) -> PyResult<Vec<u32>> {
|
||||||
long: &Bound<'_, PyLong>,
|
let mut buffer = Vec::new();
|
||||||
n_digits: usize,
|
let n_bits = int_n_bits(long)?;
|
||||||
is_signed: bool,
|
if n_bits == 0 {
|
||||||
) -> PyResult<Vec<u32>> {
|
return Ok(buffer);
|
||||||
let mut buffer = Vec::with_capacity(n_digits);
|
}
|
||||||
|
let n_digits = if SIGNED {
|
||||||
|
(n_bits + 32) / 32
|
||||||
|
} else {
|
||||||
|
(n_bits + 31) / 32
|
||||||
|
};
|
||||||
|
buffer.reserve_exact(n_digits);
|
||||||
unsafe {
|
unsafe {
|
||||||
crate::err::error_on_minusone(
|
crate::err::error_on_minusone(
|
||||||
long.py(),
|
long.py(),
|
||||||
|
@ -203,7 +238,7 @@ fn int_to_u32_vec(
|
||||||
buffer.as_mut_ptr() as *mut u8,
|
buffer.as_mut_ptr() as *mut u8,
|
||||||
n_digits * 4,
|
n_digits * 4,
|
||||||
1,
|
1,
|
||||||
is_signed.into(),
|
SIGNED.into(),
|
||||||
),
|
),
|
||||||
)?;
|
)?;
|
||||||
buffer.set_len(n_digits)
|
buffer.set_len(n_digits)
|
||||||
|
@ -215,6 +250,44 @@ fn int_to_u32_vec(
|
||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(Py_LIMITED_API), Py_3_13))]
|
||||||
|
#[inline]
|
||||||
|
fn int_to_u32_vec<const SIGNED: bool>(long: &Bound<'_, PyLong>) -> PyResult<Vec<u32>> {
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
let mut flags = ffi::Py_ASNATIVEBYTES_LITTLE_ENDIAN;
|
||||||
|
if !SIGNED {
|
||||||
|
flags |= ffi::Py_ASNATIVEBYTES_UNSIGNED_BUFFER | ffi::Py_ASNATIVEBYTES_REJECT_NEGATIVE;
|
||||||
|
}
|
||||||
|
let n_bytes =
|
||||||
|
unsafe { ffi::PyLong_AsNativeBytes(long.as_ptr().cast(), std::ptr::null_mut(), 0, flags) };
|
||||||
|
let n_bytes_unsigned: usize = n_bytes
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| crate::PyErr::fetch(long.py()))?;
|
||||||
|
if n_bytes == 0 {
|
||||||
|
return Ok(buffer);
|
||||||
|
}
|
||||||
|
// TODO: use div_ceil when MSRV >= 1.73
|
||||||
|
let n_digits = {
|
||||||
|
let adjust = if n_bytes % 4 == 0 { 0 } else { 1 };
|
||||||
|
(n_bytes_unsigned / 4) + adjust
|
||||||
|
};
|
||||||
|
buffer.reserve_exact(n_digits);
|
||||||
|
unsafe {
|
||||||
|
ffi::PyLong_AsNativeBytes(
|
||||||
|
long.as_ptr().cast(),
|
||||||
|
buffer.as_mut_ptr().cast(),
|
||||||
|
(n_digits * 4).try_into().unwrap(),
|
||||||
|
flags,
|
||||||
|
);
|
||||||
|
buffer.set_len(n_digits);
|
||||||
|
};
|
||||||
|
buffer
|
||||||
|
.iter_mut()
|
||||||
|
.for_each(|chunk| *chunk = u32::from_le(*chunk));
|
||||||
|
|
||||||
|
Ok(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
fn int_to_py_bytes<'py>(
|
fn int_to_py_bytes<'py>(
|
||||||
long: &Bound<'py, PyLong>,
|
long: &Bound<'py, PyLong>,
|
||||||
|
@ -239,6 +312,7 @@ fn int_to_py_bytes<'py>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[cfg(any(not(Py_3_13), Py_LIMITED_API))]
|
||||||
fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult<usize> {
|
fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult<usize> {
|
||||||
let py = long.py();
|
let py = long.py();
|
||||||
#[cfg(not(Py_LIMITED_API))]
|
#[cfg(not(Py_LIMITED_API))]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
use crate::inspect::types::TypeInfo;
|
use crate::inspect::types::TypeInfo;
|
||||||
use crate::types::any::PyAnyMethods;
|
use crate::types::any::PyAnyMethods;
|
||||||
|
@ -63,14 +64,8 @@ macro_rules! extract_int {
|
||||||
err_if_invalid_value($obj.py(), $error_val, unsafe { $pylong_as(long.as_ptr()) })
|
err_if_invalid_value($obj.py(), $error_val, unsafe { $pylong_as(long.as_ptr()) })
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
let num = ffi::PyNumber_Index($obj.as_ptr());
|
let num = ffi::PyNumber_Index($obj.as_ptr()).assume_owned_or_err($obj.py())?;
|
||||||
if num.is_null() {
|
err_if_invalid_value($obj.py(), $error_val, $pylong_as(num.as_ptr()))
|
||||||
Err(PyErr::fetch($obj.py()))
|
|
||||||
} else {
|
|
||||||
let result = err_if_invalid_value($obj.py(), $error_val, $pylong_as(num));
|
|
||||||
ffi::Py_DECREF(num);
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -181,7 +176,7 @@ mod fast_128bit_int_conversion {
|
||||||
|
|
||||||
// for 128bit Integers
|
// for 128bit Integers
|
||||||
macro_rules! int_convert_128 {
|
macro_rules! int_convert_128 {
|
||||||
($rust_type: ty, $is_signed: expr) => {
|
($rust_type: ty, $is_signed: literal) => {
|
||||||
impl ToPyObject for $rust_type {
|
impl ToPyObject for $rust_type {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
|
@ -190,18 +185,44 @@ mod fast_128bit_int_conversion {
|
||||||
}
|
}
|
||||||
impl IntoPy<PyObject> for $rust_type {
|
impl IntoPy<PyObject> for $rust_type {
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||||
// Always use little endian
|
#[cfg(not(Py_3_13))]
|
||||||
|
{
|
||||||
let bytes = self.to_le_bytes();
|
let bytes = self.to_le_bytes();
|
||||||
unsafe {
|
unsafe {
|
||||||
PyObject::from_owned_ptr(
|
|
||||||
py,
|
|
||||||
ffi::_PyLong_FromByteArray(
|
ffi::_PyLong_FromByteArray(
|
||||||
bytes.as_ptr() as *const std::os::raw::c_uchar,
|
bytes.as_ptr().cast(),
|
||||||
bytes.len(),
|
bytes.len(),
|
||||||
1,
|
1,
|
||||||
$is_signed,
|
$is_signed.into(),
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
.assume_owned(py)
|
||||||
|
.unbind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(Py_3_13)]
|
||||||
|
{
|
||||||
|
let bytes = self.to_ne_bytes();
|
||||||
|
|
||||||
|
if $is_signed {
|
||||||
|
unsafe {
|
||||||
|
ffi::PyLong_FromNativeBytes(
|
||||||
|
bytes.as_ptr().cast(),
|
||||||
|
bytes.len(),
|
||||||
|
ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN,
|
||||||
|
)
|
||||||
|
.assume_owned(py)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
ffi::PyLong_FromUnsignedNativeBytes(
|
||||||
|
bytes.as_ptr().cast(),
|
||||||
|
bytes.len(),
|
||||||
|
ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN,
|
||||||
|
)
|
||||||
|
.assume_owned(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unbind()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,20 +234,46 @@ mod fast_128bit_int_conversion {
|
||||||
|
|
||||||
impl FromPyObject<'_> for $rust_type {
|
impl FromPyObject<'_> for $rust_type {
|
||||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
||||||
let num = unsafe {
|
let num =
|
||||||
PyObject::from_owned_ptr_or_err(ob.py(), ffi::PyNumber_Index(ob.as_ptr()))?
|
unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? };
|
||||||
};
|
let mut buffer = [0u8; std::mem::size_of::<$rust_type>()];
|
||||||
let mut buffer = [0; std::mem::size_of::<$rust_type>()];
|
#[cfg(not(Py_3_13))]
|
||||||
crate::err::error_on_minusone(ob.py(), unsafe {
|
crate::err::error_on_minusone(ob.py(), unsafe {
|
||||||
ffi::_PyLong_AsByteArray(
|
ffi::_PyLong_AsByteArray(
|
||||||
num.as_ptr() as *mut ffi::PyLongObject,
|
num.as_ptr() as *mut ffi::PyLongObject,
|
||||||
buffer.as_mut_ptr(),
|
buffer.as_mut_ptr(),
|
||||||
buffer.len(),
|
buffer.len(),
|
||||||
1,
|
1,
|
||||||
$is_signed,
|
$is_signed.into(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok(<$rust_type>::from_le_bytes(buffer))
|
#[cfg(Py_3_13)]
|
||||||
|
{
|
||||||
|
let mut flags = ffi::Py_ASNATIVEBYTES_NATIVE_ENDIAN;
|
||||||
|
if !$is_signed {
|
||||||
|
flags |= ffi::Py_ASNATIVEBYTES_UNSIGNED_BUFFER
|
||||||
|
| ffi::Py_ASNATIVEBYTES_REJECT_NEGATIVE;
|
||||||
|
}
|
||||||
|
let actual_size: usize = unsafe {
|
||||||
|
ffi::PyLong_AsNativeBytes(
|
||||||
|
num.as_ptr(),
|
||||||
|
buffer.as_mut_ptr().cast(),
|
||||||
|
buffer
|
||||||
|
.len()
|
||||||
|
.try_into()
|
||||||
|
.expect("length of buffer fits in Py_ssize_t"),
|
||||||
|
flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| PyErr::fetch(ob.py()))?;
|
||||||
|
if actual_size as usize > buffer.len() {
|
||||||
|
return Err(crate::exceptions::PyOverflowError::new_err(
|
||||||
|
"Python int larger than 128 bits",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(<$rust_type>::from_ne_bytes(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
|
@ -237,8 +284,8 @@ mod fast_128bit_int_conversion {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
int_convert_128!(i128, 1);
|
int_convert_128!(i128, true);
|
||||||
int_convert_128!(u128, 0);
|
int_convert_128!(u128, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For ABI3 we implement the conversion manually.
|
// For ABI3 we implement the conversion manually.
|
||||||
|
|
Loading…
Reference in New Issue