Skip to content

bin(), hex() and oct() ignore __index__ #19714

Description

@jseop-lim

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:

| Constructors of *int*, *float* and *complex* will now use the *__index__()* special method, if available | |
| and the corresponding method *__int__()*, *__float__()* or *__complex__()* is not available | |

| Builtin and extension functions that take integer arguments no longer accept | |
| :class:`~decimal.Decimal`\ s, :class:`~fractions.Fraction`\ s and other | |
| objects that can be converted to integers only with a loss (e.g. that have | |
| the :meth:`~object.__int__` method but do not have the | |
| :meth:`~object.__index__` method). | |

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions