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.11",
|
||||
"3.12",
|
||||
"3.13-dev",
|
||||
"pypy3.7",
|
||||
"pypy3.8",
|
||||
"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_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")
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
|
|
|
@ -17,7 +17,7 @@ const SUPPORTED_VERSIONS_CPYTHON: SupportedVersions = SupportedVersions {
|
|||
min: PythonVersion { major: 3, minor: 7 },
|
||||
max: PythonVersion {
|
||||
major: 3,
|
||||
minor: 12,
|
||||
minor: 13,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
@ -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)]
|
||||
|
|
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
@ -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)))]
|
||||
|
|
|
@ -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;
|
||||
// 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;
|
||||
|
@ -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::*;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
|
|
@ -9,11 +9,16 @@ nox.options.sessions = ["test"]
|
|||
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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -5,6 +5,11 @@ import sys
|
|||
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
|
||||
|
@ -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():
|
||||
|
|
|
@ -47,6 +47,8 @@
|
|||
//! assert n + 1 == value
|
||||
//! ```
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
#[cfg(Py_LIMITED_API)]
|
||||
use crate::types::{bytes::PyBytesMethods, PyBytes};
|
||||
use crate::{
|
||||
|
@ -63,20 +65,47 @@ use num_bigint::Sign;
|
|||
|
||||
// for identical functionality between BigInt and BigUint
|
||||
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")))]
|
||||
impl ToPyObject for $rust_ty {
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
let bytes = $to_bytes(self);
|
||||
unsafe {
|
||||
let obj = ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr().cast(),
|
||||
bytes.len(),
|
||||
1,
|
||||
$is_signed,
|
||||
);
|
||||
PyObject::from_owned_ptr(py, obj)
|
||||
#[cfg(not(Py_3_13))]
|
||||
{
|
||||
unsafe {
|
||||
ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr().cast(),
|
||||
bytes.len(),
|
||||
1,
|
||||
$is_signed.into(),
|
||||
)
|
||||
.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 {
|
||||
let bytes = $to_bytes(self);
|
||||
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);
|
||||
kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
|
||||
Some(kwargs)
|
||||
|
@ -107,8 +136,8 @@ macro_rules! bigint_conversion {
|
|||
};
|
||||
}
|
||||
|
||||
bigint_conversion!(BigUint, 0, BigUint::to_bytes_le);
|
||||
bigint_conversion!(BigInt, 1, BigInt::to_signed_bytes_le);
|
||||
bigint_conversion!(BigUint, false, BigUint::to_bytes_le);
|
||||
bigint_conversion!(BigInt, true, BigInt::to_signed_bytes_le);
|
||||
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "num-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.bind(py)
|
||||
};
|
||||
let n_bits = int_n_bits(num)?;
|
||||
if n_bits == 0 {
|
||||
return Ok(BigInt::from(0isize));
|
||||
}
|
||||
#[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) {
|
||||
// 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)
|
||||
|
@ -152,6 +177,10 @@ impl<'py> FromPyObject<'py> for BigInt {
|
|||
}
|
||||
#[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)?;
|
||||
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.bind(py)
|
||||
};
|
||||
let n_bits = int_n_bits(num)?;
|
||||
if n_bits == 0 {
|
||||
return Ok(BigUint::from(0usize));
|
||||
}
|
||||
#[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))
|
||||
}
|
||||
#[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)?;
|
||||
Ok(BigUint::from_bytes_le(bytes.as_bytes()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
#[cfg(not(any(Py_LIMITED_API, Py_3_13)))]
|
||||
#[inline]
|
||||
fn int_to_u32_vec(
|
||||
long: &Bound<'_, PyLong>,
|
||||
n_digits: usize,
|
||||
is_signed: bool,
|
||||
) -> PyResult<Vec<u32>> {
|
||||
let mut buffer = Vec::with_capacity(n_digits);
|
||||
fn int_to_u32_vec<const SIGNED: bool>(long: &Bound<'_, PyLong>) -> PyResult<Vec<u32>> {
|
||||
let mut buffer = Vec::new();
|
||||
let n_bits = int_n_bits(long)?;
|
||||
if n_bits == 0 {
|
||||
return Ok(buffer);
|
||||
}
|
||||
let n_digits = if SIGNED {
|
||||
(n_bits + 32) / 32
|
||||
} else {
|
||||
(n_bits + 31) / 32
|
||||
};
|
||||
buffer.reserve_exact(n_digits);
|
||||
unsafe {
|
||||
crate::err::error_on_minusone(
|
||||
long.py(),
|
||||
|
@ -203,7 +238,7 @@ fn int_to_u32_vec(
|
|||
buffer.as_mut_ptr() as *mut u8,
|
||||
n_digits * 4,
|
||||
1,
|
||||
is_signed.into(),
|
||||
SIGNED.into(),
|
||||
),
|
||||
)?;
|
||||
buffer.set_len(n_digits)
|
||||
|
@ -215,6 +250,44 @@ fn int_to_u32_vec(
|
|||
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)]
|
||||
fn int_to_py_bytes<'py>(
|
||||
long: &Bound<'py, PyLong>,
|
||||
|
@ -239,6 +312,7 @@ fn int_to_py_bytes<'py>(
|
|||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(any(not(Py_3_13), Py_LIMITED_API))]
|
||||
fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult<usize> {
|
||||
let py = long.py();
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||
#[cfg(feature = "experimental-inspect")]
|
||||
use crate::inspect::types::TypeInfo;
|
||||
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()) })
|
||||
} else {
|
||||
unsafe {
|
||||
let num = ffi::PyNumber_Index($obj.as_ptr());
|
||||
if num.is_null() {
|
||||
Err(PyErr::fetch($obj.py()))
|
||||
} else {
|
||||
let result = err_if_invalid_value($obj.py(), $error_val, $pylong_as(num));
|
||||
ffi::Py_DECREF(num);
|
||||
result
|
||||
}
|
||||
let num = ffi::PyNumber_Index($obj.as_ptr()).assume_owned_or_err($obj.py())?;
|
||||
err_if_invalid_value($obj.py(), $error_val, $pylong_as(num.as_ptr()))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -181,7 +176,7 @@ mod fast_128bit_int_conversion {
|
|||
|
||||
// for 128bit Integers
|
||||
macro_rules! int_convert_128 {
|
||||
($rust_type: ty, $is_signed: expr) => {
|
||||
($rust_type: ty, $is_signed: literal) => {
|
||||
impl ToPyObject for $rust_type {
|
||||
#[inline]
|
||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||
|
@ -190,18 +185,44 @@ mod fast_128bit_int_conversion {
|
|||
}
|
||||
impl IntoPy<PyObject> for $rust_type {
|
||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||
// Always use little endian
|
||||
let bytes = self.to_le_bytes();
|
||||
unsafe {
|
||||
PyObject::from_owned_ptr(
|
||||
py,
|
||||
#[cfg(not(Py_3_13))]
|
||||
{
|
||||
let bytes = self.to_le_bytes();
|
||||
unsafe {
|
||||
ffi::_PyLong_FromByteArray(
|
||||
bytes.as_ptr() as *const std::os::raw::c_uchar,
|
||||
bytes.as_ptr().cast(),
|
||||
bytes.len(),
|
||||
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 {
|
||||
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<$rust_type> {
|
||||
let num = unsafe {
|
||||
PyObject::from_owned_ptr_or_err(ob.py(), ffi::PyNumber_Index(ob.as_ptr()))?
|
||||
};
|
||||
let mut buffer = [0; std::mem::size_of::<$rust_type>()];
|
||||
let num =
|
||||
unsafe { ffi::PyNumber_Index(ob.as_ptr()).assume_owned_or_err(ob.py())? };
|
||||
let mut buffer = [0u8; std::mem::size_of::<$rust_type>()];
|
||||
#[cfg(not(Py_3_13))]
|
||||
crate::err::error_on_minusone(ob.py(), unsafe {
|
||||
ffi::_PyLong_AsByteArray(
|
||||
num.as_ptr() as *mut ffi::PyLongObject,
|
||||
buffer.as_mut_ptr(),
|
||||
buffer.len(),
|
||||
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")]
|
||||
|
@ -237,8 +284,8 @@ mod fast_128bit_int_conversion {
|
|||
};
|
||||
}
|
||||
|
||||
int_convert_128!(i128, 1);
|
||||
int_convert_128!(u128, 0);
|
||||
int_convert_128!(i128, true);
|
||||
int_convert_128!(u128, false);
|
||||
}
|
||||
|
||||
// For ABI3 we implement the conversion manually.
|
||||
|
|
Loading…
Reference in New Issue