mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-09 22:49:57 +09:00
Add nice message for no attribute on module
This commit is contained in:
@@ -18,14 +18,30 @@ impl PyValue for PyModule {
|
||||
}
|
||||
|
||||
impl PyModuleRef {
|
||||
fn init(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm.set_attr(&self.into_object(), "__name__", name)?;
|
||||
Ok(vm.get_none())
|
||||
fn new(cls: PyClassRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult<PyModuleRef> {
|
||||
let zelf = PyModule {
|
||||
name: name.as_str().to_owned(),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)?;
|
||||
vm.set_attr(zelf.as_object(), "__name__", name)?;
|
||||
Ok(zelf)
|
||||
}
|
||||
|
||||
fn getattribute(self, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
match vm.generic_getattribute(self.as_object().clone(), name.clone()) {
|
||||
Ok(Some(val)) => Ok(val),
|
||||
Ok(None) => Err(vm.new_attribute_error(format!(
|
||||
"module '{}' has no attribute '{}'",
|
||||
self.name, name,
|
||||
))),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(context: &PyContext) {
|
||||
extend_class!(&context, &context.types.module_type, {
|
||||
"__init__" => context.new_rustfunc(PyModuleRef::init),
|
||||
extend_class!(&context, &context.module_type, {
|
||||
"__new__" => context.new_rustfunc(PyModuleRef::new),
|
||||
"__getattribute__" => context.new_rustfunc(PyModuleRef::getattribute),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -223,39 +223,11 @@ fn object_dict_setter(
|
||||
))
|
||||
}
|
||||
|
||||
fn object_getattribute(obj: PyObjectRef, name_str: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
let name = &name_str.value;
|
||||
fn object_getattribute(obj: PyObjectRef, name: PyStringRef, vm: &VirtualMachine) -> PyResult {
|
||||
vm_trace!("object.__getattribute__({:?}, {:?})", obj, name);
|
||||
let cls = obj.class();
|
||||
|
||||
if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
let attr_class = attr.class();
|
||||
if objtype::class_has_attr(&attr_class, "__set__") {
|
||||
if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
|
||||
return vm.invoke(&descriptor, vec![attr, obj, cls.into_object()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(obj_attr) = object_getattr(&obj, &name, &vm)? {
|
||||
Ok(obj_attr)
|
||||
} else if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
vm.call_get_descriptor(attr, obj)
|
||||
} else if let Some(ref getter) = objtype::class_get_attr(&cls, "__getattr__") {
|
||||
vm.invoke(getter, vec![obj, name_str.into_object()])
|
||||
} else {
|
||||
Err(vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name)))
|
||||
}
|
||||
}
|
||||
|
||||
fn object_getattr(
|
||||
obj: &PyObjectRef,
|
||||
attr_name: &str,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
if let Some(ref dict) = obj.dict {
|
||||
dict.get_item_option(attr_name, vm)
|
||||
} else {
|
||||
Ok(None)
|
||||
match vm.generic_getattribute(obj.clone(), name.clone()) {
|
||||
Ok(Some(val)) => Ok(val),
|
||||
Ok(None) => Err(vm.new_attribute_error(format!("{} has no attribute '{}'", obj, name))),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
37
vm/src/vm.rs
37
vm/src/vm.rs
@@ -863,6 +863,43 @@ impl VirtualMachine {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn generic_getattribute(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
name_str: PyStringRef,
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
let name = name_str.as_str();
|
||||
let cls = obj.class();
|
||||
|
||||
if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
let attr_class = attr.class();
|
||||
if objtype::class_has_attr(&attr_class, "__set__") {
|
||||
if let Some(descriptor) = objtype::class_get_attr(&attr_class, "__get__") {
|
||||
return self
|
||||
.invoke(&descriptor, vec![attr, obj, cls.into_object()])
|
||||
.map(Some);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let attr = if let Some(ref dict) = obj.dict {
|
||||
dict.get_item_option(name_str.clone(), self)?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(obj_attr) = attr {
|
||||
Ok(Some(obj_attr))
|
||||
} else if let Some(attr) = objtype::class_get_attr(&cls, &name) {
|
||||
self.call_get_descriptor(attr, obj).map(Some)
|
||||
} else if let Some(getter) = objtype::class_get_attr(&cls, "__getattr__") {
|
||||
self.invoke(&getter, vec![obj, name_str.into_object()])
|
||||
.map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_callable(&self, obj: &PyObjectRef) -> bool {
|
||||
match_class!(obj,
|
||||
PyFunction => true,
|
||||
|
||||
Reference in New Issue
Block a user