py: Allow retrieving a function's __name__.

Disabled by default.  Enabled on unix and stmhal ports.
diff --git a/py/bc.c b/py/bc.c
index 9498b4f..3a8dc5e 100644
--- a/py/bc.c
+++ b/py/bc.c
@@ -62,7 +62,7 @@
 #elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
     nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
         "%s() takes %d positional arguments but %d were given",
-        mp_obj_fun_get_name(f), expected, given));
+        qstr_str(mp_obj_fun_get_name(f)), expected, given));
 #endif
 }
 
diff --git a/py/mpconfig.h b/py/mpconfig.h
index e8f7cc8..094d8d7 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -359,6 +359,11 @@
 /*****************************************************************************/
 /* Fine control over Python builtins, classes, modules, etc                  */
 
+// Whether to implement attributes on functions
+#ifndef MICROPY_PY_FUNCTION_ATTRS
+#define MICROPY_PY_FUNCTION_ATTRS (0)
+#endif
+
 // Whether str object is proper unicode
 #ifndef MICROPY_PY_BUILTINS_STR_UNICODE
 #define MICROPY_PY_BUILTINS_STR_UNICODE (0)
diff --git a/py/obj.h b/py/obj.h
index b7f38d1..33b7647 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -574,8 +574,8 @@
     void *fun; // must be a pointer to a callable function in ROM
 } mp_obj_fun_builtin_t;
 
-const char *mp_obj_fun_get_name(mp_const_obj_t fun);
-const char *mp_obj_code_get_name(const byte *code_info);
+qstr mp_obj_fun_get_name(mp_const_obj_t fun);
+qstr mp_obj_code_get_name(const byte *code_info);
 
 mp_obj_t mp_identity(mp_obj_t self);
 MP_DECLARE_CONST_FUN_OBJ(mp_identity_obj);
diff --git a/py/objboundmeth.c b/py/objboundmeth.c
index 7698ed2..69ae263 100644
--- a/py/objboundmeth.c
+++ b/py/objboundmeth.c
@@ -70,6 +70,15 @@
     }
 }
 
+#if MICROPY_PY_FUNCTION_ATTRS
+STATIC void bound_meth_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+    if(attr == MP_QSTR___name__) {
+        mp_obj_bound_meth_t *o = self_in;
+        dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(o->meth));
+    }
+}
+#endif
+
 const mp_obj_type_t bound_meth_type = {
     { &mp_type_type },
     .name = MP_QSTR_bound_method,
@@ -77,6 +86,9 @@
     .print = bound_meth_print,
 #endif
     .call = bound_meth_call,
+#if MICROPY_PY_FUNCTION_ATTRS
+    .load_attr = bound_meth_load_attr,
+#endif
 };
 
 mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) {
diff --git a/py/objfun.c b/py/objfun.c
index dc068d5..d8ea22f 100644
--- a/py/objfun.c
+++ b/py/objfun.c
@@ -103,12 +103,12 @@
 /******************************************************************************/
 /* byte code functions                                                        */
 
-const char *mp_obj_code_get_name(const byte *code_info) {
+qstr mp_obj_code_get_name(const byte *code_info) {
     mp_decode_uint(&code_info); // skip code_info_size entry
-    return qstr_str(mp_decode_uint(&code_info));
+    return mp_decode_uint(&code_info);
 }
 
-const char *mp_obj_fun_get_name(mp_const_obj_t fun_in) {
+qstr mp_obj_fun_get_name(mp_const_obj_t fun_in) {
     const mp_obj_fun_bc_t *fun = fun_in;
     const byte *code_info = fun->bytecode;
     return mp_obj_code_get_name(code_info);
@@ -118,7 +118,7 @@
 STATIC void fun_bc_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t o_in, mp_print_kind_t kind) {
     (void)kind;
     mp_obj_fun_bc_t *o = o_in;
-    print(env, "<function %s at 0x%x>", mp_obj_fun_get_name(o), o);
+    print(env, "<function %s at 0x%x>", qstr_str(mp_obj_fun_get_name(o)), o);
 }
 #endif
 
@@ -246,6 +246,14 @@
     }
 }
 
+#if MICROPY_PY_FUNCTION_ATTRS
+STATIC void fun_bc_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
+    if(attr == MP_QSTR___name__) {
+        dest[0] = MP_OBJ_NEW_QSTR(mp_obj_fun_get_name(self_in));
+    }
+}
+#endif
+
 const mp_obj_type_t mp_type_fun_bc = {
     { &mp_type_type },
     .name = MP_QSTR_function,
@@ -253,6 +261,9 @@
     .print = fun_bc_print,
 #endif
     .call = fun_bc_call,
+#if MICROPY_PY_FUNCTION_ATTRS
+    .load_attr = fun_bc_load_attr,
+#endif
 };
 
 mp_obj_t mp_obj_new_fun_bc(mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t n_kwonly_args, mp_obj_t def_args_in, mp_obj_t def_kw_args, const byte *code) {
diff --git a/py/objgenerator.c b/py/objgenerator.c
index e67824d..112becb 100644
--- a/py/objgenerator.c
+++ b/py/objgenerator.c
@@ -97,7 +97,7 @@
 STATIC void gen_instance_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in, mp_print_kind_t kind) {
     (void)kind;
     mp_obj_gen_instance_t *self = self_in;
-    print(env, "<generator object '%s' at %p>", mp_obj_code_get_name(self->code_state.code_info), self_in);
+    print(env, "<generator object '%s' at %p>", qstr_str(mp_obj_code_get_name(self->code_state.code_info)), self_in);
 }
 
 mp_vm_return_kind_t mp_obj_gen_resume(mp_obj_t self_in, mp_obj_t send_value, mp_obj_t throw_value, mp_obj_t *ret_val) {
diff --git a/stmhal/mpconfigport.h b/stmhal/mpconfigport.h
index 1d33787..e1d532d 100644
--- a/stmhal/mpconfigport.h
+++ b/stmhal/mpconfigport.h
@@ -55,6 +55,7 @@
 #define MICROPY_STREAMS_NON_BLOCK   (1)
 #define MICROPY_MODULE_WEAK_LINKS   (1)
 #define MICROPY_CAN_OVERRIDE_BUILTINS (1)
+#define MICROPY_PY_FUNCTION_ATTRS   (1)
 #define MICROPY_PY_BUILTINS_STR_UNICODE (1)
 #define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
 #define MICROPY_PY_BUILTINS_FROZENSET (1)
diff --git a/tests/basics/fun_name.py b/tests/basics/fun_name.py
new file mode 100644
index 0000000..a7f9c60
--- /dev/null
+++ b/tests/basics/fun_name.py
@@ -0,0 +1,16 @@
+def Fun():

+    pass

+

+class A:

+    def __init__(self):

+        pass

+    def Fun(self):

+        pass

+

+try:

+    print(Fun.__name__)

+    print(A.__init__.__name__)

+    print(A.Fun.__name__)

+    print(A().Fun.__name__)

+except AttributeError:

+    print('SKIP')

diff --git a/unix/mpconfigport.h b/unix/mpconfigport.h
index a7ebf21..fd8e522 100644
--- a/unix/mpconfigport.h
+++ b/unix/mpconfigport.h
@@ -57,6 +57,7 @@
 #define MICROPY_OPT_COMPUTED_GOTO   (1)
 #define MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE (1)
 #define MICROPY_CAN_OVERRIDE_BUILTINS (1)
+#define MICROPY_PY_FUNCTION_ATTRS   (1)
 #define MICROPY_PY_BUILTINS_STR_UNICODE (1)
 #define MICROPY_PY_BUILTINS_MEMORYVIEW (1)
 #define MICROPY_PY_BUILTINS_FROZENSET (1)