diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 006bd4e60..5265bdd73 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -337,17 +337,16 @@ impl Class { }; if name == "pymethod" { self.add_item(Self::extract_method(sig, meta)?, meta_span)?; - attr_idxs.push(i); } else if name == "pyclassmethod" { self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?; - attr_idxs.push(i); } else if name == "pyproperty" { self.add_item(Self::extract_property(sig, meta)?, meta_span)?; - attr_idxs.push(i); } else if name == "pyslot" { self.add_item(Self::extract_slot(sig, meta)?, meta_span)?; - attr_idxs.push(i); + } else { + continue; } + attr_idxs.push(i); } let mut i = 0; let mut attr_idxs = &*attr_idxs; diff --git a/vm/src/obj/objgetset.rs b/vm/src/obj/objgetset.rs index e8ee40b67..ce3e73a13 100644 --- a/vm/src/obj/objgetset.rs +++ b/vm/src/obj/objgetset.rs @@ -3,18 +3,57 @@ */ use super::objtype::PyClassRef; use crate::function::OptionalArg; -use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{ + IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, +}; use crate::slots::PyBuiltinDescriptor; use crate::vm::VirtualMachine; -pub type PyGetter = dyn Fn(PyObjectRef, &VirtualMachine) -> PyResult; -pub type PySetter = dyn Fn(PyObjectRef, PyObjectRef, &VirtualMachine) -> PyResult<()>; +pub type PyGetterFunc = Box PyResult>; +pub type PySetterFunc = Box PyResult<()>>; + +pub trait IntoPyGetterFunc { + fn into_getter(self) -> PyGetterFunc; +} + +impl IntoPyGetterFunc for F +where + F: Fn(T, &VirtualMachine) -> R + 'static, + T: TryFromObject, + R: IntoPyObject, +{ + fn into_getter(self) -> PyGetterFunc { + Box::new(move |vm, obj| { + let obj = T::try_from_object(vm, obj)?; + (self)(obj, vm).into_pyobject(vm) + }) + } +} + +pub trait IntoPySetterFunc { + fn into_setter(self) -> PySetterFunc; +} + +impl IntoPySetterFunc for F +where + F: Fn(T, V, &VirtualMachine) -> PyResult<()> + 'static, + T: TryFromObject, + V: TryFromObject, +{ + fn into_setter(self) -> PySetterFunc { + Box::new(move |vm, obj, value| { + let obj = T::try_from_object(vm, obj)?; + let value = V::try_from_object(vm, value)?; + (self)(obj, value, vm) + }) + } +} #[pyclass] pub struct PyGetSet { - // name: String, - getter: Box, - setter: Box, + name: String, + getter: Option, + setter: Option, // doc: Option, } @@ -22,8 +61,18 @@ impl std::fmt::Debug for PyGetSet { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "PyGetSet {{ getter: {:p}, setter: {:p} }}", - self.getter, self.setter + "PyGetSet {{ name: {}, getter: {}, setter: {} }}", + self.name, + if self.getter.is_some() { + "Some" + } else { + "None" + }, + if self.setter.is_some() { + "Some" + } else { + "None" + }, ) } } @@ -43,15 +92,39 @@ impl PyBuiltinDescriptor for PyGetSet { _cls: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - (zelf.getter)(obj, vm) + if let Some(ref f) = zelf.getter { + f(vm, obj) + } else { + Err(vm.new_attribute_error(format!( + "attribute '{}' of '{}' objects is not readable", + zelf.name, + Self::class(vm).name + ))) + } } } impl PyGetSet { - pub fn new(getter: &'static PyGetter, setter: &'static PySetter) -> Self { + pub fn with_get(name: String, getter: G) -> Self + where + G: IntoPyGetterFunc, + { Self { - getter: Box::new(getter), - setter: Box::new(setter), + name, + getter: Some(getter.into_getter()), + setter: None, + } + } + + pub fn with_get_set(name: String, getter: G, setter: S) -> Self + where + G: IntoPyGetterFunc, + S: IntoPySetterFunc, + { + Self { + name, + getter: Some(getter.into_getter()), + setter: Some(setter.into_setter()), } } } @@ -62,7 +135,15 @@ impl PyGetSet { #[pymethod(magic)] fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - (self.setter)(obj, value, vm) + if let Some(ref f) = self.setter { + f(vm, obj, value) + } else { + Err(vm.new_attribute_error(format!( + "attribute '{}' of '{}' objects is not writable", + self.name, + Self::class(vm).name + ))) + } } }