Skip to content

Commit 004cdce

Browse files
committed
py: Implement base class lookup, issubclass, isinstance.
1 parent 062478e commit 004cdce

9 files changed

Lines changed: 136 additions & 36 deletions

File tree

py/builtin.h

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,12 @@ mp_obj_t mp_builtin___repl_print__(mp_obj_t o);
66
mp_obj_t mp_builtin_abs(mp_obj_t o_in);
77
mp_obj_t mp_builtin_all(mp_obj_t o_in);
88
mp_obj_t mp_builtin_any(mp_obj_t o_in);
9-
mp_obj_t mp_builtin_bool(int n_args, const mp_obj_t *args);
109
mp_obj_t mp_builtin_callable(mp_obj_t o_in);
11-
#if MICROPY_ENABLE_FLOAT
12-
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_complex_obj);
13-
#endif
1410
mp_obj_t mp_builtin_chr(mp_obj_t o_in);
15-
mp_obj_t mp_builtin_dict(void);
1611
mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in);
17-
#if MICROPY_ENABLE_FLOAT
18-
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_float_obj);
19-
#endif
2012
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_hash_obj);
21-
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_int_obj);
13+
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_isinstance_obj);
14+
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_issubclass_obj);
2215
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_iter_obj);
2316
mp_obj_t mp_builtin_len(mp_obj_t o_in);
2417
mp_obj_t mp_builtin_list(int n_args, const mp_obj_t *args);
@@ -29,6 +22,4 @@ mp_obj_t mp_builtin_ord(mp_obj_t o_in);
2922
mp_obj_t mp_builtin_pow(int n_args, const mp_obj_t *args);
3023
mp_obj_t mp_builtin_print(int n_args, const mp_obj_t *args);
3124
mp_obj_t mp_builtin_range(int n_args, const mp_obj_t *args);
32-
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_set_obj);
3325
mp_obj_t mp_builtin_sum(int n_args, const mp_obj_t *args);
34-
MP_DECLARE_CONST_FUN_OBJ(mp_builtin_type_obj);

py/mpqstrraw.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ Q(divmod)
4444
Q(float)
4545
Q(hash)
4646
Q(int)
47+
Q(isinstance)
48+
Q(issubclass)
4749
Q(iter)
4850
Q(len)
4951
Q(list)

py/obj.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,10 @@ struct _mp_obj_type_t {
147147

148148
mp_load_attr_fun_t load_attr;
149149
mp_store_attr_fun_t store_attr;
150-
mp_obj_t locals;
150+
151+
// these are for dynamically created types (classes)
152+
mp_obj_t bases_tuple;
153+
mp_obj_t locals_dict;
151154

152155
/*
153156
What we might need to add here:
@@ -180,7 +183,7 @@ extern const mp_obj_t mp_const_stop_iteration; // special object indicating end
180183

181184
// General API for objects
182185

183-
mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict);
186+
mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict);
184187
mp_obj_t mp_obj_new_none(void);
185188
mp_obj_t mp_obj_new_bool(bool value);
186189
mp_obj_t mp_obj_new_cell(mp_obj_t obj);
@@ -308,9 +311,6 @@ void mp_obj_fun_bc_get(mp_obj_t self_in, int *n_args, uint *n_state, const byte
308311
// generator
309312
extern const mp_obj_type_t gen_instance_type;
310313

311-
// class
312-
struct _mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, enum _mp_map_lookup_kind_t lookup_kind);
313-
314314
// module
315315
extern const mp_obj_type_t module_type;
316316
mp_obj_t mp_obj_new_module(qstr module_name);

py/objtuple.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ mp_obj_t mp_obj_new_tuple_reverse(uint n, const mp_obj_t *items) {
136136
}
137137

138138
void mp_obj_tuple_get(mp_obj_t self_in, uint *len, mp_obj_t **items) {
139+
assert(MP_OBJ_IS_TYPE(self_in, &tuple_type));
139140
mp_obj_tuple_t *self = self_in;
140141
*len = self->len;
141142
*items = &self->items[0];

py/objtype.c

Lines changed: 98 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,45 @@ static mp_obj_t mp_obj_new_class(mp_obj_t class) {
2727
return o;
2828
}
2929

30+
static mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lookup_kind_t lookup_kind) {
31+
for (;;) {
32+
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
33+
mp_obj_type_t *self = self_in;
34+
if (self->locals_dict == NULL) {
35+
return NULL;
36+
}
37+
assert(MP_OBJ_IS_TYPE(self->locals_dict, &dict_type)); // Micro Python restriction, for now
38+
mp_map_t *locals_map = ((void*)self->locals_dict + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object
39+
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind);
40+
if (elem != NULL) {
41+
return elem;
42+
}
43+
44+
// attribute not found, keep searching base classes
45+
46+
// for a const struct, this entry might be NULL
47+
if (self->bases_tuple == MP_OBJ_NULL) {
48+
return NULL;
49+
}
50+
51+
uint len;
52+
mp_obj_t *items;
53+
mp_obj_tuple_get(self->bases_tuple, &len, &items);
54+
if (len == 0) {
55+
return NULL;
56+
}
57+
for (uint i = 0; i < len - 1; i++) {
58+
elem = mp_obj_class_lookup(items[i], attr, lookup_kind);
59+
if (elem != NULL) {
60+
return elem;
61+
}
62+
}
63+
64+
// search last base (simple tail recursion elimination)
65+
self_in = items[len - 1];
66+
}
67+
}
68+
3069
static void class_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
3170
print(env, "<%s object at %p>", mp_obj_get_type_str(self_in), self_in);
3271
}
@@ -102,17 +141,6 @@ static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
102141
return true;
103142
}
104143

105-
mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lookup_kind_t lookup_kind) {
106-
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
107-
mp_obj_type_t *self = self_in;
108-
if (self->locals == NULL) {
109-
return NULL;
110-
}
111-
assert(MP_OBJ_IS_TYPE(self->locals, &dict_type)); // Micro Python restriction, for now
112-
mp_map_t *locals_map = ((void*)self->locals + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object
113-
return mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind);
114-
}
115-
116144
/******************************************************************************/
117145
// type object
118146
// - the struct is mp_obj_type_t and is defined in obj.h so const types can be made
@@ -131,13 +159,10 @@ static mp_obj_t type_make_new(mp_obj_t type_in, int n_args, const mp_obj_t *args
131159
return mp_obj_get_type(args[0]);
132160

133161
case 3:
134-
{
135162
// args[2] = name
136163
// args[1] = bases tuple
137164
// args[0] = locals dict
138-
139-
return mp_obj_new_type(mp_obj_get_qstr(args[2]), args[0]);
140-
}
165+
return mp_obj_new_type(mp_obj_get_qstr(args[2]), args[1], args[0]);
141166

142167
default:
143168
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "type takes at 1 or 3 arguments"));
@@ -192,15 +217,70 @@ const mp_obj_type_t mp_const_type = {
192217
.store_attr = type_store_attr,
193218
};
194219

195-
mp_obj_t mp_obj_new_type(qstr name, mp_obj_t local_dict) {
220+
mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) {
221+
assert(MP_OBJ_IS_TYPE(bases_tuple, &tuple_type)); // Micro Python restriction, for now
222+
assert(MP_OBJ_IS_TYPE(locals_dict, &dict_type)); // Micro Python restriction, for now
196223
mp_obj_type_t *o = m_new0(mp_obj_type_t, 1);
197224
o->base.type = &mp_const_type;
198225
o->name = qstr_str(name);
199226
o->print = class_print;
200227
o->make_new = class_make_new;
201228
o->load_attr = class_load_attr;
202229
o->store_attr = class_store_attr;
203-
o->locals = local_dict;
204-
assert(MP_OBJ_IS_TYPE(o->locals, &dict_type)); // Micro Python restriction, for now
230+
o->bases_tuple = bases_tuple;
231+
o->locals_dict = locals_dict;
205232
return o;
206233
}
234+
235+
/******************************************************************************/
236+
// built-ins specific to types
237+
238+
static mp_obj_t mp_builtin_issubclass(mp_obj_t object, mp_obj_t classinfo) {
239+
if (!MP_OBJ_IS_TYPE(object, &mp_const_type)) {
240+
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "issubclass() arg 1 must be a class"));
241+
}
242+
243+
// TODO support a tuple of classes for second argument
244+
if (!MP_OBJ_IS_TYPE(classinfo, &mp_const_type)) {
245+
nlr_jump(mp_obj_new_exception_msg(MP_QSTR_TypeError, "issubclass() arg 2 must be a class"));
246+
}
247+
248+
for (;;) {
249+
if (object == classinfo) {
250+
return mp_const_true;
251+
}
252+
253+
// not equivalent classes, keep searching base classes
254+
255+
assert(MP_OBJ_IS_TYPE(object, &mp_const_type));
256+
mp_obj_type_t *self = object;
257+
258+
// for a const struct, this entry might be NULL
259+
if (self->bases_tuple == MP_OBJ_NULL) {
260+
return mp_const_false;
261+
}
262+
263+
uint len;
264+
mp_obj_t *items;
265+
mp_obj_tuple_get(self->bases_tuple, &len, &items);
266+
if (len == 0) {
267+
return mp_const_false;
268+
}
269+
for (uint i = 0; i < len - 1; i++) {
270+
if (mp_builtin_issubclass(items[i], classinfo) == mp_const_true) {
271+
return mp_const_true;
272+
}
273+
}
274+
275+
// search last base (simple tail recursion elimination)
276+
object = items[len - 1];
277+
}
278+
}
279+
280+
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj, mp_builtin_issubclass);
281+
282+
static mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) {
283+
return mp_builtin_issubclass(mp_obj_get_type(object), classinfo);
284+
}
285+
286+
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance);

py/runtime.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ void rt_init(void) {
121121
mp_map_add_qstr(&map_builtins, MP_QSTR_chr, rt_make_function_1(mp_builtin_chr));
122122
mp_map_add_qstr(&map_builtins, MP_QSTR_divmod, rt_make_function_2(mp_builtin_divmod));
123123
mp_map_add_qstr(&map_builtins, MP_QSTR_hash, (mp_obj_t)&mp_builtin_hash_obj);
124+
mp_map_add_qstr(&map_builtins, MP_QSTR_isinstance, (mp_obj_t)&mp_builtin_isinstance_obj);
125+
mp_map_add_qstr(&map_builtins, MP_QSTR_issubclass, (mp_obj_t)&mp_builtin_issubclass_obj);
124126
mp_map_add_qstr(&map_builtins, MP_QSTR_iter, (mp_obj_t)&mp_builtin_iter_obj);
125127
mp_map_add_qstr(&map_builtins, MP_QSTR_len, rt_make_function_1(mp_builtin_len));
126128
mp_map_add_qstr(&map_builtins, MP_QSTR_max, rt_make_function_var(1, mp_builtin_max));

stm/pybwlan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ mp_obj_t pyb_wlan_get_ip(void) {
7878

7979
// if it doesn't already exist, make a new empty class for NetAddress objects
8080
if (net_address_type == MP_OBJ_NULL) {
81-
net_address_type = mp_obj_new_type(qstr_from_str_static("NetAddress"), mp_obj_new_dict(0));
81+
net_address_type = mp_obj_new_type(qstr_from_str_static("NetAddress"), mp_const_empty_tuple, mp_obj_new_dict(0));
8282
}
8383

8484
// make a new NetAddress object

tests/basics/tests/class3.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# inheritance
2+
3+
class A:
4+
def a():
5+
print('A.a() called')
6+
7+
class B(A):
8+
pass
9+
10+
print(type(A))
11+
print(type(B))
12+
13+
print(issubclass(A, A))
14+
print(issubclass(A, B))
15+
print(issubclass(B, A))
16+
print(issubclass(B, B))
17+
18+
print(isinstance(A(), A))
19+
print(isinstance(A(), B))
20+
print(isinstance(B(), A))
21+
print(isinstance(B(), B))
22+
23+
A.a()
24+
B.a()

unix/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ int main(int argc, char **argv) {
218218
// test_obj = TestClass()
219219
// test_obj.attr = 42
220220
mp_obj_t test_class_type, test_class_instance;
221-
test_class_type = mp_obj_new_type(qstr_from_str_static("TestClass"), mp_obj_new_dict(0));
221+
test_class_type = mp_obj_new_type(qstr_from_str_static("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0));
222222
rt_store_name(qstr_from_str_static("test_obj"), test_class_instance = rt_call_function_0(test_class_type));
223223
rt_store_attr(test_class_instance, qstr_from_str_static("attr"), mp_obj_new_int(42));
224224

0 commit comments

Comments
 (0)