Skip to content

Commit ad5f6d4

Browse files
authored
pyo3-ffi: expose PyObject_Vectorcall(Method) on the stable abi on 3.12+ (#4853)
1 parent af9f42c commit ad5f6d4

File tree

5 files changed

+147
-89
lines changed

5 files changed

+147
-89
lines changed

newsfragments/4853.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyo3-ffi: expose `PyObject_Vectorcall(Method)` on the stable abi on 3.12+

pyo3-ffi/src/abstract_.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use crate::object::*;
22
use crate::pyport::Py_ssize_t;
3+
#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))]
4+
use libc::size_t;
35
use std::os::raw::{c_char, c_int};
46

57
#[inline]
@@ -70,6 +72,28 @@ extern "C" {
7072
method: *mut PyObject,
7173
...
7274
) -> *mut PyObject;
75+
}
76+
#[cfg(any(Py_3_12, all(Py_3_8, not(Py_LIMITED_API))))]
77+
pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t =
78+
1 << (8 * std::mem::size_of::<size_t>() as size_t - 1);
79+
80+
extern "C" {
81+
#[cfg_attr(PyPy, link_name = "PyPyObject_Vectorcall")]
82+
#[cfg(any(Py_3_12, all(Py_3_11, not(Py_LIMITED_API))))]
83+
pub fn PyObject_Vectorcall(
84+
callable: *mut PyObject,
85+
args: *const *mut PyObject,
86+
nargsf: size_t,
87+
kwnames: *mut PyObject,
88+
) -> *mut PyObject;
89+
90+
#[cfg(any(Py_3_12, all(Py_3_9, not(any(Py_LIMITED_API, PyPy, GraalPy)))))]
91+
pub fn PyObject_VectorcallMethod(
92+
name: *mut PyObject,
93+
args: *const *mut PyObject,
94+
nargsf: size_t,
95+
kwnames: *mut PyObject,
96+
) -> *mut PyObject;
7397
#[cfg_attr(PyPy, link_name = "PyPyObject_Type")]
7498
pub fn PyObject_Type(o: *mut PyObject) -> *mut PyObject;
7599
#[cfg_attr(PyPy, link_name = "PyPyObject_Size")]

pyo3-ffi/src/cpython/abstract_.rs

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ extern "C" {
4141
) -> *mut PyObject;
4242
}
4343

44-
#[cfg(Py_3_8)]
45-
pub const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t =
44+
#[cfg(Py_3_8)] // NB exported as public in abstract.rs from 3.12
45+
const PY_VECTORCALL_ARGUMENTS_OFFSET: size_t =
4646
1 << (8 * std::mem::size_of::<size_t>() as size_t - 1);
4747

4848
#[cfg(Py_3_8)]
@@ -91,7 +91,7 @@ pub unsafe fn _PyObject_VectorcallTstate(
9191
}
9292
}
9393

94-
#[cfg(all(Py_3_8, not(any(PyPy, GraalPy))))]
94+
#[cfg(all(Py_3_8, not(any(PyPy, GraalPy, Py_3_11))))] // exported as a function from 3.11, see abstract.rs
9595
#[inline(always)]
9696
pub unsafe fn PyObject_Vectorcall(
9797
callable: *mut PyObject,
@@ -103,16 +103,6 @@ pub unsafe fn PyObject_Vectorcall(
103103
}
104104

105105
extern "C" {
106-
#[cfg(all(PyPy, Py_3_8))]
107-
#[cfg_attr(not(Py_3_9), link_name = "_PyPyObject_Vectorcall")]
108-
#[cfg_attr(Py_3_9, link_name = "PyPyObject_Vectorcall")]
109-
pub fn PyObject_Vectorcall(
110-
callable: *mut PyObject,
111-
args: *const *mut PyObject,
112-
nargsf: size_t,
113-
kwnames: *mut PyObject,
114-
) -> *mut PyObject;
115-
116106
#[cfg(Py_3_8)]
117107
#[cfg_attr(
118108
all(not(any(PyPy, GraalPy)), not(Py_3_9)),
@@ -187,23 +177,13 @@ pub unsafe fn PyObject_CallOneArg(func: *mut PyObject, arg: *mut PyObject) -> *m
187177
_PyObject_VectorcallTstate(tstate, func, args, nargsf, std::ptr::null_mut())
188178
}
189179

190-
extern "C" {
191-
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))]
192-
pub fn PyObject_VectorcallMethod(
193-
name: *mut PyObject,
194-
args: *const *mut PyObject,
195-
nargsf: size_t,
196-
kwnames: *mut PyObject,
197-
) -> *mut PyObject;
198-
}
199-
200180
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy))))]
201181
#[inline(always)]
202182
pub unsafe fn PyObject_CallMethodNoArgs(
203183
self_: *mut PyObject,
204184
name: *mut PyObject,
205185
) -> *mut PyObject {
206-
PyObject_VectorcallMethod(
186+
crate::PyObject_VectorcallMethod(
207187
name,
208188
&self_,
209189
1 | PY_VECTORCALL_ARGUMENTS_OFFSET,
@@ -220,7 +200,7 @@ pub unsafe fn PyObject_CallMethodOneArg(
220200
) -> *mut PyObject {
221201
let args = [self_, arg];
222202
assert!(!arg.is_null());
223-
PyObject_VectorcallMethod(
203+
crate::PyObject_VectorcallMethod(
224204
name,
225205
args.as_ptr(),
226206
2 | PY_VECTORCALL_ARGUMENTS_OFFSET,

src/instance.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,6 +2020,50 @@ mod tests {
20202020
})
20212021
}
20222022

2023+
#[test]
2024+
fn test_call_tuple_ref() {
2025+
let assert_repr = |obj: &Bound<'_, PyAny>, expected: &str| {
2026+
use crate::prelude::PyStringMethods;
2027+
assert_eq!(
2028+
obj.repr()
2029+
.unwrap()
2030+
.to_cow()
2031+
.unwrap()
2032+
.trim_matches(|c| c == '{' || c == '}'),
2033+
expected.trim_matches(|c| c == ',' || c == ' ')
2034+
);
2035+
};
2036+
2037+
macro_rules! tuple {
2038+
($py:ident, $($key: literal => $value: literal),+) => {
2039+
let ty_obj = $py.get_type::<PyDict>().into_pyobject($py).unwrap();
2040+
assert!(ty_obj.call1(&(($(($key),)+),)).is_err());
2041+
let obj = ty_obj.call1(&(($(($key, i32::from($value)),)+),)).unwrap();
2042+
assert_repr(&obj, concat!($("'", $key, "'", ": ", stringify!($value), ", ",)+));
2043+
assert!(obj.call_method1("update", &(($(($key),)+),)).is_err());
2044+
obj.call_method1("update", &(($((i32::from($value), $key),)+),)).unwrap();
2045+
assert_repr(&obj, concat!(
2046+
concat!($("'", $key, "'", ": ", stringify!($value), ", ",)+),
2047+
concat!($(stringify!($value), ": ", "'", $key, "'", ", ",)+)
2048+
));
2049+
};
2050+
}
2051+
2052+
Python::with_gil(|py| {
2053+
tuple!(py, "a" => 1);
2054+
tuple!(py, "a" => 1, "b" => 2);
2055+
tuple!(py, "a" => 1, "b" => 2, "c" => 3);
2056+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4);
2057+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5);
2058+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6);
2059+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7);
2060+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8);
2061+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9);
2062+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9, "j" => 10, "k" => 11);
2063+
tuple!(py, "a" => 1, "b" => 2, "c" => 3, "d" => 4, "e" => 5, "f" => 6, "g" => 7, "h" => 8, "i" => 9, "j" => 10, "k" => 11, "l" => 12);
2064+
})
2065+
}
2066+
20232067
#[test]
20242068
fn test_call_for_non_existing_method() {
20252069
Python::with_gil(|py| {

0 commit comments

Comments
 (0)