Skip to content

Commit 9e5b76c

Browse files
authored
Merge pull request RustPython#635 from skinny121/property_refactor
Unify property like objects
2 parents eb19eaa + 7d01323 commit 9e5b76c

File tree

10 files changed

+257
-195
lines changed

10 files changed

+257
-195
lines changed

tests/snippets/class.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,6 @@ def square(self):
1616
assert foo.square() == 25
1717

1818

19-
class Fubar:
20-
def __init__(self):
21-
self.x = 100
22-
23-
@property
24-
def foo(self):
25-
value = self.x
26-
self.x += 1
27-
return value
28-
29-
30-
f = Fubar()
31-
assert f.foo == 100
32-
assert f.foo == 101
33-
34-
3519
class Bar:
3620
""" W00t """
3721
def __init__(self, x):

tests/snippets/property.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from testutils import assertRaises
2+
3+
4+
class Fubar:
5+
def __init__(self):
6+
self.x = 100
7+
8+
@property
9+
def foo(self):
10+
value = self.x
11+
self.x += 1
12+
return value
13+
14+
15+
f = Fubar()
16+
assert f.foo == 100
17+
assert f.foo == 101
18+
19+
20+
null_property = property()
21+
assert type(null_property) is property
22+
23+
p = property(lambda x: x[0])
24+
assert p.__get__((2,), tuple) == 2
25+
# TODO owner parameter is optional
26+
# assert p.__get__((2,)) == 2
27+
28+
with assertRaises(AttributeError):
29+
null_property.__get__((), tuple)
30+
31+
with assertRaises(TypeError):
32+
property.__new__(object)

vm/src/function.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::marker::PhantomData;
33
use std::ops::Deref;
44

55
use crate::obj::objtype;
6+
use crate::obj::objtype::PyClassRef;
67
use crate::pyobject::{
78
IntoPyObject, PyContext, PyObject, PyObjectPayload, PyObjectPayload2, PyObjectRef, PyResult,
89
TryFromObject, TypeProtocol,
@@ -45,6 +46,25 @@ where
4546
}
4647
}
4748

49+
pub fn new_with_type(vm: &mut VirtualMachine, payload: T, cls: PyClassRef) -> PyResult<Self> {
50+
let required_type = T::required_type(&vm.ctx);
51+
if objtype::issubclass(&cls.obj, &required_type) {
52+
Ok(PyRef {
53+
obj: PyObject::new(
54+
PyObjectPayload::AnyRustValue {
55+
value: Box::new(payload),
56+
},
57+
cls.obj,
58+
),
59+
_payload: PhantomData,
60+
})
61+
} else {
62+
let subtype = vm.to_pystr(&cls.obj)?;
63+
let basetype = vm.to_pystr(&required_type)?;
64+
Err(vm.new_type_error(format!("{} is not a subtype of {}", subtype, basetype)))
65+
}
66+
}
67+
4868
pub fn as_object(&self) -> &PyObjectRef {
4969
&self.obj
5070
}

vm/src/obj/objcode.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn init(context: &PyContext) {
4747
("co_kwonlyargcount", code_co_kwonlyargcount),
4848
("co_name", code_co_name),
4949
] {
50-
context.set_attr(code_type, name, context.new_member_descriptor(f))
50+
context.set_attr(code_type, name, context.new_property(f))
5151
}
5252
}
5353

@@ -79,14 +79,7 @@ fn code_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
7979
}
8080

8181
fn member_code_obj(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult<bytecode::CodeObject> {
82-
arg_check!(
83-
vm,
84-
args,
85-
required = [
86-
(zelf, Some(vm.ctx.code_type())),
87-
(_cls, Some(vm.ctx.type_type()))
88-
]
89-
);
82+
arg_check!(vm, args, required = [(zelf, Some(vm.ctx.code_type()))]);
9083
Ok(get_value(zelf))
9184
}
9285

vm/src/obj/objfunction.rs

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub fn init(context: &PyContext) {
5555
context.set_attr(
5656
&function_type,
5757
"__code__",
58-
context.new_member_descriptor(function_code),
58+
context.new_property(function_code),
5959
);
6060

6161
let builtin_function_or_method_type = &context.builtin_function_or_method_type;
@@ -65,25 +65,6 @@ pub fn init(context: &PyContext) {
6565
context.new_rustfunc(bind_method),
6666
);
6767

68-
let member_descriptor_type = &context.member_descriptor_type;
69-
context.set_attr(
70-
&member_descriptor_type,
71-
"__get__",
72-
context.new_rustfunc(member_get),
73-
);
74-
75-
let data_descriptor_type = &context.data_descriptor_type;
76-
context.set_attr(
77-
&data_descriptor_type,
78-
"__get__",
79-
context.new_rustfunc(data_get),
80-
);
81-
context.set_attr(
82-
&data_descriptor_type,
83-
"__set__",
84-
context.new_rustfunc(data_set),
85-
);
86-
8768
let classmethod_type = &context.classmethod_type;
8869
context.set_attr(
8970
&classmethod_type,
@@ -130,36 +111,6 @@ fn function_code(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
130111
}
131112
}
132113

133-
fn member_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
134-
match args.shift().get_attr("function") {
135-
Some(function) => vm.invoke(function, args),
136-
None => {
137-
let attribute_error = vm.context().exceptions.attribute_error.clone();
138-
Err(vm.new_exception(attribute_error, String::from("Attribute Error")))
139-
}
140-
}
141-
}
142-
143-
fn data_get(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
144-
match args.shift().get_attr("fget") {
145-
Some(function) => vm.invoke(function, args),
146-
None => {
147-
let attribute_error = vm.context().exceptions.attribute_error.clone();
148-
Err(vm.new_exception(attribute_error, String::from("Attribute Error")))
149-
}
150-
}
151-
}
152-
153-
fn data_set(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
154-
match args.shift().get_attr("fset") {
155-
Some(function) => vm.invoke(function, args),
156-
None => {
157-
let attribute_error = vm.context().exceptions.attribute_error.clone();
158-
Err(vm.new_exception(attribute_error, String::from("Attribute Error")))
159-
}
160-
}
161-
}
162-
163114
// Classmethod type methods:
164115
fn classmethod_get(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
165116
trace!("classmethod.__get__ {:?}", args.args);

vm/src/obj/objobject.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::objstr;
22
use super::objtype;
33
use crate::function::PyRef;
4+
use crate::obj::objproperty::PropertyBuilder;
45
use crate::pyobject::{
56
AttributeProtocol, DictProtocol, IdProtocol, PyAttributes, PyContext, PyFuncArgs, PyObject,
67
PyObjectPayload, PyObjectRef, PyResult, TypeProtocol,
@@ -171,7 +172,10 @@ pub fn init(context: &PyContext) {
171172
context.set_attr(
172173
&object,
173174
"__class__",
174-
context.new_data_descriptor(object_class, object_class_setter),
175+
PropertyBuilder::new(context)
176+
.add_getter(object_class)
177+
.add_setter(object_class_setter)
178+
.create(),
175179
);
176180
context.set_attr(&object, "__eq__", context.new_rustfunc(object_eq));
177181
context.set_attr(&object, "__ne__", context.new_rustfunc(object_ne));
@@ -180,11 +184,7 @@ pub fn init(context: &PyContext) {
180184
context.set_attr(&object, "__gt__", context.new_rustfunc(object_gt));
181185
context.set_attr(&object, "__ge__", context.new_rustfunc(object_ge));
182186
context.set_attr(&object, "__delattr__", context.new_rustfunc(object_delattr));
183-
context.set_attr(
184-
&object,
185-
"__dict__",
186-
context.new_member_descriptor(object_dict),
187-
);
187+
context.set_attr(&object, "__dict__", context.new_property(object_dict));
188188
context.set_attr(&object, "__dir__", context.new_rustfunc(object_dir));
189189
context.set_attr(&object, "__hash__", context.new_rustfunc(object_hash));
190190
context.set_attr(&object, "__str__", context.new_rustfunc(object_str));
@@ -202,9 +202,8 @@ fn object_init(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
202202
Ok(vm.ctx.none())
203203
}
204204

205-
// TODO Use PyClassRef for owner to enforce type
206-
fn object_class(_obj: PyObjectRef, owner: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef {
207-
owner
205+
fn object_class(obj: PyObjectRef, _vm: &mut VirtualMachine) -> PyObjectRef {
206+
obj.typ()
208207
}
209208

210209
fn object_class_setter(

0 commit comments

Comments
 (0)