Repository navigation
fix: accept __index__ objects for unsigned integers without conversion so enum == numpy int works - #6193
fix: accept __index__ objects for unsigned integers without conversion so enum == numpy int works#6193quinncheong wants to merge 5 commits into
Conversation
Since pybind#5887 an enum with an unsigned underlying type compares unequal to numpy integers, because the no-convert pass rejects __index__ objects for unsigned targets and the catch-all overload returns false. Closes pybind#6192.
The no-convert early return was previously PyPy-only, so clang-tidy never saw it; readability-braces-around-statements now flags it.
|
Pushed 2fa7d31 for the Clang-Tidy failure ( The other red jobs on the first run were not test failures: 21 were cancelled with "The job was not acquired by Runner of type hosted even after multiple attempts", and one Windows job lost contact with its runner. The test jobs that did run passed, across ubuntu, macos and windows at C++11 through C++23, including the free-threaded builds. Prepared with an AI coding agent (Claude Code), checked by me. |
|
Fable 5.1 review below. On the points. For item 1, the Both item 1-2 are about tests, adding a test seems fine. Item 3 does sound simpler. 🤖 AI text below 🤖 The background code review of Worth acting on
Style
Ignore
|
Add uint_passthrough(_noconvert) and run the existing int conversion matrix against it, which also pins __index__ taking precedence over __int__ for unsigned targets. Gate the explicit PyNumber_Index call on PYBIND11_INDEX_CHECK so objects without __index__ no longer raise and clear an extra TypeError in convert mode, and select the PyPy/unsigned condition with a constexpr bool instead of two #if'd if-heads. Assisted-by: ClaudeCode:claude-fable-5-1
|
I don't think we support PyPy < 7.3.16, so we could drop the branch. |
PyLong_AsUnsignedLong does not call __int__, so the unsigned caster falls back to PyNumber_Long and does not warn. Assisted-by: ClaudeCode:claude-opus-5-5
PyPy below the supported 7.3.17 is not a target, and supported PyPy calls __index__ in PyLong_AsLong[Long] like CPython, so only unsigned targets need the explicit PyNumber_Index call. Also drops the complex.h comment that pointed at the removed branch. Assisted-by: ClaudeCode:claude-opus-5-5
|
The PyPy-only block in the integer caster is gone; only unsigned targets call PyNumber_Index now. |
Description
Closes #6192. Related: #5895.
Since #5887,
py::enum_comparison against a scalar uses typed overloads:(Type, Scalar)and then a catch-all(Type, const object &)that returnsfalse. For an enum with an unsigned underlying type, the integer caster rejects__index__objects (numpy integers among them) in the no-convert pass, becausePyLong_AsUnsignedLong[Long]does not call__index__. The catch-all then matches in the same pass, somember == np.int32(3)isFalsewhilenp.int32(3) == memberisTrue. Signed underlying types are unaffected becausePyLong_AsLongdoes call__index__.The caster already calls
PyNumber_Indexexplicitly on PyPy for the same reason. This change runs that path on CPython too when the target type is unsigned, gated onPYBIND11_INDEX_CHECKso objects without__index__do not pay for an extra raise. The enum test fails on master for theuint32_tenum and passes with the fix; theint32_tenum is the control.test_int_convertandtest_numpy_int_convertnow also run against newuint_passthroughfunctions, so the caster change is covered directly.One side effect: in convert mode, an unsigned target now calls
__index__before__int__, matching signed targets. Previously__int__won for unsigned (theIntAndIndexcase in the tests).Testing
Ran
test_enum.pyandtest_builtin_casters.pyon CPython 3.13 and PyPy 3.9 (7.3.16), plusprek -a. The PyPy-onlyPyNumber_Indexbranch is no longer needed on PyPy 7.3.16+ (the tests pass with it disabled), but it is kept in case older 7.3.x builds still need it.Suggested changelog entry:
__index__objects when the enum's underlying type is unsigned (regression in 3.1.0). Unsigned integer arguments now accept__index__objects without implicit conversion, and with conversion__index__takes precedence over__int__, matching signed integers.This PR was prepared with an AI coding agent (Claude Code); I reproduced the bug and ran the tests listed above before posting.
📚 Documentation preview 📚: https://pybind11--6193.org.readthedocs.build/