Skip to content

Commit 80a777e

Browse files
authored
Fix: Enum Internals Filter (#303)
Fix #302 - [x] reproducer/test - [x] fix - [x] update stubs, ensure they include no `_sunder_` members
1 parent ae80391 commit 80a777e

43 files changed

Lines changed: 1276 additions & 1 deletion

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pybind11_stubgen/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from pybind11_stubgen.parser.mixins.filter import (
2222
FilterClassMembers,
2323
FilterInvalidIdentifiers,
24+
FilterPybind11NativeEnumMembers,
2425
FilterPybind11ViewClasses,
2526
FilterPybindInternals,
2627
FilterTypingModuleAttributes,
@@ -284,6 +285,7 @@ class Parser(
284285
FixValueReprRandomAddress,
285286
FixRedundantBuiltinsAnnotation,
286287
FilterPybindInternals,
288+
FilterPybind11NativeEnumMembers,
287289
FilterPybind11ViewClasses,
288290
FixRedundantMethodsFromBuiltinObject,
289291
RemoveSelfAnnotation,

pybind11_stubgen/parser/mixins/filter.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,35 @@ def handle_module_member(
148148
return None
149149

150150
return result
151+
152+
153+
class FilterPybind11NativeEnumMembers(IParser):
154+
@staticmethod
155+
def _is_sunder(name: str) -> bool:
156+
"""Match CPython Enum's reserved ``_sunder_`` names.
157+
158+
Keep this in sync with CPython's ``enum._is_sunder``:
159+
https://github.com/python/cpython/blob/3.14/Lib/enum.py#L57-L66
160+
161+
These names are rejected by ``EnumDict.__setitem__`` unless explicitly
162+
allowlisted by ``enum`` itself:
163+
https://github.com/python/cpython/blob/3.14/Lib/enum.py#L342-L367
164+
"""
165+
return (
166+
len(name) > 2
167+
and name[0] == name[-1] == "_"
168+
and name[1] != "_"
169+
and name[-2] != "_"
170+
)
171+
172+
def handle_class_member(
173+
self, path: QualifiedName, class_: type, obj: Any
174+
) -> Docstring | Alias | Class | list[Method] | Field | Property | None:
175+
name = str(path[-1])
176+
# py::native_enum exposes Enum internals via __dict__, but emitting them
177+
# into a stub that later gets executed as Python can fail at import time.
178+
if hasattr(class_, "__pybind11_native_enum__") and (
179+
name == "__pybind11_native_enum__" or self._is_sunder(name)
180+
):
181+
return None
182+
return super().handle_class_member(path, class_, obj)

tests/py-demo/bindings/src/modules/enum.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,19 @@
22

33
#include <demo/sublibA/ConsoleColors.h>
44

5+
#if PYBIND11_VERSION_AT_LEAST(3,0)
6+
# include <pybind11/native_enum.h>
7+
#endif
8+
9+
#if PYBIND11_VERSION_AT_LEAST(3,0)
10+
namespace {
11+
enum class NativeColor : int {
12+
Red = 1,
13+
Blue = 2,
14+
};
15+
} // namespace
16+
#endif
17+
518
void bind_enum_module(py::module&&m) {
619

720
py::enum_<demo::sublibA::ConsoleForegroundColor>(m, "ConsoleForegroundColor")
@@ -16,4 +29,11 @@ void bind_enum_module(py::module&&m) {
1629
"accept_defaulted_enum",
1730
[](const demo::sublibA::ConsoleForegroundColor &color) {},
1831
py::arg("color") = demo::sublibA::ConsoleForegroundColor::None_);
32+
33+
#if PYBIND11_VERSION_AT_LEAST(3,0)
34+
py::native_enum<NativeColor>(m, "NativeColor", "enum.IntEnum")
35+
.value("Red", NativeColor::Red)
36+
.value("Blue", NativeColor::Blue)
37+
.finalize();
38+
#endif
1939
}

tests/stubs/python-3.10

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../python-3.11/pybind11-v2.11
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../python-3.11/pybind11-v2.12
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../python-3.11/pybind11-v2.13
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../python-3.11/pybind11-v3.0/numpy-array-use-type-var
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from __future__ import annotations
2+
3+
from demo._bindings import (
4+
aliases,
5+
classes,
6+
eigen,
7+
enum,
8+
flawed_bindings,
9+
functions,
10+
hidden_builtins,
11+
issues,
12+
methods,
13+
numpy,
14+
properties,
15+
stl,
16+
stl_bind,
17+
typing,
18+
values,
19+
)
20+
21+
from . import _bindings, core, pure_python
22+
23+
__all__: list[str] = [
24+
"aliases",
25+
"classes",
26+
"core",
27+
"eigen",
28+
"enum",
29+
"flawed_bindings",
30+
"functions",
31+
"hidden_builtins",
32+
"issues",
33+
"methods",
34+
"numpy",
35+
"properties",
36+
"pure_python",
37+
"stl",
38+
"stl_bind",
39+
"typing",
40+
"values",
41+
"version",
42+
]
43+
version: str = "0.0.0"
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
from __future__ import annotations
2+
3+
from . import (
4+
aliases,
5+
classes,
6+
eigen,
7+
enum,
8+
flawed_bindings,
9+
functions,
10+
hidden_builtins,
11+
issues,
12+
methods,
13+
numpy,
14+
properties,
15+
stl,
16+
stl_bind,
17+
typing,
18+
values,
19+
)
20+
21+
__all__: list[str] = [
22+
"aliases",
23+
"classes",
24+
"eigen",
25+
"enum",
26+
"flawed_bindings",
27+
"functions",
28+
"hidden_builtins",
29+
"issues",
30+
"methods",
31+
"numpy",
32+
"properties",
33+
"stl",
34+
"stl_bind",
35+
"typing",
36+
"values",
37+
]

0 commit comments

Comments
 (0)