micropython / micropython/micropython
`bin()`, `hex()` and `oct()` ignore `__index__`
Nobody has claimed this yet.
- 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:
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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