From dcecdf9bc6818c00883d0aad075684c672dadc0a Mon Sep 17 00:00:00 2001 From: Bas Schoenmaeckers Date: Mon, 13 Apr 2026 22:08:54 +0200 Subject: [PATCH] Allow creating `PyCapsule` objects --- crates/vm/src/builtins/capsule.rs | 46 ++++++++++++++++++++++++++----- crates/vm/src/vm/context.rs | 16 ++++++++--- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/crates/vm/src/builtins/capsule.rs b/crates/vm/src/builtins/capsule.rs index c9c7fd2849d..9be57c09fed 100644 --- a/crates/vm/src/builtins/capsule.rs +++ b/crates/vm/src/builtins/capsule.rs @@ -1,14 +1,19 @@ use super::PyType; -use crate::{Context, Py, PyPayload, PyResult, class::PyClassImpl, types::Representable}; +use crate::{ + AsObject, Context, Py, PyObject, PyPayload, PyResult, VirtualMachine, + class::PyClassImpl, + types::{Destructor, Representable}, +}; +use core::ffi::c_void; +use core::sync::atomic::AtomicPtr; /// PyCapsule - a container for C pointers. /// In RustPython, this is a minimal implementation for compatibility. #[pyclass(module = false, name = "PyCapsule")] -#[derive(Debug, Clone, Copy)] +#[derive(Debug)] pub struct PyCapsule { - // Capsules store opaque pointers; we don't expose the actual pointer functionality - // since RustPython doesn't have the same C extension model as CPython. - _private: (), + ptr: AtomicPtr, + destructor: Option, } impl PyPayload for PyCapsule { @@ -18,8 +23,26 @@ impl PyPayload for PyCapsule { } } -#[pyclass(with(Representable), flags(DISALLOW_INSTANTIATION))] -impl PyCapsule {} +#[pyclass(with(Representable, Destructor), flags(DISALLOW_INSTANTIATION))] +impl PyCapsule { + pub fn new( + ptr: *mut c_void, + destructor: Option, + ) -> Self { + Self { + ptr: ptr.into(), + destructor, + } + } + + pub fn pointer(&self) -> *mut c_void { + self.ptr.load(core::sync::atomic::Ordering::Relaxed) + } + + fn destructor(&self) -> Option { + self.destructor + } +} impl Representable for PyCapsule { #[inline] @@ -28,6 +51,15 @@ impl Representable for PyCapsule { } } +impl Destructor for PyCapsule { + fn del(zelf: &Py, _vm: &VirtualMachine) -> PyResult<()> { + if let Some(destructor) = zelf.destructor() { + unsafe { destructor(zelf.as_object().as_raw().cast_mut()) }; + } + Ok(()) + } +} + pub fn init(context: &'static Context) { PyCapsule::extend_class(context, context.types.capsule_type); } diff --git a/crates/vm/src/vm/context.rs b/crates/vm/src/vm/context.rs index fedb641c542..6f630fb0192 100644 --- a/crates/vm/src/vm/context.rs +++ b/crates/vm/src/vm/context.rs @@ -1,9 +1,9 @@ use crate::{ - PyResult, VirtualMachine, + PyObject, PyResult, VirtualMachine, builtins::{ - PyByteArray, PyBytes, PyComplex, PyDict, PyDictRef, PyEllipsis, PyFloat, PyFrozenSet, - PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented, PyStr, PyStrInterned, - PyTuple, PyTupleRef, PyType, PyTypeRef, PyUtf8Str, + PyByteArray, PyBytes, PyCapsule, PyComplex, PyDict, PyDictRef, PyEllipsis, PyFloat, + PyFrozenSet, PyInt, PyIntRef, PyList, PyListRef, PyNone, PyNotImplemented, PyStr, + PyStrInterned, PyTuple, PyTupleRef, PyType, PyTypeRef, PyUtf8Str, bool_::PyBool, code::{self, PyCode}, descriptor::{ @@ -752,6 +752,14 @@ impl Context { let code = code.into_code_object(self); PyRef::new_ref(PyCode::new(code), self.types.code_type.to_owned(), None) } + + pub fn new_capsule( + &self, + ptr: *mut core::ffi::c_void, + destructor: Option, + ) -> PyRef { + PyCapsule::new(ptr, destructor).into_ref(self) + } } impl AsRef for Context {