1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//! Implementation of the hasattr() builtin function.
use monty_types::ExcType;
use crate::{
args::ArgValues,
bytecode::{CallResult, VM},
defer_drop,
exception_private::{ExcTypeExt, RunError, RunResult, SimpleException},
heap::DropWithContext,
value::Value,
};
/// Implementation of the hasattr() builtin function.
///
/// Returns True if the object has the named attribute, False otherwise.
/// This function always succeeds and never raises AttributeError.
///
/// Signature: `hasattr(object, name)`
///
/// Note: This is implemented by calling getattr(object, name) and returning
/// True if it succeeds, False if it raises an exception.
///
/// Examples:
/// ```python
/// hasattr(obj, 'x') # Check if obj.x exists
/// hasattr(slice(1, 10), 'start') # True - slice has start attribute
/// hasattr(42, 'nonexistent') # False - int has no such attribute
/// ```
pub fn builtin_hasattr(vm: &mut VM<'_>, args: ArgValues) -> RunResult<Value> {
let positional = args.into_pos_only("hasattr", vm.heap)?;
defer_drop!(positional, vm);
let (object, name) = match positional.as_slice() {
[object, name] => (object, name),
other => return Err(ExcType::type_error_arg_count("hasattr", 2, other.len())),
};
let Some(name) = name.as_either_str(vm.heap) else {
return Err(SimpleException::new_msg(
ExcType::TypeError,
format!("attribute name must be string, not '{}'", name.py_type_name(vm)),
)
.into());
};
// important: we must own the returned value if py_get_attr succeeds to drop it
let has_attr = match object.py_getattr(&name, vm) {
Ok(CallResult::Value(value)) => {
value.drop_with(vm);
true
}
Ok(other) => {
other.drop_with(vm);
// hasattr() only tests attribute values — OS calls, external calls,
// method calls, and awaits are not supported here
//
// TODO: might need to support this case?
return Err(
SimpleException::new_msg(ExcType::TypeError, "hasattr(): attribute is not a simple value").into(),
);
}
Err(RunError::Exc(e)) if e.exc.exc_type() == ExcType::AttributeError => false,
Err(e) => return Err(e),
};
Ok(Value::Bool(has_attr))
}