py: Be more restrictive binding self when looking up instance attrs.

When looking up and extracting an attribute of an instance, some
attributes must bind self as the first argument to make a working method
call.  Previously to this patch, any attribute that was callable had self
bound as the first argument.  But Python specs require the check to be
more restrictive, and only functions, closures and generators should have
self bound as the first argument

Addresses issue #1675.
readthedocs
Damien George 2015-12-26 12:41:31 +00:00
parent 84b245f187
commit 78913211a9
2 changed files with 59 additions and 1 deletions

View File

@ -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

View File

@ -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)