py: Implement staticmethod and classmethod (internally).
Still need to make built-ins by these names, and write tests.
This commit is contained in:
parent
bcbeea0a47
commit
eae16445d5
4
py/obj.c
4
py/obj.c
|
@ -231,7 +231,7 @@ uint mp_get_index(const mp_obj_type_t *type, machine_uint_t len, mp_obj_t index)
|
|||
}
|
||||
}
|
||||
|
||||
// may return NULL
|
||||
// may return MP_OBJ_NULL
|
||||
mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
|
||||
mp_small_int_t len = 0;
|
||||
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) {
|
|||
} else if (MP_OBJ_IS_TYPE(o_in, &dict_type)) {
|
||||
len = mp_obj_dict_len(o_in);
|
||||
} else {
|
||||
return NULL;
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
return MP_OBJ_NEW_SMALL_INT(len);
|
||||
}
|
||||
|
|
24
py/obj.h
24
py/obj.h
|
@ -59,6 +59,15 @@ typedef struct _mp_obj_base_t mp_obj_base_t;
|
|||
#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)
|
||||
#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)
|
||||
|
||||
// These macros are used to declare and define constant staticmethond and classmethod objects
|
||||
// You can put "static" in front of the definitions to make them local
|
||||
|
||||
#define MP_DECLARE_CONST_STATICMETHOD_OBJ(obj_name) extern const mp_obj_staticmethod_t obj_name
|
||||
#define MP_DECLARE_CONST_CLASSMETHOD_OBJ(obj_name) extern const mp_obj_classmethod_t obj_name
|
||||
|
||||
#define MP_DEFINE_CONST_STATICMETHOD_OBJ(obj_name, fun_name) const mp_obj_staticmethod_t obj_name = {{&mp_type_staticmethod}, fun_name}
|
||||
#define MP_DEFINE_CONST_CLASSMETHOD_OBJ(obj_name, fun_name) const mp_obj_classmethod_t obj_name = {{&mp_type_classmethod}, fun_name}
|
||||
|
||||
// Need to declare this here so we are not dependent on map.h
|
||||
struct _mp_map_t;
|
||||
struct _mp_map_elem_t;
|
||||
|
@ -316,3 +325,18 @@ extern const mp_obj_type_t gen_instance_type;
|
|||
extern const mp_obj_type_t module_type;
|
||||
mp_obj_t mp_obj_new_module(qstr module_name);
|
||||
struct _mp_map_t *mp_obj_module_get_globals(mp_obj_t self_in);
|
||||
|
||||
// staticmethod and classmethod types; defined here so we can make const versions
|
||||
|
||||
extern const mp_obj_type_t mp_type_staticmethod;
|
||||
extern const mp_obj_type_t mp_type_classmethod;
|
||||
|
||||
typedef struct _mp_obj_staticmethod_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t fun;
|
||||
} mp_obj_staticmethod_t;
|
||||
|
||||
typedef struct _mp_obj_classmethod_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t fun;
|
||||
} mp_obj_classmethod_t;
|
||||
|
|
98
py/objdict.c
98
py/objdict.c
|
@ -139,6 +139,35 @@ static mp_obj_t dict_copy(mp_obj_t self_in) {
|
|||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(dict_copy_obj, dict_copy);
|
||||
|
||||
// this is a classmethod
|
||||
static mp_obj_t dict_fromkeys(int n_args, const mp_obj_t *args) {
|
||||
assert(2 <= n_args && n_args <= 3);
|
||||
mp_obj_t iter = rt_getiter(args[1]);
|
||||
mp_obj_t len = mp_obj_len_maybe(iter);
|
||||
mp_obj_t value = mp_const_none;
|
||||
mp_obj_t next = NULL;
|
||||
mp_obj_dict_t *self = NULL;
|
||||
|
||||
if (n_args > 2) {
|
||||
value = args[2];
|
||||
}
|
||||
|
||||
if (len == MP_OBJ_NULL) {
|
||||
/* object's type doesn't have a __len__ slot */
|
||||
self = mp_obj_new_dict(0);
|
||||
} else {
|
||||
self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
|
||||
}
|
||||
|
||||
while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
|
||||
mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_fun_obj, 2, 3, dict_fromkeys);
|
||||
static MP_DEFINE_CONST_CLASSMETHOD_OBJ(dict_fromkeys_obj, (const mp_obj_t)&dict_fromkeys_fun_obj);
|
||||
|
||||
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) {
|
||||
mp_map_elem_t *elem = mp_map_lookup(self, key, lookup_kind);
|
||||
mp_obj_t value;
|
||||
|
@ -286,17 +315,12 @@ static mp_obj_t dict_view_it_iternext(mp_obj_t self_in) {
|
|||
return mp_obj_new_tuple(2, items);
|
||||
}
|
||||
case MP_DICT_VIEW_KEYS:
|
||||
{
|
||||
return next->key;
|
||||
}
|
||||
case MP_DICT_VIEW_VALUES:
|
||||
{
|
||||
return next->value;
|
||||
}
|
||||
default:
|
||||
{
|
||||
assert(0); /* can't happen */
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
} else {
|
||||
return mp_const_stop_iteration;
|
||||
|
@ -320,7 +344,6 @@ static mp_obj_t dict_view_getiter(mp_obj_t view_in) {
|
|||
return o;
|
||||
}
|
||||
|
||||
|
||||
static void dict_view_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &dict_view_type));
|
||||
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) {
|
|||
return o;
|
||||
}
|
||||
|
||||
|
||||
static mp_obj_t dict_view(mp_obj_t self_in, mp_dict_view_kind_t kind) {
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &dict_type));
|
||||
mp_obj_dict_t *self = self_in;
|
||||
|
@ -376,67 +398,13 @@ static mp_obj_t dict_values(mp_obj_t self_in) {
|
|||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_1(dict_values_obj, dict_values);
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* dict metaclass */
|
||||
|
||||
static mp_obj_t dict_fromkeys(int n_args, const mp_obj_t *args) {
|
||||
assert(2 <= n_args && n_args <= 3);
|
||||
mp_obj_t iter = rt_getiter(args[1]);
|
||||
mp_obj_t len = mp_obj_len_maybe(iter);
|
||||
mp_obj_t value = mp_const_none;
|
||||
mp_obj_t next = NULL;
|
||||
mp_obj_dict_t *self = NULL;
|
||||
|
||||
if (n_args > 2) {
|
||||
value = args[2];
|
||||
}
|
||||
|
||||
if (len == NULL) {
|
||||
/* object's type doesn't have a __len__ slot */
|
||||
self = mp_obj_new_dict(0);
|
||||
} else {
|
||||
self = mp_obj_new_dict(MP_OBJ_SMALL_INT_VALUE(len));
|
||||
}
|
||||
|
||||
while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
|
||||
mp_map_lookup(&self->map, next, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(dict_fromkeys_obj, 2, 3, dict_fromkeys);
|
||||
|
||||
static const mp_method_t dict_class_methods[] = {
|
||||
{ "fromkeys", &dict_fromkeys_obj },
|
||||
{ NULL, NULL }, // end-of-list sentinel
|
||||
};
|
||||
|
||||
/* this should be unnecessary when inheritance works */
|
||||
static void dict_class_print(void (*print)(void *env, const char *fmt, ...), void *env, mp_obj_t self_in) {
|
||||
print(env, "<class 'dict'>");
|
||||
}
|
||||
|
||||
/* this should be unnecessary when inheritance works */
|
||||
static mp_obj_t dict_class_call_n(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
|
||||
return rt_build_map(0);
|
||||
}
|
||||
|
||||
static const mp_obj_type_t dict_class = {
|
||||
{ &mp_const_type },
|
||||
"dict_class",
|
||||
.print = dict_class_print,
|
||||
.methods = dict_class_methods,
|
||||
.call_n = dict_class_call_n,
|
||||
};
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
/* dict constructors & etc */
|
||||
/* dict constructors & public C API */
|
||||
|
||||
static const mp_method_t dict_type_methods[] = {
|
||||
{ "clear", &dict_clear_obj },
|
||||
{ "copy", &dict_copy_obj },
|
||||
{ "fromkeys", &dict_fromkeys_obj },
|
||||
{ "get", &dict_get_obj },
|
||||
{ "items", &dict_items_obj },
|
||||
{ "keys", &dict_keys_obj },
|
||||
|
@ -449,7 +417,7 @@ static const mp_method_t dict_type_methods[] = {
|
|||
};
|
||||
|
||||
const mp_obj_type_t dict_type = {
|
||||
{ &dict_class },
|
||||
{ &mp_const_type },
|
||||
"dict",
|
||||
.print = dict_print,
|
||||
.make_new = dict_make_new,
|
||||
|
|
74
py/objtype.c
74
py/objtype.c
|
@ -27,15 +27,13 @@ static mp_obj_t mp_obj_new_class(mp_obj_t class) {
|
|||
return o;
|
||||
}
|
||||
|
||||
static mp_map_elem_t *mp_obj_class_lookup(mp_obj_t self_in, qstr attr, mp_map_lookup_kind_t lookup_kind) {
|
||||
static mp_map_elem_t *mp_obj_class_lookup(const mp_obj_type_t *type, qstr attr, mp_map_lookup_kind_t lookup_kind) {
|
||||
for (;;) {
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
|
||||
mp_obj_type_t *self = self_in;
|
||||
if (self->locals_dict == NULL) {
|
||||
if (type->locals_dict == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
assert(MP_OBJ_IS_TYPE(self->locals_dict, &dict_type)); // Micro Python restriction, for now
|
||||
mp_map_t *locals_map = ((void*)self->locals_dict + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object
|
||||
assert(MP_OBJ_IS_TYPE(type->locals_dict, &dict_type)); // Micro Python restriction, for now
|
||||
mp_map_t *locals_map = ((void*)type->locals_dict + sizeof(mp_obj_base_t)); // XXX hack to get map object from dict object
|
||||
mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(attr), lookup_kind);
|
||||
if (elem != NULL) {
|
||||
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
|
|||
// attribute not found, keep searching base classes
|
||||
|
||||
// for a const struct, this entry might be NULL
|
||||
if (self->bases_tuple == MP_OBJ_NULL) {
|
||||
if (type->bases_tuple == MP_OBJ_NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint len;
|
||||
mp_obj_t *items;
|
||||
mp_obj_tuple_get(self->bases_tuple, &len, &items);
|
||||
mp_obj_tuple_get(type->bases_tuple, &len, &items);
|
||||
if (len == 0) {
|
||||
return NULL;
|
||||
}
|
||||
for (uint i = 0; i < len - 1; i++) {
|
||||
elem = mp_obj_class_lookup(items[i], attr, lookup_kind);
|
||||
assert(MP_OBJ_IS_TYPE(items[i], &mp_const_type));
|
||||
elem = mp_obj_class_lookup((mp_obj_type_t*)items[i], attr, lookup_kind);
|
||||
if (elem != NULL) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
// search last base (simple tail recursion elimination)
|
||||
self_in = items[len - 1];
|
||||
assert(MP_OBJ_IS_TYPE(items[len - 1], &mp_const_type));
|
||||
type = (mp_obj_type_t*)items[len - 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,11 +73,12 @@ static void class_print(void (*print)(void *env, const char *fmt, ...), void *en
|
|||
// args are reverse in the array
|
||||
static mp_obj_t class_make_new(mp_obj_t self_in, int n_args, const mp_obj_t *args) {
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
|
||||
mp_obj_type_t *self = self_in;
|
||||
|
||||
mp_obj_t o = mp_obj_new_class(self_in);
|
||||
|
||||
// look for __init__ function
|
||||
mp_map_elem_t *init_fn = mp_obj_class_lookup(self_in, MP_QSTR___init__, MP_MAP_LOOKUP);
|
||||
mp_map_elem_t *init_fn = mp_obj_class_lookup(self, MP_QSTR___init__, MP_MAP_LOOKUP);
|
||||
|
||||
if (init_fn != NULL) {
|
||||
// call __init__ function
|
||||
|
@ -114,7 +115,7 @@ static void class_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
|||
dest[1] = elem->value;
|
||||
return;
|
||||
}
|
||||
elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP);
|
||||
elem = mp_obj_class_lookup(self->base.type, attr, MP_MAP_LOOKUP);
|
||||
if (elem != NULL) {
|
||||
if (mp_obj_is_callable(elem->value)) {
|
||||
// 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) {
|
|||
static bool class_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
|
||||
// logic: look in class locals (no add) then obj members (add) (TODO check this against CPython)
|
||||
mp_obj_class_t *self = self_in;
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup((mp_obj_t)self->base.type, attr, MP_MAP_LOOKUP);
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup(self->base.type, attr, MP_MAP_LOOKUP);
|
||||
if (elem != NULL) {
|
||||
elem->value = value;
|
||||
} 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)
|
|||
|
||||
// for fail, do nothing; for attr, dest[1] = value; for method, dest[0] = self, dest[1] = method
|
||||
static void type_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP);
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
|
||||
mp_obj_type_t *self = self_in;
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup(self, attr, MP_MAP_LOOKUP);
|
||||
if (elem != NULL) {
|
||||
dest[1] = elem->value;
|
||||
return;
|
||||
}
|
||||
|
||||
// generic method lookup
|
||||
// this is a lookup in the class itself (ie not the classes type or instance)
|
||||
const mp_method_t *meth = self->methods;
|
||||
if (meth != NULL) {
|
||||
for (; meth->name != NULL; meth++) {
|
||||
if (strcmp(meth->name, qstr_str(attr)) == 0) {
|
||||
// check if the methods are functions, static or class methods
|
||||
// see http://docs.python.org/3.3/howto/descriptor.html
|
||||
if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_staticmethod)) {
|
||||
// return just the function
|
||||
dest[1] = ((mp_obj_staticmethod_t*)meth->fun)->fun;
|
||||
} else if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_classmethod)) {
|
||||
// return a bound method, with self being this class
|
||||
dest[1] = ((mp_obj_classmethod_t*)meth->fun)->fun;
|
||||
dest[0] = self_in;
|
||||
} else {
|
||||
// return just the function
|
||||
// TODO need to wrap in a type check for the first argument; eg list.append(1,1) needs to throw an exception
|
||||
dest[1] = (mp_obj_t)meth->fun;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool type_store_attr(mp_obj_t self_in, qstr attr, mp_obj_t value) {
|
||||
assert(MP_OBJ_IS_TYPE(self_in, &mp_const_type));
|
||||
mp_obj_type_t *self = self_in;
|
||||
|
||||
// TODO CPython allows STORE_ATTR to a class, but is this the correct implementation?
|
||||
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup(self_in, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
|
||||
mp_map_elem_t *elem = mp_obj_class_lookup(self, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
|
||||
if (elem != NULL) {
|
||||
elem->value = value;
|
||||
return true;
|
||||
|
@ -284,3 +315,16 @@ static mp_obj_t mp_builtin_isinstance(mp_obj_t object, mp_obj_t classinfo) {
|
|||
}
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj, mp_builtin_isinstance);
|
||||
|
||||
/******************************************************************************/
|
||||
// staticmethod and classmethod types (probably should go in a different file)
|
||||
|
||||
const mp_obj_type_t mp_type_staticmethod = {
|
||||
{ &mp_const_type },
|
||||
"staticmethod",
|
||||
};
|
||||
|
||||
const mp_obj_type_t mp_type_classmethod = {
|
||||
{ &mp_const_type },
|
||||
"classmethod",
|
||||
};
|
||||
|
|
13
py/runtime.c
13
py/runtime.c
|
@ -774,12 +774,25 @@ void rt_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) {
|
|||
dest[0] = base;
|
||||
} else {
|
||||
// generic method lookup
|
||||
// this is a lookup in the object (ie not class or type)
|
||||
const mp_method_t *meth = type->methods;
|
||||
if (meth != NULL) {
|
||||
for (; meth->name != NULL; meth++) {
|
||||
if (strcmp(meth->name, qstr_str(attr)) == 0) {
|
||||
// check if the methods are functions, static or class methods
|
||||
// see http://docs.python.org/3.3/howto/descriptor.html
|
||||
if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_staticmethod)) {
|
||||
// return just the function
|
||||
dest[1] = ((mp_obj_staticmethod_t*)meth->fun)->fun;
|
||||
} else if (MP_OBJ_IS_TYPE(meth->fun, &mp_type_classmethod)) {
|
||||
// return a bound method, with self being the type of this object
|
||||
dest[1] = ((mp_obj_classmethod_t*)meth->fun)->fun;
|
||||
dest[0] = mp_obj_get_type(base);
|
||||
} else {
|
||||
// return a bound method, with self being this object
|
||||
dest[1] = (mp_obj_t)meth->fun;
|
||||
dest[0] = base;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue