implement `PyCapsuleMethods`

This commit is contained in:
Icxolu 2024-01-27 20:17:12 +01:00
parent c54d8976db
commit e323fcbb9e
4 changed files with 214 additions and 75 deletions

View File

@ -29,6 +29,7 @@ pub use crate::types::any::PyAnyMethods;
pub use crate::types::boolobject::PyBoolMethods; pub use crate::types::boolobject::PyBoolMethods;
pub use crate::types::bytearray::PyByteArrayMethods; pub use crate::types::bytearray::PyByteArrayMethods;
pub use crate::types::bytes::PyBytesMethods; pub use crate::types::bytes::PyBytesMethods;
pub use crate::types::capsule::PyCapsuleMethods;
pub use crate::types::dict::PyDictMethods; pub use crate::types::dict::PyDictMethods;
pub use crate::types::float::PyFloatMethods; pub use crate::types::float::PyFloatMethods;
pub use crate::types::frozenset::PyFrozenSetMethods; pub use crate::types::frozenset::PyFrozenSetMethods;

View File

@ -1,6 +1,8 @@
use crate::Python; use crate::ffi_ptr_ext::FfiPtrExt;
use crate::{ffi, PyAny}; use crate::py_result_ext::PyResultExt;
use crate::{ffi, PyAny, PyNativeType};
use crate::{pyobject_native_type_core, PyErr, PyResult}; use crate::{pyobject_native_type_core, PyErr, PyResult};
use crate::{Bound, Python};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
@ -27,7 +29,7 @@ use std::os::raw::{c_char, c_int, c_void};
/// let foo = Foo { val: 123 }; /// let foo = Foo { val: 123 };
/// let name = CString::new("builtins.capsule").unwrap(); /// let name = CString::new("builtins.capsule").unwrap();
/// ///
/// let capsule = PyCapsule::new(py, foo, Some(name.clone()))?; /// let capsule = PyCapsule::new_bound(py, foo, Some(name.clone()))?;
/// ///
/// let module = PyModule::import(py, "builtins")?; /// let module = PyModule::import(py, "builtins")?;
/// module.add("capsule", capsule)?; /// module.add("capsule", capsule)?;
@ -44,6 +46,22 @@ pub struct PyCapsule(PyAny);
pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), #checkfunction=ffi::PyCapsule_CheckExact); pyobject_native_type_core!(PyCapsule, pyobject_native_static_type_object!(ffi::PyCapsule_Type), #checkfunction=ffi::PyCapsule_CheckExact);
impl PyCapsule { impl PyCapsule {
/// Deprecated form of [`PyCapsule::new_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyCapsule::new` will be replaced by `PyCapsule::new_bound` in a future PyO3 version"
)
)]
pub fn new<T: 'static + Send + AssertNotZeroSized>(
py: Python<'_>,
value: T,
name: Option<CString>,
) -> PyResult<&Self> {
Self::new_bound(py, value, name).map(Bound::into_gil_ref)
}
/// Constructs a new capsule whose contents are `value`, associated with `name`. /// Constructs a new capsule whose contents are `value`, associated with `name`.
/// `name` is the identifier for the capsule; if it is stored as an attribute of a module, /// `name` is the identifier for the capsule; if it is stored as an attribute of a module,
/// the name should be in the format `"modulename.attribute"`. /// the name should be in the format `"modulename.attribute"`.
@ -59,7 +77,7 @@ impl PyCapsule {
/// ///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let name = CString::new("foo").unwrap(); /// let name = CString::new("foo").unwrap();
/// let capsule = PyCapsule::new(py, 123_u32, Some(name)).unwrap(); /// let capsule = PyCapsule::new_bound(py, 123_u32, Some(name)).unwrap();
/// let val = unsafe { capsule.reference::<u32>() }; /// let val = unsafe { capsule.reference::<u32>() };
/// assert_eq!(*val, 123); /// assert_eq!(*val, 123);
/// }); /// });
@ -72,24 +90,25 @@ impl PyCapsule {
/// use std::ffi::CString; /// use std::ffi::CString;
/// ///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let capsule = PyCapsule::new(py, (), None).unwrap(); // Oops! `()` is zero sized! /// let capsule = PyCapsule::new_bound(py, (), None).unwrap(); // Oops! `()` is zero sized!
/// }); /// });
/// ``` /// ```
pub fn new<T: 'static + Send + AssertNotZeroSized>( pub fn new_bound<T: 'static + Send + AssertNotZeroSized>(
py: Python<'_>, py: Python<'_>,
value: T, value: T,
name: Option<CString>, name: Option<CString>,
) -> PyResult<&Self> { ) -> PyResult<Bound<'_, Self>> {
Self::new_with_destructor(py, value, name, |_, _| {}) Self::new_bound_with_destructor(py, value, name, |_, _| {})
} }
/// Constructs a new capsule whose contents are `value`, associated with `name`. /// Deprecated form of [`PyCapsule::new_bound_with_destructor`].
/// #[cfg_attr(
/// Also provides a destructor: when the `PyCapsule` is destroyed, it will be passed the original object, not(feature = "gil-refs"),
/// as well as a `*mut c_void` which will point to the capsule's context, if any. deprecated(
/// since = "0.21.0",
/// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually note = "`PyCapsule::new_with_destructor` will be replaced by `PyCapsule::new_bound_with_destructor` in a future PyO3 version"
/// be called from. )
)]
pub fn new_with_destructor< pub fn new_with_destructor<
T: 'static + Send + AssertNotZeroSized, T: 'static + Send + AssertNotZeroSized,
F: FnOnce(T, *mut c_void) + Send, F: FnOnce(T, *mut c_void) + Send,
@ -99,6 +118,25 @@ impl PyCapsule {
name: Option<CString>, name: Option<CString>,
destructor: F, destructor: F,
) -> PyResult<&'_ Self> { ) -> PyResult<&'_ Self> {
Self::new_bound_with_destructor(py, value, name, destructor).map(Bound::into_gil_ref)
}
/// Constructs a new capsule whose contents are `value`, associated with `name`.
///
/// Also provides a destructor: when the `PyCapsule` is destroyed, it will be passed the original object,
/// as well as a `*mut c_void` which will point to the capsule's context, if any.
///
/// The `destructor` must be `Send`, because there is no guarantee which thread it will eventually
/// be called from.
pub fn new_bound_with_destructor<
T: 'static + Send + AssertNotZeroSized,
F: FnOnce(T, *mut c_void) + Send,
>(
py: Python<'_>,
value: T,
name: Option<CString>,
destructor: F,
) -> PyResult<Bound<'_, Self>> {
AssertNotZeroSized::assert_not_zero_sized(&value); AssertNotZeroSized::assert_not_zero_sized(&value);
// Sanity check for capsule layout // Sanity check for capsule layout
@ -112,12 +150,13 @@ impl PyCapsule {
}); });
unsafe { unsafe {
let cap_ptr = ffi::PyCapsule_New( ffi::PyCapsule_New(
Box::into_raw(val) as *mut c_void, Box::into_raw(val).cast(),
name_ptr, name_ptr,
Some(capsule_destructor::<T, F>), Some(capsule_destructor::<T, F>),
); )
py.from_owned_ptr_or_err(cap_ptr) .assume_owned_or_err(py)
.downcast_into_unchecked()
} }
} }
@ -134,7 +173,7 @@ impl PyCapsule {
if ptr.is_null() { if ptr.is_null() {
Err(PyErr::fetch(py)) Err(PyErr::fetch(py))
} else { } else {
Ok(&*(ptr as *const T)) Ok(&*ptr.cast::<T>())
} }
} }
@ -160,29 +199,23 @@ impl PyCapsule {
/// let (tx, rx) = channel::<String>(); /// let (tx, rx) = channel::<String>();
/// ///
/// fn destructor(val: u32, context: *mut c_void) { /// fn destructor(val: u32, context: *mut c_void) {
/// let ctx = unsafe { *Box::from_raw(context as *mut Sender<String>) }; /// let ctx = unsafe { *Box::from_raw(context.cast::<Sender<String>>()) };
/// ctx.send("Destructor called!".to_string()).unwrap(); /// ctx.send("Destructor called!".to_string()).unwrap();
/// } /// }
/// ///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let capsule = /// let capsule =
/// PyCapsule::new_with_destructor(py, 123, None, destructor as fn(u32, *mut c_void)) /// PyCapsule::new_bound_with_destructor(py, 123, None, destructor as fn(u32, *mut c_void))
/// .unwrap(); /// .unwrap();
/// let context = Box::new(tx); // `Sender<String>` is our context, box it up and ship it! /// let context = Box::new(tx); // `Sender<String>` is our context, box it up and ship it!
/// capsule.set_context(Box::into_raw(context) as *mut c_void).unwrap(); /// capsule.set_context(Box::into_raw(context).cast()).unwrap();
/// // This scope will end, causing our destructor to be called... /// // This scope will end, causing our destructor to be called...
/// }); /// });
/// ///
/// assert_eq!(rx.recv(), Ok("Destructor called!".to_string())); /// assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
/// ``` /// ```
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn set_context(&self, context: *mut c_void) -> PyResult<()> { pub fn set_context(&self, context: *mut c_void) -> PyResult<()> {
let result = unsafe { ffi::PyCapsule_SetContext(self.as_ptr(), context) }; self.as_borrowed().set_context(context)
if result != 0 {
Err(PyErr::fetch(self.py()))
} else {
Ok(())
}
} }
/// Gets the current context stored in the capsule. If there is no context, the pointer /// Gets the current context stored in the capsule. If there is no context, the pointer
@ -190,11 +223,7 @@ impl PyCapsule {
/// ///
/// Returns an error if this capsule is not valid. /// Returns an error if this capsule is not valid.
pub fn context(&self) -> PyResult<*mut c_void> { pub fn context(&self) -> PyResult<*mut c_void> {
let ctx = unsafe { ffi::PyCapsule_GetContext(self.as_ptr()) }; self.as_borrowed().context()
if ctx.is_null() {
ensure_no_error(self.py())?
}
Ok(ctx)
} }
/// Obtains a reference to the value of this capsule. /// Obtains a reference to the value of this capsule.
@ -203,15 +232,132 @@ impl PyCapsule {
/// ///
/// It must be known that this capsule is valid and its pointer is to an item of type `T`. /// It must be known that this capsule is valid and its pointer is to an item of type `T`.
pub unsafe fn reference<T>(&self) -> &T { pub unsafe fn reference<T>(&self) -> &T {
&*(self.pointer() as *const T) self.as_borrowed().reference()
} }
/// Gets the raw `c_void` pointer to the value in this capsule. /// Gets the raw `c_void` pointer to the value in this capsule.
/// ///
/// Returns null if this capsule is not valid. /// Returns null if this capsule is not valid.
pub fn pointer(&self) -> *mut c_void { pub fn pointer(&self) -> *mut c_void {
self.as_borrowed().pointer()
}
/// Checks if this is a valid capsule.
///
/// Returns true if the stored `pointer()` is non-null.
pub fn is_valid(&self) -> bool {
self.as_borrowed().is_valid()
}
/// Retrieves the name of this capsule, if set.
///
/// Returns an error if this capsule is not valid.
pub fn name(&self) -> PyResult<Option<&CStr>> {
self.as_borrowed().name()
}
}
/// Implementation of functionality for [`PyCapsule`].
///
/// These methods are defined for the `Bound<'py, PyCapsule>` smart pointer, so to use method call
/// syntax these methods are separated into a trait, because stable Rust does not yet support
/// `arbitrary_self_types`.
#[doc(alias = "PyCapsule")]
pub trait PyCapsuleMethods<'py> {
/// Sets the context pointer in the capsule.
///
/// Returns an error if this capsule is not valid.
///
/// # Notes
///
/// The context is treated much like the value of the capsule, but should likely act as
/// a place to store any state management when using the capsule.
///
/// If you want to store a Rust value as the context, and drop it from the destructor, use
/// `Box::into_raw` to convert it into a pointer, see the example.
///
/// # Example
///
/// ```
/// use std::sync::mpsc::{channel, Sender};
/// use libc::c_void;
/// use pyo3::{prelude::*, types::PyCapsule};
///
/// let (tx, rx) = channel::<String>();
///
/// fn destructor(val: u32, context: *mut c_void) {
/// let ctx = unsafe { *Box::from_raw(context.cast::<Sender<String>>()) };
/// ctx.send("Destructor called!".to_string()).unwrap();
/// }
///
/// Python::with_gil(|py| {
/// let capsule =
/// PyCapsule::new_bound_with_destructor(py, 123, None, destructor as fn(u32, *mut c_void))
/// .unwrap();
/// let context = Box::new(tx); // `Sender<String>` is our context, box it up and ship it!
/// capsule.set_context(Box::into_raw(context).cast()).unwrap();
/// // This scope will end, causing our destructor to be called...
/// });
///
/// assert_eq!(rx.recv(), Ok("Destructor called!".to_string()));
/// ```
fn set_context(&self, context: *mut c_void) -> PyResult<()>;
/// Gets the current context stored in the capsule. If there is no context, the pointer
/// will be null.
///
/// Returns an error if this capsule is not valid.
fn context(&self) -> PyResult<*mut c_void>;
/// Obtains a reference to the value of this capsule.
///
/// # Safety
///
/// It must be known that this capsule is valid and its pointer is to an item of type `T`.
unsafe fn reference<T>(&self) -> &'py T;
/// Gets the raw `c_void` pointer to the value in this capsule.
///
/// Returns null if this capsule is not valid.
fn pointer(&self) -> *mut c_void;
/// Checks if this is a valid capsule.
///
/// Returns true if the stored `pointer()` is non-null.
fn is_valid(&self) -> bool;
/// Retrieves the name of this capsule, if set.
///
/// Returns an error if this capsule is not valid.
fn name(&self) -> PyResult<Option<&'py CStr>>;
}
impl<'py> PyCapsuleMethods<'py> for Bound<'py, PyCapsule> {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
fn set_context(&self, context: *mut c_void) -> PyResult<()> {
let result = unsafe { ffi::PyCapsule_SetContext(self.as_ptr(), context) };
if result != 0 {
Err(PyErr::fetch(self.py()))
} else {
Ok(())
}
}
fn context(&self) -> PyResult<*mut c_void> {
let ctx = unsafe { ffi::PyCapsule_GetContext(self.as_ptr()) };
if ctx.is_null() {
ensure_no_error(self.py())?
}
Ok(ctx)
}
unsafe fn reference<T>(&self) -> &'py T {
&*self.pointer().cast()
}
fn pointer(&self) -> *mut c_void {
unsafe { unsafe {
let ptr = ffi::PyCapsule_GetPointer(self.0.as_ptr(), self.name_ptr_ignore_error()); let ptr = ffi::PyCapsule_GetPointer(self.as_ptr(), name_ptr_ignore_error(self));
if ptr.is_null() { if ptr.is_null() {
ffi::PyErr_Clear(); ffi::PyErr_Clear();
} }
@ -219,21 +365,15 @@ impl PyCapsule {
} }
} }
/// Checks if this is a valid capsule. fn is_valid(&self) -> bool {
///
/// Returns true if the stored `pointer()` is non-null.
pub fn is_valid(&self) -> bool {
// As well as if the stored pointer is null, PyCapsule_IsValid also returns false if // As well as if the stored pointer is null, PyCapsule_IsValid also returns false if
// self.as_ptr() is null or not a ptr to a PyCapsule object. Both of these are guaranteed // self.as_ptr() is null or not a ptr to a PyCapsule object. Both of these are guaranteed
// to not be the case thanks to invariants of this PyCapsule struct. // to not be the case thanks to invariants of this PyCapsule struct.
let r = unsafe { ffi::PyCapsule_IsValid(self.as_ptr(), self.name_ptr_ignore_error()) }; let r = unsafe { ffi::PyCapsule_IsValid(self.as_ptr(), name_ptr_ignore_error(self)) };
r != 0 r != 0
} }
/// Retrieves the name of this capsule, if set. fn name(&self) -> PyResult<Option<&'py CStr>> {
///
/// Returns an error if this capsule is not valid.
pub fn name(&self) -> PyResult<Option<&CStr>> {
unsafe { unsafe {
let ptr = ffi::PyCapsule_GetName(self.as_ptr()); let ptr = ffi::PyCapsule_GetName(self.as_ptr());
if ptr.is_null() { if ptr.is_null() {
@ -244,18 +384,6 @@ impl PyCapsule {
} }
} }
} }
/// Attempts to retrieve the raw name pointer of this capsule.
///
/// On error, clears the error indicator and returns NULL. This is a private function and next
/// use of this capsule will error anyway.
fn name_ptr_ignore_error(&self) -> *const c_char {
let ptr = unsafe { ffi::PyCapsule_GetName(self.as_ptr()) };
if ptr.is_null() {
unsafe { ffi::PyErr_Clear() };
}
ptr
}
} }
// C layout, as PyCapsule::get_reference depends on `T` being first. // C layout, as PyCapsule::get_reference depends on `T` being first.
@ -277,7 +405,7 @@ unsafe extern "C" fn capsule_destructor<T: 'static + Send, F: FnOnce(T, *mut c_v
let ctx = ffi::PyCapsule_GetContext(capsule); let ctx = ffi::PyCapsule_GetContext(capsule);
let CapsuleContents { let CapsuleContents {
value, destructor, .. value, destructor, ..
} = *Box::from_raw(ptr as *mut CapsuleContents<T, F>); } = *Box::from_raw(ptr.cast::<CapsuleContents<T, F>>());
destructor(value, ctx) destructor(value, ctx)
} }
@ -304,11 +432,20 @@ fn ensure_no_error(py: Python<'_>) -> PyResult<()> {
} }
} }
fn name_ptr_ignore_error(slf: &Bound<'_, PyCapsule>) -> *const c_char {
let ptr = unsafe { ffi::PyCapsule_GetName(slf.as_ptr()) };
if ptr.is_null() {
unsafe { ffi::PyErr_Clear() };
}
ptr
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use libc::c_void; use libc::c_void;
use crate::prelude::PyModule; use crate::prelude::PyModule;
use crate::types::capsule::PyCapsuleMethods;
use crate::{types::PyCapsule, Py, PyResult, Python}; use crate::{types::PyCapsule, Py, PyResult, Python};
use std::ffi::CString; use std::ffi::CString;
use std::sync::mpsc::{channel, Sender}; use std::sync::mpsc::{channel, Sender};
@ -330,7 +467,7 @@ mod tests {
let foo = Foo { val: 123 }; let foo = Foo { val: 123 };
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let cap = PyCapsule::new(py, foo, Some(name.clone()))?; let cap = PyCapsule::new_bound(py, foo, Some(name.clone()))?;
assert!(cap.is_valid()); assert!(cap.is_valid());
let foo_capi = unsafe { cap.reference::<Foo>() }; let foo_capi = unsafe { cap.reference::<Foo>() };
@ -349,7 +486,7 @@ mod tests {
let cap: Py<PyCapsule> = Python::with_gil(|py| { let cap: Py<PyCapsule> = Python::with_gil(|py| {
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let cap = PyCapsule::new(py, foo as fn(u32) -> u32, Some(name)).unwrap(); let cap = PyCapsule::new_bound(py, foo as fn(u32) -> u32, Some(name)).unwrap();
cap.into() cap.into()
}); });
@ -363,16 +500,16 @@ mod tests {
fn test_pycapsule_context() -> PyResult<()> { fn test_pycapsule_context() -> PyResult<()> {
Python::with_gil(|py| { Python::with_gil(|py| {
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let cap = PyCapsule::new(py, 0, Some(name))?; let cap = PyCapsule::new_bound(py, 0, Some(name))?;
let c = cap.context()?; let c = cap.context()?;
assert!(c.is_null()); assert!(c.is_null());
let ctx = Box::new(123_u32); let ctx = Box::new(123_u32);
cap.set_context(Box::into_raw(ctx) as _)?; cap.set_context(Box::into_raw(ctx).cast())?;
let ctx_ptr: *mut c_void = cap.context()?; let ctx_ptr: *mut c_void = cap.context()?;
let ctx = unsafe { *Box::from_raw(ctx_ptr as *mut u32) }; let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<u32>()) };
assert_eq!(ctx, 123); assert_eq!(ctx, 123);
Ok(()) Ok(())
}) })
@ -389,7 +526,7 @@ mod tests {
let foo = Foo { val: 123 }; let foo = Foo { val: 123 };
let name = CString::new("builtins.capsule").unwrap(); let name = CString::new("builtins.capsule").unwrap();
let capsule = PyCapsule::new(py, foo, Some(name.clone()))?; let capsule = PyCapsule::new_bound(py, foo, Some(name.clone()))?;
let module = PyModule::import(py, "builtins")?; let module = PyModule::import(py, "builtins")?;
module.add("capsule", capsule)?; module.add("capsule", capsule)?;
@ -412,7 +549,7 @@ mod tests {
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let stuff: Vec<u8> = vec![1, 2, 3, 4]; let stuff: Vec<u8> = vec![1, 2, 3, 4];
let cap = PyCapsule::new(py, stuff, Some(name)).unwrap(); let cap = PyCapsule::new_bound(py, stuff, Some(name)).unwrap();
cap.into() cap.into()
}); });
@ -429,8 +566,8 @@ mod tests {
let cap: Py<PyCapsule> = Python::with_gil(|py| { let cap: Py<PyCapsule> = Python::with_gil(|py| {
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let cap = PyCapsule::new(py, 0, Some(name)).unwrap(); let cap = PyCapsule::new_bound(py, 0, Some(name)).unwrap();
cap.set_context(Box::into_raw(Box::new(&context)) as _) cap.set_context(Box::into_raw(Box::new(&context)).cast())
.unwrap(); .unwrap();
cap.into() cap.into()
@ -438,7 +575,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let ctx_ptr: *mut c_void = cap.as_ref(py).context().unwrap(); let ctx_ptr: *mut c_void = cap.as_ref(py).context().unwrap();
let ctx = unsafe { *Box::from_raw(ctx_ptr as *mut &Vec<u8>) }; let ctx = unsafe { *Box::from_raw(ctx_ptr.cast::<&Vec<u8>>()) };
assert_eq!(ctx, &vec![1_u8, 2, 3, 4]); assert_eq!(ctx, &vec![1_u8, 2, 3, 4]);
}) })
} }
@ -449,14 +586,14 @@ mod tests {
fn destructor(_val: u32, ctx: *mut c_void) { fn destructor(_val: u32, ctx: *mut c_void) {
assert!(!ctx.is_null()); assert!(!ctx.is_null());
let context = unsafe { *Box::from_raw(ctx as *mut Sender<bool>) }; let context = unsafe { *Box::from_raw(ctx.cast::<Sender<bool>>()) };
context.send(true).unwrap(); context.send(true).unwrap();
} }
Python::with_gil(|py| { Python::with_gil(|py| {
let name = CString::new("foo").unwrap(); let name = CString::new("foo").unwrap();
let cap = PyCapsule::new_with_destructor(py, 0, Some(name), destructor).unwrap(); let cap = PyCapsule::new_bound_with_destructor(py, 0, Some(name), destructor).unwrap();
cap.set_context(Box::into_raw(Box::new(tx)) as _).unwrap(); cap.set_context(Box::into_raw(Box::new(tx)).cast()).unwrap();
}); });
// the destructor was called. // the destructor was called.
@ -466,7 +603,7 @@ mod tests {
#[test] #[test]
fn test_pycapsule_no_name() { fn test_pycapsule_no_name() {
Python::with_gil(|py| { Python::with_gil(|py| {
let cap = PyCapsule::new(py, 0usize, None).unwrap(); let cap = PyCapsule::new_bound(py, 0usize, None).unwrap();
assert_eq!(unsafe { cap.reference::<usize>() }, &0usize); assert_eq!(unsafe { cap.reference::<usize>() }, &0usize);
assert_eq!(cap.name().unwrap(), None); assert_eq!(cap.name().unwrap(), None);

View File

@ -1,6 +1,7 @@
use crate::derive_utils::PyFunctionArguments; use crate::derive_utils::PyFunctionArguments;
use crate::methods::PyMethodDefDestructor; use crate::methods::PyMethodDefDestructor;
use crate::prelude::*; use crate::prelude::*;
use crate::types::capsule::PyCapsuleMethods;
use crate::{ use crate::{
ffi, ffi,
impl_::pymethods::{self, PyMethodDef}, impl_::pymethods::{self, PyMethodDef},
@ -80,7 +81,7 @@ impl PyCFunction {
); );
let (def, def_destructor) = method_def.as_method_def()?; let (def, def_destructor) = method_def.as_method_def()?;
let capsule = PyCapsule::new( let capsule = PyCapsule::new_bound(
py, py,
ClosureDestructor::<F> { ClosureDestructor::<F> {
closure, closure,

View File

@ -277,7 +277,7 @@ pub(crate) mod any;
pub(crate) mod boolobject; pub(crate) mod boolobject;
pub(crate) mod bytearray; pub(crate) mod bytearray;
pub(crate) mod bytes; pub(crate) mod bytes;
mod capsule; pub(crate) mod capsule;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
mod code; mod code;
mod complex; mod complex;