Skip to content

Commit eae1644

Browse files
committed
py: Implement staticmethod and classmethod (internally).
Still need to make built-ins by these names, and write tests.
1 parent bcbeea0 commit eae1644

5 files changed

Lines changed: 144 additions & 95 deletions

File tree

py/obj.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index)
231231
}
232232
}
233233

234-
// may return NULL
234+
// may return MP_OBJ_NULL
235235
mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
236236
mp_small_int_t len = 0;
237237
if (MP_OBJ_IS_TYPE(o_in, &str_type)) {
@@ -249,7 +249,7 @@ mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
249249
} else if (MP_OBJ_IS_TYPE(o_in, &dict_type)) {
250250
len = mp_obj_dict_len(o_in);
251251
} else {
252-
return NULL;
252+
return MP_OBJ_NULL;
253253
}
254254
return MP_OBJ_NEW_SMALL_INT(len);
255255
}

py/obj.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ typedef struct _mp_obj_base_t mp_obj_base_t;
5959
#define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, false, n_args_min, n_args_max, (mp_fun_var_t)fun_name)
6060
#define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, fun_name) MP_DEFINE_CONST_FUN_OBJ_VOID_PTR(obj_name, true, 0, (~((machine_uint_t)0)), (mp_fun_kw_t)fun_name)
6161

62+
// These macros are used to declare and define constant staticmethond and classmethod objects
63+
// You can put "static" in front of the definitions to make them local
64+
65+
#define MP_DECLARE_CONST_STATICMETHOD_OBJ(obj_name) extern const mp_obj_staticmethod_t obj_name
66+
#define MP_DECLARE_CONST_CLASSMETHOD_OBJ(obj_name) extern const mp_obj_classmethod_t obj_name
67+
68+
#define MP_DEFINE_CONST_STATICMETHOD_OBJ(obj_name, fun_name) const mp_obj_staticmethod_t obj_name = {{&mp_type_staticmethod}, fun_name}
69+
#define MP_DEFINE_CONST_CLASSMETHOD_OBJ(obj_name, fun_name) const mp_obj_classmethod_t obj_name = {{&mp_type_classmethod}, fun_name}
70+
6271
// Need to declare this here so we are not dependent on map.h
6372
struct _mp_map_t;
6473
struct _mp_map_elem_t;
@@ -316,3 +325,18 @@ extern const mp_obj_type_t gen_instance_type;
316325
extern const mp_obj_type_t module_type;
317326
mp_obj_t mp_obj_new_module(qstr module_name);
318327
struct _mp_map_t *mp_obj_module_get_globals(mp_obj_t self_in);
328+
329+
// staticmethod and classmethod types; defined here so we can make const versions
330+
331+
extern const mp_obj_type_t mp_type_staticmethod;
332+
extern const mp_obj_type_t mp_type_classmethod;
333+
334+
typedef struct _mp_obj_staticmethod_t {
335+
mp_obj_base_t base;
336+
mp_obj_t fun;
337+
} mp_obj_staticmethod_t;
338+
339+
typedef struct _mp_obj_classmethod_t {
340+
mp_obj_base_t base;
341+
mp_obj_t fun;
342+
} mp_obj_classmethod_t;

py/objdict.c

Lines changed: 44 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,35 @@ static mp_obj_t dict_copy(mp_obj_t self_in) {
139139
}
140140
static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
141141

142+
// this is a classmethod
143+
static mp_obj_t dict_fromkeys(int n_args, const mp_obj_t *args) {
144+
assert(2 <= n_args && n_args <= 3);
145+
mp_obj_t iter = rt_getiter(args[1]);
146+
mp_obj_t len = mp_obj_len_maybe(iter);
147+
mp_obj_t value = mp_const_none;
148+
mp_obj_t next = NULL;
149+
mp_obj_dict_t *self = NULL;
150+
151+
if (n_args > 2) {
152+
value = args[2];
153+
}
154+
155+
if (len == MP_OBJ_NULL) {
156+
/* object's type doesn't have a __len__ slot */
157+
self = mp_obj_new_dict(0);
158+
} else {
159+
self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
160+
}
161+
162+
while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
163+
mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
164+
}
165+
166+
return self;
167+
}
168+
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_fun_obj, 2, 3, dict_fromkeys);
169+
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, (const mp_obj_t)&dict_fromkeys_fun_obj);
170+
142171
static mp_obj_t dict_get_helper(mp_map_t *self, mp_obj_t key, mp_obj_t deflt, mp_map_lookup_kind_t lookup_kind) {
143172
mp_map_elem_t *elem = mp_map_lookup(self, key, lookup_kind);
144173
mp_obj_t value;
@@ -280,23 +309,18 @@ static mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
280309

281310
if (next != NULL) {
282311
switch (self->kind) {
283-
case MP_DICT_VIEW_ITEMS:
284-
{
285-
mp_obj_t items[] = {next->key, next->value};
286-
return mp_obj_new_tuple(2, items);
287-
}
288-
case MP_DICT_VIEW_KEYS:
289-
{
290-
return next->key;
291-
}
292-
case MP_DICT_VIEW_VALUES:
293-
{
294-
return next->value;
295-
}
296-
default:
297-
{
298-
assert(0); /* can't happen */
299-
}
312+
case MP_DICT_VIEW_ITEMS:
313+
{
314+
mp_obj_t items[] = {next->key, next->value};
315+
return mp_obj_new_tuple(2, items);
316+
}
317+
case MP_DICT_VIEW_KEYS:
318+
return next->key;
319+
case MP_DICT_VIEW_VALUES:
320+
return next->value;
321+
default:
322+
assert(0); /* can't happen */
323+
return mp_const_none;
300324
}
301325
} else {
302326
return mp_const_stop_iteration;
@@ -320,7 +344,6 @@ static mp_obj_t dict_view_getiter(mp_obj_t view_in) {
320344
return o;
321345
}
322346

323-
324347
static void dict_view_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
325348
assert(MP_OBJ_IS_TYPE(self_in, &dict_view_type));
326349
mp_obj_dict_view_t *self = self_in;
@@ -354,7 +377,6 @@ mp_obj_t mp_obj_new_dict_view(mp_obj_dict_t *dict, mp_dict_view_kind_t kind) {
354377
return o;
355378
}
356379

357-
358380
static mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) {
359381
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
360382
mp_obj_dict_t *self = self_in;
@@ -376,67 +398,13 @@ static mp_obj_t dict_values(mp_obj_t self_in) {
376398
}
377399
static MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values);
378400

379-
380-
/******************************************************************************/
381-
/* dict metaclass */
382-
383-
static mp_obj_t dict_fromkeys(int n_args, const mp_obj_t *args) {
384-
assert(2 <= n_args && n_args <= 3);
385-
mp_obj_t iter = rt_getiter(args[1]);
386-
mp_obj_t len = mp_obj_len_maybe(iter);
387-
mp_obj_t value = mp_const_none;
388-
mp_obj_t next = NULL;
389-
mp_obj_dict_t *self = NULL;
390-
391-
if (n_args > 2) {
392-
value = args[2];
393-
}
394-
395-
if (len == NULL) {
396-
/* object's type doesn't have a __len__ slot */
397-
self = mp_obj_new_dict(0);
398-
} else {
399-
self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
400-
}
401-
402-
while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
403-
mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
404-
}
405-
406-
return self;
407-
}
408-
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_obj, 2, 3, dict_fromkeys);
409-
410-
static const mp_method_t dict_class_methods[] = {
411-
{ "fromkeys", &dict_fromkeys_obj },
412-
{ NULL, NULL }, // end-of-list sentinel
413-
};
414-
415-
/* this should be unnecessary when inheritance works */
416-
static void dict_class_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
417-
print(env, "<class 'dict'>");
418-
}
419-
420-
/* this should be unnecessary when inheritance works */
421-
static mp_obj_t dict_class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
422-
return rt_build_map(0);
423-
}
424-
425-
static const mp_obj_type_t dict_class = {
426-
{ &mp_const_type },
427-
"dict_class",
428-
.print = dict_class_print,
429-
.methods = dict_class_methods,
430-
.call_n = dict_class_call_n,
431-
};
432-
433-
434401
/******************************************************************************/
435-
/* dict constructors & etc */
402+
/* dict constructors & public C API */
436403

437404
static const mp_method_t dict_type_methods[] = {
438405
{ "clear", &dict_clear_obj },
439406
{ "copy", &dict_copy_obj },
407+
{ "fromkeys", &dict_fromkeys_obj },
440408
{ "get", &dict_get_obj },
441409
{ "items", &dict_items_obj },
442410
{ "keys", &dict_keys_obj },
@@ -449,7 +417,7 @@ static const mp_method_t dict_type_methods[] = {
449417
};
450418

451419
const mp_obj_type_t dict_type = {
452-
{ &dict_class },
420+
{ &mp_const_type },
453421
"dict",
454422
.print = dict_print,
455423
.make_new = dict_make_new,

py/objtype.c

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,13 @@ 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) {
30+
static mp_map_elem_t *mp_obj_class_lookup(const mp_obj_type_t *type, qstr attr, mp_map_lookup_kind_t lookup_kind) {
3131
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) {
32+
if (type->locals_dict == NULL) {
3533
return NULL;
3634
}
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
35+
assert(MP_OBJ_IS_TYPE(type->locals_dict, &dict_type)); // Micro Python restriction, for now
36+
mp_map_t *locals_map = ((void*)type->locals_dict + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object
3937
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind);
4038
if (elem != NULL) {
4139
return elem;
@@ -44,25 +42,27 @@ static mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lo
4442
// attribute not found, keep searching base classes
4543

4644
// for a const struct, this entry might be NULL
47-
if (self->bases_tuple == MP_OBJ_NULL) {
45+
if (type->bases_tuple == MP_OBJ_NULL) {
4846
return NULL;
4947
}
5048

5149
uint len;
5250
mp_obj_t *items;
53-
mp_obj_tuple_get(self->bases_tuple, &len, &items);
51+
mp_obj_tuple_get(type->bases_tuple, &len, &items);
5452
if (len == 0) {
5553
return NULL;
5654
}
5755
for (uint i = 0; i < len - 1; i++) {
58-
elem = mp_obj_class_lookup(items[i], attr, lookup_kind);
56+
assert(MP_OBJ_IS_TYPE(items[i], &mp_const_type));
57+
elem = mp_obj_class_lookup((mp_obj_type_t*)items[i], attr, lookup_kind);
5958
if (elem != NULL) {
6059
return elem;
6160
}
6261
}
6362

6463
// search last base (simple tail recursion elimination)
65-
self_in = items[len - 1];
64+
assert(MP_OBJ_IS_TYPE(items[len - 1], &mp_const_type));
65+
type = (mp_obj_type_t*)items[len - 1];
6666
}
6767
}
6868

@@ -73,11 +73,12 @@ static void class_print(void (*print)(void *env, const char *fmt, ...), void *en
7373
// args are reverse in the array
7474
static mp_obj_t class_make_new(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
7575
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
76+
mp_obj_type_t *self = self_in;
7677

7778
mp_obj_t o = mp_obj_new_class(self_in);
7879

7980
// look for __init__ function
80-
mp_map_elem_t *init_fn = mp_obj_class_lookup(self_in, MP_QSTR___init__, MP_MAP_LOOKUP);
81+
mp_map_elem_t *init_fn = mp_obj_class_lookup(self, MP_QSTR___init__, MP_MAP_LOOKUP);
8182

8283
if (init_fn != NULL) {
8384
// call __init__ function
@@ -114,7 +115,7 @@ static void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
114115
dest[1] = elem->value;
115116
return;
116117
}
117-
elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP);
118+
elem = mp_obj_class_lookup(self->base.type, attr, MP_MAP_LOOKUP);
118119
if (elem != NULL) {
119120
if (mp_obj_is_callable(elem->value)) {
120121
// class member is callable so build a bound method
@@ -132,7 +133,7 @@ static void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
132133
static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
133134
// logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
134135
mp_obj_class_t *self = self_in;
135-
mp_map_elem_t *elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP);
136+
mp_map_elem_t *elem = mp_obj_class_lookup(self->base.type, attr, MP_MAP_LOOKUP);
136137
if (elem != NULL) {
137138
elem->value = value;
138139
} else {
@@ -188,17 +189,47 @@ static mp_obj_t type_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args)
188189

189190
// for fail, do nothing; for attr, dest[1] = value; for method, dest[0] = self, dest[1] = method
190191
static void type_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
191-
mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP);
192+
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
193+
mp_obj_type_t *self = self_in;
194+
mp_map_elem_t *elem = mp_obj_class_lookup(self, attr, MP_MAP_LOOKUP);
192195
if (elem != NULL) {
193196
dest[1] = elem->value;
194197
return;
195198
}
199+
200+
// generic method lookup
201+
// this is a lookup in the class itself (ie not the classes type or instance)
202+
const mp_method_t *meth = self->methods;
203+
if (meth != NULL) {
204+
for (; meth->name != NULL; meth++) {
205+
if (strcmp(meth->name, qstr_str(attr)) == 0) {
206+
// check if the methods are functions, static or class methods
207+
// see http://docs.python.org/3.3/howto/descriptor.html
208+
if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_staticmethod)) {
209+
// return just the function
210+
dest[1] = ((mp_obj_staticmethod_t*)meth->fun)->fun;
211+
} else if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_classmethod)) {
212+
// return a bound method, with self being this class
213+
dest[1] = ((mp_obj_classmethod_t*)meth->fun)->fun;
214+
dest[0] = self_in;
215+
} else {
216+
// return just the function
217+
// TODO need to wrap in a type check for the first argument; eg list.append(1,1) needs to throw an exception
218+
dest[1] = (mp_obj_t)meth->fun;
219+
}
220+
return;
221+
}
222+
}
223+
}
196224
}
197225

198226
static bool type_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
227+
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
228+
mp_obj_type_t *self = self_in;
229+
199230
// TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
200231

201-
mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
232+
mp_map_elem_t *elem = mp_obj_class_lookup(self, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
202233
if (elem != NULL) {
203234
elem->value = value;
204235
return true;
@@ -284,3 +315,16 @@ static mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) {
284315
}
285316

286317
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance);
318+
319+
/******************************************************************************/
320+
// staticmethod and classmethod types (probably should go in a different file)
321+
322+
const mp_obj_type_t mp_type_staticmethod = {
323+
{ &mp_const_type },
324+
"staticmethod",
325+
};
326+
327+
const mp_obj_type_t mp_type_classmethod = {
328+
{ &mp_const_type },
329+
"classmethod",
330+
};

py/runtime.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -774,12 +774,25 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
774774
dest[0] = base;
775775
} else {
776776
// generic method lookup
777+
// this is a lookup in the object (ie not class or type)
777778
const mp_method_t *meth = type->methods;
778779
if (meth != NULL) {
779780
for (; meth->name != NULL; meth++) {
780781
if (strcmp(meth->name, qstr_str(attr)) == 0) {
781-
dest[1] = (mp_obj_t)meth->fun;
782-
dest[0] = base;
782+
// check if the methods are functions, static or class methods
783+
// see http://docs.python.org/3.3/howto/descriptor.html
784+
if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_staticmethod)) {
785+
// return just the function
786+
dest[1] = ((mp_obj_staticmethod_t*)meth->fun)->fun;
787+
} else if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_classmethod)) {
788+
// return a bound method, with self being the type of this object
789+
dest[1] = ((mp_obj_classmethod_t*)meth->fun)->fun;
790+
dest[0] = mp_obj_get_type(base);
791+
} else {
792+
// return a bound method, with self being this object
793+
dest[1] = (mp_obj_t)meth->fun;
794+
dest[0] = base;
795+
}
783796
break;
784797
}
785798
}

0 commit comments

Comments
 (0)