Fix custom __str__ for enum_#6078
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds coverage and a small pybind11 change to ensure custom __str__ implementations for bound enums take precedence without breaking enum name/value behavior.
Changes:
- Add a new C++ test enum with a custom
__str__implementation. - Add a Python test asserting
__str__override works whilename/valueremain correct. - Special-case
enum_::def("__str__", ...)to applyprepend{}so the override wins over the generated__str__.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/test_enum.py | Adds regression test for custom enum __str__ while preserving name/value. |
| tests/test_enum.cpp | Defines a new test enum and binds a custom __str__ implementation. |
| include/pybind11/pybind11.h | Changes py::enum_::def to prepend custom __str__ overloads. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| template <typename Func, typename... Extra> | ||
| enum_ &def(const char *name_, Func &&f, const Extra &...extra) { | ||
| if (std::strcmp(name_, "__str__") == 0) { | ||
| Base::def(name_, std::forward<Func>(f), prepend{}, extra...); | ||
| } else { | ||
| Base::def(name_, std::forward<Func>(f), extra...); | ||
| } | ||
| return *this; | ||
| } |
There was a problem hiding this comment.
Thanks, good catch. This is addressed in 995104f.
The fix adds an explicit forwarding overload for non-string def(...) calls, so uses such as def(py::init<...>()) and operator/helper forms still delegate to Base::def(...).
I intentionally did not re-add using Base::def, because GCC 15/MinGW treats the duplicate dependent base def(const char *, ...) template as ambiguous with enum_::def(const char *, ...), which broke the C++11 mingw builds. The new forwarding overload keeps the base functionality available without reintroducing that ambiguity.
GPT-5.5 1M Extra High
There was a problem hiding this comment.
Thanks for the follow-up commits and explanation! I reviewed the updated changes, and the approach looks good to me.
There was a problem hiding this comment.
Thanks, good catch. This is addressed in 995104f.
The fix adds an explicit forwarding overload for non-string
def(...)calls, so uses such asdef(py::init<...>())and operator/helper forms still delegate toBase::def(...).I intentionally did not re-add
using Base::def, because GCC 15/MinGW treats the duplicate dependent basedef(const char *, ...)template as ambiguous withenum_::def(const char *, ...), which broke the C++11 mingw builds. The new forwarding overload keeps the base functionality available without reintroducing that ambiguity.GPT-5.5 1M Extra High
Thanks for the follow-up commits and explanation! I reviewed the updated changes, and the approach looks good to me.
352b6f8 to
146f598
Compare
Keep class_::def() generic and let enum_ own the enum-specific
behavior for custom __str__ overloads. This avoids using the private
__entries attribute as a runtime sentinel for py::enum_ while preserving
the prepend behavior that lets user-defined enum __str__ methods take
precedence over the generated default.
One caveat is that this applies to normal py::enum_ API usage. Code that
intentionally upcasts an enum_ binding to class_& and then calls
class_::def("__str__", ...) will bypass this enum_ override and keep the
generic class_ behavior.
|
@ctmd1234567 I added two commits (I used GPT-5.5 as assistant):
Could you please review from your side? |
Remove the inherited class_::def overload set from enum_ and add an explicit forwarding overload for non-string def() calls. GCC 15 on MinGW otherwise sees the duplicate dependent-base def(const char *, ...) template as ambiguous with enum_::def(const char *, ...), breaking the C++11 build.
Fixes #4585.
This makes user-defined
enum___str__methods take precedence over the defaultenum___str__implementation by prepending the custom overload. A regression test verifies that a custom__str__works while the generated name and value properties remain intact.