Macro cpython::py_capsule
source · macro_rules! py_capsule { (from $($capsmod:ident).+ import $capsname:ident as $rustmod:ident for $ruststruct: ident ) => { ... }; }
Expand description
Macro to retrieve a Python capsule pointing to an array of data, with a layer of caching.
For more details on capsules, see PyCapsule
The caller has to define an appropriate repr(C)
struct
first, and put it in
scope (use
) if needed along the macro invocation.
§Usage
py_capsule!(from some.python.module import capsulename as rustmodule for CapsuleStruct)
where CapsuleStruct
is the above mentioned struct
defined by the caller.
The macro defines a Rust module named rustmodule
, as specified by the caller.
This module provides a retrieval function with the following signature:
mod rustmodule {
pub unsafe fn retrieve<'a>(py: Python) -> PyResult<&'a CapsuleStruct> { ... }
}
The retrieve()
function is unsafe for the same reasons as PyCapsule::import_data
,
upon which it relies.
The newly defined module also contains a RawPyObject
type suitable to represent C-level
Python objects. It can be used in cpython
public API involving raw FFI pointers, such as
[from_owned_ptr
].
§Examples
§Using a capsule from the standard library
This retrieves and uses 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.
In this case, as with all capsules from the Python standard library, the capsule data
is an array (static struct
) with constants and function pointers.
use cpython::{Python, PyCapsule, py_capsule};
use libc::{c_char, c_int};
use std::ffi::{c_void, 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])
}
}
py_capsule!(from unicodedata import ucnhash_CAPI as capsmod for unicode_name_CAPI);
fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
let capi = unsafe { capsmod::retrieve(py).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));
}
§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};
use libc::c_void;
// In the struct, we still have to use c_void for C-level Python objects.
#[repr(C)]
pub struct spawn_CAPI {
spawnfrom: unsafe extern "C" fn(obj: *const c_void) -> *mut c_void,
}
py_capsule!(from some.mod import CAPI as capsmod for spawn_CAPI);
impl spawn_CAPI {
pub fn spawn_from(&self, py: Python, obj: PyObject) -> PyResult<PyObject> {
let raw = obj.as_ptr() as *const c_void;
Ok(unsafe {
PyObject::from_owned_ptr(
py,
((*self).spawnfrom)(raw) as *mut capsmod::RawPyObject)
})
}
}