Skip to main content

PyCapsuleMethods

Trait PyCapsuleMethods 

Source
pub trait PyCapsuleMethods<'py>: Sealed {
    // Required methods
    fn set_context(&self, context: *mut c_void) -> PyResult<()>;
    fn context(&self) -> PyResult<*mut c_void>;
    fn pointer_checked(&self, name: Option<&CStr>) -> PyResult<NonNull<c_void>>;
    fn is_valid_checked(&self, name: Option<&CStr>) -> bool;
    fn name(&self) -> PyResult<Option<CapsuleName>>;
}
Expand description

Implementation of functionality for PyCapsule.

These methods are defined for the Bound<'py, PyCapsule> smart pointer, so to use method call syntax these methods are separated into a trait, because stable Rust does not yet support arbitrary_self_types.

§Name checking

Capsules contain pointers to arbitrary data which is cast to a specific type at runtime. This is inherently quite dangerous, so Python allows capsules to be “named” to provide a hint as to what data is contained in the capsule. Although not a perfect solution, this is better than nothing.

The methods in this trait take the name as an Option<&CStr>, which is compared to the name stored in the capsule (with None being used to indicate the capsule has no name).

Required Methods§

Source

fn set_context(&self, context: *mut c_void) -> PyResult<()>

Sets the context pointer in the capsule.

Returns an error if this capsule is not valid.

§Notes

The context is treated much like the value of the capsule, but should likely act as a place to store any state management when using the capsule.

If you want to store a Rust value as the context, and drop it from the destructor, use Box::into_raw to convert it into a pointer, see the example.

§Example
use core::ffi::c_void;
use std::sync::mpsc::{channel, Sender};
use pyo3::{prelude::*, types::PyCapsule};

let (tx, rx) = channel::<String>();

fn destructor(val: u32, context: *mut c_void) {
    let ctx = unsafe { *Box::from_raw(context.cast::<Sender<String>>()) };
    ctx.send("Destructor called!".to_string()).unwrap();
}

Python::attach(|py| {
    let capsule = PyCapsule::new_with_value_and_destructor(
        py,
        123,
        c"foo",
        destructor as fn(u32, *mut c_void)
    )?;
    let context = Box::new(tx);  // `Sender<String>` is our context, box it up and ship it!
    capsule.set_context(Box::into_raw(context).cast())?;
    // This scope will end, causing our destructor to be called...
})?;

assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
}
Source

fn context(&self) -> PyResult<*mut c_void>

Gets the current context stored in the capsule. If there is no context, the pointer will be null.

Returns an error if this capsule is not valid.

Source

fn pointer_checked(&self, name: Option<&CStr>) -> PyResult<NonNull<c_void>>

Gets the raw pointer stored in this capsule.

Returns an error if the capsule is not valid with the given name.

§Safety

This function itself is not unsafe, but dereferencing the returned pointer to produce a reference is very dangerous:

  • The pointer will need to be .cast() to a concrete type before dereferencing. As per name checking, there is no way to statically guarantee this cast is correct, the name is the best available hint to guard against accidental misuse.
  • Arbitrary Python code can change the contents of the capsule, which may invalidate the pointer. The pointer and the reference produced by dereferencing the pointer should both be considered invalid after arbitrary Python code has run.

Users should take care to cast to the correct type and consume the pointer for as little duration as possible.

Source

fn is_valid_checked(&self, name: Option<&CStr>) -> bool

Checks that the capsule name matches name and that the pointer is not null.

Source

fn name(&self) -> PyResult<Option<CapsuleName>>

Retrieves the name of this capsule, if set.

Returns an error if this capsule is not valid.

See CapsuleName for details of how to consume the return value.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<'py> PyCapsuleMethods<'py> for Bound<'py, PyCapsule>