Skip to content

Commit 5c7b0da

Browse files
committed
Magic methods aren't looked up via getattribute.
1 parent 3e07c19 commit 5c7b0da

File tree

3 files changed

+89
-51
lines changed

3 files changed

+89
-51
lines changed

vm/src/obj/objobject.rs

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,7 @@ fn object_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
145145
if let Some(obj_attr) = obj.get_attr(&name) {
146146
Ok(obj_attr)
147147
} else if let Some(attr) = cls.get_attr(&name) {
148-
let attr_class = attr.typ();
149-
if let Some(descriptor) = attr_class.get_attr("__get__") {
150-
vm.invoke(
151-
descriptor,
152-
PyFuncArgs {
153-
args: vec![attr, obj.clone(), cls],
154-
kwargs: vec![],
155-
},
156-
)
157-
} else {
158-
Ok(attr)
159-
}
148+
vm.call_get_descriptor(attr, obj.clone())
160149
} else {
161150
if let Some(getter) = cls.get_attr("__getattr__") {
162151
vm.invoke(

vm/src/obj/objtype.rs

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use super::super::pyobject::{
44
};
55
use super::super::vm::VirtualMachine;
66
use super::objdict;
7+
use super::objstr;
78
use super::objtype; // Required for arg_check! to use isinstance
89
use std::collections::HashMap;
910

@@ -137,34 +138,70 @@ pub fn type_call(vm: &mut VirtualMachine, mut args: PyFuncArgs) -> PyResult {
137138
Ok(obj)
138139
}
139140

140-
// pub fn get_attribute(vm: &mut VirtualMachine, obj: PyObjectRef, name: &str) -> PyResult {
141-
// let cls = obj.typ();
142-
// trace!("get_attribute: {:?}, {:?}, {:?}", cls, obj, name);
143-
// if let Some(attr) = cls.get_attr(name) {
144-
// let attr_class = attr.typ();
145-
// if let Some(descriptor) = attr_class.get_attr("__get__") {
146-
// return vm.invoke(
147-
// descriptor,
148-
// PyFuncArgs {
149-
// args: vec![attr, obj, cls],
150-
// kwargs: vec![],
151-
// },
152-
// );
153-
// }
154-
// }
155-
156-
// if let Some(obj_attr) = obj.get_attr(name) {
157-
// Ok(obj_attr)
158-
// } else if let Some(cls_attr) = cls.get_attr(name) {
159-
// Ok(cls_attr)
160-
// } else {
161-
// let attribute_error = vm.context().exceptions.attribute_error.clone();
162-
// Err(vm.new_exception(
163-
// attribute_error,
164-
// format!("{:?} object has no attribute {}", cls, name),
165-
// ))
166-
// }
167-
// }
141+
pub fn type_getattribute(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
142+
arg_check!(
143+
vm,
144+
args,
145+
required = [
146+
(cls, Some(vm.ctx.object())),
147+
(name_str, Some(vm.ctx.str_type()))
148+
]
149+
);
150+
let name = objstr::get_value(&name_str);
151+
trace!("type.__getattribute__({:?}, {:?})", cls, name);
152+
let mcl = cls.typ();
153+
154+
if let Some(attr) = mcl.get_attr(&name) {
155+
let attr_class = attr.typ();
156+
if attr_class.has_attr("__set__") {
157+
if let Some(descriptor) = attr_class.get_attr("__get__") {
158+
return vm.invoke(
159+
descriptor,
160+
PyFuncArgs {
161+
args: vec![attr, cls.clone(), mcl],
162+
kwargs: vec![],
163+
},
164+
);
165+
}
166+
}
167+
}
168+
169+
if let Some(attr) = cls.get_attr(&name) {
170+
let attr_class = attr.typ();
171+
if let Some(descriptor) = attr_class.get_attr("__get__") {
172+
let none = vm.get_none();
173+
return vm.invoke(
174+
descriptor,
175+
PyFuncArgs {
176+
args: vec![attr, none, cls.clone()],
177+
kwargs: vec![],
178+
},
179+
);
180+
}
181+
}
182+
183+
if let Some(cls_attr) = cls.get_attr(&name) {
184+
Ok(cls_attr)
185+
} else if let Some(attr) = mcl.get_attr(&name) {
186+
vm.call_get_descriptor(attr, cls.clone())
187+
} else {
188+
if let Some(getter) = cls.get_attr("__getattr__") {
189+
vm.invoke(
190+
getter,
191+
PyFuncArgs {
192+
args: vec![mcl, name_str.clone()],
193+
kwargs: vec![],
194+
},
195+
)
196+
} else {
197+
let attribute_error = vm.context().exceptions.attribute_error.clone();
198+
Err(vm.new_exception(
199+
attribute_error,
200+
format!("{:?} object {:?} has no attribute {}", mcl, cls, name),
201+
))
202+
}
203+
}
204+
}
168205

169206
pub fn get_attributes(obj: &PyObjectRef) -> HashMap<String, PyObjectRef> {
170207
// Gather all members here:

vm/src/vm.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use super::frame::{copy_code, Frame};
1414
use super::obj::objgenerator;
1515
use super::obj::objiter;
1616
use super::obj::objlist;
17-
use super::obj::objobject;
1817
use super::obj::objtuple;
1918
use super::obj::objtype;
2019
use super::pyobject::{
@@ -150,18 +149,37 @@ impl VirtualMachine {
150149
self.call_method(obj, "__repr__", vec![])
151150
}
152151

152+
pub fn call_get_descriptor(&mut self, attr: PyObjectRef, obj: PyObjectRef) -> PyResult {
153+
let attr_class = attr.typ();
154+
if let Some(descriptor) = attr_class.get_attr("__get__") {
155+
let cls = obj.typ();
156+
self.invoke(
157+
descriptor,
158+
PyFuncArgs {
159+
args: vec![attr, obj.clone(), cls],
160+
kwargs: vec![],
161+
},
162+
)
163+
} else {
164+
Ok(attr)
165+
}
166+
}
167+
153168
pub fn call_method(
154169
&mut self,
155170
obj: &PyObjectRef,
156171
method_name: &str,
157172
args: Vec<PyObjectRef>,
158173
) -> PyResult {
159-
let func = self.get_attribute(obj.clone(), method_name)?;
174+
let cls = obj.typ();
175+
let func = cls.get_attr(method_name).unwrap();
176+
trace!("vm.call_method {:?} {:?} -> {:?}", obj, method_name, func);
177+
let wrapped = self.call_get_descriptor(func, obj.clone())?;
160178
let args = PyFuncArgs {
161179
args: args,
162180
kwargs: vec![],
163181
};
164-
self.invoke(func, args)
182+
self.invoke(wrapped, args)
165183
}
166184

167185
pub fn invoke(&mut self, func_ref: PyObjectRef, args: PyFuncArgs) -> PyResult {
@@ -177,18 +195,12 @@ impl VirtualMachine {
177195
name: _,
178196
dict: _,
179197
mro: _,
180-
} => {
181-
let function = self.get_attribute(func_ref.clone(), "__call__")?;
182-
self.invoke(function, args)
183-
}
198+
} => self.call_method(&func_ref, "__call__", args.args),
184199
PyObjectKind::BoundMethod {
185200
ref function,
186201
ref object,
187202
} => self.invoke(function.clone(), args.insert(object.clone())),
188-
PyObjectKind::Instance { .. } => {
189-
let function = self.get_attribute(func_ref.clone(), "__call__")?;
190-
self.invoke(function, args)
191-
}
203+
PyObjectKind::Instance { .. } => self.call_method(&func_ref, "__call__", args.args),
192204
ref kind => {
193205
unimplemented!("invoke unimplemented for: {:?}", kind);
194206
}

0 commit comments

Comments
 (0)