Add weakref support to c-api (#8006)

This commit is contained in:
Bas Schoenmaeckers authored and GitHub committed 2026-06-02 15:31:21 +09:00
1 parent 8af0f8ab7b
commit d411a2b9d0
3 files changed
+137 -7

No files matched your search

+1
View File
@@ -30,6 +30,7 @@ pub mod traceback;
pub mod tupleobject;
pub mod unicodeobject;
mod util;
pub mod weakrefobject;
/// Get main interpreter of this process. Will be None if it has not been initialized yet.
pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> {
+106
View File
@@ -0,0 +1,106 @@
use crate::PyObject;
use crate::object::define_py_check;
use crate::pystate::with_vm;
use core::ffi::c_int;
use rustpython_vm::builtins::{PyWeak, PyWeakProxy};
define_py_check!(fn PyWeakref_CheckProxy, types.weakproxy_type);
define_py_check!(fn PyWeakref_CheckRef, types.weakref_type);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyWeakref_GetRef(
reference: *mut PyObject,
result: *mut *mut PyObject,
) -> c_int {
with_vm(|vm| {
unsafe {
*result = core::ptr::null_mut();
}
let reference = unsafe { &*reference };
let upgraded = if let Some(weak) = reference.downcast_ref::<PyWeak>() {
weak.upgrade()
} else if let Some(proxy) = reference.downcast_ref::<PyWeakProxy>() {
proxy.get_weak().upgrade()
} else {
return Err(vm.new_type_error("expected a weakref"));
};
if let Some(obj) = upgraded {
unsafe {
*result = obj.into_raw().as_ptr();
}
Ok(true)
} else {
Ok(false)
}
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyWeakref_NewProxy(
ob: *mut PyObject,
callback: *mut PyObject,
) -> *mut PyObject {
with_vm(|vm| {
let ob = unsafe { &*ob };
let callback = unsafe { callback.as_ref() }
.filter(|callback| !vm.is_none(callback))
.map(ToOwned::to_owned);
PyWeakProxy::new_weakproxy(ob, callback, vm)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyWeakref_NewRef(
ob: *mut PyObject,
callback: *mut PyObject,
) -> *mut PyObject {
with_vm(|vm| {
let ob = unsafe { &*ob };
let callback = unsafe { callback.as_ref() }
.filter(|callback| !vm.is_none(callback))
.map(ToOwned::to_owned);
ob.downgrade(callback, vm)
})
}
#[cfg(false)]
mod tests {
use pyo3::prelude::*;
use pyo3::types::PyAnyMethods;
use pyo3::types::{PyInt, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference};
#[test]
fn check_ref_and_proxy() {
Python::attach(|py| {
let object_ty = py.get_type::<PyInt>();
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
assert!(weak_ref.is_instance_of::<PyWeakrefReference>());
assert!(weak_proxy.is_instance_of::<PyWeakrefProxy>());
});
}
#[test]
fn new_ref_and_get_ref() {
Python::attach(|py| {
let object_ty = py.get_type::<PyInt>();
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
assert!(weak_ref.upgrade().is_some_and(|obj| obj.is(&object_ty)));
});
}
#[test]
fn new_proxy() {
Python::attach(|py| {
let object_ty = py.get_type::<PyInt>();
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
assert!(weak_proxy.upgrade().is_some_and(|obj| obj.is(&object_ty)));
});
}
}
+30 -7
View File
@@ -42,6 +42,22 @@ impl Constructor for PyWeakProxy {
Self::Args { referent, callback }: Self::Args,
vm: &VirtualMachine,
) -> PyResult<Self> {
let weak = Self::new_weak(referent.as_ref(), callback.into_option(), vm)?;
// TODO: PyWeakProxy should use the same payload as PyWeak
Ok(Self { weak })
}
}
crate::common::static_cell! {
static WEAK_SUBCLASS: PyTypeRef;
}
impl PyWeakProxy {
fn new_weak(
referent: &PyObject,
callback: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<PyRef<PyWeak>> {
// using an internal subclass as the class prevents us from getting the generic weakref,
// which would mess up the weakref count
let weak_cls = WEAK_SUBCLASS.get_or_init(|| {
@@ -52,15 +68,22 @@ impl Constructor for PyWeakProxy {
super::PyWeak::make_slots(),
)
});
// TODO: PyWeakProxy should use the same payload as PyWeak
Ok(Self {
weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?,
})
referent.downgrade_with_typ(callback, weak_cls.clone(), vm)
}
}
crate::common::static_cell! {
static WEAK_SUBCLASS: PyTypeRef;
pub fn new_weakproxy(
referent: &PyObject,
callback: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<PyRef<Self>> {
let weak = Self::new_weak(referent, callback, vm)?;
Ok(Self { weak }.into_ref(&vm.ctx))
}
#[must_use]
pub fn get_weak(&self) -> &PyRef<PyWeak> {
&self.weak
}
}
#[pyclass(with(