Ralf W. Grosse-Kunstleve
f365314ec0
Enable Conversions Between Native Python Enum Types and C++ Enums (#5555)
* Apply smart_holder-branch-based PR #5280 on top of master.
* Add pytest.skip("GraalPy does not raise UnicodeDecodeError")
* Add `parent_scope` as first argument to `py::native_enum` ctor.
* Replace `operator+=` API with `.finalize()` API. The error messages still need cleanup.
* Resolve clang-tidy performance-unnecessary-value-param errors
* Rename (effectively) native_enum_add_to_parent() -> finalize()
* Update error message: pybind11::native_enum<...>("Fake", ...): MISSING .finalize()
* Pass py::module_ by reference to resolve clang-tidy errors (this is entirely inconsequential otherwise for all practical purposes).
* test_native_enum_correct_use_failure -> test_native_enum_missing_finalize_failure
* Add test_native_enum_double_finalize(), test_native_enum_value_after_finalize()
* Clean up public/protected API.
* [ci skip] Update the Enumerations section in classes.rst
* Rename `py::native_enum_kind` → `py::enum_kind` as suggested by gh-henryiii:
https://github.com/pybind/pybind11/pull/5555#issuecomment-2711672335
* Experiment: StrEnum
enum.StrEnum does not map to C++ enum:
* https://chatgpt.com/share/67d5e965-ccb0-8008-95b7-0df2502309b3
```
============================= test session starts ==============================
platform linux -- Python 3.12.3, pytest-8.3.3, pluggy-1.5.0
C++ Info: 13.3.0 C++20 __pybind11_internals_v10000000_system_libstdcpp_gxx_abi_1xxx_use_cxx11_abi_1__ PYBIND11_SIMPLE_GIL_MANAGEMENT=False PYBIND11_NUMPY_1_ONLY=False
configfile: pytest.ini
plugins: parallel-0.1.1, xdist-3.6.1
collected 40 items / 39 deselected / 1 selected
test_native_enum.py F [100%]
=================================== FAILURES ===================================
________________________ test_native_enum_StrEnum_greek ________________________
def test_native_enum_StrEnum_greek():
assert not hasattr(m, "greek")
> m.native_enum_StrEnum_greek(m)
test_native_enum.py:150:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/lib/python3.12/enum.py:764: in __call__
return cls._create_(
boundary = None
cls = <enum 'StrEnum'>
module = None
names = [('Alpha', 10), ('Omega', 20)]
qualname = None
start = 1
type = None
value = 'greek'
values = ()
/usr/lib/python3.12/enum.py:917: in _create_
return metacls.__new__(metacls, class_name, bases, classdict, boundary=boundary)
_ = <class 'str'>
bases = (<enum 'StrEnum'>,)
boundary = None
class_name = 'greek'
classdict = {'_generate_next_value_': <function StrEnum._generate_next_value_ at 0x701ec1711e40>, 'Alpha': 10, 'Omega': 20, '__module__': 'test_native_enum'}
cls = <enum 'StrEnum'>
first_enum = <enum 'StrEnum'>
item = ('Omega', 20)
member_name = 'Omega'
member_value = 20
metacls = <class 'enum.EnumType'>
module = 'test_native_enum'
names = [('Alpha', 10), ('Omega', 20)]
qualname = None
start = 1
type = None
/usr/lib/python3.12/enum.py:606: in __new__
raise exc.with_traceback(tb)
__class__ = <class 'enum.EnumType'>
__new__ = <function StrEnum.__new__ at 0x701ec1711da0>
_gnv = <staticmethod(<function StrEnum._generate_next_value_ at 0x701ec1711e40>)>
_order_ = None
_simple = False
bases = (<enum 'StrEnum'>,)
boundary = None
classdict = {'Alpha': <enum._proto_member object at 0x701ebc74f9b0>, 'Omega': <enum._proto_member object at 0x701ebc74cce0>, '__module__': 'test_native_enum', '_all_bits_': 0, ...}
cls = 'greek'
exc = TypeError('10 is not a string')
first_enum = <enum 'StrEnum'>
ignore = ['_ignore_']
invalid_names = set()
key = '_ignore_'
kwds = {}
member_names = {'Alpha': None, 'Omega': None}
member_type = <class 'str'>
metacls = <class 'enum.EnumType'>
name = 'Omega'
save_new = False
tb = <traceback object at 0x701ebc7a6cc0>
use_args = True
value = 20
/usr/lib/python3.12/enum.py:596: in __new__
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
__class__ = <class 'enum.EnumType'>
__new__ = <function StrEnum.__new__ at 0x701ec1711da0>
_gnv = <staticmethod(<function StrEnum._generate_next_value_ at 0x701ec1711e40>)>
_order_ = None
_simple = False
bases = (<enum 'StrEnum'>,)
boundary = None
classdict = {'Alpha': <enum._proto_member object at 0x701ebc74f9b0>, 'Omega': <enum._proto_member object at 0x701ebc74cce0>, '__module__': 'test_native_enum', '_all_bits_': 0, ...}
cls = 'greek'
exc = TypeError('10 is not a string')
first_enum = <enum 'StrEnum'>
ignore = ['_ignore_']
invalid_names = set()
key = '_ignore_'
kwds = {}
member_names = {'Alpha': None, 'Omega': None}
member_type = <class 'str'>
metacls = <class 'enum.EnumType'>
name = 'Omega'
save_new = False
tb = <traceback object at 0x701ebc7a6cc0>
use_args = True
value = 20
/usr/lib/python3.12/enum.py:271: in __set_name__
enum_member = enum_class._new_member_(enum_class, *args)
args = (10,)
enum_class = <enum 'greek'>
member_name = 'Alpha'
self = <enum._proto_member object at 0x701ebc74f9b0>
value = 10
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
cls = <enum 'greek'>, values = (10,)
def __new__(cls, *values):
"values must already be of type `str`"
if len(values) > 3:
raise TypeError('too many arguments for str(): %r' % (values, ))
if len(values) == 1:
# it must be a string
if not isinstance(values[0], str):
> raise TypeError('%r is not a string' % (values[0], ))
E TypeError: 10 is not a string
cls = <enum 'greek'>
values = (10,)
/usr/lib/python3.12/enum.py:1322: TypeError
=========================== short test summary info ============================
FAILED test_native_enum.py::test_native_enum_StrEnum_greek - TypeError: 10 is...
======================= 1 failed, 39 deselected in 0.07s =======================
ERROR: completed_process.returncode=1
```
* Remove StrEnum code.
* Make enum_kind::Enum the default kind.
* Catch redundant .export_values() calls.
* [ci skip] Add back original documentation for `py::enum_` under new advanced/deprecated.rst
* [ci skip] Add documentation for `py::enum_kind` and `py::detail::type_caster_enum_type_enabled`
* Rename `Type` to `EnumType` for readability.
* Eliminate py::enum_kind, use "enum.Enum", "enum.IntEnum" directly. This is still WIP.
* EXPERIMENTAL StrEnum code. To be removed.
* Remove experimental StrEnum code:
My judgement: Supporting StrEnum is maybe nice, but not very valuable. I don't think it is worth the extra C++ code.
A level of indirection would need to be managed, e.g.
RED ↔ Python "r" ↔ C++ 0
Green ↔ Python "g" ↔ C++ 1
These mappings would need to be stored and processed.
* Add test with enum.IntFlag (no production code changes required).
* First import_or_getattr() implementation (dedicated tests are still missing).
* Fix import_or_getattr() implementation, add tests, fix clang-tidy errors.
* [ci skip] Update classes.rst: replace `py::enum_kind` with `native_type_name`
* For "constructor similar to that of enum.Enum" point to https://docs.python.org/3/howto/enum.html#functional-api, as suggested by gh-timohl (https://github.com/pybind/pybind11/pull/5555#discussion_r2009277507).
* Advertise Enum, IntEnum, Flag, IntFlags are compatible stdlib enum types in the documentation (as suggested by gh-timohl, https://github.com/pybind/pybind11/pull/5555#pullrequestreview-2708832587); add test for enum.Flag to ensure that is actually true.
2025-03-24 20:31:59 -07:00
..
2025-03-24 20:31:59 -07:00
2024-06-22 00:55:00 -04:00
2025-03-17 17:18:00 -04:00
2024-10-07 14:12:04 -07:00
2025-03-17 17:18:00 -04:00
2025-03-24 20:31:59 -07:00
2025-02-04 08:36:08 -08:00
2024-11-18 14:39:59 -08:00
2024-06-18 13:54:38 -07:00
2024-06-18 13:54:38 -07:00
2024-09-17 09:47:20 -07:00
2024-06-18 13:54:38 -07:00
2024-10-07 14:12:04 -07:00
2024-11-10 12:17:35 -08:00
2024-09-13 00:18:29 -04:00
2024-09-13 00:18:29 -04:00
2024-08-22 00:27:50 -04:00
2022-02-10 12:17:07 -08:00
2024-06-18 13:54:38 -07:00
2024-11-07 09:32:09 -08:00
2024-11-07 09:32:09 -08:00
2024-08-14 18:21:04 -04:00
2024-05-24 12:26:40 -04:00
2024-10-07 14:12:04 -07:00
2022-02-10 12:17:07 -08:00
2024-09-03 10:51:21 -04:00
2024-11-05 10:14:24 -08:00
2024-11-05 10:14:24 -08:00
2025-03-18 07:56:34 -07:00
2025-03-18 07:56:34 -07:00
2024-10-07 14:12:04 -07:00
2024-10-25 08:28:15 -07:00
2025-02-19 09:24:02 -08:00
2025-03-18 07:56:34 -07:00
2022-02-10 12:17:07 -08:00
2024-06-22 00:55:00 -04:00
2025-02-16 11:01:41 -08:00
2025-02-16 11:01:41 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-05 12:40:53 -08:00
2025-03-18 07:56:34 -07:00
2022-02-14 11:36:22 -08:00
2024-06-22 00:55:00 -04:00
2024-03-26 13:57:05 -04:00
2024-06-22 00:55:00 -04:00
2024-06-24 08:59:55 -07:00
2024-10-25 08:28:15 -07:00
2024-09-13 00:18:29 -04:00
2024-09-13 00:18:29 -04:00
2024-09-13 00:18:29 -04:00
2024-10-07 14:12:04 -07:00
2024-04-02 11:14:08 -04:00
2025-03-18 07:56:34 -07:00
2021-09-24 12:08:22 -07:00
2024-10-07 14:12:04 -07:00
2025-01-24 17:01:06 -05:00
2024-12-08 11:30:49 -08:00
2024-04-02 11:14:08 -04:00
2025-03-18 07:56:34 -07:00
2025-02-17 23:13:05 -08:00
2025-02-17 23:13:05 -08:00
2022-12-20 05:34:00 -08:00
2024-07-29 11:10:03 -07:00
2025-02-17 23:13:05 -08:00
2025-03-24 20:31:59 -07:00
2025-03-24 20:31:59 -07:00
2024-06-22 00:55:00 -04:00
2022-02-10 12:17:07 -08:00
2024-10-07 14:12:04 -07:00
2024-09-17 09:47:20 -07:00
2023-08-07 20:48:20 -07:00
2025-03-20 22:41:33 -07:00
2022-02-14 11:36:22 -08:00
2025-03-18 07:56:34 -07:00
2022-10-30 08:57:23 -07:00
2025-02-04 07:48:47 -08:00
2022-02-14 11:36:22 -08:00
2024-11-18 14:39:59 -08:00
2024-11-11 14:51:01 -08:00
2025-03-18 07:56:34 -07:00
2022-02-10 12:17:07 -08:00
2025-03-23 20:35:14 -07:00
2024-11-11 15:35:28 -08:00
2025-03-18 07:56:34 -07:00
2024-07-29 11:10:03 -07:00
2024-12-30 19:53:30 -08:00
2022-02-10 12:17:07 -08:00
2025-03-23 20:35:14 -07:00
2025-03-24 20:31:59 -07:00
2025-03-24 20:31:59 -07:00
2025-02-17 23:13:05 -08:00
2025-03-20 20:57:51 -07:00
2025-03-06 00:19:54 -08:00
2025-03-18 07:56:34 -07:00
2022-05-26 11:07:40 -04:00
2025-03-18 07:56:34 -07:00
2024-08-22 00:27:50 -04:00
2025-03-18 11:29:57 -07:00
2022-11-28 07:39:38 -08:00
2024-10-25 08:28:15 -07:00
2022-05-02 12:39:36 -07:00
2024-11-11 15:35:28 -08:00
2023-11-08 12:44:04 -08:00
2024-06-22 00:55:00 -04:00
2025-03-23 10:09:46 -07:00
2025-03-23 20:35:14 -07:00
2024-08-22 00:27:50 -04:00
2024-11-11 15:35:28 -08:00
2024-08-22 00:27:50 -04:00
2025-02-04 08:36:08 -08:00
2024-04-02 11:14:08 -04:00
2024-10-08 10:49:35 -07:00
2024-12-08 11:30:49 -08:00
2025-03-18 07:56:34 -07:00
2024-08-22 00:27:50 -04:00
2024-06-22 00:55:00 -04:00
2025-01-16 11:13:21 -08:00
2025-01-16 11:13:21 -08:00
2024-10-07 14:12:04 -07:00
2025-03-18 07:56:34 -07:00
2024-08-09 13:28:12 -07:00
2024-08-09 13:28:12 -07:00
2019-06-11 23:28:58 +02:00
2024-06-22 00:55:00 -04:00
2025-02-19 09:24:02 -08:00
2025-02-19 09:24:02 -08:00
2023-04-25 14:03:24 -07:00
2024-06-22 00:55:00 -04:00
2023-04-27 07:24:48 -07:00
2024-06-22 00:55:00 -04:00
2024-08-22 00:27:50 -04:00
2024-10-07 14:12:04 -07:00
2024-08-28 20:55:50 -07:00
2024-08-28 20:55:50 -07:00
2021-07-04 19:58:35 -04:00
2021-01-31 17:29:09 -05:00