#19714·micropython

`bin()`, `hex()` and `oct()` ignore `__index__`

Author: jseop-limCreated Sep 17, 2026Updated Sep 18, 2026
Labelsbug

Port, board and/or hardware

Unix port, macOS arm64

MicroPython version

MicroPython v1.30.0-preview.66.gcc12057519

Reproduction

class A:
    def __index__(self):
        return 2

print(bin(A()))

Expected behaviour

CPython prints (3.11.15):

0b10

Observed behaviour

MicroPython raises:

ValueError: unknown format code 'b' for object of type 'A'

hex(A()) and oct(A()) raise TypeError: can't convert A to int, while an object defining only __int__ is accepted by hex() and oct() where CPython raises TypeError.

Additional Information

The data model names __index__ as the method bin(), hex() and oct() use (docs). MicroPython's own difference tables list the related 3.8 and 3.10 changes without a status:

https://github.com/micropython/micropython/blob/b0310f567bc6ec7a357d519ddb8d42bea5a55e68/docs/differences/python_38.rst#L46-L47

https://github.com/micropython/micropython/blob/b0310f567bc6ec7a357d519ddb8d42bea5a55e68/docs/differences/python_310.rst#L76-L80

Root Cause

mp_builtin_bin (py/modbuiltins.c#L122-L125) passes its argument unconverted to '{:#b}'.format(), so the object lands in the formatter's default: arm and raises ValueError (py/objstr.c#L1607-L1613). The instance coercion used by hex() and oct() goes through MP_UNARY_OP_INT_MAYBE, which looks up only __int__ (py/objtype.c#L387), and __index__ is not looked up anywhere. CPython's bin() calls PyNumber_ToBase, which converts through _PyNumber_Index and calls nb_index.

Fix Suggestion

Map a new MP_UNARY_OP_INDEX_MAYBE to __index__, try it before __int__ in mp_obj_get_int_maybe() (py/obj.c#L358-L373), and coerce the argument in mp_builtin_bin() before formatting. The extra qstr and unary op could sit behind a config option.

Code of Conduct

Yes, I agree