Struct cpython::PyCapsule

source ·
pub struct PyCapsule(/* private fields */);
Expand description

Capsules are the preferred way to export/import C APIs between extension modules, see Providing a C API for an Extension Module.

In particular, capsules can be very useful to start adding Rust extensions besides existing traditional C ones, be it for gradual rewrites or to extend with new functionality. They can also be used for interaction between independently compiled Rust extensions if needed.

Capsules can point to data, usually static arrays of constants and function pointers, or to function pointers directly. These two cases have to be handled differently in Rust, and the latter is possible only for architectures were data and function pointers have the same sizes.

§Examples

§Using a capsule defined in another extension module

This retrieves and use one of the simplest capsules in the Python standard library, found in the unicodedata module. The C API enclosed in this capsule is the same for all Python versions supported by this crate. This is not the case of all capsules from the standard library. For instance the struct referenced by datetime.datetime_CAPI gets a new member in version 3.7.

Note: this example is a lower-level version of the py_capsule! example. Only the capsule retrieval actually differs.

use cpython::{Python, PyCapsule};
use libc::{c_void, c_char, c_int};
use std::ffi::{CStr, CString};
use std::mem;
use std::ptr::null;

#[allow(non_camel_case_types)]
type Py_UCS4 = u32;
const UNICODE_NAME_MAXLEN: usize = 256;

#[repr(C)]
pub struct unicode_name_CAPI {
    // the `ucd` signature arguments are actually optional (can be `NULL`) FFI PyObject
    // pointers used to pass alternate (former) versions of Unicode data.
    // We won't need to use them with an actual value in these examples, so it's enough to
    // specify them as `*const c_void`, and it spares us a direct reference to the lower
    // level Python FFI bindings.
    size: c_int,
    getname: unsafe extern "C" fn(
        ucd: *const c_void,
        code: Py_UCS4,
        buffer: *const c_char,
        buflen: c_int,
        with_alias_and_seq: c_int,
    ) -> c_int,
    getcode: unsafe extern "C" fn(
        ucd: *const c_void,
        name: *const c_char,
        namelen: c_int,
        code: *const Py_UCS4,
    ) -> c_int,
}

#[derive(Debug, PartialEq)]
pub enum UnicodeDataError {
    InvalidCode,
    UnknownName,
}

impl unicode_name_CAPI {
    pub fn get_name(&self, code: Py_UCS4) -> Result<CString, UnicodeDataError> {
        let mut buf: Vec<c_char> = Vec::with_capacity(UNICODE_NAME_MAXLEN);
        let buf_ptr = buf.as_mut_ptr();
        if unsafe {
          ((*self).getname)(null(), code, buf_ptr, UNICODE_NAME_MAXLEN as c_int, 0)
        } != 1 {
            return Err(UnicodeDataError::InvalidCode);
        }
        mem::forget(buf);
        Ok(unsafe { CString::from_raw(buf_ptr) })
    }

    pub fn get_code(&self, name: &CStr) -> Result<Py_UCS4, UnicodeDataError> {
        let namelen = name.to_bytes().len() as c_int;
        let mut code: [Py_UCS4; 1] = [0; 1];
        if unsafe {
            ((*self).getcode)(null(), name.as_ptr(), namelen, code.as_mut_ptr())
        } != 1 {
            return Err(UnicodeDataError::UnknownName);
        }
        Ok(code[0])
    }
}

let gil = Python::acquire_gil();
let py = gil.python();

let capi: &unicode_name_CAPI = unsafe {
    PyCapsule::import_data(
        py,
        CStr::from_bytes_with_nul_unchecked(b"unicodedata.ucnhash_CAPI\0"),
    )
}
.unwrap();

assert_eq!(capi.get_name(32).unwrap().to_str(), Ok("SPACE"));
assert_eq!(capi.get_name(0), Err(UnicodeDataError::InvalidCode));

assert_eq!(
    capi.get_code(CStr::from_bytes_with_nul(b"COMMA\0").unwrap()),
    Ok(44)
);
assert_eq!(
    capi.get_code(CStr::from_bytes_with_nul(b"\0").unwrap()),
    Err(UnicodeDataError::UnknownName)
);

§Creating a capsule from Rust

In this example, we enclose some data and a function in a capsule, using an intermediate struct as enclosing type, then retrieve them back and use them.

Warning: you definitely need to declare the data as static. If it’s only const, it’s possible it would get cloned elsewhere, with the orginal location being deallocated before it’s actually used from another Python extension.

use libc::{c_void, c_int};
use cpython::{PyCapsule, Python};
use std::ffi::{CStr, CString};

#[repr(C)]
struct CapsData {
    value: c_int,
    fun: fn(c_int, c_int) -> c_int,
}

fn add(a: c_int, b: c_int) -> c_int {
    a + b
}

static DATA: CapsData = CapsData{value: 1, fun: add};

fn main() {
    let gil = Python::acquire_gil();
    let py = gil.python();
    let caps = PyCapsule::new_data(py, &DATA, "somemod.capsdata").unwrap();

    let retrieved: &CapsData = unsafe {caps.data_ref("somemod.capsdata")}.unwrap();
    assert_eq!(retrieved.value, 1);
    assert_eq!((retrieved.fun)(2 as c_int, 3 as c_int), 5);
}

Of course, a more realistic example would be to store the capsule in a Python module, allowing another extension (possibly foreign) to retrieve and use it. Note that in that case, the capsule name must be full dotted name of the capsule object, as we’re doing here.

py_module_initializer!(somemod, |py, m| {
  m.add(py, "__doc__", "A module holding a capsule")?;
  m.add(py, "capsdata", PyCapsule::new_data(py, &DATA, "somemod.capsdata").unwrap())?;
  Ok(())
});

Another Rust extension could then declare CapsData and use PyCapsule::import_data to fetch it back.

Implementations§

source§

impl PyCapsule

source

pub unsafe fn import_data<'a, T>(py: Python<'_>, name: &CStr) -> PyResult<&'a T>

Retrieve the contents of a capsule pointing to some data as a reference.

The retrieved data would typically be an array of static data and/or function pointers. This method doesn’t work for standalone function pointers.

§Safety

This method is unsafe, because

  • nothing guarantees that the T type is appropriate for the data referenced by the capsule pointer
  • the returned lifetime doesn’t guarantee either to cover the actual lifetime of the data (although capsule data is usually static)
source

pub fn import(py: Python<'_>, name: &CStr) -> PyResult<*const c_void>

Retrieves the contents of a capsule as a void pointer by its name.

This is suitable in particular for later conversion as a function pointer with mem::transmute, for architectures where data and function pointers have the same size (see details about this in the documentation of the Rust standard library).

source

pub fn new_data<T, N>( py: Python<'_>, data: &'static T, name: N ) -> Result<Self, NulError>
where N: Into<Vec<u8>>,

Convenience method to create a capsule for some data

The encapsuled data may be an array of functions, but it can’t be itself a function directly.

May panic when running out of memory.

source

pub fn new<N>( py: Python<'_>, pointer: *const c_void, name: N ) -> Result<Self, NulError>
where N: Into<Vec<u8>>,

Creates a new capsule from a raw void pointer

This is suitable in particular to store a function pointer in a capsule. These can be obtained simply by a simple cast:

use libc::c_void;

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

fn main() {
    let ptr = inc as *const c_void;
}
§Errors

This method returns NulError if name contains a 0 byte (see also CString::new)

source

pub unsafe fn data_ref<'a, T, N>(&self, name: N) -> Result<&'a T, NulError>
where N: Into<Vec<u8>>,

Returns a reference to the capsule data.

The name must match exactly the one given at capsule creation time (see new_data) and is converted to a C string under the hood. If that’s too much overhead, consider using data_ref_cstr() or caching strategies.

This is unsafe, because

  • nothing guarantees that the T type is appropriate for the data referenced by the capsule pointer
  • the returned lifetime doesn’t guarantee either to cover the actual lifetime of the data (although capsule data is usually static)
§Errors

This method returns NulError if name contains a 0 byte (see also CString::new)

source

pub unsafe fn data_ref_cstr<'a, T>(&self, name: &CStr) -> &'a T

Returns a reference to the capsule data.

This is identical to data_ref, except for the name passing. This allows to use lower level constructs without overhead, such as CStr::from_bytes_with_nul_unchecked or the cstr! macro of rust-cpython

Trait Implementations§

source§

impl<'s> FromPyObject<'s> for &'s PyCapsule

source§

fn extract(py: Python<'_>, obj: &'s PyObject) -> PyResult<&'s PyCapsule>

Extracts Self from the source PyObject.
source§

impl<'s> FromPyObject<'s> for PyCapsule

source§

fn extract(py: Python<'_>, obj: &'s PyObject) -> PyResult<PyCapsule>

Extracts Self from the source PyObject.
source§

impl PythonObject for PyCapsule

source§

unsafe fn unchecked_downcast_from(obj: PyObject) -> Self

Unchecked downcast from PyObject to Self. Undefined behavior if the input object does not have the expected type.

source§

unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a PyObject) -> &'a Self

Unchecked downcast from PyObject to Self. Undefined behavior if the input object does not have the expected type.

source§

fn as_object(&self) -> &PyObject

Casts the Python object to PyObject.
source§

fn into_object(self) -> PyObject

Casts the Python object to PyObject.
source§

impl PythonObjectWithCheckedDowncast for PyCapsule

source§

fn downcast_from<'p>( py: Python<'p>, obj: PyObject ) -> Result<PyCapsule, PythonObjectDowncastError<'p>>

Cast from PyObject to a concrete Python object type.
source§

fn downcast_borrow_from<'a, 'p>( py: Python<'p>, obj: &'a PyObject ) -> Result<&'a PyCapsule, PythonObjectDowncastError<'p>>

Cast from PyObject to a concrete Python object type.
source§

impl PythonObjectWithTypeObject for PyCapsule

source§

fn type_object(py: Python<'_>) -> PyType

Retrieves the type object for this Python object type.
source§

impl ToPyObject for PyCapsule

Identity conversion: allows using existing PyObject instances where T: ToPyObject is expected.

§

type ObjectType = PyCapsule

source§

fn to_py_object(&self, py: Python<'_>) -> PyCapsule

Converts self into a Python object.
source§

fn into_py_object(self, _py: Python<'_>) -> PyCapsule

Converts self into a Python object. Read more
source§

fn with_borrowed_ptr<F, R>(&self, _py: Python<'_>, f: F) -> R
where F: FnOnce(*mut PyObject) -> R,

Converts self into a Python object and calls the specified closure on the native FFI pointer underlying the Python object. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> PyClone for T
where T: PythonObject,

source§

fn clone_ref(&self, py: Python<'_>) -> T

source§

impl<T> PyDrop for T
where T: PythonObject,

source§

fn release_ref(self, _py: Python<'_>)

source§

impl<T> RefFromPyObject for T
where &'a T: for<'a> FromPyObject<'a>, T: ?Sized,

source§

fn with_extracted<F, R>( py: Python<'_>, obj: &PyObject, f: F ) -> Result<R, PyErr>
where F: FnOnce(&T) -> R,

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.