diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 74f937014..5b5b50eff 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -403,7 +403,6 @@ class ReferencesTestCase(TestBase): p2 = makeref(o) self.assertIs(p1, p2, "callbacks were None, NULL in the C API") - @unittest.expectedFailure # TODO: RUSTPYTHON def test_callable_proxy(self): o = Callable() ref1 = weakref.proxy(o) diff --git a/crates/vm/src/builtins/mod.rs b/crates/vm/src/builtins/mod.rs index f08a2b467..cb5ee93c7 100644 --- a/crates/vm/src/builtins/mod.rs +++ b/crates/vm/src/builtins/mod.rs @@ -90,7 +90,7 @@ pub use traceback::PyTraceback; pub(crate) mod tuple; pub use tuple::{PyTuple, PyTupleRef}; pub(crate) mod weakproxy; -pub use weakproxy::PyWeakProxy; +pub use weakproxy::{PyWeakCallableProxy, PyWeakProxy}; pub(crate) mod weakref; pub use weakref::PyWeak; pub(crate) mod zip; diff --git a/crates/vm/src/builtins/weakproxy.rs b/crates/vm/src/builtins/weakproxy.rs index 35871c07b..7f39044b4 100644 --- a/crates/vm/src/builtins/weakproxy.rs +++ b/crates/vm/src/builtins/weakproxy.rs @@ -8,8 +8,8 @@ use crate::{ protocol::{PyIter, PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods}, stdlib::builtins::reversed, types::{ - AsMapping, AsNumber, AsSequence, Comparable, Constructor, GetAttr, Hashable, IterNext, - Iterable, PyComparisonOp, Representable, SetAttr, + AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, GetAttr, Hashable, + IterNext, Iterable, PyComparisonOp, Representable, SetAttr, }, }; @@ -67,7 +67,11 @@ impl PyWeakProxy { callback: Option, vm: &VirtualMachine, ) -> PyResult> { - let typ = vm.ctx.types.weakproxy_type.to_owned(); + let typ = if referent.is_callable() { + vm.ctx.types.weakcallableproxy_type.to_owned() + } else { + vm.ctx.types.weakproxy_type.to_owned() + }; referent.downgrade_with_typ(callback, typ, vm) } @@ -77,6 +81,28 @@ impl PyWeakProxy { } } +#[pyclass( + module = false, + name = "weakcallableproxy", + base = PyWeakProxy, + ctx = "weakcallableproxy_type", + unhashable = true +)] +#[derive(Debug)] +#[repr(transparent)] +pub struct PyWeakCallableProxy(PyWeakProxy); + +#[pyclass(with(Callable))] +impl PyWeakCallableProxy {} + +impl Callable for PyWeakCallableProxy { + type Args = FuncArgs; + + fn call(zelf: &Py, args: FuncArgs, vm: &VirtualMachine) -> PyResult { + zelf.0.try_upgrade(vm)?.call(args, vm) + } +} + #[pyclass(with( GetAttr, SetAttr, @@ -371,6 +397,7 @@ impl Representable for PyWeakProxy { pub(crate) fn init(context: &'static Context) { PyWeakProxy::extend_class(context, context.types.weakproxy_type); + PyWeakCallableProxy::extend_class(context, context.types.weakcallableproxy_type); } impl Hashable for PyWeakProxy { diff --git a/crates/vm/src/object/core.rs b/crates/vm/src/object/core.rs index b0ebfb186..be9eef29b 100644 --- a/crates/vm/src/object/core.rs +++ b/crates/vm/src/object/core.rs @@ -771,10 +771,12 @@ impl WeakRefList { Some(candidate) => { let node = unsafe { candidate.as_ref() }; let has_callback = unsafe { (&*node.0.payload.callback.get()).is_some() }; + let node_cls = node.class(); // PyWeakref_CheckProxy: the basic-proxy slot is reserved for // the canonical proxy type; subclasses and callback-less ref // subclasses must not be mistaken for it. - let is_proxy = node.class().is(crate::builtins::PyWeakProxy::static_type()); + let is_proxy = node_cls.is(crate::builtins::PyWeakProxy::static_type()) + || node_cls.is(crate::builtins::PyWeakCallableProxy::static_type()); if has_callback || !is_proxy { ptr::null_mut() } else { @@ -1602,7 +1604,8 @@ impl PyObject { None }; let cls_is_weakref = typ.is(vm.ctx.types.weakref_type); - let cls_is_weakproxy = typ.is(vm.ctx.types.weakproxy_type); + let cls_is_weakproxy = + typ.is(vm.ctx.types.weakproxy_type) || typ.is(vm.ctx.types.weakcallableproxy_type); let wrl = self.weak_ref_list().ok_or_else(|| { vm.new_type_error(format!( "cannot create weak reference to '{}' object", diff --git a/crates/vm/src/stdlib/_weakref.rs b/crates/vm/src/stdlib/_weakref.rs index 4db8970ba..ee8f1153b 100644 --- a/crates/vm/src/stdlib/_weakref.rs +++ b/crates/vm/src/stdlib/_weakref.rs @@ -35,7 +35,7 @@ mod _weakref { #[pyattr(name = "CallableProxyType")] fn callable_proxy_type(vm: &VirtualMachine) -> PyTypeRef { - vm.ctx.types.weakproxy_type.to_owned() + vm.ctx.types.weakcallableproxy_type.to_owned() } #[pyfunction] diff --git a/crates/vm/src/types/zoo.rs b/crates/vm/src/types/zoo.rs index c8667050b..49afa928a 100644 --- a/crates/vm/src/types/zoo.rs +++ b/crates/vm/src/types/zoo.rs @@ -86,6 +86,7 @@ pub struct TypeZoo { pub bound_method_type: &'static Py, pub weakref_type: &'static Py, pub weakproxy_type: &'static Py, + pub weakcallableproxy_type: &'static Py, pub mappingproxy_type: &'static Py, pub traceback_type: &'static Py, pub object_type: &'static Py, @@ -198,6 +199,7 @@ impl TypeZoo { traceback_type: traceback::PyTraceback::init_builtin_type(), tuple_iterator_type: tuple::PyTupleIterator::init_builtin_type(), weakproxy_type: weakproxy::PyWeakProxy::init_builtin_type(), + weakcallableproxy_type: weakproxy::PyWeakCallableProxy::init_builtin_type(), method_descriptor_type: descriptor::PyMethodDescriptor::init_builtin_type(), none_type: singletons::PyNone::init_builtin_type(), typing_no_default_type: crate::stdlib::_typing::NoDefault::init_builtin_type(),