-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
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
Conversation
Based on the considerations in #4084 |
This patch as it is has issues. It was written to add minimal code, in particular it doesn't patch mp_obj_int_make_new(). So, it relies solely on mp_obj_get_int() to do the https://github.com/micropython/micropython/blob/master/py/objint.c#L66
|
So, this calls MP_OBJ_NEW_SMALL_INT() because it was intended to work with bools, and that's probably it. But when extended to work with arbitrary types, it will lose the highest bit of mp_int_t on converting it to small int. Replacing it with mp_obj_new_int() is still not ideal. Let's trace how works
It will:
I.e., long int will be created 2 times, one time for garbage. And if object value doesn't fit into mp_int_t, it won't work at all. So, mp_obj_int_make_new() should directly call This shows that adding such special methods bit by bit adds more code. I still think it's good idea, as int is a universal type to which various other could be coerced. But that's also why stopping at supporting just I'll submit update patch soon. |
@@ -235,12 +235,17 @@ 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_PY_ALL_SPECIAL_METHODS | |||
mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg); | |||
return mp_obj_int_get_checked(res); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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.
py/obj.c
Outdated
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))); | ||
} | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the error message of this function if an integer-like object is not passed in. It now relies on mp_unary_op
to raise the error message which is more generic (too generic) than the error message that is here. Eg when you pass something that's not an int into a function expecting an int, it should say "can't convert to int".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This changes the error message
Yes, it does. But the new message is not false nor unclear:
>>> int([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported type for __int__: 'list'
That can be seen as the price of supporting special methods like __int__
: users now should know that operators are implemented in terms of them. And the error message like above offers a good opportunity to learn! ;-)
Beyond that, uPy's error message was different from CPy's already:
>>> int([])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
So, no much harm if it's different again.
As you imagine, trying to do anything else about error messages would be rather cumbersome.
6e8ce16
to
cee2a7b
Compare
So, posted an update to address problem described in that comment. Will look into addressing value type checking/error message concerns next. |
cee2a7b
to
9772de3
Compare
This is now addressed. But it's done on the level of instance_unary_op(), where wrong-typed value can be easily returned by user. This is consistent with handling special requirements for |
9772de3
to
6caeff6
Compare
This also has been addressed. It's done in a separate commit so far, and I guess it's better if it stays that way. |
Note that #ifdef'ing required (location of the "detailed" error gets swapped depending on whether MICROPY_PY_ALL_SPECIAL_METHODS is defined) makes to wonder if it's cleaner to just enable |
This should be ready for review, there's just random minor coveralls coverage decrease. |
6caeff6
to
e0fc938
Compare
@dpgeorge, Ping. |
Sorry for the delay here.
Yes, I agree. After having a good look at this code again I see that it spends a lot of time trying to keep the old behaviour when the feature is disabled, which makes the overall code harder to understand and maintain in the future. I'd be happy to just have this unconditionally enabled. After all, Re tests: the new test seems to never run. I think the feature test should be |
Well, hashing is surely more fundamental, as it allows to use objects as keys in dict, etc. Otherwise the reason to make it conditional was to remain fair re: -1'ing patches by others which unconditionally add new features. How can I complain about that, if I don't do that myself. So, if there's understanding that it's an exception to enable
Will have a look, but it's copied from special_methods2.py, and the idea is that it runs in the coverage build. For normal build, it's enabled in some other patch. |
8a2325e
to
350032c
Compare
Based on the discussion, this special method is available unconditionally, as converting to int is a common operation.
350032c
to
9302a0d
Compare
Ok, reworked to make it unconditional. A test then added to existing special_methods.py. |
Thanks for updating.
The standard unix build has all-special-methods enabled too, so it'll run there. What happened with the special_methods3.py test is that the |
The failure in Travis is only due to increase in minimal firmware size. |
Pull Request Test Coverage Report for Build 9953
💛 - Coveralls |
Depends on existing MICROPY_PY_ALL_SPECIAL_METHODS.