[][src]Struct cpython::PyCapsule

pub struct PyCapsule(_);

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

impl PyCapsule[src]

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

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)

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

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).

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

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.

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

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)

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

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)

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

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

impl<'s> FromPyObject<'s> for PyCapsule[src]

impl<'s> FromPyObject<'s> for &'s PyCapsule[src]

impl PythonObject for PyCapsule[src]

pub unsafe fn unchecked_downcast_from(obj: PyObject) -> Self[src]

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

pub unsafe fn unchecked_downcast_borrow_from<'a>(obj: &'a PyObject) -> &'a Self[src]

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

impl PythonObjectWithCheckedDowncast for PyCapsule[src]

impl PythonObjectWithTypeObject for PyCapsule[src]

impl ToPyObject for PyCapsule[src]

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

type ObjectType = PyCapsule

Auto Trait Implementations

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

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

The type returned in the event of a conversion error.