pyo3/types/
capsule.rs

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