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