Skip to content

Commit c3d5f6c

Browse files
committed
IntoPyGetterFunc, IntoPySetterFunc
1 parent 9f5cd17 commit c3d5f6c

File tree

2 files changed

+97
-17
lines changed

2 files changed

+97
-17
lines changed

derive/src/pyclass.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -337,17 +337,16 @@ impl Class {
337337
};
338338
if name == "pymethod" {
339339
self.add_item(Self::extract_method(sig, meta)?, meta_span)?;
340-
attr_idxs.push(i);
341340
} else if name == "pyclassmethod" {
342341
self.add_item(Self::extract_classmethod(sig, meta)?, meta_span)?;
343-
attr_idxs.push(i);
344342
} else if name == "pyproperty" {
345343
self.add_item(Self::extract_property(sig, meta)?, meta_span)?;
346-
attr_idxs.push(i);
347344
} else if name == "pyslot" {
348345
self.add_item(Self::extract_slot(sig, meta)?, meta_span)?;
349-
attr_idxs.push(i);
346+
} else {
347+
continue;
350348
}
349+
attr_idxs.push(i);
351350
}
352351
let mut i = 0;
353352
let mut attr_idxs = &*attr_idxs;

vm/src/obj/objgetset.rs

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,76 @@
33
*/
44
use super::objtype::PyClassRef;
55
use crate::function::OptionalArg;
6-
use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue};
6+
use crate::pyobject::{
7+
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
8+
};
79
use crate::slots::PyBuiltinDescriptor;
810
use crate::vm::VirtualMachine;
911

10-
pub type PyGetter = dyn Fn(PyObjectRef, &VirtualMachine) -> PyResult;
11-
pub type PySetter = dyn Fn(PyObjectRef, PyObjectRef, &VirtualMachine) -> PyResult<()>;
12+
pub type PyGetterFunc = Box<dyn Fn(&VirtualMachine, PyObjectRef) -> PyResult>;
13+
pub type PySetterFunc = Box<dyn Fn(&VirtualMachine, PyObjectRef, PyObjectRef) -> PyResult<()>>;
14+
15+
pub trait IntoPyGetterFunc<T, R> {
16+
fn into_getter(self) -> PyGetterFunc;
17+
}
18+
19+
impl<F, T, R> IntoPyGetterFunc<T, R> for F
20+
where
21+
F: Fn(T, &VirtualMachine) -> R + 'static,
22+
T: TryFromObject,
23+
R: IntoPyObject,
24+
{
25+
fn into_getter(self) -> PyGetterFunc {
26+
Box::new(move |vm, obj| {
27+
let obj = T::try_from_object(vm, obj)?;
28+
(self)(obj, vm).into_pyobject(vm)
29+
})
30+
}
31+
}
32+
33+
pub trait IntoPySetterFunc<T, V> {
34+
fn into_setter(self) -> PySetterFunc;
35+
}
36+
37+
impl<F, T, V> IntoPySetterFunc<T, V> for F
38+
where
39+
F: Fn(T, V, &VirtualMachine) -> PyResult<()> + 'static,
40+
T: TryFromObject,
41+
V: TryFromObject,
42+
{
43+
fn into_setter(self) -> PySetterFunc {
44+
Box::new(move |vm, obj, value| {
45+
let obj = T::try_from_object(vm, obj)?;
46+
let value = V::try_from_object(vm, value)?;
47+
(self)(obj, value, vm)
48+
})
49+
}
50+
}
1251

1352
#[pyclass]
1453
pub struct PyGetSet {
15-
// name: String,
16-
getter: Box<PyGetter>,
17-
setter: Box<PySetter>,
54+
name: String,
55+
getter: Option<PyGetterFunc>,
56+
setter: Option<PySetterFunc>,
1857
// doc: Option<String>,
1958
}
2059

2160
impl std::fmt::Debug for PyGetSet {
2261
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2362
write!(
2463
f,
25-
"PyGetSet {{ getter: {:p}, setter: {:p} }}",
26-
self.getter, self.setter
64+
"PyGetSet {{ name: {}, getter: {}, setter: {} }}",
65+
self.name,
66+
if self.getter.is_some() {
67+
"Some"
68+
} else {
69+
"None"
70+
},
71+
if self.setter.is_some() {
72+
"Some"
73+
} else {
74+
"None"
75+
},
2776
)
2877
}
2978
}
@@ -43,15 +92,39 @@ impl PyBuiltinDescriptor for PyGetSet {
4392
_cls: OptionalArg<PyObjectRef>,
4493
vm: &VirtualMachine,
4594
) -> PyResult {
46-
(zelf.getter)(obj, vm)
95+
if let Some(ref f) = zelf.getter {
96+
f(vm, obj)
97+
} else {
98+
Err(vm.new_attribute_error(format!(
99+
"attribute '{}' of '{}' objects is not readable",
100+
zelf.name,
101+
Self::class(vm).name
102+
)))
103+
}
47104
}
48105
}
49106

50107
impl PyGetSet {
51-
pub fn new(getter: &'static PyGetter, setter: &'static PySetter) -> Self {
108+
pub fn with_get<G, T, R>(name: String, getter: G) -> Self
109+
where
110+
G: IntoPyGetterFunc<T, R>,
111+
{
112+
Self {
113+
name,
114+
getter: Some(getter.into_getter()),
115+
setter: None,
116+
}
117+
}
118+
119+
pub fn with_get_set<G, S, GT, GR, ST, SV>(name: String, getter: G, setter: S) -> Self
120+
where
121+
G: IntoPyGetterFunc<GT, GR>,
122+
S: IntoPySetterFunc<ST, SV>,
123+
{
52124
Self {
53-
getter: Box::new(getter),
54-
setter: Box::new(setter),
125+
name,
126+
getter: Some(getter.into_getter()),
127+
setter: Some(setter.into_setter()),
55128
}
56129
}
57130
}
@@ -62,7 +135,15 @@ impl PyGetSet {
62135

63136
#[pymethod(magic)]
64137
fn set(&self, obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
65-
(self.setter)(obj, value, vm)
138+
if let Some(ref f) = self.setter {
139+
f(vm, obj, value)
140+
} else {
141+
Err(vm.new_attribute_error(format!(
142+
"attribute '{}' of '{}' objects is not writable",
143+
self.name,
144+
Self::class(vm).name
145+
)))
146+
}
66147
}
67148
}
68149

0 commit comments

Comments
 (0)