py: Fix builtin callable so it checks user-defined instances correctly.
Addresses issue #953.
This commit is contained in:
parent
2cd79fa924
commit
0344fa1ddf
7
py/obj.c
7
py/obj.c
@ -34,6 +34,7 @@
|
||||
#include "misc.h"
|
||||
#include "qstr.h"
|
||||
#include "obj.h"
|
||||
#include "objtype.h"
|
||||
#include "mpz.h"
|
||||
#include "objint.h"
|
||||
#include "runtime0.h"
|
||||
@ -145,7 +146,11 @@ bool mp_obj_is_true(mp_obj_t arg) {
|
||||
}
|
||||
|
||||
bool mp_obj_is_callable(mp_obj_t o_in) {
|
||||
return mp_obj_get_type(o_in)->call != NULL;
|
||||
mp_call_fun_t call = mp_obj_get_type(o_in)->call;
|
||||
if (call != mp_obj_instance_call) {
|
||||
return call != NULL;
|
||||
}
|
||||
return mp_obj_instance_is_callable(o_in);
|
||||
}
|
||||
|
||||
mp_int_t mp_obj_hash(mp_obj_t o_in) {
|
||||
|
17
py/objtype.c
17
py/objtype.c
@ -583,7 +583,7 @@ STATIC mp_obj_t instance_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t instance_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
|
||||
bool mp_obj_instance_is_callable(mp_obj_t self_in) {
|
||||
mp_obj_instance_t *self = self_in;
|
||||
mp_obj_t member[2] = {MP_OBJ_NULL};
|
||||
struct class_lookup_data lookup = {
|
||||
@ -593,6 +593,19 @@ STATIC mp_obj_t instance_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw
|
||||
.dest = member,
|
||||
};
|
||||
mp_obj_class_lookup(&lookup, self->base.type);
|
||||
return member[0] != MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_instance_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
|
||||
mp_obj_instance_t *self = self_in;
|
||||
mp_obj_t member[2] = {MP_OBJ_NULL, MP_OBJ_NULL};
|
||||
struct class_lookup_data lookup = {
|
||||
.obj = self,
|
||||
.attr = MP_QSTR___call__,
|
||||
.meth_offset = offsetof(mp_obj_type_t, call),
|
||||
.dest = member,
|
||||
};
|
||||
mp_obj_class_lookup(&lookup, self->base.type);
|
||||
if (member[0] == MP_OBJ_NULL) {
|
||||
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError, "'%s' object is not callable", mp_obj_get_type_str(self_in)));
|
||||
}
|
||||
@ -777,7 +790,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict)
|
||||
o->load_attr = instance_load_attr;
|
||||
o->store_attr = instance_store_attr;
|
||||
o->subscr = instance_subscr;
|
||||
o->call = instance_call;
|
||||
o->call = mp_obj_instance_call;
|
||||
o->getiter = instance_getiter;
|
||||
o->bases_tuple = bases_tuple;
|
||||
o->locals_dict = locals_dict;
|
||||
|
@ -32,3 +32,7 @@ typedef struct _mp_obj_instance_t {
|
||||
mp_obj_t subobj[];
|
||||
// TODO maybe cache __getattr__ and __setattr__ for efficient lookup of them
|
||||
} mp_obj_instance_t;
|
||||
|
||||
// these need to be exposed so mp_obj_is_callable can work correctly
|
||||
bool mp_obj_instance_is_callable(mp_obj_t self_in);
|
||||
mp_obj_t mp_obj_instance_call(mp_obj_t self_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args);
|
||||
|
@ -1,5 +1,43 @@
|
||||
import sys
|
||||
# test builtin callable
|
||||
|
||||
# primitives should not be callable
|
||||
print(callable(None))
|
||||
print(callable(1))
|
||||
print(callable([]))
|
||||
print(callable("dfsd"))
|
||||
print(callable(callable))
|
||||
|
||||
# modules should not be callabe
|
||||
import sys
|
||||
print(callable(sys))
|
||||
|
||||
# builtins should be callable
|
||||
print(callable(callable))
|
||||
|
||||
# lambdas should be callable
|
||||
print(callable(lambda:None))
|
||||
|
||||
# user defined functions should be callable
|
||||
def f():
|
||||
pass
|
||||
print(callable(f))
|
||||
|
||||
# types should be callable, but not instances
|
||||
class A:
|
||||
pass
|
||||
print(callable(A))
|
||||
print(callable(A()))
|
||||
|
||||
# instances with __call__ method should be callable
|
||||
class B:
|
||||
def __call__(self):
|
||||
pass
|
||||
print(callable(B()))
|
||||
|
||||
# this checks internal use of callable when extracting members from an instance
|
||||
class C:
|
||||
def f(self):
|
||||
return "A.f"
|
||||
class D:
|
||||
g = C() # g is a value and is not callable
|
||||
print(callable(D().g))
|
||||
print(D().g.f())
|
||||
|
Loading…
Reference in New Issue
Block a user