1 | use crate::ffi_ptr_ext::FfiPtrExt; |
2 | use crate::py_result_ext::PyResultExt; |
3 | use crate::{ffi, PyAny}; |
4 | use crate::{Bound, Python}; |
5 | use crate::{PyErr, PyResult}; |
6 | use std::ffi::{CStr, CString}; |
7 | use 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)] |
48 | pub struct PyCapsule(PyAny); |
49 | |
50 | pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), #checkfunction=ffi::PyCapsule_CheckExact); |
51 | |
52 | impl 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" )] |
181 | pub 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 | |
250 | impl<'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)] |
306 | struct 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 |
316 | unsafe 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)] |
332 | pub 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 | |
342 | impl<T> AssertNotZeroSized for T {} |
343 | |
344 | fn 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 | |
352 | fn 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)] |
361 | mod 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 | |