@@ -5,6 +5,7 @@ use crate::{Bound, Python};
55use crate :: { PyErr , PyResult } ;
66use std:: ffi:: { CStr , CString } ;
77use std:: os:: raw:: { c_char, c_int, c_void} ;
8+ use std:: ptr:: { self , NonNull } ;
89/// Represents a Python Capsule
910/// as described in [Capsules](https://docs.python.org/3/c-api/capsule.html#capsules):
1011/// > This subtype of PyObject represents an opaque value, useful for C extension
@@ -153,6 +154,32 @@ impl PyCapsule {
153154/// `arbitrary_self_types`.
154155#[ doc( alias = "PyCapsule" ) ]
155156pub trait PyCapsuleMethods < ' py > : crate :: sealed:: Sealed {
157+ /// Gets the raw pointer stored in the capsule.
158+ ///
159+ /// Returns an error if the `name` does not exactly match the name stored in the capsule, or if
160+ /// the pointer stored in the capsule is null.
161+ fn pointer ( & self , name : Option < & CStr > ) -> PyResult < NonNull < c_void > > ;
162+
163+ /// Obtains a reference dereferenced from the pointer of this capsule.
164+ ///
165+ /// # Safety
166+ ///
167+ /// It must be known that the pointer stored in the capsule points to an item of type `T`.
168+ unsafe fn reference < T > ( & self , name : Option < & CStr > ) -> PyResult < & ' py T > ;
169+
170+ /// Checks if this is a valid capsule.
171+ ///
172+ /// Returns true if all of these conditions are satisfied:
173+ /// - `self` is a valid, non-null capsule (should be always true)
174+ /// - the stored pointer is not null
175+ /// - the name of the capsule is equal to `name`
176+ fn is_valid ( & self , name : Option < & CStr > ) -> bool ;
177+
178+ /// Retrieves the name of this capsule, if set.
179+ ///
180+ /// Returns an error if this capsule is not valid.
181+ fn name ( & self ) -> PyResult < Option < & ' py CStr > > ;
182+
156183 /// Sets the context pointer in the capsule.
157184 ///
158185 /// Returns an error if this capsule is not valid.
@@ -197,68 +224,27 @@ pub trait PyCapsuleMethods<'py>: crate::sealed::Sealed {
197224 ///
198225 /// Returns an error if this capsule is not valid.
199226 fn context ( & self ) -> PyResult < * mut c_void > ;
200-
201- /// Obtains a reference to the value of this capsule.
202- ///
203- /// # Safety
204- ///
205- /// It must be known that this capsule is valid and its pointer is to an item of type `T`.
206- unsafe fn reference < T > ( & self ) -> & ' py T ;
207-
208- /// Gets the raw `c_void` pointer to the value in this capsule.
209- ///
210- /// Returns null if this capsule is not valid.
211- fn pointer ( & self ) -> * mut c_void ;
212-
213- /// Checks if this is a valid capsule.
214- ///
215- /// Returns true if the stored `pointer()` is non-null.
216- fn is_valid ( & self ) -> bool ;
217-
218- /// Retrieves the name of this capsule, if set.
219- ///
220- /// Returns an error if this capsule is not valid.
221- fn name ( & self ) -> PyResult < Option < & ' py CStr > > ;
222227}
223228
224229impl < ' py > PyCapsuleMethods < ' py > for Bound < ' py , PyCapsule > {
225- #[ allow( clippy:: not_unsafe_ptr_arg_deref) ]
226- fn set_context ( & self , context : * mut c_void ) -> PyResult < ( ) > {
227- let result = unsafe { ffi:: PyCapsule_SetContext ( self . as_ptr ( ) , context) } ;
228- if result != 0 {
229- Err ( PyErr :: fetch ( self . py ( ) ) )
230- } else {
231- Ok ( ( ) )
230+ fn pointer ( & self , name : Option < & CStr > ) -> PyResult < NonNull < c_void > > {
231+ let ptr = unsafe { ffi:: PyCapsule_GetPointer ( self . as_ptr ( ) , name_ptr ( name) ) } ;
232+ match NonNull :: new ( ptr) {
233+ Some ( ptr) => Ok ( ptr) ,
234+ None => Err ( PyErr :: fetch ( self . py ( ) ) ) ,
232235 }
233236 }
234237
235- fn context ( & self ) -> PyResult < * mut c_void > {
236- let ctx = unsafe { ffi:: PyCapsule_GetContext ( self . as_ptr ( ) ) } ;
237- if ctx. is_null ( ) {
238- ensure_no_error ( self . py ( ) ) ?
239- }
240- Ok ( ctx)
241- }
242-
243- unsafe fn reference < T > ( & self ) -> & ' py T {
244- unsafe { & * self . pointer ( ) . cast ( ) }
245- }
246-
247- fn pointer ( & self ) -> * mut c_void {
248- unsafe {
249- let ptr = ffi:: PyCapsule_GetPointer ( self . as_ptr ( ) , name_ptr_ignore_error ( self ) ) ;
250- if ptr. is_null ( ) {
251- ffi:: PyErr_Clear ( ) ;
252- }
253- ptr
254- }
238+ unsafe fn reference < T > ( & self , name : Option < & CStr > ) -> PyResult < & ' py T > {
239+ let ptr = self . pointer ( name) ?;
240+ Ok ( unsafe { & * ptr. as_ptr ( ) . cast ( ) } )
255241 }
256242
257- fn is_valid ( & self ) -> bool {
243+ fn is_valid ( & self , name : Option < & CStr > ) -> bool {
258244 // As well as if the stored pointer is null, PyCapsule_IsValid also returns false if
259245 // self.as_ptr() is null or not a ptr to a PyCapsule object. Both of these are guaranteed
260246 // to not be the case thanks to invariants of this PyCapsule struct.
261- let r = unsafe { ffi:: PyCapsule_IsValid ( self . as_ptr ( ) , name_ptr_ignore_error ( self ) ) } ;
247+ let r = unsafe { ffi:: PyCapsule_IsValid ( self . as_ptr ( ) , name_ptr ( name ) ) } ;
262248 r != 0
263249 }
264250
@@ -273,9 +259,27 @@ impl<'py> PyCapsuleMethods<'py> for Bound<'py, PyCapsule> {
273259 }
274260 }
275261 }
262+
263+ #[ allow( clippy:: not_unsafe_ptr_arg_deref) ]
264+ fn set_context ( & self , context : * mut c_void ) -> PyResult < ( ) > {
265+ let result = unsafe { ffi:: PyCapsule_SetContext ( self . as_ptr ( ) , context) } ;
266+ if result != 0 {
267+ Err ( PyErr :: fetch ( self . py ( ) ) )
268+ } else {
269+ Ok ( ( ) )
270+ }
271+ }
272+
273+ fn context ( & self ) -> PyResult < * mut c_void > {
274+ let ctx = unsafe { ffi:: PyCapsule_GetContext ( self . as_ptr ( ) ) } ;
275+ if ctx. is_null ( ) {
276+ ensure_no_error ( self . py ( ) ) ?
277+ }
278+ Ok ( ctx)
279+ }
276280}
277281
278- // C layout, as PyCapsule::get_reference depends on `T` being first.
282+ // C layout, as PyCapsule::reference depends on `T` being first.
279283#[ repr( C ) ]
280284struct CapsuleContents < T : ' static + Send , D : FnOnce ( T , * mut c_void ) + Send > {
281285 /// Value of the capsule
@@ -323,12 +327,11 @@ fn ensure_no_error(py: Python<'_>) -> PyResult<()> {
323327 }
324328}
325329
326- fn name_ptr_ignore_error ( slf : & Bound < ' _ , PyCapsule > ) -> * const c_char {
327- let ptr = unsafe { ffi :: PyCapsule_GetName ( slf . as_ptr ( ) ) } ;
328- if ptr . is_null ( ) {
329- unsafe { ffi :: PyErr_Clear ( ) } ;
330+ fn name_ptr ( name : Option < & CStr > ) -> * const c_char {
331+ match name {
332+ Some ( name ) => name . as_ptr ( ) ,
333+ None => ptr :: null ( ) ,
330334 }
331- ptr
332335}
333336
334337#[ cfg( test) ]
@@ -359,9 +362,9 @@ mod tests {
359362 let name = CString :: new ( "foo" ) . unwrap ( ) ;
360363
361364 let cap = PyCapsule :: new ( py, foo, Some ( name. clone ( ) ) ) ?;
362- assert ! ( cap. is_valid( ) ) ;
365+ assert ! ( cap. is_valid( Some ( c"foo" ) ) ) ;
363366
364- let foo_capi = unsafe { cap. reference :: < Foo > ( ) } ;
367+ let foo_capi = unsafe { cap. reference :: < Foo > ( Some ( name . as_ref ( ) ) ) } . unwrap ( ) ;
365368 assert_eq ! ( foo_capi. val, 123 ) ;
366369 assert_eq ! ( foo_capi. get_val( ) , 123 ) ;
367370 assert_eq ! ( cap. name( ) . unwrap( ) , Some ( name. as_ref( ) ) ) ;
@@ -375,14 +378,18 @@ mod tests {
375378 x
376379 }
377380
381+ let name = CString :: new ( "foo" ) . unwrap ( ) ;
378382 let cap: Py < PyCapsule > = Python :: attach ( |py| {
379- let name = CString :: new ( "foo" ) . unwrap ( ) ;
380- let cap = PyCapsule :: new ( py, foo as fn ( u32 ) -> u32 , Some ( name) ) . unwrap ( ) ;
383+ let cap = PyCapsule :: new ( py, foo as fn ( u32 ) -> u32 , Some ( name. clone ( ) ) ) . unwrap ( ) ;
381384 cap. into ( )
382385 } ) ;
383386
384387 Python :: attach ( move |py| {
385- let f = unsafe { cap. bind ( py) . reference :: < fn ( u32 ) -> u32 > ( ) } ;
388+ let f = unsafe {
389+ cap. bind ( py)
390+ . reference :: < fn ( u32 ) -> u32 > ( Some ( name. as_ref ( ) ) )
391+ }
392+ . unwrap ( ) ;
386393 assert_eq ! ( f( 123 ) , 123 ) ;
387394 } ) ;
388395 }
@@ -436,17 +443,16 @@ mod tests {
436443
437444 #[ test]
438445 fn test_vec_storage ( ) {
446+ let name = CString :: new ( "foo" ) . unwrap ( ) ;
439447 let cap: Py < PyCapsule > = Python :: attach ( |py| {
440- let name = CString :: new ( "foo" ) . unwrap ( ) ;
441-
442448 let stuff: Vec < u8 > = vec ! [ 1 , 2 , 3 , 4 ] ;
443- let cap = PyCapsule :: new ( py, stuff, Some ( name) ) . unwrap ( ) ;
449+ let cap = PyCapsule :: new ( py, stuff, Some ( name. clone ( ) ) ) . unwrap ( ) ;
444450
445451 cap. into ( )
446452 } ) ;
447453
448454 Python :: attach ( move |py| {
449- let ctx: & Vec < u8 > = unsafe { cap. bind ( py) . reference ( ) } ;
455+ let ctx: & Vec < u8 > = unsafe { cap. bind ( py) . reference ( Some ( name . as_ref ( ) ) ) } . unwrap ( ) ;
450456 assert_eq ! ( ctx, & [ 1 , 2 , 3 , 4 ] ) ;
451457 } )
452458 }
@@ -496,7 +502,7 @@ mod tests {
496502 Python :: attach ( |py| {
497503 let cap = PyCapsule :: new ( py, 0usize , None ) . unwrap ( ) ;
498504
499- assert_eq ! ( unsafe { cap. reference:: <usize >( ) } , & 0usize ) ;
505+ assert_eq ! ( unsafe { cap. reference:: <usize >( None ) } . unwrap ( ) , & 0usize ) ;
500506 assert_eq ! ( cap. name( ) . unwrap( ) , None ) ;
501507 assert_eq ! ( cap. context( ) . unwrap( ) , std:: ptr:: null_mut( ) ) ;
502508 } ) ;
0 commit comments