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}