From bfb7d6a26d6fe5b9a75447085514c609204eb469 Mon Sep 17 00:00:00 2001
From: Paul Sokolovsky <pfalcon@users.sourceforge.net>
Date: Sat, 5 Apr 2014 13:33:04 +0300
Subject: [PATCH] py: Support 3-arg getattr() builtin (with default value).

---
 py/builtin.c             | 12 ++++++++----
 py/runtime.c             | 14 ++++++++++----
 py/runtime.h             |  1 +
 tests/basics/getattr1.py |  2 ++
 4 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/py/builtin.c b/py/builtin.c
index 145bc65b1..2bf12e492 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -400,9 +400,13 @@ STATIC mp_obj_t mp_builtin_id(mp_obj_t o_in) {
 
 MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_id_obj, mp_builtin_id);
 
-STATIC mp_obj_t mp_builtin_getattr(mp_obj_t o_in, mp_obj_t attr) {
-    assert(MP_OBJ_IS_QSTR(attr));
-    return mp_load_attr(o_in, MP_OBJ_QSTR_VALUE(attr));
+STATIC mp_obj_t mp_builtin_getattr(uint n_args, const mp_obj_t *args) {
+    assert(MP_OBJ_IS_QSTR(args[1]));
+    mp_obj_t defval = MP_OBJ_NULL;
+    if (n_args > 2) {
+        defval = args[2];
+    }
+    return mp_load_attr_default(args[0], MP_OBJ_QSTR_VALUE(args[1]), defval);
 }
 
-MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_getattr_obj, mp_builtin_getattr);
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj, 2, 3, mp_builtin_getattr);
diff --git a/py/runtime.c b/py/runtime.c
index 773f998d3..df7de3e54 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -689,12 +689,14 @@ too_long:
     nlr_jump(mp_obj_new_exception_msg_varg(&mp_type_ValueError, "too many values to unpack (expected %d)", num));
 }
 
-mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
+mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) {
     DEBUG_OP_printf("load attr %p.%s\n", base, qstr_str(attr));
-    // use load_method
     mp_obj_t dest[2];
-    mp_load_method(base, attr, dest);
-    if (dest[1] == MP_OBJ_NULL) {
+    // use load_method, raising or not raising exception
+    ((defval == MP_OBJ_NULL) ? mp_load_method : mp_load_method_maybe)(base, attr, dest);
+    if (dest[0] == MP_OBJ_NULL) {
+        return defval;
+    } else if (dest[1] == MP_OBJ_NULL) {
         // load_method returned just a normal attribute
         return dest[0];
     } else {
@@ -703,6 +705,10 @@ mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
     }
 }
 
+mp_obj_t mp_load_attr(mp_obj_t base, qstr attr) {
+    return mp_load_attr_default(base, attr, MP_OBJ_NULL);
+}
+
 // no attribute found, returns:     dest[0] == MP_OBJ_NULL, dest[1] == MP_OBJ_NULL
 // normal attribute found, returns: dest[0] == <attribute>, dest[1] == MP_OBJ_NULL
 // method attribute found, returns: dest[0] == <method>,    dest[1] == <self>
diff --git a/py/runtime.h b/py/runtime.h
index b233d23b4..22e7781be 100644
--- a/py/runtime.h
+++ b/py/runtime.h
@@ -45,6 +45,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, uint n_args_n_kw, const mp_obj_
 void mp_unpack_sequence(mp_obj_t seq, uint num, mp_obj_t *items);
 mp_obj_t mp_store_map(mp_obj_t map, mp_obj_t key, mp_obj_t value);
 mp_obj_t mp_load_attr(mp_obj_t base, qstr attr);
+mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval);
 void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void mp_load_method_maybe(mp_obj_t base, qstr attr, mp_obj_t *dest);
 void mp_store_attr(mp_obj_t base, qstr attr, mp_obj_t val);
diff --git a/tests/basics/getattr1.py b/tests/basics/getattr1.py
index 7b7e07392..9a96154ca 100644
--- a/tests/basics/getattr1.py
+++ b/tests/basics/getattr1.py
@@ -13,3 +13,5 @@ a = A()
 print(getattr(a, "var"))
 print(getattr(a, "var2"))
 print(getattr(a, "meth")(5))
+print(getattr(a, "_none_such", 123))
+print(getattr(list, "foo", 456))
-- 
GitLab