Macro cpython::py_capsule_fn

source ·
macro_rules! py_capsule_fn {
    (from $($capsmod:ident).+ import $capsname:ident as $rustmod:ident signature $( $sig: tt)* ) => { ... };
}
Expand description

Macro to retrieve a function pointer capsule.

This is not suitable for architectures where the sizes of function and data pointers differ. For general explanations about capsules, see PyCapsule.

§Usage

   py_capsule_fn!(from some.python.module import capsulename as rustmodule
                      signature (args) -> ret_type)

Similarly to py_capsule!, the macro defines

  • a Rust module according to the name provided by the caller (here, rustmodule)
  • a type alias for the given signature
  • a retrieval function:
mod $rustmod {
    pub type CapsuleFn = unsafe extern "C" (args) -> ret_type ;
    pub unsafe fn retrieve<'a>(py: Python) -> PyResult<CapsuleFn) { ... }
}
  • a RawPyObject type suitable for signatures that involve Python C objects; it can be used in cpython public API involving raw FFI pointers, such as from_owned_ptr.

The first call to retrieve() is cached for subsequent calls.

§Examples

§Full example with primitive types

There is in the Python library no capsule enclosing a function pointer directly, although the documentation presents it as a valid use-case. For this example, we’ll therefore have to create one, using the PyCapsule constructor, and to set it in an existing module (not to imply that a real extension should follow that example and set capsules in modules they don’t define!)

use cpython::{PyCapsule, Python, FromPyObject, py_capsule_fn};
use libc::{c_int, c_void};

extern "C" fn inc(a: c_int) -> c_int {
    a + 1
}

/// for testing purposes, stores a capsule named `sys.capsfn`` pointing to `inc()`.
fn create_capsule() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let pymod = py.import("sys").unwrap();
    let caps = PyCapsule::new(py, inc as *const c_void, "sys.capsfn").unwrap();
    pymod.add(py, "capsfn", caps).unwrap();
 }

py_capsule_fn!(from sys import capsfn as capsmod signature (a: c_int) -> c_int);

// One could, e.g., reexport if needed:
pub use capsmod::CapsuleFn;

fn retrieve_use_capsule() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let fun = capsmod::retrieve(py).unwrap();
    assert_eq!( unsafe { fun(1) }, 2);

    // let's demonstrate the (reexported) function type
    let g: CapsuleFn = fun;
}

fn main() {
    create_capsule();
    retrieve_use_capsule();
    // second call uses the cached function pointer
    retrieve_use_capsule();
}

§With Python objects

In this example, we lend a Python object and receive a new one of which we take ownership.

use cpython::{PyCapsule, PyObject, PyResult, Python, py_capsule_fn};

py_capsule_fn!(from some.mod import capsfn as capsmod
    signature (raw: *mut RawPyObject) -> *mut RawPyObject);

fn retrieve_use_capsule(py: Python, obj: PyObject) -> PyResult<PyObject> {
    let fun = capsmod::retrieve(py)?;
    let raw = obj.as_ptr();
    Ok(unsafe { PyObject::from_owned_ptr(py, fun(raw)) })
}