Added bunch af attribute has/get/set/delete tests and fixed getattr and

hasattr handling of exceptions.
This commit is contained in:
ben
2019-03-16 20:49:29 +13:00
parent 4e42bd077c
commit 63d40edbc6
2 changed files with 111 additions and 19 deletions

84
tests/snippets/attr.py Normal file
View File

@@ -0,0 +1,84 @@
from testutils import assertRaises
class A:
pass
a = A()
a.b = 10
assert hasattr(a, 'b')
assert a.b == 10
# test override attribute
setattr(a, 'b', 12)
assert a.b == 12
assert getattr(a, 'b') == 12
# test non-existent attribute
with assertRaises(AttributeError):
_ = a.c
with assertRaises(AttributeError):
getattr(a, 'c')
assert getattr(a, 'c', 21) == 21
# test set attribute
setattr(a, 'c', 20)
assert hasattr(a, 'c')
assert a.c == 20
# test delete attribute
delattr(a, 'c')
assert not hasattr(a, 'c')
with assertRaises(AttributeError):
_ = a.c
# test setting attribute on builtin
with assertRaises(AttributeError):
None.a = 1
with assertRaises(AttributeError):
setattr(None, 'a', 2)
attrs = {}
class CustomLookup:
def __getattr__(self, item):
return "value_{}".format(item)
def __setattr__(self, key, value):
attrs[key] = value
custom = CustomLookup()
assert custom.attr == "value_attr"
custom.a = 2
custom.b = 5
assert attrs == {'a': 2, 'b': 5}
class GetRaise:
def __init__(self, ex):
self.ex = ex
def __getattr__(self, item):
raise self.ex
assert not hasattr(GetRaise(AttributeError()), 'a')
with assertRaises(AttributeError):
getattr(GetRaise(AttributeError()), 'a')
assert getattr(GetRaise(AttributeError()), 'a', 11) == 11
with assertRaises(KeyError):
hasattr(GetRaise(KeyError()), 'a')
with assertRaises(KeyError):
getattr(GetRaise(KeyError()), 'a')
with assertRaises(KeyError):
getattr(GetRaise(KeyError()), 'a', 11)

View File

@@ -18,7 +18,7 @@ use crate::obj::objstr::{self, PyStringRef};
use crate::obj::objtype;
use crate::frame::Scope;
use crate::function::{Args, PyFuncArgs};
use crate::function::{Args, OptionalArg, PyFuncArgs};
use crate::pyobject::{
AttributeProtocol, IdProtocol, PyContext, PyObjectRef, PyResult, TypeProtocol,
};
@@ -302,30 +302,38 @@ fn builtin_format(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
vm.call_method(obj, "__format__", vec![format_spec])
}
fn builtin_getattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
);
vm.get_attribute(obj.clone(), attr.clone())
fn catch_attr_exception<T>(ex: PyObjectRef, default: T, vm: &mut VirtualMachine) -> PyResult<T> {
if objtype::isinstance(&ex, &vm.ctx.exceptions.attribute_error) {
Ok(default)
} else {
Err(ex)
}
}
fn builtin_getattr(
obj: PyObjectRef,
attr: PyStringRef,
default: OptionalArg<PyObjectRef>,
vm: &mut VirtualMachine,
) -> PyResult {
let ret = vm.get_attribute(obj.clone(), attr.into_object());
if let OptionalArg::Present(default) = default {
ret.or_else(|ex| catch_attr_exception(ex, default, vm))
} else {
ret
}
}
fn builtin_globals(vm: &mut VirtualMachine, _args: PyFuncArgs) -> PyResult {
Ok(vm.current_scope().globals.clone())
}
fn builtin_hasattr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
arg_check!(
vm,
args,
required = [(obj, None), (attr, Some(vm.ctx.str_type()))]
);
let has_attr = match vm.get_attribute(obj.clone(), attr.clone()) {
Ok(..) => true,
Err(..) => false,
};
Ok(vm.context().new_bool(has_attr))
fn builtin_hasattr(obj: PyObjectRef, attr: PyStringRef, vm: &mut VirtualMachine) -> PyResult<bool> {
if let Err(ex) = vm.get_attribute(obj.clone(), attr.into_object()) {
catch_attr_exception(ex, false, vm)
} else {
Ok(true)
}
}
fn builtin_hash(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {