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
impl PyCapsule
sourcepub unsafe fn import_data<'a, T>(py: Python<'_>, name: &CStr) -> PyResult<&'a T>
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)
sourcepub fn import(py: Python<'_>, name: &CStr) -> PyResult<*const c_void>
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).
sourcepub fn new_data<T, N>(
py: Python<'_>,
data: &'static T,
name: N
) -> Result<Self, NulError>
pub fn new_data<T, N>( py: Python<'_>, data: &'static T, name: N ) -> Result<Self, NulError>
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.
sourcepub fn new<N>(
py: Python<'_>,
pointer: *const c_void,
name: N
) -> Result<Self, NulError>
pub fn new<N>( py: Python<'_>, pointer: *const c_void, name: N ) -> Result<Self, NulError>
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
)
sourcepub unsafe fn data_ref<'a, T, N>(&self, name: N) -> Result<&'a T, NulError>
pub unsafe fn data_ref<'a, T, N>(&self, name: N) -> Result<&'a T, NulError>
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
)
sourcepub unsafe fn data_ref_cstr<'a, T>(&self, name: &CStr) -> &'a T
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
impl<'s> FromPyObject<'s> for &'s PyCapsule
source§impl<'s> FromPyObject<'s> for PyCapsule
impl<'s> FromPyObject<'s> for PyCapsule
source§impl PythonObject for PyCapsule
impl PythonObject for PyCapsule
source§unsafe fn unchecked_downcast_from(obj: PyObject) -> Self
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
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 into_object(self) -> PyObject
fn into_object(self) -> PyObject
source§impl PythonObjectWithCheckedDowncast for PyCapsule
impl PythonObjectWithCheckedDowncast for PyCapsule
source§fn downcast_from<'p>(
py: Python<'p>,
obj: PyObject
) -> Result<PyCapsule, PythonObjectDowncastError<'p>>
fn downcast_from<'p>( py: Python<'p>, obj: PyObject ) -> Result<PyCapsule, PythonObjectDowncastError<'p>>
source§fn downcast_borrow_from<'a, 'p>(
py: Python<'p>,
obj: &'a PyObject
) -> Result<&'a PyCapsule, PythonObjectDowncastError<'p>>
fn downcast_borrow_from<'a, 'p>( py: Python<'p>, obj: &'a PyObject ) -> Result<&'a PyCapsule, PythonObjectDowncastError<'p>>
source§impl PythonObjectWithTypeObject for PyCapsule
impl PythonObjectWithTypeObject for PyCapsule
source§fn type_object(py: Python<'_>) -> PyType
fn type_object(py: Python<'_>) -> PyType
source§impl ToPyObject for PyCapsule
impl ToPyObject for PyCapsule
Identity conversion: allows using existing PyObject
instances where
T: ToPyObject
is expected.