Skip to content

py/obj: Add support for __float__ converter function. #8857

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

Conversation

andrewleech
Copy link
Contributor

I've run into a number of situation where I'm using a custom class that's intended to wrap an integer or float value, eg. micropython/micropython-lib#502

It's confusing that other dunder converter functions like __int__ and __str__ work but not __float__. This PR resolves this, though in a different way to these other functions.

Another similar question was raised here: https://forum.micropython.org/viewtopic.php?f=2&t=6467

@andrewleech
Copy link
Contributor Author

@andrewleech andrewleech added the py-core Relates to py/ directory in source label Jul 4, 2022
@mattytrentini
Copy link
Contributor

One point of discussion; if __float__ is not present then Python will call __index__ as a fallback (and expect an int which it will promote to a float). The behaviour is neatly summarised here.

We looked at adding the conversion fallback behaviour to improve Python compatibility but found that it added enough complexity that we were questioning it's value. Further, the same behaviour (call __index__ if the regular dunder conversion method isn't present) should also be applied to int and complex - but these were a little harder to implement.

I guess the question is should we bother? It seems like an esoteric corner of the language...

py/obj.c Outdated
@@ -67,10 +67,10 @@ const mp_obj_type_t *MICROPY_WRAP_MP_OBJ_GET_TYPE(mp_obj_get_type)(mp_const_obj_
} else if (mp_obj_is_obj(o_in)) {
const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in);
return o->type;
#if MICROPY_PY_BUILTINS_FLOAT
#if MICROPY_PY_BUILTINS_FLOAT
Copy link
Member

Choose a reason for hiding this comment

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

is it possible to not reformat these #'s ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah whoops, the first ~5 times I pushed this branch I undid all the auto-codeformat on these lines, must have missed that on the current / last push. Not sure why my uncrustify wants to do this here :-(

@dpgeorge
Copy link
Member

dpgeorge commented Jul 5, 2022

It's confusing that other dunder converter functions like int and str work but not float. This PR resolves this, though in a different way to these other functions.

Did you consider using the same approach as those existing ones, using MP_UNARY_OP_FLOAT?

@andrewleech
Copy link
Contributor Author

andrewleech commented Jul 5, 2022

Did you consider using the same approach as those existing ones, using MP_UNARY_OP_FLOAT?

I briefly did but saw the comment here about if forcing a bytecode update:

// These ops may appear in the bytecode. Changing this group

I presume this was is more efficient? I haven't followed that code path enough to know how it works yet.

That being said I guess "not updating the bytecode version" isn't a great excuse for taking shortcuts though, consistency with existing code is important...

update: geez I am blind sometimes... that's what I get for rushing I guess. Only a few lines below that comment I linked above is the follow up comment // Following ops cannot appear in the bytecode which include int etc, so yeah it's really not an issue to do this properly. I'll look into it.

@codecov-commenter
Copy link

Codecov Report

Merging #8857 (c3bc82c) into master (6519b1b) will increase coverage by 0.00%.
The diff coverage is 100.00%.

@@           Coverage Diff           @@
##           master    #8857   +/-   ##
=======================================
  Coverage   98.31%   98.31%           
=======================================
  Files         157      157           
  Lines       20349    20365   +16     
=======================================
+ Hits        20006    20022   +16     
  Misses        343      343           
Impacted Files Coverage Δ
py/obj.c 98.02% <100.00%> (+0.11%) ⬆️
py/objcomplex.c 100.00% <100.00%> (ø)
ports/unix/moduos.c 18.91% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6519b1b...c3bc82c. Read the comment docs.

@andrewleech
Copy link
Contributor Author

andrewleech commented Jul 6, 2022

Looking closer at the existing __int__ UNARY handling:

// Qstrs for special methods are guaranteed to have a small value, so we use byte
// type to represent them.
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__,

Is the point of all these functions that they return a small int / non-gc object? As such float and complex wouldn't fit here as they return larger objects?

@dpgeorge
Copy link
Member

Is the point of all these functions that they return a small int / non-gc object?

No. It's just that the mp_unary_op_method_name array has byte-size elements, not qstr-size elements.

@andrewleech
Copy link
Contributor Author

@dpgeorge new approach added in #8899

@dpgeorge
Copy link
Member

Superseded by #8899.

@dpgeorge dpgeorge closed this Jul 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants