kutil 0.0.6

Kutil utilities collection
Documentation
use {
    pyo3::{
        intern,
        {prelude::*, types::*},
    },
    std::{ffi::*, ptr::*},
};

// See: https://github.com/PyO3/pyo3/issues/5820

/// Convert a value into a capsule.
///
/// Warning!
///
/// A common use case is creating a capsule in one Python extension and reading it in another. This
/// has caveats beyond the obvious need for both extensions to use the same memory structure for
/// the value.
///
/// Specifically you also need to make sure that your type's functionality does not rely on Rust's
/// global state (i.e. static or const variables) because that state will exist *independently* for
/// each dynamically loaded Python extension. Such a dependency is neither undefined behavior nor
/// an error and yet it can lead to subtle behavioral bugs.
///
/// Instead of relying on Rust's static variables you can use Python's global state, which indeed
/// will be truly global for all loaded extensions. For example, global capsules can be stored as
/// attributes in the "builtins" module and then loaded with [PyCapsule::import].
pub fn into_capsule<'py, ValueT>(value: ValueT, name: CString, py: Python<'py>) -> PyResult<Bound<'py, PyCapsule>>
where
    ValueT: 'static + Send + Sync,
{
    PyCapsule::new(py, value, Some(name))
}

/// Get a pointer to a capsule value.
pub fn get_capsule_ptr<ValueT>(any: &Bound<'_, PyAny>, name: &CStr) -> PyResult<NonNull<ValueT>> {
    Ok(any.cast::<PyCapsule>()?.pointer_checked(Some(name))?.cast())
}

/// Clone a capsule value.
pub fn clone_capsule<ValueT>(any: &Bound<'_, PyAny>, name: &CStr) -> PyResult<ValueT>
where
    ValueT: Clone,
{
    let capsule: NonNull<ValueT> = get_capsule_ptr(any, name)?;

    // SAFETY: because we are only cloning there is no possibility of a mutation or a leak
    Ok(unsafe { capsule.as_ref().clone() })
}

/// Clone the "capsule" attribute's capsule value.
pub fn clone_capsule_attr<ValueT>(any: &Bound<'_, PyAny>, name: &CStr) -> PyResult<ValueT>
where
    ValueT: Clone,
{
    clone_capsule_from_attr(any, intern!(any.py(), "capsule"), name)
}

/// Clone an attribute's capsule value.
pub fn clone_capsule_from_attr<ValueT>(
    any: &Bound<'_, PyAny>,
    attr: &Bound<'_, PyString>,
    name: &CStr,
) -> PyResult<ValueT>
where
    ValueT: Clone,
{
    clone_capsule(&any.getattr(attr)?, name)
}