Skip to content

Commit 2df776f

Browse files
committed
pyo3-ffi: expose PyObject_Vectorcall(Method) on the stable abi on 3.12+
1 parent 21132a8 commit 2df776f

4 files changed

Lines changed: 103 additions & 89 deletions

File tree

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_9, 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_9))))]
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/types/tuple.rs

Lines changed: 73 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
594594
}
595595
}
596596

597-
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
597+
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
598598
fn call_positional(
599599
self,
600600
function: Borrowed<'_, 'py, PyAny>,
@@ -603,30 +603,32 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
603603
let py = function.py();
604604
// We need this to drop the arguments correctly.
605605
let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
606+
607+
#[cfg(not(Py_LIMITED_API))]
606608
if $length == 1 {
607-
unsafe {
609+
return unsafe {
608610
ffi::PyObject_CallOneArg(
609611
function.as_ptr(),
610612
args_bound[0].as_ptr()
611613
)
612614
.assume_owned_or_err(py)
613-
}
614-
} else {
615-
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
616-
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
617-
unsafe {
618-
ffi::PyObject_Vectorcall(
619-
function.as_ptr(),
620-
args.as_mut_ptr().add(1),
621-
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
622-
std::ptr::null_mut(),
623-
)
624-
.assume_owned_or_err(py)
625-
}
615+
};
616+
}
617+
618+
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
619+
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
620+
unsafe {
621+
ffi::PyObject_Vectorcall(
622+
function.as_ptr(),
623+
args.as_mut_ptr().add(1),
624+
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
625+
std::ptr::null_mut(),
626+
)
627+
.assume_owned_or_err(py)
626628
}
627629
}
628630

629-
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
631+
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
630632
fn call_method_positional(
631633
self,
632634
object: Borrowed<'_, 'py, PyAny>,
@@ -636,28 +638,31 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
636638
let py = object.py();
637639
// We need this to drop the arguments correctly.
638640
let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
641+
642+
#[cfg(not(Py_LIMITED_API))]
639643
if $length == 1 {
640-
unsafe {
644+
return unsafe {
641645
ffi::PyObject_CallMethodOneArg(
642646
object.as_ptr(),
643647
method_name.as_ptr(),
644648
args_bound[0].as_ptr(),
645649
)
646650
.assume_owned_or_err(py)
647-
}
648-
} else {
649-
let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
650-
unsafe {
651-
ffi::PyObject_VectorcallMethod(
652-
method_name.as_ptr(),
653-
args.as_mut_ptr(),
654-
// +1 for the receiver.
655-
1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
656-
std::ptr::null_mut(),
657-
)
658-
.assume_owned_or_err(py)
659-
}
651+
};
652+
}
653+
654+
let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
655+
unsafe {
656+
ffi::PyObject_VectorcallMethod(
657+
method_name.as_ptr(),
658+
args.as_mut_ptr(),
659+
// +1 for the receiver.
660+
1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
661+
std::ptr::null_mut(),
662+
)
663+
.assume_owned_or_err(py)
660664
}
665+
661666
}
662667

663668
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
@@ -670,7 +675,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
670675
self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
671676
}
672677

673-
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
678+
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
674679
fn call_positional(
675680
self,
676681
function: Borrowed<'_, 'py, PyAny>,
@@ -679,7 +684,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
679684
self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
680685
}
681686

682-
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
687+
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
683688
fn call_method_positional(
684689
self,
685690
object: Borrowed<'_, 'py, PyAny>,
@@ -719,7 +724,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
719724
}
720725
}
721726

722-
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
727+
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
723728
fn call_positional(
724729
self,
725730
function: Borrowed<'_, 'py, PyAny>,
@@ -728,30 +733,32 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
728733
let py = function.py();
729734
// We need this to drop the arguments correctly.
730735
let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
736+
737+
#[cfg(not(Py_LIMITED_API))]
731738
if $length == 1 {
732-
unsafe {
739+
return unsafe {
733740
ffi::PyObject_CallOneArg(
734741
function.as_ptr(),
735742
args_bound[0].as_ptr()
736743
)
737744
.assume_owned_or_err(py)
738-
}
739-
} else {
740-
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
741-
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
742-
unsafe {
743-
ffi::PyObject_Vectorcall(
744-
function.as_ptr(),
745-
args.as_mut_ptr().add(1),
746-
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
747-
std::ptr::null_mut(),
748-
)
749-
.assume_owned_or_err(py)
750-
}
745+
};
746+
}
747+
748+
// Prepend one null argument for `PY_VECTORCALL_ARGUMENTS_OFFSET`.
749+
let mut args = [std::ptr::null_mut(), $(args_bound[$n].as_ptr()),*];
750+
unsafe {
751+
ffi::PyObject_Vectorcall(
752+
function.as_ptr(),
753+
args.as_mut_ptr().add(1),
754+
$length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
755+
std::ptr::null_mut(),
756+
)
757+
.assume_owned_or_err(py)
751758
}
752759
}
753760

754-
#[cfg(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API))))]
761+
#[cfg(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12)))]
755762
fn call_method_positional(
756763
self,
757764
object: Borrowed<'_, 'py, PyAny>,
@@ -761,27 +768,29 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
761768
let py = object.py();
762769
// We need this to drop the arguments correctly.
763770
let args_bound = [$(self.$n.into_bound_py_any(py)?,)*];
771+
772+
#[cfg(not(Py_LIMITED_API))]
764773
if $length == 1 {
765-
unsafe {
774+
return unsafe {
766775
ffi::PyObject_CallMethodOneArg(
767776
object.as_ptr(),
768777
method_name.as_ptr(),
769778
args_bound[0].as_ptr(),
770779
)
771780
.assume_owned_or_err(py)
772-
}
773-
} else {
774-
let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
775-
unsafe {
776-
ffi::PyObject_VectorcallMethod(
777-
method_name.as_ptr(),
778-
args.as_mut_ptr(),
779-
// +1 for the receiver.
780-
1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
781-
std::ptr::null_mut(),
782-
)
783-
.assume_owned_or_err(py)
784-
}
781+
};
782+
}
783+
784+
let mut args = [object.as_ptr(), $(args_bound[$n].as_ptr()),*];
785+
unsafe {
786+
ffi::PyObject_VectorcallMethod(
787+
method_name.as_ptr(),
788+
args.as_mut_ptr(),
789+
// +1 for the receiver.
790+
1 + $length + ffi::PY_VECTORCALL_ARGUMENTS_OFFSET,
791+
std::ptr::null_mut(),
792+
)
793+
.assume_owned_or_err(py)
785794
}
786795
}
787796

@@ -795,7 +804,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
795804
self.into_pyobject_or_pyerr(function.py())?.call(function, kwargs, token)
796805
}
797806

798-
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
807+
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
799808
fn call_positional(
800809
self,
801810
function: Borrowed<'_, 'py, PyAny>,
@@ -804,7 +813,7 @@ macro_rules! tuple_conversion ({$length:expr,$(($refN:ident, $n:tt, $T:ident)),+
804813
self.into_pyobject_or_pyerr(function.py())?.call_positional(function, token)
805814
}
806815

807-
#[cfg(not(all(Py_3_9, not(any(PyPy, GraalPy, Py_LIMITED_API)))))]
816+
#[cfg(not(all(not(any(PyPy, GraalPy)), any(all(Py_3_9, not(Py_LIMITED_API)), Py_3_12))))]
808817
fn call_method_positional(
809818
self,
810819
object: Borrowed<'_, 'py, PyAny>,

0 commit comments

Comments
 (0)