summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--py/obj.c7
-rw-r--r--py/objtype.c17
-rw-r--r--py/objtype.h4
-rw-r--r--tests/basics/builtin_callable.py42
4 files changed, 65 insertions, 5 deletions
diff --git a/py/obj.c b/py/obj.c
index 0b57f24c5c..02f75c7c40 100644
--- a/py/obj.c
+++ b/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) {
diff --git a/py/objtype.c b/py/objtype.c
index cb6b957336..b0c6a629d0 100644
--- a/py/objtype.c
+++ b/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;
diff --git a/py/objtype.h b/py/objtype.h
index 6a8a18c50a..dd2e44a799 100644
--- a/py/objtype.h
+++ b/py/objtype.h
@@ -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);
diff --git a/tests/basics/builtin_callable.py b/tests/basics/builtin_callable.py
index caddb885cf..3ae49f004d 100644
--- a/tests/basics/builtin_callable.py
+++ b/tests/basics/builtin_callable.py
@@ -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())