diff --git a/include/pybind11/detail/native_enum_data.h b/include/pybind11/detail/native_enum_data.h index 6770378fc1..a95e63d6dd 100644 --- a/include/pybind11/detail/native_enum_data.h +++ b/include/pybind11/detail/native_enum_data.h @@ -200,9 +200,13 @@ inline void native_enum_data::finalize() { } parent_scope.attr(enum_name) = py_enum; if (export_values_flag) { + // Allow an already-exported Enum member with the same name to be replaced, + // so two native_enums can share exported value names (see issue #6031). + auto enum_base = import_or_getattr("enum.Enum", " (native_enum export_values)"); for (auto member : members) { auto member_name = member[int_(0)]; - if (hasattr(parent_scope, member_name)) { + if (hasattr(parent_scope, member_name) + && !isinstance(parent_scope.attr(member_name), enum_base)) { pybind11_fail("pybind11::native_enum<...>(\"" + enum_name_encoded + "\").value(\"" + member_name.cast() + "\"): an object with that name is already defined"); diff --git a/tests/test_native_enum.cpp b/tests/test_native_enum.cpp index 816ace01d1..9ff99bee70 100644 --- a/tests/test_native_enum.cpp +++ b/tests/test_native_enum.cpp @@ -25,6 +25,8 @@ enum class flags_uint : unsigned int { bit0 = 0x1u, bit1 = 0x2u, bit2 = 0x4u }; enum class export_values { exv0, exv1 }; +enum class export_values2 { exv0, exv2 }; + enum class member_doc { mem0, mem1, mem2 }; struct class_with_enum { @@ -120,6 +122,13 @@ TEST_SUBMODULE(native_enum, m) { .export_values() .finalize(); + // Shares "exv0" with export_values above (issue #6031). + py::native_enum(m, "export_values2", "enum.IntEnum") + .value("exv0", export_values2::exv0) + .value("exv2", export_values2::exv2) + .export_values() + .finalize(); + py::native_enum(m, "member_doc", "enum.IntEnum") .value("mem0", member_doc::mem0, "docA") .value("mem1", member_doc::mem1) diff --git a/tests/test_native_enum.py b/tests/test_native_enum.py index 426220013e..135829a47f 100644 --- a/tests/test_native_enum.py +++ b/tests/test_native_enum.py @@ -48,6 +48,11 @@ ("exv1", 1), ) +EXPORT_VALUES2_MEMBERS = ( + ("exv0", 0), + ("exv2", 1), +) + MEMBER_DOC_MEMBERS = ( ("mem0", 0), ("mem1", 1), @@ -62,6 +67,7 @@ (m.flags_uchar, FLAGS_UCHAR_MEMBERS), (m.flags_uint, FLAGS_UINT_MEMBERS), (m.export_values, EXPORT_VALUES_MEMBERS), + (m.export_values2, EXPORT_VALUES2_MEMBERS), (m.member_doc, MEMBER_DOC_MEMBERS), (m.func_sig_rendering, FUNC_SIG_RENDERING_MEMBERS), (m.class_with_enum.in_class, CLASS_WITH_ENUM_IN_CLASS_MEMBERS), @@ -101,8 +107,11 @@ def test_enum_flag(enum_type): def test_export_values(): - assert m.exv0 is m.export_values.exv0 + # Regression test for issue #6031. assert m.exv1 is m.export_values.exv1 + assert m.exv2 is m.export_values2.exv2 + assert m.export_values.exv0 is not m.export_values2.exv0 + assert m.exv0 is m.export_values2.exv0 def test_class_doc():