pyo3/types/
capsule.rs

1use crate::ffi_ptr_ext::FfiPtrExt;
2use crate::py_result_ext::PyResultExt;
3use crate::{ffi, PyAny};
4use crate::{Bound, Python};
5use crate::{PyErr, PyResult};
6use std::ffi::{CStr, CString};
7use std::os::raw::{c_char, c_int, c_void};
8/// Represents a Python Capsule
9/// as described in [Capsules](https://docs.python.org/3/c-api/capsule.html#capsules):
10/// > This subtype of PyObject represents an opaque value, useful for C extension
11/// > modules who need to pass an opaque value (as a void* pointer) through Python
12/// > code to other C code. It is often used to make a C function pointer defined
13/// > in one module available to other modules, so the regular import mechanism can
14/// > be used to access C APIs defined in dynamically loaded modules.
15///
16/// Values of this type are accessed via PyO3's smart pointers, e.g. as
17/// [`Py<PyCapsule>`][crate::Py] or [`Bound<'py, PyCapsule>`][Bound].
18///
19/// For APIs available on capsule objects, see the [`PyCapsuleMethods`] trait which is implemented for
20/// [`Bound<'py, PyCapsule>`][Bound].
21///
22/// # Example
23/// ```
24/// use pyo3::{prelude::*, types::PyCapsule};
25/// use std::ffi::CString;
26///
27/// #[repr(C)]
28/// struct Foo {
29///     pub val: u32,
30/// }
31///
32/// let r = Python::with_gil(|py| -> PyResult<()> {
33///     let foo = Foo { val: 123 };
34///     let name = CString::new("builtins.capsule").unwrap();
35///
36///     let capsule = PyCapsule::new(py, foo, Some(name.clone()))?;
37///
38///     let module = PyModule::import(py, "builtins")?;
39///     module.add("capsule", capsule)?;
40///
41///     let cap: &Foo = unsafe { PyCapsule::import(py, name.as_ref())? };
42///     assert_eq!(cap.val, 123);
43///     Ok(())
44/// });
45/// assert!(r.is_ok());
46/// ```
47#[repr(transparent)]
48pub struct PyCapsule(PyAny);
49
50pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), #checkfunction=ffi::PyCapsule_CheckExact);
51
52impl PyCapsule {
53    /// Constructs a new capsule whose contents are `value`, associated with `name`.
54    /// `name` is the identifier for the capsule; if it is stored as an attribute of a module,
55    /// the name should be in the format `"modulename.attribute"`.
56    ///
57    /// It is checked at compile time that the type T is not zero-sized. Rust function items
58    /// need to be cast to a function pointer (`fn(args) -> result`) to be put into a capsule.
59    ///
60    /// # Example
61    ///
62    /// ```
63    /// use pyo3::{prelude::*, types::PyCapsule};
64    /// use std::ffi::CString;
65    ///
66    /// Python::with_gil(|py| {
67    ///     let name = CString::new("foo").unwrap();
68    ///     let capsule = PyCapsule::new(py, 123_u32, Some(name)).unwrap();
69    ///     let val = unsafe { capsule.reference::<u32>() };
70    ///     assert_eq!(*val, 123);
71    /// });
72    /// ```
73    ///
74    /// However, attempting to construct a `PyCapsule` with a zero-sized type will not compile:
75    ///
76    /// ```compile_fail
77    /// use pyo3::{prelude::*, types::PyCapsule};
78    /// use std::ffi::CString;
79    ///
80    /// Python::with_gil(|py| {
81    ///     let capsule = PyCapsule::new(py, (), None).unwrap();  // Oops! `()` is zero sized!
82    /// });
83    /// ```
84    pub fn new<T: 'static + Send + AssertNotZeroSized>(
85        py: Python<'_>,
86        value: T,
87        name: Option<CString>,
88    ) -> PyResult<Bound<'_, Self>> {
89        Self::new_with_destructor(py, value, name, |_, _| {})
90    }
91
92    /// Deprecated name for [`PyCapsule::new`].
93    #[deprecated(since = "0.23.0", note = "renamed to `PyCapsule::new`")]
94    #[inline]
95    pub fn new_bound<T: 'static + Send + AssertNotZeroSized>(
96        py: Python<'_>,
97        value: T,
98        name: Option<CString>,
99    ) -> PyResult<Bound<'_, Self>> {
100        Self::new(py, value, name)
101    }
102
103    /// Constructs a new capsule whose contents are `value`, associated with `name`.
104    ///
105    /// Also provides a destructor: when the `PyCapsule` is destroyed, it will be passed the original object,
106    /// as well as a `*mut c_void` which will point to the capsule's context, if any.
107    ///
108    /// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually
109    /// be called from.
110    pub fn new_with_destructor<
111        T: 'static + Send + AssertNotZeroSized,
112        F: FnOnce(T, *mut c_void) + Send,
113    >(
114        py: Python<'_>,
115        value: T,
116        name: Option<CString>,
117        destructor: F,
118    ) -> PyResult<Bound<'_, Self>> {
119        AssertNotZeroSized::assert_not_zero_sized(&value);
120
121        // Sanity check for capsule layout
122        debug_assert_eq!(memoffset::offset_of!(CapsuleContents::<T, F>, value), 0);
123
124        let name_ptr = name.as_ref().map_or(std::ptr::null(), |name| name.as_ptr());
125        let val = Box::new(CapsuleContents {
126            value,
127            destructor,
128            name,
129        });
130
131        unsafe {
132            ffi::PyCapsule_New(
133                Box::into_raw(val).cast(),
134                name_ptr,
135                Some(capsule_destructor::<T, F>),
136            )
137            .assume_owned_or_err(py)
138            .downcast_into_unchecked()
139        }
140    }
141
142    /// Deprecated name for [`PyCapsule::new_with_destructor`].
143    #[deprecated(since = "0.23.0", note = "renamed to `PyCapsule::new_with_destructor`")]
144    #[inline]
145    pub fn new_bound_with_destructor<
146        T: 'static + Send + AssertNotZeroSized,
147        F: FnOnce(T, *mut c_void) + Send,
148    >(
149        py: Python<'_>,
150        value: T,
151        name: Option<CString>,
152        destructor: F,
153    ) -> PyResult<Bound<'_, Self>> {
154        Self::new_with_destructor(py, value, name, destructor)
155    }
156
157    /// Imports an existing capsule.
158    ///
159    /// The `name` should match the path to the module attribute exactly in the form
160    /// of `"module.attribute"`, which should be the same as the name within the capsule.
161    ///
162    /// # Safety
163    ///
164    /// It must be known that the capsule imported by `name` contains an item of type `T`.
165    pub unsafe fn import<'py, T>(py: Python<'py>, name: &CStr) -> PyResult<&'py T> {
166        let ptr = ffi::PyCapsule_Import(name.as_ptr(), false as c_int);
167        if ptr.is_null() {
168            Err(PyErr::fetch(py))
169        } else {
170            Ok(&*ptr.cast::<T>())
171        }
172    }
173}
174
175/// Implementation of functionality for [`PyCapsule`].
176///
177/// These methods are defined for the `Bound<'py, PyCapsule>` smart pointer, so to use method call
178/// syntax these methods are separated into a trait, because stable Rust does not yet support
179/// `arbitrary_self_types`.
180#[doc(alias = "PyCapsule")]
181pub trait PyCapsuleMethods<'py>: crate::sealed::Sealed {
182    /// Sets the context pointer in the capsule.
183    ///
184    /// Returns an error if this capsule is not valid.
185    ///
186    /// # Notes
187    ///
188    /// The context is treated much like the value of the capsule, but should likely act as
189    /// a place to store any state management when using the capsule.
190    ///
191    /// If you want to store a Rust value as the context, and drop it from the destructor, use
192    /// `Box::into_raw` to convert it into a pointer, see the example.
193    ///
194    /// # Example
195    ///
196    /// ```
197    /// use std::os::raw::c_void;
198    /// use std::sync::mpsc::{channel, Sender};
199    /// use pyo3::{prelude::*, types::PyCapsule};
200    ///
201    /// let (tx, rx) = channel::<String>();
202    ///
203    /// fn destructor(val: u32, context: *mut c_void) {
204    ///     let ctx = unsafe { *Box::from_raw(context.cast::<Sender<String>>()) };
205    ///     ctx.send("Destructor called!".to_string()).unwrap();
206    /// }
207    ///
208    /// Python::with_gil(|py| {
209    ///     let capsule =
210    ///         PyCapsule::new_with_destructor(py, 123, None, destructor as fn(u32, *mut c_void))
211    ///             .unwrap();
212    ///     let context = Box::new(tx);  // `Sender<String>` is our context, box it up and ship it!
213    ///     capsule.set_context(Box::into_raw(context).cast()).unwrap();
214    ///     // This scope will end, causing our destructor to be called...
215    /// });
216    ///
217    /// assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
218    /// ```
219    fn set_context(&self, context: *mut c_void) -> PyResult<()>;
220
221    /// Gets the current context stored in the capsule. If there is no context, the pointer
222    /// will be null.
223    ///
224    /// Returns an error if this capsule is not valid.
225    fn context(&self) -> PyResult<*mut c_void>;
226
227    /// Obtains a reference to the value of this capsule.
228    ///
229    /// # Safety
230    ///
231    /// It must be known that this capsule is valid and its pointer is to an item of type `T`.
232    unsafe fn reference<T>(&self) -> &'py T;
233
234    /// Gets the raw `c_void` pointer to the value in this capsule.
235    ///
236    /// Returns null if this capsule is not valid.
237    fn pointer(&self) -> *mut c_void;
238
239    /// Checks if this is a valid capsule.
240    ///
241    /// Returns true if the stored `pointer()` is non-null.
242    fn is_valid(&self) -> bool;
243
244    /// Retrieves the name of this capsule, if set.
245    ///
246    /// Returns an error if this capsule is not valid.
247    fn name(&self) -> PyResult<Option<&'py CStr>>;
248}
249
250impl<'py> PyCapsuleMethods<'py> for Bound<'py, PyCapsule> {
251    #[allow(clippy::not_unsafe_ptr_arg_deref)]
252    fn set_context(&self, context: *mut c_void) -> PyResult<()> {
253        let result = unsafe { ffi::PyCapsule_SetContext(self.as_ptr(), context) };
254        if result != 0 {
255            Err(PyErr::fetch(self.py()))
256        } else {
257            Ok(())
258        }
259    }
260
261    fn context(&self) -> PyResult<*mut c_void> {
262        let ctx = unsafe { ffi::PyCapsule_GetContext(self.as_ptr()) };
263        if ctx.is_null() {
264            ensure_no_error(self.py())?
265        }
266        Ok(ctx)
267    }
268
269    unsafe fn reference<T>(&self) -> &'py T {
270        &*self.pointer().cast()
271    }
272
273    fn pointer(&self) -> *mut c_void {
274        unsafe {
275            let ptr = ffi::PyCapsule_GetPointer(self.as_ptr(), name_ptr_ignore_error(self));
276            if ptr.is_null() {
277                ffi::PyErr_Clear();
278            }
279            ptr
280        }
281    }
282
283    fn is_valid(&self) -> bool {
284        // As well as if the stored pointer is null, PyCapsule_IsValid also returns false if
285        // self.as_ptr() is null or not a ptr to a PyCapsule object. Both of these are guaranteed
286        // to not be the case thanks to invariants of this PyCapsule struct.
287        let r = unsafe { ffi::PyCapsule_IsValid(self.as_ptr(), name_ptr_ignore_error(self)) };
288        r != 0
289    }
290
291    fn name(&self) -> PyResult<Option<&'py CStr>> {
292        unsafe {
293            let ptr = ffi::PyCapsule_GetName(self.as_ptr());
294            if ptr.is_null() {
295                ensure_no_error(self.py())?;
296                Ok(None)
297            } else {
298                Ok(Some(CStr::from_ptr(ptr)))
299            }
300        }
301    }
302}
303
304// C layout, as PyCapsule::get_reference depends on `T` being first.
305#[repr(C)]
306struct CapsuleContents<T: 'static + Send, D: FnOnce(T, *mut c_void) + Send> {
307    /// Value of the capsule
308    value: T,
309    /// Destructor to be used by the capsule
310    destructor: D,
311    /// Name used when creating the capsule
312    name: Option<CString>,
313}
314
315// Wrapping ffi::PyCapsule_Destructor for a user supplied FnOnce(T) for capsule destructor
316unsafe extern "C" fn capsule_destructor<T: 'static + Send, F: FnOnce(T, *mut c_void) + Send>(
317    capsule: *mut ffi::PyObject,
318) {
319    let ptr = ffi::PyCapsule_GetPointer(capsule, ffi::PyCapsule_GetName(capsule));
320    let ctx = ffi::PyCapsule_GetContext(capsule);
321    let CapsuleContents {
322        value, destructor, ..
323    } = *Box::from_raw(ptr.cast::<CapsuleContents<T, F>>());
324    destructor(value, ctx)
325}
326
327/// Guarantee `T` is not zero sized at compile time.
328// credit: `<https://users.rust-lang.org/t/is-it-possible-to-assert-at-compile-time-that-foo-t-is-not-called-with-a-zst/67685>`
329#[doc(hidden)]
330pub trait AssertNotZeroSized: Sized {
331    const _CONDITION: usize = (std::mem::size_of::<Self>() == 0) as usize;
332    const _CHECK: &'static str =
333        ["PyCapsule value type T must not be zero-sized!"][Self::_CONDITION];
334    #[allow(path_statements, clippy::no_effect)]
335    fn assert_not_zero_sized(&self) {
336        <Self as AssertNotZeroSized>::_CHECK;
337    }
338}
339
340impl<T> AssertNotZeroSized for T {}
341
342fn ensure_no_error(py: Python<'_>) -> PyResult<()> {
343    if let Some(err) = PyErr::take(py) {
344        Err(err)
345    } else {
346        Ok(())
347    }
348}
349
350fn name_ptr_ignore_error(slf: &Bound<'_, PyCapsule>) -> *const c_char {
351    let ptr = unsafe { ffi::PyCapsule_GetName(slf.as_ptr()) };
352    if ptr.is_null() {
353        unsafe { ffi::PyErr_Clear() };
354    }
355    ptr
356}
357
358#[cfg(test)]
359mod tests {
360    use crate::prelude::PyModule;
361    use crate::types::capsule::PyCapsuleMethods;
362    use crate::types::module::PyModuleMethods;
363    use crate::{types::PyCapsule, Py, PyResult, Python};
364    use std::ffi::CString;
365    use std::os::raw::c_void;
366    use std::sync::mpsc::{channel, Sender};
367
368    #[test]
369    fn test_pycapsule_struct() -> PyResult<()> {
370        #[repr(C)]
371        struct Foo {
372            pub val: u32,
373        }
374
375        impl Foo {
376            fn get_val(&self) -> u32 {
377                self.val
378            }
379        }
380
381        Python::with_gil(|py| -> PyResult<()> {
382            let foo = Foo { val: 123 };
383            let name = CString::new("foo").unwrap();
384
385            let cap = PyCapsule::new(py, foo, Some(name.clone()))?;
386            assert!(cap.is_valid());
387
388            let foo_capi = unsafe { cap.reference::<Foo>() };
389            assert_eq!(foo_capi.val, 123);
390            assert_eq!(foo_capi.get_val(), 123);
391            assert_eq!(cap.name().unwrap(), Some(name.as_ref()));
392            Ok(())
393        })
394    }
395
396    #[test]
397    fn test_pycapsule_func() {
398        fn foo(x: u32) -> u32 {
399            x
400        }
401
402        let cap: Py<PyCapsule> = Python::with_gil(|py| {
403            let name = CString::new("foo").unwrap();
404            let cap = PyCapsule::new(py, foo as fn(u32) -> u32, Some(name)).unwrap();
405            cap.into()
406        });
407
408        Python::with_gil(move |py| {
409            let f = unsafe { cap.bind(py).reference::<fn(u32) -> u32>() };
410            assert_eq!(f(123), 123);
411        });
412    }
413
414    #[test]
415    fn test_pycapsule_context() -> PyResult<()> {
416        Python::with_gil(|py| {
417            let name = CString::new("foo").unwrap();
418            let cap = PyCapsule::new(py, 0, Some(name))?;
419
420            let c = cap.context()?;
421            assert!(c.is_null());
422
423            let ctx = Box::new(123_u32);
424            cap.set_context(Box::into_raw(ctx).cast())?;
425
426            let ctx_ptr: *mut c_void = cap.context()?;
427            let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<u32>()) };
428            assert_eq!(ctx, 123);
429            Ok(())
430        })
431    }
432
433    #[test]
434    fn test_pycapsule_import() -> PyResult<()> {
435        #[repr(C)]
436        struct Foo {
437            pub val: u32,
438        }
439
440        Python::with_gil(|py| -> PyResult<()> {
441            let foo = Foo { val: 123 };
442            let name = CString::new("builtins.capsule").unwrap();
443
444            let capsule = PyCapsule::new(py, foo, Some(name.clone()))?;
445
446            let module = PyModule::import(py, "builtins")?;
447            module.add("capsule", capsule)?;
448
449            // check error when wrong named passed for capsule.
450            let wrong_name = CString::new("builtins.non_existant").unwrap();
451            let result: PyResult<&Foo> = unsafe { PyCapsule::import(py, wrong_name.as_ref()) };
452            assert!(result.is_err());
453
454            // corret name is okay.
455            let cap: &Foo = unsafe { PyCapsule::import(py, name.as_ref())? };
456            assert_eq!(cap.val, 123);
457            Ok(())
458        })
459    }
460
461    #[test]
462    fn test_vec_storage() {
463        let cap: Py<PyCapsule> = Python::with_gil(|py| {
464            let name = CString::new("foo").unwrap();
465
466            let stuff: Vec<u8> = vec![1, 2, 3, 4];
467            let cap = PyCapsule::new(py, stuff, Some(name)).unwrap();
468
469            cap.into()
470        });
471
472        Python::with_gil(move |py| {
473            let ctx: &Vec<u8> = unsafe { cap.bind(py).reference() };
474            assert_eq!(ctx, &[1, 2, 3, 4]);
475        })
476    }
477
478    #[test]
479    fn test_vec_context() {
480        let context: Vec<u8> = vec![1, 2, 3, 4];
481
482        let cap: Py<PyCapsule> = Python::with_gil(|py| {
483            let name = CString::new("foo").unwrap();
484            let cap = PyCapsule::new(py, 0, Some(name)).unwrap();
485            cap.set_context(Box::into_raw(Box::new(&context)).cast())
486                .unwrap();
487
488            cap.into()
489        });
490
491        Python::with_gil(move |py| {
492            let ctx_ptr: *mut c_void = cap.bind(py).context().unwrap();
493            let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<&Vec<u8>>()) };
494            assert_eq!(ctx, &vec![1_u8, 2, 3, 4]);
495        })
496    }
497
498    #[test]
499    fn test_pycapsule_destructor() {
500        let (tx, rx) = channel::<bool>();
501
502        fn destructor(_val: u32, ctx: *mut c_void) {
503            assert!(!ctx.is_null());
504            let context = unsafe { *Box::from_raw(ctx.cast::<Sender<bool>>()) };
505            context.send(true).unwrap();
506        }
507
508        Python::with_gil(move |py| {
509            let name = CString::new("foo").unwrap();
510            let cap = PyCapsule::new_with_destructor(py, 0, Some(name), destructor).unwrap();
511            cap.set_context(Box::into_raw(Box::new(tx)).cast()).unwrap();
512        });
513
514        // the destructor was called.
515        assert_eq!(rx.recv(), Ok(true));
516    }
517
518    #[test]
519    fn test_pycapsule_no_name() {
520        Python::with_gil(|py| {
521            let cap = PyCapsule::new(py, 0usize, None).unwrap();
522
523            assert_eq!(unsafe { cap.reference::<usize>() }, &0usize);
524            assert_eq!(cap.name().unwrap(), None);
525            assert_eq!(cap.context().unwrap(), std::ptr::null_mut());
526        });
527    }
528}