Skip to main content

pyo3/types/
capsule.rs

1#![deny(clippy::undocumented_unsafe_blocks)]
2
3use crate::ffi_ptr_ext::FfiPtrExt;
4use crate::internal_tricks::box_into_non_null;
5use crate::py_result_ext::PyResultExt;
6use crate::{ffi, PyAny};
7use crate::{Bound, Python};
8use crate::{PyErr, PyResult};
9use std::ffi::{c_char, c_int, c_void};
10use std::ffi::{CStr, CString};
11use std::mem::offset_of;
12use std::ptr::{self, NonNull};
13
14/// Represents a Python Capsule
15/// as described in [Capsules](https://docs.python.org/3/c-api/capsule.html#capsules):
16/// > This subtype of PyObject represents an opaque value, useful for C extension
17/// > modules who need to pass an opaque value (as a void* pointer) through Python
18/// > code to other C code. It is often used to make a C function pointer defined
19/// > in one module available to other modules, so the regular import mechanism can
20/// > be used to access C APIs defined in dynamically loaded modules.
21///
22/// Values of this type are accessed via PyO3's smart pointers, e.g. as
23/// [`Py<PyCapsule>`][crate::Py] or [`Bound<'py, PyCapsule>`][Bound].
24///
25/// For APIs available on capsule objects, see the [`PyCapsuleMethods`] trait which is implemented for
26/// [`Bound<'py, PyCapsule>`][Bound].
27///
28/// # Example
29/// ```
30/// use pyo3::{prelude::*, types::PyCapsule, ffi::c_str};
31///
32/// #[repr(C)]
33/// struct Foo {
34///     pub val: u32,
35/// }
36///
37/// let r = Python::attach(|py| -> PyResult<()> {
38///     let foo = Foo { val: 123 };
39///     let capsule = PyCapsule::new_with_value(py, foo, c"builtins.capsule")?;
40///
41///     let module = PyModule::import(py, "builtins")?;
42///     module.add("capsule", capsule)?;
43///
44///     let cap: &Foo = unsafe { PyCapsule::import(py, c"builtins.capsule")? };
45///     assert_eq!(cap.val, 123);
46///     Ok(())
47/// });
48/// assert!(r.is_ok());
49/// ```
50#[repr(transparent)]
51pub struct PyCapsule(PyAny);
52
53pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), "types", "CapsuleType", #checkfunction=ffi::PyCapsule_CheckExact);
54
55impl PyCapsule {
56    /// Constructs a new capsule whose contents are `value`, associated with `name`.
57    /// `name` is the identifier for the capsule; if it is stored as an attribute of a module,
58    /// the name should be in the format `"modulename.attribute"`.
59    ///
60    /// Rust function items need to be cast to a function pointer (`fn(args) -> result`) to be put
61    /// into a capsule.
62    ///
63    /// # Example
64    ///
65    /// ```
66    /// use pyo3::{prelude::*, types::PyCapsule, ffi::c_str};
67    /// use std::ffi::CStr;
68    /// use std::ptr::NonNull;
69    ///
70    /// // this can be c"foo" on Rust 1.77+
71    /// const NAME: &CStr = c"foo";
72    ///
73    /// # fn main() -> PyResult<()> {
74    /// Python::attach(|py| {
75    ///     let capsule = PyCapsule::new_with_value(py, 123_u32, NAME)?;
76    ///     let val: NonNull<u32> = capsule.pointer_checked(Some(NAME))?.cast();
77    ///     assert_eq!(unsafe { *val.as_ref() }, 123);
78    /// #   Ok(())
79    /// })
80    /// # }
81    /// ```
82    ///
83    /// # Note
84    /// This function will [`Box`] the `T` and store the pointer to the box in the capsule. For
85    /// manual control over allocations use [`PyCapsule::new_with_pointer`] instead.
86    pub fn new_with_value<'py, T>(
87        py: Python<'py>,
88        value: T,
89        name: &'static CStr,
90    ) -> PyResult<Bound<'py, Self>>
91    where
92        T: 'static + Send,
93    {
94        // NOTE: Not implemented in terms of `new_with_value_and_destructor` as this allows for
95        // storing the `Box<T>` directly without allocating additionally for the destructor
96        let val = box_into_non_null(Box::new(value));
97
98        unsafe extern "C" fn destructor<T>(capsule: *mut ffi::PyObject) {
99            // SAFETY: `capsule` is known to be a borrowed reference to the capsule being destroyed
100            let name = unsafe { ffi::PyCapsule_GetName(capsule) };
101
102            // SAFETY:
103            // - `capsule` is known to be a borrowed reference to the capsule being destroyed
104            // - `name` is known to be the capsule's name
105            let ptr = unsafe { ffi::PyCapsule_GetPointer(capsule, name) };
106
107            // SAFETY: `capsule` was knowingly constructed from a `Box<T>` and is now being
108            // destroyed, so we reconstruct the Box and drop it.
109            let _ = unsafe { Box::<T>::from_raw(ptr.cast()) };
110        }
111
112        // SAFETY:
113        // - `val` is aligned and valid for a `T` until the destructor runs
114        // - `destructor` will deallocate the `Box`
115        // - `destructor` can be called from any thread as `T: Send`
116        unsafe {
117            Self::new_with_pointer_and_destructor(py, val.cast(), name, Some(destructor::<T>))
118        }
119    }
120
121    /// See [`PyCapsule::new_with_value`]
122    #[deprecated(since = "0.29.0", note = "use `PyCapsule::new_with_value` instead")]
123    pub fn new<T: 'static + Send>(
124        py: Python<'_>,
125        value: T,
126        name: Option<CString>,
127    ) -> PyResult<Bound<'_, Self>> {
128        #[allow(deprecated)]
129        Self::new_with_destructor(py, value, name, |_, _| {})
130    }
131
132    /// Constructs a new capsule whose contents are `value`, associated with `name`.
133    ///
134    /// Also provides a destructor: when the `PyCapsule` is destroyed, it will be passed the original object,
135    /// as well as a `*mut c_void` which will point to the capsule's context, if any.
136    ///
137    /// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually
138    /// be called from.
139    ///
140    /// # Note
141    /// If `destructor` panics the process will (safely) abort.
142    ///
143    /// This function will [`Box`] the `T` together with the provided destructor and store the
144    /// pointer to the box in the capsule. For manual control over allocations use
145    /// [`PyCapsule::new_with_pointer_and_destructor`] instead.
146    pub fn new_with_value_and_destructor<'py, T, F>(
147        py: Python<'py>,
148        value: T,
149        name: &'static CStr,
150        destructor: F,
151    ) -> PyResult<Bound<'py, Self>>
152    where
153        T: 'static + Send,
154        F: FnOnce(T, *mut c_void) + Send,
155    {
156        // Sanity check for capsule layout
157        debug_assert_eq!(offset_of!(CapsuleContents::<T, F>, value), 0);
158
159        // SAFETY: `Box` pointers are guaranteed to be non-null
160        let val = box_into_non_null(Box::new(CapsuleContents {
161            value,
162            destructor,
163            name: None,
164        }));
165
166        // SAFETY:
167        // - `val` is an aligned and valid pointer to a `CapsuleContents` until the destructor runs
168        // - `CapsuleContents` is `#[repr(C)]` with `T` as its first field, so `val` may be used as
169        //   an aligned and valid pointer to a `T`
170        // - `capsule_destructor` will deallocate the `Box` and call the user provided `destructor`
171        // - `capsule_destructor` can be called from any thread as `T: Send` and `F: Send`
172        unsafe {
173            Self::new_with_pointer_and_destructor(
174                py,
175                val.cast(),
176                name,
177                Some(capsule_destructor::<T, F>),
178            )
179        }
180    }
181
182    /// See [`PyCapsule::new_with_value_and_destructor`]
183    #[deprecated(
184        since = "0.29.0",
185        note = "use `PyCapsule::new_with_value_and_destructor` instead"
186    )]
187    pub fn new_with_destructor<T: 'static + Send, F: FnOnce(T, *mut c_void) + Send>(
188        py: Python<'_>,
189        value: T,
190        name: Option<CString>,
191        destructor: F,
192    ) -> PyResult<Bound<'_, Self>> {
193        // Sanity check for capsule layout
194        debug_assert_eq!(offset_of!(CapsuleContents::<T, F>, value), 0);
195
196        let name_ptr = name.as_ref().map_or(std::ptr::null(), |name| name.as_ptr());
197        let val = Box::into_raw(Box::new(CapsuleContents {
198            value,
199            destructor,
200            name,
201        }));
202
203        // SAFETY:
204        // - `val` is a non-null pointer to valid capsule data
205        // - `name_ptr` is either a valid C string or null
206        // - `destructor` will delete this data when called
207        // - thread is attached to the Python interpreter
208        // - `PyCapsule_New` returns a new reference or null on error
209        unsafe {
210            ffi::PyCapsule_New(val.cast(), name_ptr, Some(capsule_destructor::<T, F>))
211                .assume_owned_or_err(py)
212                .cast_into_unchecked()
213        }
214    }
215
216    /// Constructs a new capsule from a raw pointer.
217    ///
218    /// Unlike [`PyCapsule::new`], which stores a value and sets the capsule's pointer
219    /// to that value's address, this method uses the pointer directly. This is useful
220    /// for APIs that expect the capsule to hold a specific address (e.g., a function
221    /// pointer for FFI) rather than a pointer to owned data.
222    ///
223    /// The capsule's name should follow Python's naming convention:
224    /// `"module.attribute"` for capsules stored as module attributes.
225    ///
226    /// # Safety
227    ///
228    /// - The pointer must be valid for its intended use case.
229    /// - If the pointer refers to data, that data must outlive the capsule.
230    /// - No destructor is registered; use [`PyCapsule::new_with_pointer_and_destructor`]
231    ///   if cleanup is needed.
232    ///
233    /// # Example
234    ///
235    /// ```
236    /// use pyo3::{prelude::*, types::PyCapsule};
237    /// use std::ffi::c_void;
238    /// use std::ptr::NonNull;
239    ///
240    /// extern "C" fn my_ffi_handler(_: *mut c_void) -> *mut c_void {
241    ///     std::ptr::null_mut()
242    /// }
243    ///
244    /// Python::attach(|py| {
245    ///     let ptr = NonNull::new(my_ffi_handler as *mut c_void).unwrap();
246    ///
247    ///     // SAFETY: `ptr` is a valid function pointer
248    ///     let capsule = unsafe {
249    ///         PyCapsule::new_with_pointer(py, ptr, c"my_module.my_ffi_handler")
250    ///     }.unwrap();
251    ///
252    ///     let retrieved = capsule.pointer_checked(Some(c"my_module.my_ffi_handler")).unwrap();
253    ///     assert_eq!(retrieved.as_ptr(), my_ffi_handler as *mut c_void);
254    /// });
255    /// ```
256    pub unsafe fn new_with_pointer<'py>(
257        py: Python<'py>,
258        pointer: NonNull<c_void>,
259        name: &'static CStr,
260    ) -> PyResult<Bound<'py, Self>> {
261        // SAFETY: Caller guarantees pointer validity; destructor is None.
262        unsafe { Self::new_with_pointer_and_destructor(py, pointer, name, None) }
263    }
264
265    /// Constructs a new capsule from a raw pointer with an optional destructor.
266    ///
267    /// This is the full-featured version of [`PyCapsule::new_with_pointer`], allowing
268    /// a destructor to be called when the capsule is garbage collected.
269    ///
270    /// Unlike [`PyCapsule::new_with_destructor`], the destructor here must be a raw
271    /// `extern "C"` function pointer, not a Rust closure. This is because there is
272    /// no internal storage for a closure—the capsule holds only the raw pointer you
273    /// provide.
274    ///
275    /// # Safety
276    ///
277    /// - The pointer must be valid for its intended use case.
278    /// - If the pointer refers to data, that data must remain valid for the capsule's
279    ///   lifetime, or the destructor must clean it up.
280    /// - The destructor, if provided, must be safe to call from any thread.
281    ///
282    /// # Note
283    /// If `destructor` panics the process will (safely) abort.
284    ///
285    /// # Example
286    ///
287    /// ```
288    /// use pyo3::{prelude::*, types::PyCapsule};
289    /// use std::ffi::c_void;
290    /// use std::ptr::NonNull;
291    ///
292    /// unsafe extern "C" fn free_data(capsule: *mut pyo3::ffi::PyObject) {
293    ///     let ptr = pyo3::ffi::PyCapsule_GetPointer(capsule, c"my_module.data".as_ptr());
294    ///     if !ptr.is_null() {
295    ///         drop(Box::from_raw(ptr as *mut u32));
296    ///     }
297    /// }
298    ///
299    /// Python::attach(|py| {
300    ///     let data = Box::new(42u32);
301    ///     let ptr = NonNull::new(Box::into_raw(data).cast::<c_void>()).unwrap();
302    ///
303    ///     // SAFETY: `ptr` is valid; `free_data` will deallocate it
304    ///     let capsule = unsafe {
305    ///         PyCapsule::new_with_pointer_and_destructor(
306    ///             py,
307    ///             ptr,
308    ///             c"my_module.data",
309    ///             Some(free_data),
310    ///         )
311    ///     }.unwrap();
312    /// });
313    /// ```
314    pub unsafe fn new_with_pointer_and_destructor<'py>(
315        py: Python<'py>,
316        pointer: NonNull<c_void>,
317        name: &'static CStr,
318        destructor: Option<ffi::PyCapsule_Destructor>,
319    ) -> PyResult<Bound<'py, Self>> {
320        let name_ptr = name.as_ptr();
321
322        // SAFETY:
323        // - `pointer` is non-null (guaranteed by `NonNull`)
324        // - `name_ptr` points to a valid C string (guaranteed by `&'static CStr`)
325        // - `destructor` is either None or a valid function pointer (caller guarantees)
326        // - Thread is attached to the Python interpreter
327        unsafe {
328            ffi::PyCapsule_New(pointer.as_ptr(), name_ptr, destructor)
329                .assume_owned_or_err(py)
330                .cast_into_unchecked()
331        }
332    }
333
334    /// Imports an existing capsule.
335    ///
336    /// The `name` should match the path to the module attribute exactly in the form
337    /// of `"module.attribute"`, which should be the same as the name within the capsule.
338    ///
339    /// # Safety
340    ///
341    /// It must be known that the capsule imported by `name` contains an item of type `T`.
342    pub unsafe fn import<'py, T>(py: Python<'py>, name: &CStr) -> PyResult<&'py T> {
343        // SAFETY: `name` is a valid C string, thread is attached to the Python interpreter
344        let ptr = unsafe { ffi::PyCapsule_Import(name.as_ptr(), false as c_int) };
345        if ptr.is_null() {
346            Err(PyErr::fetch(py))
347        } else {
348            // SAFETY: caller has upheld the safety contract
349            Ok(unsafe { &*ptr.cast::<T>() })
350        }
351    }
352}
353
354/// Implementation of functionality for [`PyCapsule`].
355///
356/// These methods are defined for the `Bound<'py, PyCapsule>` smart pointer, so to use method call
357/// syntax these methods are separated into a trait, because stable Rust does not yet support
358/// `arbitrary_self_types`.
359///
360/// # Name checking
361///
362/// Capsules contain pointers to arbitrary data which is cast to a specific type at runtime. This is
363/// inherently quite dangerous, so Python allows capsules to be "named" to provide a hint as to
364/// what data is contained in the capsule. Although not a perfect solution, this is better than
365/// nothing.
366///
367/// The methods in this trait take the `name` as an `Option<&CStr>`, which is compared to the name
368/// stored in the capsule (with `None` being used to indicate the capsule has no name).
369#[doc(alias = "PyCapsule")]
370pub trait PyCapsuleMethods<'py>: crate::sealed::Sealed {
371    /// Sets the context pointer in the capsule.
372    ///
373    /// Returns an error if this capsule is not valid.
374    ///
375    /// # Notes
376    ///
377    /// The context is treated much like the value of the capsule, but should likely act as
378    /// a place to store any state management when using the capsule.
379    ///
380    /// If you want to store a Rust value as the context, and drop it from the destructor, use
381    /// `Box::into_raw` to convert it into a pointer, see the example.
382    ///
383    /// # Example
384    ///
385    /// ```
386    /// use std::ffi::c_void;
387    /// use std::sync::mpsc::{channel, Sender};
388    /// use pyo3::{prelude::*, types::PyCapsule};
389    ///
390    /// # fn main() -> PyResult<()> {
391    /// let (tx, rx) = channel::<String>();
392    ///
393    /// fn destructor(val: u32, context: *mut c_void) {
394    ///     let ctx = unsafe { *Box::from_raw(context.cast::<Sender<String>>()) };
395    ///     ctx.send("Destructor called!".to_string()).unwrap();
396    /// }
397    ///
398    /// Python::attach(|py| {
399    ///     let capsule = PyCapsule::new_with_value_and_destructor(
400    ///         py,
401    ///         123,
402    ///         c"foo",
403    ///         destructor as fn(u32, *mut c_void)
404    ///     )?;
405    ///     let context = Box::new(tx);  // `Sender<String>` is our context, box it up and ship it!
406    ///     capsule.set_context(Box::into_raw(context).cast())?;
407    ///     // This scope will end, causing our destructor to be called...
408    /// #   Ok::<_, PyErr>(())
409    /// })?;
410    ///
411    /// assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
412    /// # Ok(())
413    /// }
414    /// ```
415    fn set_context(&self, context: *mut c_void) -> PyResult<()>;
416
417    /// Gets the current context stored in the capsule. If there is no context, the pointer
418    /// will be null.
419    ///
420    /// Returns an error if this capsule is not valid.
421    fn context(&self) -> PyResult<*mut c_void>;
422
423    /// Obtains a reference dereferenced from the pointer of this capsule, without checking its name.
424    ///
425    /// Because this method encourages dereferencing the pointer for longer than necessary, it
426    /// is deprecated. Prefer to use [`pointer_checked()`][PyCapsuleMethods::pointer_checked]
427    /// and dereference the pointer only for as short a time as possible.
428    ///
429    /// # Safety
430    ///
431    /// This performs a dereference of the pointer returned from [`pointer()`][PyCapsuleMethods::pointer].
432    ///
433    /// See the safety notes on [`pointer_checked()`][PyCapsuleMethods::pointer_checked].
434    #[deprecated(since = "0.27.0", note = "to be removed, see `pointer_checked()`")]
435    unsafe fn reference<T>(&self) -> &T;
436
437    /// Gets the raw pointer stored in this capsule, without checking its name.
438    #[deprecated(since = "0.27.0", note = "use `pointer_checked()` instead")]
439    fn pointer(&self) -> *mut c_void;
440
441    /// Gets the raw pointer stored in this capsule.
442    ///
443    /// Returns an error if the capsule is not [valid][`PyCapsuleMethods::is_valid_checked`] with the given `name`.
444    ///
445    /// # Safety
446    ///
447    /// This function itself is not `unsafe`, but dereferencing the returned pointer to produce a reference
448    /// is very dangerous:
449    /// - The pointer will need to be [.cast()][NonNull::cast] to a concrete type before dereferencing.
450    ///   As per [name checking](#name-checking), there is no way to statically guarantee this cast is
451    ///   correct, the name is the best available hint to guard against accidental misuse.
452    /// - Arbitrary Python code can change the contents of the capsule, which may invalidate the
453    ///   pointer. The pointer and the reference produced by dereferencing the pointer should both
454    ///   be considered invalid after arbitrary Python code has run.
455    ///
456    /// Users should take care to cast to the correct type and consume the pointer for as little
457    /// duration as possible.
458    fn pointer_checked(&self, name: Option<&CStr>) -> PyResult<NonNull<c_void>>;
459
460    /// Checks if the capsule pointer is not null.
461    ///
462    /// This does not perform any check on the name of the capsule, which is the only mechanism
463    /// that Python provides to make sure that the pointer has the expected type. Prefer to use
464    /// [`is_valid_checked()`][Self::is_valid_checked()] instead.
465    #[deprecated(since = "0.27.0", note = "use `is_valid_checked()` instead")]
466    fn is_valid(&self) -> bool;
467
468    /// Checks that the capsule name matches `name` and that the pointer is not null.
469    fn is_valid_checked(&self, name: Option<&CStr>) -> bool;
470
471    /// Retrieves the name of this capsule, if set.
472    ///
473    /// Returns an error if this capsule is not valid.
474    ///
475    /// See [`CapsuleName`] for details of how to consume the return value.
476    fn name(&self) -> PyResult<Option<CapsuleName>>;
477}
478
479impl<'py> PyCapsuleMethods<'py> for Bound<'py, PyCapsule> {
480    #[allow(clippy::not_unsafe_ptr_arg_deref)]
481    fn set_context(&self, context: *mut c_void) -> PyResult<()> {
482        // SAFETY:
483        // - `self.as_ptr()` is a valid object pointer
484        // - `context` is user-provided
485        // - thread is attached to the Python interpreter
486        let result = unsafe { ffi::PyCapsule_SetContext(self.as_ptr(), context) };
487        if result != 0 {
488            Err(PyErr::fetch(self.py()))
489        } else {
490            Ok(())
491        }
492    }
493
494    fn context(&self) -> PyResult<*mut c_void> {
495        // SAFETY:
496        // - `self.as_ptr()` is a valid object pointer
497        // - thread is attached to the Python interpreter
498        let ctx = unsafe { ffi::PyCapsule_GetContext(self.as_ptr()) };
499        if ctx.is_null() {
500            ensure_no_error(self.py())?
501        }
502        Ok(ctx)
503    }
504
505    #[allow(deprecated)]
506    unsafe fn reference<T>(&self) -> &T {
507        // SAFETY:
508        // - caller has upheld the safety contract
509        // - thread is attached to the Python interpreter
510        unsafe { &*self.pointer().cast() }
511    }
512
513    fn pointer(&self) -> *mut c_void {
514        // SAFETY: arguments to `PyCapsule_GetPointer` are valid, errors are handled properly
515        unsafe {
516            let ptr = ffi::PyCapsule_GetPointer(self.as_ptr(), name_ptr_ignore_error(self));
517            if ptr.is_null() {
518                ffi::PyErr_Clear();
519            }
520            ptr
521        }
522    }
523
524    fn pointer_checked(&self, name: Option<&CStr>) -> PyResult<NonNull<c_void>> {
525        // SAFETY:
526        // - `self.as_ptr()` is a valid object pointer
527        // - `name_ptr` is either a valid C string or null
528        // - thread is attached to the Python interpreter
529        let ptr = unsafe { ffi::PyCapsule_GetPointer(self.as_ptr(), name_ptr(name)) };
530        NonNull::new(ptr).ok_or_else(|| PyErr::fetch(self.py()))
531    }
532
533    fn is_valid(&self) -> bool {
534        // SAFETY: As well as if the stored pointer is null, PyCapsule_IsValid also returns false if
535        // self.as_ptr() is null or not a ptr to a PyCapsule object. Both of these are guaranteed
536        // to not be the case thanks to invariants of this PyCapsule struct.
537        let r = unsafe { ffi::PyCapsule_IsValid(self.as_ptr(), name_ptr_ignore_error(self)) };
538        r != 0
539    }
540
541    fn is_valid_checked(&self, name: Option<&CStr>) -> bool {
542        // SAFETY:
543        // - `self.as_ptr()` is a valid object pointer
544        // - `name_ptr` is either a valid C string or null
545        // - thread is attached to the Python interpreter
546        let r = unsafe { ffi::PyCapsule_IsValid(self.as_ptr(), name_ptr(name)) };
547        r != 0
548    }
549
550    fn name(&self) -> PyResult<Option<CapsuleName>> {
551        // SAFETY:
552        // - `self.as_ptr()` is a valid object pointer
553        // - thread is attached to the Python interpreter
554        let name = unsafe { ffi::PyCapsule_GetName(self.as_ptr()) };
555
556        match NonNull::new(name.cast_mut()) {
557            Some(name) => Ok(Some(CapsuleName { ptr: name })),
558            None => {
559                ensure_no_error(self.py())?;
560                Ok(None)
561            }
562        }
563    }
564}
565
566/// The name given to a `capsule` object.
567///
568/// This is a thin wrapper around `*const c_char`, which can be accessed with the [`as_ptr`][Self::as_ptr]
569/// method. The [`as_cstr`][Self::as_cstr] method can be used as a convenience to access the name as a `&CStr`.
570///
571/// There is no guarantee that this capsule name pointer valid for any length of time, as arbitrary
572/// Python code may change the name of a capsule object (by reaching native code which calls
573/// [`PyCapsule_SetName`][ffi::PyCapsule_SetName]). See the safety notes on [`as_cstr`][Self::as_cstr].
574#[derive(Clone, Copy)]
575pub struct CapsuleName {
576    /// Pointer to the name c-string, known to be non-null.
577    ptr: NonNull<c_char>,
578}
579
580impl CapsuleName {
581    /// Returns the capsule name as a `&CStr`.
582    ///
583    /// Note: this method is a thin wrapper around [`CStr::from_ptr`] so (as of Rust 1.91) incurs a
584    /// length calculation on each call.
585    ///
586    /// # Safety
587    ///
588    /// There is no guarantee that the capsule name remains valid for any length of time, as arbitrary
589    /// Python code may change the name of the capsule. The caller should be aware of any conventions
590    /// of the capsule in question related to the lifetime of the name (many capsule names are
591    /// statically allocated, i.e. have the `'static` lifetime, but Python does not require this).
592    ///
593    /// The returned lifetime `'a` is not related to the lifetime of the capsule itself, and the caller is
594    /// responsible for using the `&CStr` for as short a time as possible.
595    pub unsafe fn as_cstr<'a>(self) -> &'a CStr {
596        // SAFETY: caller has upheld the safety contract
597        unsafe { CStr::from_ptr(self.as_ptr()) }
598    }
599
600    /// Returns the raw pointer to the capsule name.
601    pub fn as_ptr(self) -> *const c_char {
602        self.ptr.as_ptr().cast_const()
603    }
604}
605
606// C layout, as casting the capsule pointer to `T` depends on `T` being first.
607#[repr(C)]
608struct CapsuleContents<T: 'static + Send, D: FnOnce(T, *mut c_void) + Send> {
609    /// Value of the capsule
610    value: T,
611    /// Destructor to be used by the capsule
612    destructor: D,
613    /// Name used when creating the capsule
614    // TODO: remove this field when `PyCapsule::new` and `PyCapsule::new_with_destructor` are removed
615    name: Option<CString>,
616}
617
618// Wrapping ffi::PyCapsule_Destructor for a user supplied FnOnce(T) for capsule destructor
619unsafe extern "C" fn capsule_destructor<T: 'static + Send, F: FnOnce(T, *mut c_void) + Send>(
620    capsule: *mut ffi::PyObject,
621) {
622    /// Gets the pointer and context from the capsule.
623    ///
624    /// # Safety
625    ///
626    /// - `capsule` must be a valid capsule object
627    unsafe fn get_pointer_ctx(capsule: *mut ffi::PyObject) -> (*mut c_void, *mut c_void) {
628        // SAFETY: `capsule` is known to be a borrowed reference to the capsule being destroyed
629        let name = unsafe { ffi::PyCapsule_GetName(capsule) };
630
631        // SAFETY:
632        // - `capsule` is known to be a borrowed reference to the capsule being destroyed
633        // - `name` is known to be the capsule's name
634        let ptr = unsafe { ffi::PyCapsule_GetPointer(capsule, name) };
635
636        // SAFETY:
637        // - `capsule` is known to be a borrowed reference to the capsule being destroyed
638        let ctx = unsafe { ffi::PyCapsule_GetContext(capsule) };
639
640        (ptr, ctx)
641    }
642
643    // SAFETY: `capsule` is known to be a valid capsule object
644    let (ptr, ctx) = unsafe { get_pointer_ctx(capsule) };
645
646    // SAFETY: `capsule` was knowingly constructed with a boxed `CapsuleContents<T, F>`
647    // and is now being destroyed, so we can move the data from the box.
648    let CapsuleContents::<T, F> {
649        value, destructor, ..
650    } = *unsafe { Box::from_raw(ptr.cast()) };
651
652    destructor(value, ctx);
653}
654
655fn ensure_no_error(py: Python<'_>) -> PyResult<()> {
656    if let Some(err) = PyErr::take(py) {
657        Err(err)
658    } else {
659        Ok(())
660    }
661}
662
663fn name_ptr_ignore_error(slf: &Bound<'_, PyCapsule>) -> *const c_char {
664    // SAFETY:
665    // - `slf` is known to be a valid capsule object
666    // - thread is attached to the Python interpreter
667    let ptr = unsafe { ffi::PyCapsule_GetName(slf.as_ptr()) };
668    if ptr.is_null() {
669        // SAFETY: thread is attached to the Python interpreter
670        unsafe { ffi::PyErr_Clear() };
671    }
672    ptr
673}
674
675fn name_ptr(name: Option<&CStr>) -> *const c_char {
676    match name {
677        Some(name) => name.as_ptr(),
678        None => ptr::null(),
679    }
680}
681
682#[cfg(test)]
683mod tests {
684    use crate::prelude::PyModule;
685    use crate::types::capsule::PyCapsuleMethods;
686    use crate::types::module::PyModuleMethods;
687    use crate::{types::PyCapsule, Py, PyResult, Python};
688    use std::ffi::{c_void, CStr};
689    use std::ptr::NonNull;
690    use std::sync::mpsc::{channel, Sender};
691
692    const NAME: &CStr = c"foo";
693
694    #[test]
695    fn test_pycapsule_struct() {
696        #[repr(C)]
697        struct Foo {
698            pub val: u32,
699        }
700
701        impl Foo {
702            fn get_val(&self) -> u32 {
703                self.val
704            }
705        }
706
707        Python::attach(|py| {
708            let foo = Foo { val: 123 };
709
710            let cap = PyCapsule::new_with_value(py, foo, NAME).unwrap();
711            assert!(cap.is_valid_checked(Some(NAME)));
712
713            let foo_capi = cap.pointer_checked(Some(NAME)).unwrap().cast::<Foo>();
714            // SAFETY: `foo_capi` contains a `Foo` and will be valid for the duration of the assert
715            assert_eq!(unsafe { foo_capi.as_ref() }.val, 123);
716            // SAFETY: as above
717            assert_eq!(unsafe { foo_capi.as_ref() }.get_val(), 123);
718            assert_eq!(
719                // SAFETY: `cap.name()` has a non-null name
720                unsafe { CStr::from_ptr(cap.name().unwrap().unwrap().as_ptr()) },
721                NAME
722            );
723            // SAFETY: as above
724            assert_eq!(unsafe { cap.name().unwrap().unwrap().as_cstr() }, NAME)
725        })
726    }
727
728    #[test]
729    fn test_pycapsule_func() {
730        fn foo(x: u32) -> u32 {
731            x
732        }
733
734        let cap: Py<PyCapsule> = Python::attach(|py| {
735            let cap = PyCapsule::new_with_value(py, foo as fn(u32) -> u32, NAME).unwrap();
736            cap.into()
737        });
738
739        Python::attach(move |py| {
740            let f = cap
741                .bind(py)
742                .pointer_checked(Some(NAME))
743                .unwrap()
744                .cast::<fn(u32) -> u32>();
745            // SAFETY: `f` contains a `fn(u32) -> u32` and will be valid for the duration of the assert
746            assert_eq!(unsafe { f.as_ref() }(123), 123);
747        });
748    }
749
750    #[test]
751    fn test_pycapsule_context() {
752        Python::attach(|py| {
753            let cap = PyCapsule::new_with_value(py, 0, NAME).unwrap();
754
755            let c = cap.context().unwrap();
756            assert!(c.is_null());
757
758            let ctx = Box::new(123_u32);
759            cap.set_context(Box::into_raw(ctx).cast()).unwrap();
760
761            let ctx_ptr: *mut c_void = cap.context().unwrap();
762            // SAFETY: `ctx_ptr` contains a boxed `u32` which is being moved out of the capsule
763            let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<u32>()) };
764            assert_eq!(ctx, 123);
765        })
766    }
767
768    #[test]
769    fn test_pycapsule_import() {
770        #[repr(C)]
771        struct Foo {
772            pub val: u32,
773        }
774
775        Python::attach(|py| {
776            let foo = Foo { val: 123 };
777            let name = c"builtins.capsule";
778
779            let capsule = PyCapsule::new_with_value(py, foo, name).unwrap();
780
781            let module = PyModule::import(py, "builtins").unwrap();
782            module.add("capsule", capsule).unwrap();
783
784            // check error when wrong named passed for capsule.
785            // SAFETY: this function will fail so the cast is never done
786            let result: PyResult<&Foo> = unsafe { PyCapsule::import(py, c"builtins.non_existent") };
787            assert!(result.is_err());
788
789            // correct name is okay.
790            // SAFETY: we know the capsule at `name` contains a `Foo`
791            let cap: &Foo = unsafe { PyCapsule::import(py, name) }.unwrap();
792            assert_eq!(cap.val, 123);
793        })
794    }
795
796    #[test]
797    fn test_vec_storage() {
798        let cap: Py<PyCapsule> = Python::attach(|py| {
799            let stuff: Vec<u8> = vec![1, 2, 3, 4];
800            let cap = PyCapsule::new_with_value(py, stuff, NAME).unwrap();
801            cap.into()
802        });
803
804        Python::attach(move |py| {
805            let stuff = cap
806                .bind(py)
807                .pointer_checked(Some(NAME))
808                .unwrap()
809                .cast::<Vec<u8>>();
810            // SAFETY: `stuff` contains a `Vec<u8>` and will be valid for the duration of the assert
811            assert_eq!(unsafe { stuff.as_ref() }, &[1, 2, 3, 4]);
812        })
813    }
814
815    #[test]
816    fn test_vec_context() {
817        let context: Vec<u8> = vec![1, 2, 3, 4];
818
819        let cap: Py<PyCapsule> = Python::attach(|py| {
820            let cap = PyCapsule::new_with_value(py, 0, NAME).unwrap();
821            cap.set_context(Box::into_raw(Box::new(&context)).cast())
822                .unwrap();
823
824            cap.into()
825        });
826
827        Python::attach(move |py| {
828            let ctx_ptr: *mut c_void = cap.bind(py).context().unwrap();
829            // SAFETY: `ctx_ptr` contains a boxed `&Vec<u8>` which is being moved out of the capsule
830            let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<&Vec<u8>>()) };
831            assert_eq!(ctx, &vec![1_u8, 2, 3, 4]);
832        })
833    }
834
835    #[test]
836    fn test_pycapsule_destructor() {
837        let (tx, rx) = channel::<bool>();
838
839        fn destructor(_val: u32, ctx: *mut c_void) {
840            assert!(!ctx.is_null());
841            // SAFETY: `ctx` is known to be a boxed `Sender<bool>` needing deletion
842            let context = unsafe { *Box::from_raw(ctx.cast::<Sender<bool>>()) };
843            context.send(true).unwrap();
844        }
845
846        Python::attach(move |py| {
847            let cap = PyCapsule::new_with_value_and_destructor(py, 0, NAME, destructor).unwrap();
848            cap.set_context(Box::into_raw(Box::new(tx)).cast()).unwrap();
849        });
850
851        // the destructor was called.
852        assert_eq!(rx.recv(), Ok(true));
853    }
854
855    #[test]
856    #[allow(deprecated)]
857    fn test_pycapsule_no_name() {
858        Python::attach(|py| {
859            let cap = PyCapsule::new(py, 0usize, None).unwrap();
860
861            assert_eq!(
862                // SAFETY: `cap` is known to contain a `usize`
863                unsafe { cap.pointer_checked(None).unwrap().cast::<usize>().as_ref() },
864                &0usize
865            );
866            assert!(cap.name().unwrap().is_none());
867            assert_eq!(cap.context().unwrap(), std::ptr::null_mut());
868        });
869    }
870
871    #[test]
872    fn test_pycapsule_new_with_pointer() {
873        extern "C" fn dummy_handler(_: *mut c_void) -> *mut c_void {
874            std::ptr::null_mut()
875        }
876
877        let fn_ptr =
878            NonNull::new(dummy_handler as *mut c_void).expect("function pointer is non-null");
879
880        Python::attach(|py| {
881            // SAFETY: `fn_ptr` is known to point to `dummy_handler`
882            let capsule =
883                unsafe { PyCapsule::new_with_pointer(py, fn_ptr, c"test.dummy_handler") }.unwrap();
884
885            let retrieved_ptr = capsule
886                .pointer_checked(Some(c"test.dummy_handler"))
887                .unwrap();
888            assert_eq!(retrieved_ptr.as_ptr(), fn_ptr.as_ptr());
889        });
890    }
891
892    #[test]
893    fn test_pycapsule_new_with_pointer_and_destructor() {
894        use std::sync::mpsc::{channel, TryRecvError};
895
896        let (tx, rx) = channel::<bool>();
897
898        unsafe extern "C" fn destructor_fn(capsule: *mut crate::ffi::PyObject) {
899            // SAFETY:
900            // - `capsule` is a valid capsule object being destroyed by Python
901            // - The context was set to a valid `Box<Sender<bool>>` below
902            unsafe {
903                let ctx = crate::ffi::PyCapsule_GetContext(capsule);
904                if !ctx.is_null() {
905                    let sender: Box<Sender<bool>> = Box::from_raw(ctx.cast());
906                    let _ = sender.send(true);
907                }
908            }
909        }
910
911        let dummy_ptr =
912            NonNull::new(0xDEADBEEF as *mut c_void).expect("function pointer is non-null");
913
914        Python::attach(|py| {
915            // SAFETY:
916            // - `dummy_ptr` is non-null (it's a made-up address for testing)
917            // - We're providing a valid destructor function
918            let capsule = unsafe {
919                PyCapsule::new_with_pointer_and_destructor(
920                    py,
921                    dummy_ptr,
922                    c"test.destructor_capsule",
923                    Some(destructor_fn),
924                )
925            }
926            .unwrap();
927
928            // Store the sender in the capsule's context
929            let sender_box = Box::new(tx);
930            capsule
931                .set_context(Box::into_raw(sender_box).cast())
932                .unwrap();
933
934            // The destructor hasn't fired yet
935            assert_eq!(rx.try_recv(), Err(TryRecvError::Empty));
936        });
937
938        // After Python::attach scope ends, the capsule should be destroyed
939        assert_eq!(rx.recv(), Ok(true));
940    }
941
942    #[test]
943    fn test_pycapsule_pointer_checked_wrong_name() {
944        Python::attach(|py| {
945            let cap = PyCapsule::new_with_value(py, 123u32, c"correct.name").unwrap();
946
947            // Requesting with wrong name should fail
948            let result = cap.pointer_checked(Some(c"wrong.name"));
949            assert!(result.is_err());
950
951            // Requesting with None when capsule has a name should also fail
952            let result = cap.pointer_checked(None);
953            assert!(result.is_err());
954        });
955    }
956
957    #[test]
958    #[allow(deprecated)]
959    fn test_pycapsule_pointer_checked_none_vs_some() {
960        Python::attach(|py| {
961            // Capsule with no name
962            let cap_no_name = PyCapsule::new(py, 123u32, None).unwrap();
963
964            // Should succeed with None
965            assert!(cap_no_name.pointer_checked(None).is_ok());
966
967            // Should fail with Some(name)
968            let result = cap_no_name.pointer_checked(Some(c"some.name"));
969            assert!(result.is_err());
970        });
971    }
972
973    #[test]
974    fn test_pycapsule_is_valid_checked_wrong_name() {
975        Python::attach(|py| {
976            let cap = PyCapsule::new_with_value(py, 123u32, c"correct.name").unwrap();
977
978            // Should be valid with correct name
979            assert!(cap.is_valid_checked(Some(c"correct.name")));
980
981            // Should be invalid with wrong name
982            assert!(!cap.is_valid_checked(Some(c"wrong.name")));
983
984            // Should be invalid with None when capsule has a name
985            assert!(!cap.is_valid_checked(None));
986        });
987    }
988
989    #[test]
990    #[allow(deprecated)]
991    fn test_pycapsule_is_valid_checked_no_name() {
992        Python::attach(|py| {
993            let cap = PyCapsule::new(py, 123u32, None).unwrap();
994
995            // Should be valid with None
996            assert!(cap.is_valid_checked(None));
997
998            // Should be invalid with any name
999            assert!(!cap.is_valid_checked(Some(c"any.name")));
1000        });
1001    }
1002
1003    #[test]
1004    fn test_pycapsule_context_on_invalid_capsule() {
1005        Python::attach(|py| {
1006            let cap = PyCapsule::new_with_value(py, 123u32, NAME).unwrap();
1007
1008            // Invalidate the capsule
1009            // SAFETY: intentionally breaking the capsule for testing
1010            unsafe {
1011                crate::ffi::PyCapsule_SetPointer(cap.as_ptr(), std::ptr::null_mut());
1012            }
1013
1014            // context() on invalid capsule should fail
1015            let result = cap.context();
1016            assert!(result.is_err());
1017        });
1018    }
1019
1020    #[test]
1021    fn test_pycapsule_import_wrong_module() {
1022        Python::attach(|py| {
1023            // Try to import from a non-existent module
1024            // SAFETY: we expect this to fail, no cast will occur
1025            let result: PyResult<&u32> =
1026                unsafe { PyCapsule::import(py, c"nonexistent_module.capsule") };
1027            assert!(result.is_err());
1028        });
1029    }
1030
1031    #[test]
1032    fn test_pycapsule_import_wrong_attribute() {
1033        Python::attach(|py| {
1034            // Create a capsule and register it
1035            let cap = PyCapsule::new_with_value(py, 123u32, c"builtins.test_cap").unwrap();
1036            let module = PyModule::import(py, "builtins").unwrap();
1037            module.add("test_cap", cap).unwrap();
1038
1039            // Try to import with wrong attribute name
1040            // SAFETY: we expect this to fail
1041            let result: PyResult<&u32> =
1042                unsafe { PyCapsule::import(py, c"builtins.wrong_attribute") };
1043            assert!(result.is_err());
1044        });
1045    }
1046
1047    #[test]
1048    fn test_capsule_with_zero_sized_type() {
1049        Python::attach(|py| {
1050            let cap = PyCapsule::new_with_value(py, (), c"test_capsule_zst").unwrap();
1051            let content = cap.pointer_checked(Some(c"test_capsule_zst")).unwrap();
1052            // SAFETY: Capsule invariant: the returned pointer is the given pointer
1053            assert_eq!(*unsafe { content.cast::<()>().as_ref() }, ());
1054        })
1055    }
1056}