Skip to content

Commit 964a041

Browse files
Merge pull request RustPython#677 from skinny121/property
Implement property attributes and functions
2 parents 36204f2 + 3f301bd commit 964a041

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
@@ -65,6 +65,8 @@ impl PyPropertyRef {
6565
)
6666
}
6767

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

93147
pub struct PropertyBuilder<'a> {
@@ -184,5 +238,13 @@ pub fn init(context: &PyContext) {
184238
"__get__" => context.new_rustfunc(PyPropertyRef::get),
185239
"__set__" => context.new_rustfunc(PyPropertyRef::set),
186240
"__delete__" => context.new_rustfunc(PyPropertyRef::delete),
241+
242+
"fget" => context.new_property(PyPropertyRef::fget),
243+
"fset" => context.new_property(PyPropertyRef::fset),
244+
"fdel" => context.new_property(PyPropertyRef::fdel),
245+
246+
"getter" => context.new_rustfunc(PyPropertyRef::getter),
247+
"setter" => context.new_rustfunc(PyPropertyRef::setter),
248+
"deleter" => context.new_rustfunc(PyPropertyRef::deleter),
187249
});
188250
}

vm/src/pyobject.rs

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

749756
impl<T> Deref for PyRef<T>
@@ -1210,6 +1217,16 @@ impl TryFromObject for PyObjectRef {
12101217
}
12111218
}
12121219

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

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

0 commit comments

Comments
 (0)