* vm: use non-raising lookup for default attribute access
get_attribute_opt() backs hasattr() and getattr() with a default. It used
get_attr_inner() for every object, then converted AttributeError from a
missing attribute into None. The usual user-object miss therefore built and
dropped an exception just to answer an optional lookup.
Route objects using PyBaseObject::getattro through generic_getattr_opt(),
which returns the missing case without constructing AttributeError. Objects
with a custom getattro slot keep the raising path: their __getattribute__ and
__getattr__ hooks can run Python code, so their exceptions must preserve the
existing behavior. AttributeError from either route, including a descriptor,
is still converted to None; non-AttributeError exceptions propagate.
The new snippet covers ordinary hits and misses, custom __getattribute__ /
__getattr__, descriptor exceptions, and dynamic getattro-slot updates.
Refs youknowone#48
Assisted-by: Codex:GPT-5.6-terra
* vm: use non-raising lookup for optional attributes