You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two native_enum types that both call .export_values() currently fail to import when they share a value name. The second binding hits the "an object with that name is already defined" check, even though the existing attribute is just an Enum member from the first binding.
The collision check in native_enum_data::finalize() now only treats an existing parent-scope attribute as a conflict when it is not already an Enum instance. Collisions with non-Enum attributes are still rejected, so names the user has explicitly set in the parent scope remain protected (the existing test_native_enum_value_name_clash still passes).
Classic py::enum_::export_values() has no collision check at all, so two classic enums sharing a value name have always worked with last-wins semantics. native_enum now behaves consistently in this respect, while keeping a stricter check against non-Enum attributes.
Test
A second native_enum, export_values2, is added to the test submodule. It shares exv0 with the existing export_values enum. test_export_values verifies:
both enums import successfully;
each enum keeps its own distinct members (export_values.exv0 is not export_values2.exv0);
the shared parent-scope attribute points to the later binding (m.exv0 is m.export_values2.exv0).
Confirmed by reverting just the header change: the test module then fails to import with exactly the error from the issue.
Suggested changelog entry:
Fix ImportError when two native_enum types call export_values() and share a value name.
Silently clobbering previously exported names in the parent scope is exactly the kind of footgun native_enum was designed to avoid. It turns a clear, deterministic import-time error into a non-local, version-dependent behavior change: an upstream dependency that you do not control can add a new enum or a new member name, your extension still compiles and imports, and an existing exported symbol now resolves to a different enum member, or even a different enum type. The resulting failures may surface much later and far away from the binding code that introduced the collision, which makes them disproportionately difficult and time-consuming to diagnose. For native_enum, fail-fast on exported-name collisions is a feature, not something we should relax by default.
It'd seem acceptable to me to add an opt-in API, for example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #6031.
Two
native_enumtypes that both call.export_values()currently fail to import when they share a value name. The second binding hits the "an object with that name is already defined" check, even though the existing attribute is just an Enum member from the first binding.The collision check in
native_enum_data::finalize()now only treats an existing parent-scope attribute as a conflict when it is not already an Enum instance. Collisions with non-Enum attributes are still rejected, so names the user has explicitly set in the parent scope remain protected (the existingtest_native_enum_value_name_clashstill passes).Classic
py::enum_::export_values()has no collision check at all, so two classic enums sharing a value name have always worked with last-wins semantics.native_enumnow behaves consistently in this respect, while keeping a stricter check against non-Enum attributes.Test
A second
native_enum,export_values2, is added to the test submodule. It sharesexv0with the existingexport_valuesenum.test_export_valuesverifies:export_values.exv0 is not export_values2.exv0);m.exv0 is m.export_values2.exv0).Confirmed by reverting just the header change: the test module then fails to import with exactly the error from the issue.
Suggested changelog entry:
ImportErrorwhen twonative_enumtypes callexport_values()and share a value name.