Skip to content

Commit fe0fdd5

Browse files
add PyLong* API (3.14+) (#6016)
* add `PyLong*` API (3.14+) * add newsfragments * cargo fmt * try fix * fix typo (inr -> int) * try fix (x2) * fix clippy * try fix clippy * add bench int 128 * add tests * digits_ptr -> ptr * review * cargo fmt * fix * sync 3.14 (python/cpython@af65a8b) * add `PyLong_Is*` * remove PyUnstable_Long_IsCompact * bench new line * PyLong_FromUnicodeObject * remove #[cfg(not(Py_LIMITED_API))] * review * add 6016.changed.md * update * update * update * fix * fix * review * review (x2) * review (x3) * add #[inline] * update * review * review (x2) * fix * fix * fmt * review * review * std -> core * std -> core (x2) * review * review (x2) * Update pyo3-ffi/src/cpython/longintrepr.rs Co-authored-by: David Hewitt <mail@davidhewitt.dev> * std -> core * fix * fix * fix * fix * try fix * preserve existing `ValueError` * try fix * try fix (x2) * skip test on Py_LIMITED_API --------- Co-authored-by: David Hewitt <mail@davidhewitt.dev>
1 parent f41b1df commit fe0fdd5

7 files changed

Lines changed: 324 additions & 70 deletions

File tree

newsfragments/6016.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added FFI wrappers for [PEP 757](https://peps.python.org/pep-0757/) `PyLong` import / export API on Python 3.14.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use crate::{PyObject, Py_ssize_t};
2+
use core::ffi::{c_int, c_void};
3+
4+
use crate::Py_uintptr_t;
5+
6+
// skipped PyLong_BASE
7+
// skipped PyLong_MASK
8+
// skipped _PyLong_New
9+
// skipped _PyLong_Copy
10+
// skipped _PyLong_FromDigits
11+
// skipped _PyLong_SIGN_MASK
12+
// skipped _PyLong_NON_SIZE_BITS
13+
// skipped PyUnstable_Long_IsCompact
14+
// skipped PyUnstable_Long_CompactValue
15+
16+
#[derive(Copy, Clone)]
17+
#[repr(C)]
18+
pub struct PyLongLayout {
19+
pub bits_per_digit: u8,
20+
pub digit_size: u8,
21+
pub digits_order: i8,
22+
pub digit_endianness: i8,
23+
}
24+
25+
extern_libpython! {
26+
pub fn PyLong_GetNativeLayout() -> *const PyLongLayout;
27+
}
28+
29+
#[repr(C)]
30+
pub struct PyLongExport {
31+
pub value: i64,
32+
pub negative: u8,
33+
pub ndigits: Py_ssize_t,
34+
pub digits: *const c_void,
35+
_reserved: Py_uintptr_t,
36+
}
37+
38+
extern_libpython! {
39+
pub fn PyLong_Export(obj: *mut PyObject, export_long: *mut PyLongExport) -> c_int;
40+
pub fn PyLong_FreeExport(export_long: *mut PyLongExport);
41+
}
42+
43+
opaque_struct!(pub PyLongWriter);
44+
45+
extern_libpython! {
46+
pub fn PyLongWriter_Create(
47+
negative: c_int,
48+
ndigits: Py_ssize_t,
49+
digits: *mut *mut c_void,
50+
) -> *mut PyLongWriter;
51+
52+
pub fn PyLongWriter_Finish(writer: *mut PyLongWriter) -> *mut PyObject;
53+
54+
pub fn PyLongWriter_Discard(writer: *mut PyLongWriter);
55+
}

pyo3-ffi/src/cpython/longobject.rs

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,35 @@
11
#[cfg(not(Py_3_13))]
22
use crate::longobject::*;
33
use crate::object::*;
4-
#[cfg(Py_3_13)]
5-
use crate::pyport::Py_ssize_t;
4+
65
use core::ffi::c_int;
6+
77
#[cfg(not(Py_3_13))]
88
use core::ffi::c_uchar;
9-
#[cfg(Py_3_13)]
10-
use core::ffi::c_void;
11-
use libc::size_t;
129

13-
#[cfg(Py_3_13)]
14-
extern_libpython! {
15-
pub fn PyLong_FromUnicodeObject(u: *mut PyObject, base: c_int) -> *mut PyObject;
16-
}
10+
#[cfg(not(Py_3_13))]
11+
use libc::size_t;
1712

18-
#[cfg(Py_3_13)]
19-
pub const Py_ASNATIVEBYTES_DEFAULTS: c_int = -1;
20-
#[cfg(Py_3_13)]
21-
pub const Py_ASNATIVEBYTES_BIG_ENDIAN: c_int = 0;
22-
#[cfg(Py_3_13)]
23-
pub const Py_ASNATIVEBYTES_LITTLE_ENDIAN: c_int = 1;
24-
#[cfg(Py_3_13)]
25-
pub const Py_ASNATIVEBYTES_NATIVE_ENDIAN: c_int = 3;
26-
#[cfg(Py_3_13)]
27-
pub const Py_ASNATIVEBYTES_UNSIGNED_BUFFER: c_int = 4;
28-
#[cfg(Py_3_13)]
29-
pub const Py_ASNATIVEBYTES_REJECT_NEGATIVE: c_int = 8;
13+
// skipped _PyLong_CAST
3014

3115
extern_libpython! {
32-
// skipped _PyLong_Sign
33-
3416
#[cfg(Py_3_13)]
35-
pub fn PyLong_AsNativeBytes(
36-
v: *mut PyObject,
37-
buffer: *mut c_void,
38-
n_bytes: Py_ssize_t,
39-
flags: c_int,
40-
) -> Py_ssize_t;
41-
42-
#[cfg(Py_3_13)]
43-
pub fn PyLong_FromNativeBytes(
44-
buffer: *const c_void,
45-
n_bytes: size_t,
46-
flags: c_int,
47-
) -> *mut PyObject;
48-
49-
#[cfg(Py_3_13)]
50-
pub fn PyLong_FromUnsignedNativeBytes(
51-
buffer: *const c_void,
52-
n_bytes: size_t,
53-
flags: c_int,
54-
) -> *mut PyObject;
17+
pub fn PyLong_FromUnicodeObject(u: *mut PyObject, base: c_int) -> *mut PyObject;
5518

5619
// skipped PyUnstable_Long_IsCompact
5720
// skipped PyUnstable_Long_CompactValue
5821

22+
#[cfg(Py_3_14)]
23+
pub fn PyLong_IsPositive(obj: *mut PyObject) -> c_int;
24+
#[cfg(Py_3_14)]
25+
pub fn PyLong_IsNegative(obj: *mut PyObject) -> c_int;
26+
#[cfg(Py_3_14)]
27+
pub fn PyLong_IsZero(obj: *mut PyObject) -> c_int;
28+
29+
// skipped PyLong_GetSign
30+
// skipped _PyLong_Sign
31+
// skipped non-limited _PyLong_NumBits
32+
5933
#[cfg(not(Py_3_13))]
6034
#[cfg_attr(PyPy, link_name = "_PyPyLong_FromByteArray")]
6135
#[doc(hidden)] // used in PyO3's older bytes conversions, but not otherwise public API

pyo3-ffi/src/cpython/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub(crate) mod initconfig;
2424
pub(crate) mod listobject;
2525
#[cfg(Py_3_13)]
2626
pub(crate) mod lock;
27+
#[cfg(Py_3_14)]
28+
pub(crate) mod longintrepr;
2729
pub(crate) mod longobject;
2830
pub(crate) mod marshal;
2931
#[cfg(all(Py_3_9, not(PyPy)))]
@@ -71,6 +73,8 @@ pub use self::initconfig::*;
7173
pub use self::listobject::*;
7274
#[cfg(Py_3_13)]
7375
pub use self::lock::*;
76+
#[cfg(Py_3_14)]
77+
pub use self::longintrepr::*;
7478
pub use self::longobject::*;
7579
pub use self::marshal::*;
7680
#[cfg(all(Py_3_9, not(PyPy)))]

pyo3-ffi/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ mod import;
539539
mod intrcheck;
540540
mod iterobject;
541541
mod listobject;
542-
// skipped longintrepr.h
543542
mod longobject;
544543
mod memoryobject;
545544
mod methodobject;

pyo3-ffi/src/longobject.rs

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,66 @@ extern_libpython! {
4545
pub fn PyLong_AsUnsignedLong(arg1: *mut PyObject) -> c_ulong;
4646
#[cfg_attr(PyPy, link_name = "PyPyLong_AsUnsignedLongMask")]
4747
pub fn PyLong_AsUnsignedLongMask(arg1: *mut PyObject) -> c_ulong;
48-
// skipped non-limited _PyLong_AsInt
48+
49+
// skipped non-limited PyLong_AsInt
50+
51+
#[cfg(Py_3_14)]
52+
pub fn PyLong_FromInt32(arg1: i32) -> *mut PyObject;
53+
#[cfg(Py_3_14)]
54+
pub fn PyLong_FromUInt32(arg1: u32) -> *mut PyObject;
55+
#[cfg(Py_3_14)]
56+
pub fn PyLong_FromInt64(arg1: i64) -> *mut PyObject;
57+
#[cfg(Py_3_14)]
58+
pub fn PyLong_FromUInt64(arg1: u64) -> *mut PyObject;
59+
60+
#[cfg(Py_3_14)]
61+
pub fn PyLong_AsInt32(arg1: *mut PyObject, arg2: *mut i32) -> c_int;
62+
#[cfg(Py_3_14)]
63+
pub fn PyLong_AsUInt32(arg1: *mut PyObject, arg2: *mut u32) -> c_int;
64+
#[cfg(Py_3_14)]
65+
pub fn PyLong_AsInt64(arg1: *mut PyObject, arg2: *mut i64) -> c_int;
66+
#[cfg(Py_3_14)]
67+
pub fn PyLong_AsUInt64(arg1: *mut PyObject, arg2: *mut u64) -> c_int;
68+
}
69+
70+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
71+
pub const Py_ASNATIVEBYTES_DEFAULTS: c_int = -1;
72+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
73+
pub const Py_ASNATIVEBYTES_BIG_ENDIAN: c_int = 0;
74+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
75+
pub const Py_ASNATIVEBYTES_LITTLE_ENDIAN: c_int = 1;
76+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
77+
pub const Py_ASNATIVEBYTES_NATIVE_ENDIAN: c_int = 3;
78+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
79+
pub const Py_ASNATIVEBYTES_UNSIGNED_BUFFER: c_int = 4;
80+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
81+
pub const Py_ASNATIVEBYTES_REJECT_NEGATIVE: c_int = 8;
82+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
83+
pub const Py_ASNATIVEBYTES_ALLOW_INDEX: c_int = 16;
84+
85+
extern_libpython! {
86+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
87+
pub fn PyLong_AsNativeBytes(
88+
v: *mut PyObject,
89+
buffer: *mut c_void,
90+
n_bytes: Py_ssize_t,
91+
flags: c_int,
92+
) -> Py_ssize_t;
93+
94+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
95+
pub fn PyLong_FromNativeBytes(
96+
buffer: *const c_void,
97+
n_bytes: size_t,
98+
flags: c_int,
99+
) -> *mut PyObject;
100+
101+
#[cfg(any(Py_3_14, all(Py_3_13, not(Py_LIMITED_API))))]
102+
pub fn PyLong_FromUnsignedNativeBytes(
103+
buffer: *const c_void,
104+
n_bytes: size_t,
105+
flags: c_int,
106+
) -> *mut PyObject;
107+
49108
pub fn PyLong_GetInfo() -> *mut PyObject;
50109
// skipped PyLong_AS_LONG
51110

@@ -54,15 +113,6 @@ extern_libpython! {
54113
// skipped _Py_PARSE_INTPTR
55114
// skipped _Py_PARSE_UINTPTR
56115

57-
// skipped non-limited _PyLong_UnsignedShort_Converter
58-
// skipped non-limited _PyLong_UnsignedInt_Converter
59-
// skipped non-limited _PyLong_UnsignedLong_Converter
60-
// skipped non-limited _PyLong_UnsignedLongLong_Converter
61-
// skipped non-limited _PyLong_Size_t_Converter
62-
63-
// skipped non-limited _PyLong_DigitValue
64-
// skipped non-limited _PyLong_Frexp
65-
66116
#[cfg_attr(PyPy, link_name = "PyPyLong_AsDouble")]
67117
pub fn PyLong_AsDouble(arg1: *mut PyObject) -> c_double;
68118
#[cfg_attr(PyPy, link_name = "PyPyLong_FromVoidPtr")]
@@ -89,17 +139,7 @@ extern_libpython! {
89139
) -> *mut PyObject;
90140
}
91141

92-
// skipped non-limited _PyLong_NumBits
93-
94-
// skipped non-limited _PyLong_Format
95-
// skipped non-limited _PyLong_FormatWriter
96-
// skipped non-limited _PyLong_FormatBytesWriter
97-
// skipped non-limited _PyLong_FormatAdvancedWriter
98-
99142
extern_libpython! {
100143
pub fn PyOS_strtoul(arg1: *const c_char, arg2: *mut *mut c_char, arg3: c_int) -> c_ulong;
101144
pub fn PyOS_strtol(arg1: *const c_char, arg2: *mut *mut c_char, arg3: c_int) -> c_long;
102145
}
103-
104-
// skipped non-limited _PyLong_Rshift
105-
// skipped non-limited _PyLong_Lshift

0 commit comments

Comments
 (0)