Skip to content

Fix custom __str__ for enum_#6078

Merged
rwgk merged 4 commits into
pybind:masterfrom
ctmd1234567:fix-enum-str-name-4585
May 25, 2026
Merged

Fix custom __str__ for enum_#6078
rwgk merged 4 commits into
pybind:masterfrom
ctmd1234567:fix-enum-str-name-4585

Conversation

@ctmd1234567
Copy link
Copy Markdown
Contributor

@ctmd1234567 ctmd1234567 commented May 24, 2026

Fixes #4585.

This makes user-defined enum_ __str__ methods take precedence over the default enum_ __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.

Copilot AI review requested due to automatic review settings May 24, 2026 15:39
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 while name/value remain correct.
  • Special-case enum_::def("__str__", ...) to apply prepend{} 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.

Comment on lines +3284 to +3292
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;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up commits and explanation! I reviewed the updated changes, and the approach looks good to me.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Thanks for the follow-up commits and explanation! I reviewed the updated changes, and the approach looks good to me.

@ctmd1234567 ctmd1234567 force-pushed the fix-enum-str-name-4585 branch from 352b6f8 to 146f598 Compare May 24, 2026 16:01
rwgk added 2 commits May 24, 2026 09:58
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.
@rwgk
Copy link
Copy Markdown
Collaborator

rwgk commented May 24, 2026

@ctmd1234567 I added two commits (I used GPT-5.5 as assistant):

  • commit 5c46a3a — test: strengthen custom enum __str__ regression
  • commit d73162a — refactor: move enum __str__ handling into enum_

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.
Copy link
Copy Markdown
Collaborator

@rwgk rwgk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @ctmd1234567!

@rwgk rwgk merged commit f891299 into pybind:master May 25, 2026
153 of 154 checks passed
@github-actions github-actions Bot added the needs changelog Possibly needs a changelog entry label May 25, 2026
@ctmd1234567 ctmd1234567 deleted the fix-enum-str-name-4585 branch May 25, 2026 04:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs changelog Possibly needs a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG]: implementing __str__ on an enum turn the name attribute into a method

3 participants