micropython / micropython/micropython

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

Open
#19,714 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C
Stars
22.1k
Forks
9k
Avg merge
6d 4h
Merged PRs (30d)
16

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the reproduction, then read py/modbuiltins.c, py/obj.c, and py/objtype.c at the referenced locations to trace how bin(), hex(), and oct() perform coercion. Compare the results with CPython for objects defining index or only int; done means the three built-ins follow the documented index behavior without accepting int alone.

Written by the indexing model from the issue text.

Assessment

Tech stack
c, python
Domain
embedded-iot
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.