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
//! Implementation of the getattr() 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 getattr() builtin function.
///
/// Returns the value of the named attribute of an object.
/// If the attribute doesn't exist and a default is provided, returns the default.
/// If no default is provided and the attribute doesn't exist, raises AttributeError.
///
/// Note: name must be a string. Per Python docs, "Since private name mangling happens
/// at compilation time, one must manually mangle a private attribute's (attributes with
/// two leading underscores) name in order to retrieve it with getattr()."
///
/// Examples:
/// ```python
/// getattr(obj, 'x') # Get obj.x
/// getattr(obj, 'y', None) # Get obj.y or None if not found
/// getattr(module, 'function') # Get module.function
/// ```
pub fn builtin_getattr(vm: &mut VM<'_>, args: ArgValues) -> RunResult<Value> {
let positional = args.into_pos_only("getattr", vm.heap)?;
defer_drop!(positional, vm);
let (object, name, default) = match positional.as_slice() {
too_few @ ([] | [_]) => return Err(ExcType::type_error_at_least("getattr", 2, too_few.len())),
[object, name] => (object, name, None),
[object, name, default] => (object, name, Some(default)),
too_many => return Err(ExcType::type_error_at_most("getattr", 3, too_many.len())),
};
let Some(attr) = name.as_either_str(vm.heap) else {
let ty = name.py_type_name(vm);
return Err(
SimpleException::new_msg(ExcType::TypeError, format!("attribute name must be string, not '{ty}'")).into(),
);
};
match object.py_getattr(&attr, vm) {
Ok(CallResult::Value(value)) => Ok(value),
Ok(other) => {
other.drop_with(vm);
// getattr() only retrieves attribute values — OS calls, external calls,
// method calls, and awaits are not supported here
//
// TODO: might need to support this case?
Err(SimpleException::new_msg(ExcType::TypeError, "getattr(): attribute is not a simple value").into())
}
Err(RunError::Exc(e))
if let Some(d) = default
&& e.exc.exc_type() == ExcType::AttributeError =>
{
Ok(d.clone_with_heap(vm))
}
Err(e) => Err(e),
}
}