Skip to content

Commit 07ccb55

Browse files
Jongydpgeorge
authored andcommitted
py/objobject: Add object.__setattr__ function.
Allows assigning attributes on class instances that implement their own __setattr__. Both object.__setattr__ and super(A, b).__setattr__ will work with this commit.
1 parent 90f2864 commit 07ccb55

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

py/objobject.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___init___obj, object___init__);
5050

5151
STATIC mp_obj_t object___new__(mp_obj_t cls) {
5252
if (!mp_obj_is_type(cls, &mp_type_type) || !mp_obj_is_instance_type((mp_obj_type_t*)MP_OBJ_TO_PTR(cls))) {
53-
mp_raise_TypeError("__new__ arg must be a user-type");
53+
mp_raise_TypeError("arg must be user-type");
5454
}
5555
// This executes only "__new__" part of instance creation.
5656
// TODO: This won't work well for classes with native bases.
@@ -62,13 +62,33 @@ STATIC mp_obj_t object___new__(mp_obj_t cls) {
6262
STATIC MP_DEFINE_CONST_FUN_OBJ_1(object___new___fun_obj, object___new__);
6363
STATIC MP_DEFINE_CONST_STATICMETHOD_OBJ(object___new___obj, MP_ROM_PTR(&object___new___fun_obj));
6464

65+
#if MICROPY_PY_DELATTR_SETATTR
66+
STATIC mp_obj_t object___setattr__(mp_obj_t self_in, mp_obj_t attr, mp_obj_t value) {
67+
if (!mp_obj_is_instance_type(mp_obj_get_type(MP_OBJ_TO_PTR(self_in)))) {
68+
mp_raise_TypeError("arg must be user-type");
69+
}
70+
71+
if (!mp_obj_is_str(attr)) {
72+
mp_raise_TypeError(NULL);
73+
}
74+
75+
mp_obj_instance_t *self = MP_OBJ_TO_PTR(self_in);
76+
mp_map_lookup(&self->members, attr, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = value;
77+
return mp_const_none;
78+
}
79+
STATIC MP_DEFINE_CONST_FUN_OBJ_3(object___setattr___obj, object___setattr__);
80+
#endif
81+
6582
STATIC const mp_rom_map_elem_t object_locals_dict_table[] = {
6683
#if MICROPY_CPYTHON_COMPAT
6784
{ MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&object___init___obj) },
6885
#endif
6986
#if MICROPY_CPYTHON_COMPAT
7087
{ MP_ROM_QSTR(MP_QSTR___new__), MP_ROM_PTR(&object___new___obj) },
7188
#endif
89+
#if MICROPY_PY_DELATTR_SETATTR
90+
{ MP_ROM_QSTR(MP_QSTR___setattr__), MP_ROM_PTR(&object___setattr___obj) },
91+
#endif
7292
};
7393

7494
STATIC MP_DEFINE_CONST_DICT(object_locals_dict, object_locals_dict_table);

py/runtime.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1038,9 +1038,11 @@ void mp_convert_member_lookup(mp_obj_t self, const mp_obj_type_t *type, mp_obj_t
10381038
|| m_type == &mp_type_fun_builtin_1
10391039
|| m_type == &mp_type_fun_builtin_2
10401040
|| m_type == &mp_type_fun_builtin_3
1041-
|| m_type == &mp_type_fun_builtin_var)) {
1041+
|| m_type == &mp_type_fun_builtin_var)
1042+
&& type != &mp_type_object) {
10421043
// we extracted a builtin method without a first argument, so we must
10431044
// wrap this function in a type checker
1045+
// Note that object will do its own checking so shouldn't be wrapped.
10441046
dest[0] = mp_obj_new_checked_fun(type, member);
10451047
} else
10461048
#endif

tests/basics/class_delattr_setattr.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,29 @@ def __delattr__(self, attr):
6060
print(a.a)
6161
except AttributeError:
6262
print("AttributeError")
63+
64+
# test object.__setattr__
65+
class C:
66+
def __init__(self):
67+
pass
68+
69+
def __setattr__(self, attr, value):
70+
print(attr, "=", value)
71+
72+
c = C()
73+
c.a = 5
74+
try:
75+
print(c.a)
76+
except AttributeError:
77+
print("AttributeError")
78+
79+
object.__setattr__(c, "a", 5)
80+
super(C, c).__setattr__("b", 6)
81+
print(c.a)
82+
print(c.b)
83+
84+
try:
85+
# attribute name must be string
86+
object.__setattr__(c, 5, 5)
87+
except TypeError:
88+
print("TypeError")

0 commit comments

Comments
 (0)