@@ -35,7 +35,7 @@ use std::ptr::{self, NonNull};
3535///
3636/// let r = Python::attach(|py| -> PyResult<()> {
3737/// let foo = Foo { val: 123 };
38- /// let capsule = PyCapsule::new (py, foo, Some( c"builtins.capsule".to_owned()) )?;
38+ /// let capsule = PyCapsule::new_with_value (py, foo, c"builtins.capsule")?;
3939///
4040/// let module = PyModule::import(py, "builtins")?;
4141/// module.add("capsule", capsule)?;
@@ -69,27 +69,77 @@ impl PyCapsule {
6969 /// // this can be c"foo" on Rust 1.77+
7070 /// const NAME: &CStr = c"foo";
7171 ///
72+ /// # fn main() -> PyResult<()> {
7273 /// Python::attach(|py| {
73- /// let capsule = PyCapsule::new (py, 123_u32, Some( NAME.to_owned())).unwrap() ;
74- /// let val: NonNull<u32> = capsule.pointer_checked(Some(NAME)).unwrap() .cast();
74+ /// let capsule = PyCapsule::new_with_value (py, 123_u32, NAME)? ;
75+ /// let val: NonNull<u32> = capsule.pointer_checked(Some(NAME))? .cast();
7576 /// assert_eq!(unsafe { *val.as_ref() }, 123);
76- /// });
77+ /// # Ok(())
78+ /// })
79+ /// # }
7780 /// ```
7881 ///
7982 /// However, attempting to construct a `PyCapsule` with a zero-sized type will not compile:
8083 ///
8184 /// ```compile_fail
8285 /// use pyo3::{prelude::*, types::PyCapsule};
8386 ///
87+ /// # fn main() -> PyResult<()> {
8488 /// Python::attach(|py| {
85- /// let capsule = PyCapsule::new(py, (), None).unwrap(); // Oops! `()` is zero sized!
86- /// });
89+ /// let capsule = PyCapsule::new_with_value(py, (), c"foo")?; // Oops! `()` is zero sized!
90+ /// # Ok(())
91+ /// })
92+ /// # }
8793 /// ```
94+ pub fn new_with_value < ' py , T > (
95+ py : Python < ' py > ,
96+ value : T ,
97+ name : & ' static CStr ,
98+ ) -> PyResult < Bound < ' py , Self > >
99+ where
100+ T : ' static + Send + AssertNotZeroSized ,
101+ {
102+ AssertNotZeroSized :: assert_not_zero_sized ( & value) ;
103+
104+ // NOTE: Not implemented in terms of `new_with_value_and_destructor` as this allows for
105+ // storing the `Box<T>` directly without allocating additionally for the destructor
106+ let val = Box :: into_raw ( Box :: new ( value) ) ;
107+
108+ unsafe extern "C" fn destructor < T > ( capsule : * mut ffi:: PyObject ) {
109+ // SAFETY: `capsule` is known to be a borrowed reference to the capsule being destroyed
110+ let name = unsafe { ffi:: PyCapsule_GetName ( capsule) } ;
111+
112+ // SAFETY:
113+ // - `capsule` is known to be a borrowed reference to the capsule being destroyed
114+ // - `name` is known to be the capsule's name
115+ let ptr = unsafe { ffi:: PyCapsule_GetPointer ( capsule, name) } ;
116+
117+ // SAFETY: `capsule` was knowingly constructed from a `Box<T>` and is now being
118+ // destroyed, so we reconstruct the Box and drop it.
119+ let _ = unsafe { Box :: < T > :: from_raw ( ptr. cast ( ) ) } ;
120+ }
121+
122+ // SAFETY:
123+ // - `val` is a non-null pointer to valid capsule data
124+ // - `name` is a static valid C string
125+ // - `destructor` will delete this data when called
126+ // - thread is attached to the Python interpreter
127+ // - `PyCapsule_New` returns a new reference or null on error
128+ unsafe {
129+ ffi:: PyCapsule_New ( val. cast ( ) , name. as_ptr ( ) , Some ( destructor :: < T > ) )
130+ . assume_owned_or_err ( py)
131+ . cast_into_unchecked ( )
132+ }
133+ }
134+
135+ /// See [`PyCapsule::new_with_value`]
136+ #[ deprecated( since = "0.29.0" , note = "use `PyCapsule::new_with_value` instead" ) ]
88137 pub fn new < T : ' static + Send + AssertNotZeroSized > (
89138 py : Python < ' _ > ,
90139 value : T ,
91140 name : Option < CString > ,
92141 ) -> PyResult < Bound < ' _ , Self > > {
142+ #[ allow( deprecated) ]
93143 Self :: new_with_destructor ( py, value, name, |_, _| { } )
94144 }
95145
@@ -100,6 +150,45 @@ impl PyCapsule {
100150 ///
101151 /// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually
102152 /// be called from.
153+ pub fn new_with_value_and_destructor < ' py , T , F > (
154+ py : Python < ' py > ,
155+ value : T ,
156+ name : & ' static CStr ,
157+ destructor : F ,
158+ ) -> PyResult < Bound < ' py , Self > >
159+ where
160+ T : ' static + Send + AssertNotZeroSized ,
161+ F : FnOnce ( T , * mut c_void ) + Send ,
162+ {
163+ AssertNotZeroSized :: assert_not_zero_sized ( & value) ;
164+
165+ // Sanity check for capsule layout
166+ debug_assert_eq ! ( offset_of!( CapsuleContents :: <T , F >, value) , 0 ) ;
167+
168+ let val = Box :: into_raw ( Box :: new ( CapsuleContents {
169+ value,
170+ destructor,
171+ name : None ,
172+ } ) ) ;
173+
174+ // SAFETY:
175+ // - `val` is a non-null pointer to valid capsule data
176+ // - `name` is a static valid C string
177+ // - `destructor` will delete this data when called
178+ // - thread is attached to the Python interpreter
179+ // - `PyCapsule_New` returns a new reference or null on error
180+ unsafe {
181+ ffi:: PyCapsule_New ( val. cast ( ) , name. as_ptr ( ) , Some ( capsule_destructor :: < T , F > ) )
182+ . assume_owned_or_err ( py)
183+ . cast_into_unchecked ( )
184+ }
185+ }
186+
187+ /// See [`PyCapsule::new_with_value_and_destructor`]
188+ #[ deprecated(
189+ since = "0.29.0" ,
190+ note = "use `PyCapsule::new_with_value_and_destructor` instead"
191+ ) ]
103192 pub fn new_with_destructor <
104193 T : ' static + Send + AssertNotZeroSized ,
105194 F : FnOnce ( T , * mut c_void ) + Send ,
@@ -307,6 +396,7 @@ pub trait PyCapsuleMethods<'py>: crate::sealed::Sealed {
307396 /// use std::sync::mpsc::{channel, Sender};
308397 /// use pyo3::{prelude::*, types::PyCapsule};
309398 ///
399+ /// # fn main() -> PyResult<()> {
310400 /// let (tx, rx) = channel::<String>();
311401 ///
312402 /// fn destructor(val: u32, context: *mut c_void) {
@@ -315,15 +405,21 @@ pub trait PyCapsuleMethods<'py>: crate::sealed::Sealed {
315405 /// }
316406 ///
317407 /// Python::attach(|py| {
318- /// let capsule =
319- /// PyCapsule::new_with_destructor(py, 123, None, destructor as fn(u32, *mut c_void))
320- /// .unwrap();
408+ /// let capsule = PyCapsule::new_with_value_and_destructor(
409+ /// py,
410+ /// 123,
411+ /// c"foo",
412+ /// destructor as fn(u32, *mut c_void)
413+ /// )?;
321414 /// let context = Box::new(tx); // `Sender<String>` is our context, box it up and ship it!
322- /// capsule.set_context(Box::into_raw(context).cast()).unwrap() ;
415+ /// capsule.set_context(Box::into_raw(context).cast())? ;
323416 /// // This scope will end, causing our destructor to be called...
324- /// });
417+ /// # Ok::<_, PyErr>(())
418+ /// })?;
325419 ///
326420 /// assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
421+ /// # Ok(())
422+ /// }
327423 /// ```
328424 fn set_context ( & self , context : * mut c_void ) -> PyResult < ( ) > ;
329425
@@ -524,6 +620,7 @@ struct CapsuleContents<T: 'static + Send, D: FnOnce(T, *mut c_void) + Send> {
524620 /// Destructor to be used by the capsule
525621 destructor : D ,
526622 /// Name used when creating the capsule
623+ // TODO: remove this field when `PyCapsule::new` and `PyCapsule::new_with_destructor` are removed
527624 name : Option < CString > ,
528625}
529626
@@ -634,7 +731,7 @@ mod tests {
634731 Python :: attach ( |py| {
635732 let foo = Foo { val : 123 } ;
636733
637- let cap = PyCapsule :: new ( py, foo, Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
734+ let cap = PyCapsule :: new_with_value ( py, foo, NAME ) . unwrap ( ) ;
638735 assert ! ( cap. is_valid_checked( Some ( NAME ) ) ) ;
639736
640737 let foo_capi = cap. pointer_checked ( Some ( NAME ) ) . unwrap ( ) . cast :: < Foo > ( ) ;
@@ -659,7 +756,7 @@ mod tests {
659756 }
660757
661758 let cap: Py < PyCapsule > = Python :: attach ( |py| {
662- let cap = PyCapsule :: new ( py, foo as fn ( u32 ) -> u32 , Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
759+ let cap = PyCapsule :: new_with_value ( py, foo as fn ( u32 ) -> u32 , NAME ) . unwrap ( ) ;
663760 cap. into ( )
664761 } ) ;
665762
@@ -677,7 +774,7 @@ mod tests {
677774 #[ test]
678775 fn test_pycapsule_context ( ) {
679776 Python :: attach ( |py| {
680- let cap = PyCapsule :: new ( py, 0 , Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
777+ let cap = PyCapsule :: new_with_value ( py, 0 , NAME ) . unwrap ( ) ;
681778
682779 let c = cap. context ( ) . unwrap ( ) ;
683780 assert ! ( c. is_null( ) ) ;
@@ -703,7 +800,7 @@ mod tests {
703800 let foo = Foo { val : 123 } ;
704801 let name = c"builtins.capsule" ;
705802
706- let capsule = PyCapsule :: new ( py, foo, Some ( name. to_owned ( ) ) ) . unwrap ( ) ;
803+ let capsule = PyCapsule :: new_with_value ( py, foo, name) . unwrap ( ) ;
707804
708805 let module = PyModule :: import ( py, "builtins" ) . unwrap ( ) ;
709806 module. add ( "capsule" , capsule) . unwrap ( ) ;
@@ -724,7 +821,7 @@ mod tests {
724821 fn test_vec_storage ( ) {
725822 let cap: Py < PyCapsule > = Python :: attach ( |py| {
726823 let stuff: Vec < u8 > = vec ! [ 1 , 2 , 3 , 4 ] ;
727- let cap = PyCapsule :: new ( py, stuff, Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
824+ let cap = PyCapsule :: new_with_value ( py, stuff, NAME ) . unwrap ( ) ;
728825 cap. into ( )
729826 } ) ;
730827
@@ -744,7 +841,7 @@ mod tests {
744841 let context: Vec < u8 > = vec ! [ 1 , 2 , 3 , 4 ] ;
745842
746843 let cap: Py < PyCapsule > = Python :: attach ( |py| {
747- let cap = PyCapsule :: new ( py, 0 , Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
844+ let cap = PyCapsule :: new_with_value ( py, 0 , NAME ) . unwrap ( ) ;
748845 cap. set_context ( Box :: into_raw ( Box :: new ( & context) ) . cast ( ) )
749846 . unwrap ( ) ;
750847
@@ -771,8 +868,7 @@ mod tests {
771868 }
772869
773870 Python :: attach ( move |py| {
774- let cap =
775- PyCapsule :: new_with_destructor ( py, 0 , Some ( NAME . to_owned ( ) ) , destructor) . unwrap ( ) ;
871+ let cap = PyCapsule :: new_with_value_and_destructor ( py, 0 , NAME , destructor) . unwrap ( ) ;
776872 cap. set_context ( Box :: into_raw ( Box :: new ( tx) ) . cast ( ) ) . unwrap ( ) ;
777873 } ) ;
778874
@@ -781,6 +877,7 @@ mod tests {
781877 }
782878
783879 #[ test]
880+ #[ allow( deprecated) ]
784881 fn test_pycapsule_no_name ( ) {
785882 Python :: attach ( |py| {
786883 let cap = PyCapsule :: new ( py, 0usize , None ) . unwrap ( ) ;
@@ -869,7 +966,7 @@ mod tests {
869966 #[ test]
870967 fn test_pycapsule_pointer_checked_wrong_name ( ) {
871968 Python :: attach ( |py| {
872- let cap = PyCapsule :: new ( py, 123u32 , Some ( c"correct.name" . to_owned ( ) ) ) . unwrap ( ) ;
969+ let cap = PyCapsule :: new_with_value ( py, 123u32 , c"correct.name" ) . unwrap ( ) ;
873970
874971 // Requesting with wrong name should fail
875972 let result = cap. pointer_checked ( Some ( c"wrong.name" ) ) ;
@@ -882,6 +979,7 @@ mod tests {
882979 }
883980
884981 #[ test]
982+ #[ allow( deprecated) ]
885983 fn test_pycapsule_pointer_checked_none_vs_some ( ) {
886984 Python :: attach ( |py| {
887985 // Capsule with no name
@@ -899,7 +997,7 @@ mod tests {
899997 #[ test]
900998 fn test_pycapsule_is_valid_checked_wrong_name ( ) {
901999 Python :: attach ( |py| {
902- let cap = PyCapsule :: new ( py, 123u32 , Some ( c"correct.name" . to_owned ( ) ) ) . unwrap ( ) ;
1000+ let cap = PyCapsule :: new_with_value ( py, 123u32 , c"correct.name" ) . unwrap ( ) ;
9031001
9041002 // Should be valid with correct name
9051003 assert ! ( cap. is_valid_checked( Some ( c"correct.name" ) ) ) ;
@@ -913,6 +1011,7 @@ mod tests {
9131011 }
9141012
9151013 #[ test]
1014+ #[ allow( deprecated) ]
9161015 fn test_pycapsule_is_valid_checked_no_name ( ) {
9171016 Python :: attach ( |py| {
9181017 let cap = PyCapsule :: new ( py, 123u32 , None ) . unwrap ( ) ;
@@ -928,7 +1027,7 @@ mod tests {
9281027 #[ test]
9291028 fn test_pycapsule_context_on_invalid_capsule ( ) {
9301029 Python :: attach ( |py| {
931- let cap = PyCapsule :: new ( py, 123u32 , Some ( NAME . to_owned ( ) ) ) . unwrap ( ) ;
1030+ let cap = PyCapsule :: new_with_value ( py, 123u32 , NAME ) . unwrap ( ) ;
9321031
9331032 // Invalidate the capsule
9341033 // SAFETY: intentionally breaking the capsule for testing
@@ -957,7 +1056,7 @@ mod tests {
9571056 fn test_pycapsule_import_wrong_attribute ( ) {
9581057 Python :: attach ( |py| {
9591058 // Create a capsule and register it
960- let cap = PyCapsule :: new ( py, 123u32 , Some ( c"builtins.test_cap" . to_owned ( ) ) ) . unwrap ( ) ;
1059+ let cap = PyCapsule :: new_with_value ( py, 123u32 , c"builtins.test_cap" ) . unwrap ( ) ;
9611060 let module = crate :: prelude:: PyModule :: import ( py, "builtins" ) . unwrap ( ) ;
9621061 module. add ( "test_cap" , cap) . unwrap ( ) ;
9631062
0 commit comments