diff --git a/py/runtime.c b/py/runtime.c index 3a1f588ab7..9ec282685f 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -947,7 +947,11 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t } else if (MP_OBJ_IS_TYPE(member, &mp_type_type)) { // Don't try to bind types (even though they're callable) dest[0] = member; - } else if (mp_obj_is_callable(member)) { + } else if (MP_OBJ_IS_FUN(member) + || (MP_OBJ_IS_OBJ(member) + && (((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_closure + || ((mp_obj_base_t*)MP_OBJ_TO_PTR(member))->type->name == MP_QSTR_generator))) { + // only functions, closures and generators objects can be bound to self #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG if (self == MP_OBJ_NULL && mp_obj_get_type(member) == &mp_type_fun_builtin) { // we extracted a builtin method without a first argument, so we must diff --git a/tests/basics/class_bind_self.py b/tests/basics/class_bind_self.py new file mode 100644 index 0000000000..16e4f5ac95 --- /dev/null +++ b/tests/basics/class_bind_self.py @@ -0,0 +1,54 @@ +# test for correct binding of self when accessing attr of an instance + +class A: + def __init__(self, arg): + self.val = arg + def __str__(self): + return 'A.__str__ ' + str(self.val) + def __call__(self, arg): + return 'A.__call__', arg + def foo(self, arg): + return 'A.foo', self.val, arg + +def make_closure(x_in): + x = x_in + def closure(y): + return x, y is c + return closure + +class C: + # these act like methods and bind self + + def f1(self, arg): + return 'C.f1', self is c, arg + f2 = lambda self, arg: ('C.f2', self is c, arg) + f3 = make_closure('f3') # closure + def f4(self, arg): # generator + yield self is c, arg + + # these act like simple variables and don't bind self + + f5 = int # builtin type + f6 = abs # builtin function + f7 = A # user type + f8 = A(8) # user instance which is callable + f9 = A(9).foo # user bound method + +c = C() +print(c.f1(1)) +print(c.f2(2)) +print(c.f3()) +print(next(c.f4(4))) +print(c.f5(5)) +#print(c.f6(-6)) not working in uPy +print(c.f7(7)) +print(c.f8(8)) +print(c.f9(9)) + +# not working in uPy +#class C(list): +# # this acts like a method and binds self +# f1 = list.extend +#c = C() +#c.f1([3, 1, 2]) +#print(c)