Skip to content

Commit 3f301bd

Browse files
committed
Implement property attributes and functions
1 parent 8dcea92 commit 3f301bd

File tree

4 files changed

+103
-6
lines changed

4 files changed

+103
-6
lines changed

tests/snippets/property.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,18 @@ def foo(self):
3030

3131
with assertRaises(TypeError):
3232
property.__new__(object)
33+
34+
35+
p1 = property("a", "b", "c")
36+
37+
assert p1.fget == "a"
38+
assert p1.fset == "b"
39+
assert p1.fdel == "c"
40+
41+
assert p1.getter(1).fget == 1
42+
assert p1.setter(2).fset == 2
43+
assert p1.deleter(3).fdel == 3
44+
45+
assert p1.getter(None).fget == "a"
46+
assert p1.setter(None).fset == "b"
47+
assert p1.deleter(None).fdel == "c"

vm/src/obj/objnone.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,15 @@ impl IntoPyObject for () {
2121
}
2222
}
2323

24+
impl<T: IntoPyObject> IntoPyObject for Option<T> {
25+
fn into_pyobject(self, ctx: &PyContext) -> PyResult {
26+
match self {
27+
Some(x) => x.into_pyobject(ctx),
28+
None => Ok(ctx.none()),
29+
}
30+
}
31+
}
32+
2433
impl PyNoneRef {
2534
fn repr(self, _vm: &mut VirtualMachine) -> PyResult<String> {
2635
Ok("None".to_string())

vm/src/obj/objproperty.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ impl PyPropertyRef {
6767
)
6868
}
6969

70+
// Descriptor methods
71+
7072
fn get(self, obj: PyObjectRef, _owner: PyClassRef, vm: &mut VirtualMachine) -> PyResult {
7173
if let Some(getter) = self.getter.as_ref() {
7274
vm.invoke(getter.clone(), obj)
@@ -90,6 +92,58 @@ impl PyPropertyRef {
9092
Err(vm.new_attribute_error("can't delete attribute".to_string()))
9193
}
9294
}
95+
96+
// Access functions
97+
98+
fn fget(self, _vm: &mut VirtualMachine) -> Option<PyObjectRef> {
99+
self.getter.clone()
100+
}
101+
102+
fn fset(self, _vm: &mut VirtualMachine) -> Option<PyObjectRef> {
103+
self.setter.clone()
104+
}
105+
106+
fn fdel(self, _vm: &mut VirtualMachine) -> Option<PyObjectRef> {
107+
self.deleter.clone()
108+
}
109+
110+
// Python builder functions
111+
112+
fn getter(self, getter: Option<PyObjectRef>, vm: &mut VirtualMachine) -> PyResult<Self> {
113+
Self::new_with_type(
114+
vm,
115+
PyProperty {
116+
getter: getter.or_else(|| self.getter.clone()),
117+
setter: self.setter.clone(),
118+
deleter: self.deleter.clone(),
119+
},
120+
self.typ(),
121+
)
122+
}
123+
124+
fn setter(self, setter: Option<PyObjectRef>, vm: &mut VirtualMachine) -> PyResult<Self> {
125+
Self::new_with_type(
126+
vm,
127+
PyProperty {
128+
getter: self.getter.clone(),
129+
setter: setter.or_else(|| self.setter.clone()),
130+
deleter: self.deleter.clone(),
131+
},
132+
self.typ(),
133+
)
134+
}
135+
136+
fn deleter(self, deleter: Option<PyObjectRef>, vm: &mut VirtualMachine) -> PyResult<Self> {
137+
Self::new_with_type(
138+
vm,
139+
PyProperty {
140+
getter: self.getter.clone(),
141+
setter: self.setter.clone(),
142+
deleter: deleter.or_else(|| self.deleter.clone()),
143+
},
144+
self.typ(),
145+
)
146+
}
93147
}
94148

95149
pub struct PropertyBuilder<'a, T> {
@@ -190,5 +244,13 @@ pub fn init(context: &PyContext) {
190244
"__get__" => context.new_rustfunc(PyPropertyRef::get),
191245
"__set__" => context.new_rustfunc(PyPropertyRef::set),
192246
"__delete__" => context.new_rustfunc(PyPropertyRef::delete),
247+
248+
"fget" => context.new_property(PyPropertyRef::fget),
249+
"fset" => context.new_property(PyPropertyRef::fset),
250+
"fdel" => context.new_property(PyPropertyRef::fdel),
251+
252+
"getter" => context.new_rustfunc(PyPropertyRef::getter),
253+
"setter" => context.new_rustfunc(PyPropertyRef::setter),
254+
"deleter" => context.new_rustfunc(PyPropertyRef::deleter),
193255
});
194256
}

vm/src/pyobject.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,13 @@ impl<T: PyValue> PyRef<T> {
745745
pub fn into_object(self) -> PyObjectRef {
746746
self.obj
747747
}
748+
749+
pub fn typ(&self) -> PyClassRef {
750+
PyRef {
751+
obj: self.obj.typ(),
752+
_payload: PhantomData,
753+
}
754+
}
748755
}
749756

750757
impl<T> Deref for PyRef<T>
@@ -1211,6 +1218,16 @@ impl TryFromObject for PyObjectRef {
12111218
}
12121219
}
12131220

1221+
impl<T: TryFromObject> TryFromObject for Option<T> {
1222+
fn try_from_object(vm: &mut VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
1223+
if vm.get_none().is(&obj) {
1224+
Ok(None)
1225+
} else {
1226+
T::try_from_object(vm, obj).map(|x| Some(x))
1227+
}
1228+
}
1229+
}
1230+
12141231
/// A map of keyword arguments to their values.
12151232
///
12161233
/// A built-in function with a `KwArgs` parameter is analagous to a Python
@@ -1389,12 +1406,6 @@ where
13891406
}
13901407
}
13911408

1392-
// TODO: Allow a built-in function to return an `Option`, i.e.:
1393-
//
1394-
// impl<T: IntoPyObject> IntoPyObject for Option<T>
1395-
//
1396-
// Option::None should map to a Python `None`.
1397-
13981409
// Allows a built-in function to return any built-in object payload without
13991410
// explicitly implementing `IntoPyObject`.
14001411
impl<T> IntoPyObject for T

0 commit comments

Comments
 (0)