Skip to content

py/obj: Add support for __int__ special method. #4088

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/axtls
Submodule axtls updated 1 files
+6 −0 crypto/crypto_misc.c
8 changes: 2 additions & 6 deletions py/obj.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,8 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) {
} else if (MP_OBJ_IS_TYPE(arg, &mp_type_int)) {
return mp_obj_int_get_checked(arg);
} else {
if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
mp_raise_TypeError("can't convert to int");
} else {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"can't convert %s to int", mp_obj_get_type_str(arg)));
}
mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg);
return mp_obj_int_get_checked(res);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the return value of mp_unary_op is not an integer (small or big) then this will segfault.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the return value of mp_unary_op is not an integer (small or big) then this will segfault.

So, do we have similar checks everywhere by now? I can add that check to mp_unary_op(), too bad we don't have a define to disable such checks. (MICROPY_FULL_CHECKS is described to be crash-safe.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed as described in the main comment stream.

}
}

Expand Down
3 changes: 1 addition & 2 deletions py/objint.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ STATIC mp_obj_t mp_obj_int_make_new(const mp_obj_type_t *type_in, size_t n_args,
return mp_obj_new_int_from_float(mp_obj_float_get(args[0]));
#endif
} else {
// try to convert to small int (eg from bool)
return MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(args[0]));
return mp_unary_op(MP_UNARY_OP_INT, args[0]);
}

case 2:
Expand Down
19 changes: 16 additions & 3 deletions py/objtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ const byte mp_unary_op_method_name[MP_UNARY_OP_NUM_RUNTIME] = {
[MP_UNARY_OP_BOOL] = MP_QSTR___bool__,
[MP_UNARY_OP_LEN] = MP_QSTR___len__,
[MP_UNARY_OP_HASH] = MP_QSTR___hash__,
[MP_UNARY_OP_INT] = MP_QSTR___int__,
#if MICROPY_PY_ALL_SPECIAL_METHODS
[MP_UNARY_OP_POSITIVE] = MP_QSTR___pos__,
[MP_UNARY_OP_NEGATIVE] = MP_QSTR___neg__,
Expand Down Expand Up @@ -421,9 +422,21 @@ STATIC mp_obj_t instance_unary_op(mp_unary_op_t op, mp_obj_t self_in) {
return mp_unary_op(op, self->subobj[0]);
} else if (member[0] != MP_OBJ_NULL) {
mp_obj_t val = mp_call_function_1(member[0], self_in);
// __hash__ must return a small int
if (op == MP_UNARY_OP_HASH) {
val = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int_truncated(val));

switch (op) {
case MP_UNARY_OP_HASH:
// __hash__ must return a small int
val = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int_truncated(val));
break;
case MP_UNARY_OP_INT:
// Must return int
if (!MP_OBJ_IS_INT(val)) {
mp_raise_TypeError(NULL);
}
break;
default:
// No need to do anything
;
}
return val;
} else {
Expand Down
15 changes: 12 additions & 3 deletions py/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
case MP_UNARY_OP_HASH:
return arg;
case MP_UNARY_OP_POSITIVE:
case MP_UNARY_OP_INT:
return arg;
case MP_UNARY_OP_NEGATIVE:
// check for overflow
Expand Down Expand Up @@ -268,9 +269,17 @@ mp_obj_t mp_unary_op(mp_unary_op_t op, mp_obj_t arg) {
if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
mp_raise_TypeError("unsupported type for operator");
} else {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"unsupported type for %q: '%s'",
mp_unary_op_method_name[op], mp_obj_get_type_str(arg)));
// mp_unary_op() becomes a fallback for mp_obj_get_int() in this
// case. Provide a more focused error message to not confuse
// novices e.g. on chr(1.0)
if (op == MP_UNARY_OP_INT) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"can't convert %s to int", mp_obj_get_type_str(arg)));
} else {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
"unsupported type for %q: '%s'",
mp_unary_op_method_name[op], mp_obj_get_type_str(arg)));
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions py/runtime0.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ typedef enum {
MP_UNARY_OP_LEN, // __len__
MP_UNARY_OP_HASH, // __hash__; must return a small int
MP_UNARY_OP_ABS, // __abs__
MP_UNARY_OP_INT, // __int__
MP_UNARY_OP_SIZEOF, // for sys.getsizeof()

MP_UNARY_OP_NUM_RUNTIME,
Expand Down
14 changes: 14 additions & 0 deletions tests/basics/special_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ def __isub__(self, other):
print("__isub__ called")
return self

def __int__(self):
return 42

cud1 = Cud()
cud2 = Cud()

Expand All @@ -104,5 +107,16 @@ def __isub__(self, other):
cud1 > cud2
cud1 + cud2
cud1 - cud2
print(int(cud1))

class BadInt:
def __int__(self):
print("__int__ called")
return None

try:
int(BadInt())
except TypeError:
print("TypeError")

# more in special_methods2.py