summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/runtime.c6
-rw-r--r--tests/basics/class_bind_self.py54
2 files changed, 59 insertions, 1 deletions
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)