Skip to content

Commit 9edf71d

Browse files
Add weakref support to c-api
1 parent 885cf5c commit 9edf71d

3 files changed

Lines changed: 136 additions & 7 deletions

File tree

crates/capi/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ pub mod traceback;
3030
pub mod tupleobject;
3131
pub mod unicodeobject;
3232
mod util;
33+
pub mod weakrefobject;
3334

3435
/// Get main interpreter of this process. Will be None if it has not been initialized yet.
3536
pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> {

crates/capi/src/weakrefobject.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use crate::PyObject;
2+
use crate::object::define_py_check;
3+
use crate::pystate::with_vm;
4+
use core::ffi::c_int;
5+
use rustpython_vm::builtins::{PyWeak, PyWeakProxy};
6+
7+
define_py_check!(fn PyWeakref_CheckProxy, types.weakproxy_type);
8+
define_py_check!(fn PyWeakref_CheckRef, types.weakref_type);
9+
10+
#[unsafe(no_mangle)]
11+
pub unsafe extern "C" fn PyWeakref_GetRef(
12+
reference: *mut PyObject,
13+
result: *mut *mut PyObject,
14+
) -> c_int {
15+
with_vm(|vm| {
16+
unsafe {
17+
*result = core::ptr::null_mut();
18+
}
19+
20+
let reference = unsafe { &*reference };
21+
let upgraded = if let Some(weak) = reference.downcast_ref::<PyWeak>() {
22+
weak.upgrade()
23+
} else if let Some(proxy) = reference.downcast_ref::<PyWeakProxy>() {
24+
proxy.get_weak().upgrade()
25+
} else {
26+
return Err(vm.new_type_error("expected a weakref"));
27+
};
28+
29+
if let Some(obj) = upgraded {
30+
unsafe {
31+
*result = obj.into_raw().as_ptr();
32+
}
33+
Ok(true)
34+
} else {
35+
Ok(false)
36+
}
37+
})
38+
}
39+
40+
#[unsafe(no_mangle)]
41+
pub unsafe extern "C" fn PyWeakref_NewProxy(
42+
ob: *mut PyObject,
43+
callback: *mut PyObject,
44+
) -> *mut PyObject {
45+
with_vm(|vm| {
46+
let ob = unsafe { &*ob };
47+
let callback = unsafe { callback.as_ref() }
48+
.filter(|callback| !vm.is_none(callback))
49+
.map(ToOwned::to_owned);
50+
PyWeakProxy::new_weakproxy(ob, callback, vm)
51+
})
52+
}
53+
54+
#[unsafe(no_mangle)]
55+
pub unsafe extern "C" fn PyWeakref_NewRef(
56+
ob: *mut PyObject,
57+
callback: *mut PyObject,
58+
) -> *mut PyObject {
59+
with_vm(|vm| {
60+
let ob = unsafe { &*ob };
61+
let callback = unsafe { callback.as_ref() }
62+
.filter(|callback| !vm.is_none(callback))
63+
.map(ToOwned::to_owned);
64+
ob.downgrade(callback, vm)
65+
})
66+
}
67+
68+
#[cfg(false)]
69+
mod tests {
70+
use pyo3::prelude::*;
71+
use pyo3::types::PyAnyMethods;
72+
use pyo3::types::{PyInt, PyWeakrefMethods, PyWeakrefProxy, PyWeakrefReference};
73+
74+
#[test]
75+
fn check_ref_and_proxy() {
76+
Python::attach(|py| {
77+
let object_ty = py.get_type::<PyInt>();
78+
79+
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
80+
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
81+
82+
assert!(weak_ref.is_instance_of::<PyWeakrefReference>());
83+
assert!(weak_proxy.is_instance_of::<PyWeakrefProxy>());
84+
});
85+
}
86+
87+
#[test]
88+
fn new_ref_and_get_ref() {
89+
Python::attach(|py| {
90+
let object_ty = py.get_type::<PyInt>();
91+
let weak_ref = PyWeakrefReference::new(&object_ty).unwrap();
92+
93+
assert!(weak_ref.upgrade().is_some_and(|obj| obj.is(&object_ty)));
94+
});
95+
}
96+
97+
#[test]
98+
fn new_proxy() {
99+
Python::attach(|py| {
100+
let object_ty = py.get_type::<PyInt>();
101+
let weak_proxy = PyWeakrefProxy::new(&object_ty).unwrap();
102+
103+
assert!(weak_proxy.upgrade().is_some_and(|obj| obj.is(&object_ty)));
104+
});
105+
}
106+
}

crates/vm/src/builtins/weakproxy.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,21 @@ impl Constructor for PyWeakProxy {
4242
Self::Args { referent, callback }: Self::Args,
4343
vm: &VirtualMachine,
4444
) -> PyResult<Self> {
45+
let weak = Self::new_weak(referent.as_ref(), callback.into_option(), vm)?;
46+
Ok(Self { weak })
47+
}
48+
}
49+
50+
crate::common::static_cell! {
51+
static WEAK_SUBCLASS: PyTypeRef;
52+
}
53+
54+
impl PyWeakProxy {
55+
fn new_weak(
56+
referent: &PyObject,
57+
callback: Option<PyObjectRef>,
58+
vm: &VirtualMachine,
59+
) -> PyResult<PyRef<PyWeak>> {
4560
// using an internal subclass as the class prevents us from getting the generic weakref,
4661
// which would mess up the weakref count
4762
let weak_cls = WEAK_SUBCLASS.get_or_init(|| {
@@ -52,15 +67,22 @@ impl Constructor for PyWeakProxy {
5267
super::PyWeak::make_slots(),
5368
)
5469
});
55-
// TODO: PyWeakProxy should use the same payload as PyWeak
56-
Ok(Self {
57-
weak: referent.downgrade_with_typ(callback.into_option(), weak_cls.clone(), vm)?,
58-
})
70+
referent.downgrade_with_typ(callback, weak_cls.clone(), vm)
5971
}
60-
}
6172

62-
crate::common::static_cell! {
63-
static WEAK_SUBCLASS: PyTypeRef;
73+
pub fn new_weakproxy(
74+
referent: &PyObject,
75+
callback: Option<PyObjectRef>,
76+
vm: &VirtualMachine,
77+
) -> PyResult<PyRef<Self>> {
78+
let weak = Self::new_weak(referent, callback, vm)?;
79+
Ok(Self { weak }.into_ref(&vm.ctx))
80+
}
81+
82+
#[must_use]
83+
pub fn get_weak(&self) -> &PyRef<PyWeak> {
84+
&self.weak
85+
}
6486
}
6587

6688
#[pyclass(with(

0 commit comments

Comments
 (0)