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 = unsafe { ffi::PyCapsule_Import(name.as_ptr(), false as c_int) };
167 if ptr.is_null() {
168 Err(PyErr::fetch(py))
169 } else {
170 Ok(unsafe { &*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 unsafe { &*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 unsafe {
320 let ptr: *mut c_void = ffi::PyCapsule_GetPointer(capsule, name:ffi::PyCapsule_GetName(capsule));
321 let ctx: *mut c_void = ffi::PyCapsule_GetContext(capsule);
322 let CapsuleContents {
323 value: T, destructor: F, ..
324 } = *Box::from_raw(ptr.cast::<CapsuleContents<T, F>>());
325 destructor(value, ctx)
326 }
327}
328
329/// Guarantee `T` is not zero sized at compile time.
330// 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>`
331#[doc(hidden)]
332pub trait AssertNotZeroSized: Sized {
333 const _CONDITION: usize = (std::mem::size_of::<Self>() == 0) as usize;
334 const _CHECK: &'static str =
335 ["PyCapsule value type T must not be zero-sized!"][Self::_CONDITION];
336 #[allow(path_statements, clippy::no_effect)]
337 fn assert_not_zero_sized(&self) {
338 <Self as AssertNotZeroSized>::_CHECK;
339 }
340}
341
342impl<T> AssertNotZeroSized for T {}
343
344fn ensure_no_error(py: Python<'_>) -> PyResult<()> {
345 if let Some(err: PyErr) = PyErr::take(py) {
346 Err(err)
347 } else {
348 Ok(())
349 }
350}
351
352fn name_ptr_ignore_error(slf: &Bound<'_, PyCapsule>) -> *const c_char {
353 let ptr: *const {unknown} = unsafe { ffi::PyCapsule_GetName(capsule:slf.as_ptr()) };
354 if ptr.is_null() {
355 unsafe { ffi::PyErr_Clear() };
356 }
357 ptr
358}
359
360#[cfg(test)]
361mod tests {
362 use crate::prelude::PyModule;
363 use crate::types::capsule::PyCapsuleMethods;
364 use crate::types::module::PyModuleMethods;
365 use crate::{types::PyCapsule, Py, PyResult, Python};
366 use std::ffi::CString;
367 use std::os::raw::c_void;
368 use std::sync::mpsc::{channel, Sender};
369
370 #[test]
371 fn test_pycapsule_struct() -> PyResult<()> {
372 #[repr(C)]
373 struct Foo {
374 pub val: u32,
375 }
376
377 impl Foo {
378 fn get_val(&self) -> u32 {
379 self.val
380 }
381 }
382
383 Python::with_gil(|py| -> PyResult<()> {
384 let foo = Foo { val: 123 };
385 let name = CString::new("foo").unwrap();
386
387 let cap = PyCapsule::new(py, foo, Some(name.clone()))?;
388 assert!(cap.is_valid());
389
390 let foo_capi = unsafe { cap.reference::<Foo>() };
391 assert_eq!(foo_capi.val, 123);
392 assert_eq!(foo_capi.get_val(), 123);
393 assert_eq!(cap.name().unwrap(), Some(name.as_ref()));
394 Ok(())
395 })
396 }
397
398 #[test]
399 fn test_pycapsule_func() {
400 fn foo(x: u32) -> u32 {
401 x
402 }
403
404 let cap: Py<PyCapsule> = Python::with_gil(|py| {
405 let name = CString::new("foo").unwrap();
406 let cap = PyCapsule::new(py, foo as fn(u32) -> u32, Some(name)).unwrap();
407 cap.into()
408 });
409
410 Python::with_gil(move |py| {
411 let f = unsafe { cap.bind(py).reference::<fn(u32) -> u32>() };
412 assert_eq!(f(123), 123);
413 });
414 }
415
416 #[test]
417 fn test_pycapsule_context() -> PyResult<()> {
418 Python::with_gil(|py| {
419 let name = CString::new("foo").unwrap();
420 let cap = PyCapsule::new(py, 0, Some(name))?;
421
422 let c = cap.context()?;
423 assert!(c.is_null());
424
425 let ctx = Box::new(123_u32);
426 cap.set_context(Box::into_raw(ctx).cast())?;
427
428 let ctx_ptr: *mut c_void = cap.context()?;
429 let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<u32>()) };
430 assert_eq!(ctx, 123);
431 Ok(())
432 })
433 }
434
435 #[test]
436 fn test_pycapsule_import() -> PyResult<()> {
437 #[repr(C)]
438 struct Foo {
439 pub val: u32,
440 }
441
442 Python::with_gil(|py| -> PyResult<()> {
443 let foo = Foo { val: 123 };
444 let name = CString::new("builtins.capsule").unwrap();
445
446 let capsule = PyCapsule::new(py, foo, Some(name.clone()))?;
447
448 let module = PyModule::import(py, "builtins")?;
449 module.add("capsule", capsule)?;
450
451 // check error when wrong named passed for capsule.
452 let wrong_name = CString::new("builtins.non_existant").unwrap();
453 let result: PyResult<&Foo> = unsafe { PyCapsule::import(py, wrong_name.as_ref()) };
454 assert!(result.is_err());
455
456 // corret name is okay.
457 let cap: &Foo = unsafe { PyCapsule::import(py, name.as_ref())? };
458 assert_eq!(cap.val, 123);
459 Ok(())
460 })
461 }
462
463 #[test]
464 fn test_vec_storage() {
465 let cap: Py<PyCapsule> = Python::with_gil(|py| {
466 let name = CString::new("foo").unwrap();
467
468 let stuff: Vec<u8> = vec![1, 2, 3, 4];
469 let cap = PyCapsule::new(py, stuff, Some(name)).unwrap();
470
471 cap.into()
472 });
473
474 Python::with_gil(move |py| {
475 let ctx: &Vec<u8> = unsafe { cap.bind(py).reference() };
476 assert_eq!(ctx, &[1, 2, 3, 4]);
477 })
478 }
479
480 #[test]
481 fn test_vec_context() {
482 let context: Vec<u8> = vec![1, 2, 3, 4];
483
484 let cap: Py<PyCapsule> = Python::with_gil(|py| {
485 let name = CString::new("foo").unwrap();
486 let cap = PyCapsule::new(py, 0, Some(name)).unwrap();
487 cap.set_context(Box::into_raw(Box::new(&context)).cast())
488 .unwrap();
489
490 cap.into()
491 });
492
493 Python::with_gil(move |py| {
494 let ctx_ptr: *mut c_void = cap.bind(py).context().unwrap();
495 let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<&Vec<u8>>()) };
496 assert_eq!(ctx, &vec![1_u8, 2, 3, 4]);
497 })
498 }
499
500 #[test]
501 fn test_pycapsule_destructor() {
502 let (tx, rx) = channel::<bool>();
503
504 fn destructor(_val: u32, ctx: *mut c_void) {
505 assert!(!ctx.is_null());
506 let context = unsafe { *Box::from_raw(ctx.cast::<Sender<bool>>()) };
507 context.send(true).unwrap();
508 }
509
510 Python::with_gil(move |py| {
511 let name = CString::new("foo").unwrap();
512 let cap = PyCapsule::new_with_destructor(py, 0, Some(name), destructor).unwrap();
513 cap.set_context(Box::into_raw(Box::new(tx)).cast()).unwrap();
514 });
515
516 // the destructor was called.
517 assert_eq!(rx.recv(), Ok(true));
518 }
519
520 #[test]
521 fn test_pycapsule_no_name() {
522 Python::with_gil(|py| {
523 let cap = PyCapsule::new(py, 0usize, None).unwrap();
524
525 assert_eq!(unsafe { cap.reference::<usize>() }, &0usize);
526 assert_eq!(cap.name().unwrap(), None);
527 assert_eq!(cap.context().unwrap(), std::ptr::null_mut());
528 });
529 }
530}
531