From 6037e43f6c8acb0e52552d3fba20ff0ec2e3c981 Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Tue, 30 Jun 2026 19:58:38 +0200 Subject: [PATCH 1/2] Add more abstract functions to c-api --- crates/capi/src/abstract_.rs | 59 +++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/crates/capi/src/abstract_.rs b/crates/capi/src/abstract_.rs index cd390135be4..0867302406e 100644 --- a/crates/capi/src/abstract_.rs +++ b/crates/capi/src/abstract_.rs @@ -1,6 +1,6 @@ use crate::{PyObject, pystate::with_vm}; use alloc::slice; -use core::ffi::c_int; +use core::ffi::{CStr, c_char, c_int}; pub use iter::*; pub use mapping::*; pub use number::*; @@ -57,6 +57,21 @@ pub unsafe extern "C" fn PyObject_CallNoArgs(callable: *mut PyObject) -> *mut Py with_vm(|vm| unsafe { &*callable }.call((), vm)) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyObject_CallObject( + callable: *mut PyObject, + args: *mut PyObject, +) -> *mut PyObject { + with_vm(|vm| { + let callable = unsafe { &*callable }; + if let Some(args) = unsafe { args.as_ref() } { + callable.call(tuple_to_args(args.try_downcast_ref::(vm)?), vm) + } else { + callable.call((), vm) + } + }) +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn PyObject_Vectorcall( callable: *mut PyObject, @@ -121,6 +136,15 @@ pub unsafe extern "C" fn PyObject_VectorcallMethod( }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyVectorcall_Call( + callable: *mut PyObject, + tuple: *mut PyObject, + kwargs: *mut PyObject, +) -> *mut PyObject { + unsafe { PyObject_Call(callable, tuple, kwargs) } +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn PyObject_GetItem(obj: *mut PyObject, key: *mut PyObject) -> *mut PyObject { with_vm(|vm| { @@ -153,6 +177,29 @@ pub unsafe extern "C" fn PyObject_DelItem(obj: *mut PyObject, key: *mut PyObject }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyObject_DelItemString(obj: *mut PyObject, key: *const c_char) -> c_int { + with_vm(|vm| { + let obj = unsafe { &*obj }; + let key = unsafe { CStr::from_ptr(key) } + .to_str() + .map_err(|_| vm.new_value_error("mapping key must be valid UTF-8"))?; + obj.del_item(key, vm) + }) +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyObject_Format( + obj: *mut PyObject, + format_spec: *mut PyObject, +) -> *mut PyObject { + with_vm(|vm| { + let obj = unsafe { &*obj }; + let format_spec = unsafe { &*format_spec }.try_downcast_ref::(vm)?; + vm.format(obj, format_spec.to_owned()) + }) +} + #[unsafe(no_mangle)] pub unsafe extern "C" fn PyObject_IsSubclass(derived: *mut PyObject, cls: *mut PyObject) -> c_int { with_vm(|vm| { @@ -179,6 +226,16 @@ pub unsafe extern "C" fn PyObject_Size(obj: *mut PyObject) -> isize { }) } +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyObject_Length(obj: *mut PyObject) -> isize { + unsafe { PyObject_Size(obj) } +} + +#[unsafe(no_mangle)] +pub unsafe extern "C" fn PyObject_Type(obj: *mut PyObject) -> *mut PyObject { + with_vm(|_vm| unsafe { &*obj }.obj_type()) +} + #[cfg(test)] mod tests { use pyo3::prelude::*; From 3301c679b3709d48cbf672af46412f9cb340a76c Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Tue, 30 Jun 2026 20:27:13 +0200 Subject: [PATCH 2/2] Review --- crates/capi/src/abstract_.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/capi/src/abstract_.rs b/crates/capi/src/abstract_.rs index 0867302406e..36d949a3022 100644 --- a/crates/capi/src/abstract_.rs +++ b/crates/capi/src/abstract_.rs @@ -142,7 +142,34 @@ pub unsafe extern "C" fn PyVectorcall_Call( tuple: *mut PyObject, kwargs: *mut PyObject, ) -> *mut PyObject { - unsafe { PyObject_Call(callable, tuple, kwargs) } + with_vm(|vm| { + let callable = unsafe { &*callable }; + let tuple = unsafe { &*tuple }.try_downcast_ref::(vm)?; + + let mut args = tuple.iter().cloned().collect::>(); + let num_positional_args = args.len(); + + let mut kwnames = Vec::new(); + if let Some(kwargs) = unsafe { kwargs.as_ref() } { + let kwargs = kwargs.try_downcast_ref::(vm)?; + for (key, value) in kwargs.items_vec() { + let key = key + .downcast_ref::() + .map(ToOwned::to_owned) + .ok_or_else(|| vm.new_type_error("keywords must be strings"))?; + kwnames.push(key.into()); + args.push(value); + } + } + + let kwnames = if kwnames.is_empty() { + None + } else { + Some(kwnames.as_slice()) + }; + + callable.vectorcall(args, num_positional_args, kwnames, vm) + }) } #[unsafe(no_mangle)] @@ -195,8 +222,11 @@ pub unsafe extern "C" fn PyObject_Format( ) -> *mut PyObject { with_vm(|vm| { let obj = unsafe { &*obj }; - let format_spec = unsafe { &*format_spec }.try_downcast_ref::(vm)?; - vm.format(obj, format_spec.to_owned()) + let spec = unsafe { format_spec.as_ref() } + .map(|spec| spec.try_downcast_ref::(vm)) + .transpose()? + .unwrap_or_else(|| vm.ctx.empty_str); + vm.format(obj, spec.to_owned()) }) }