Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pybind11_stubgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from pybind11_stubgen.parser.mixins.filter import (
FilterClassMembers,
FilterInvalidIdentifiers,
FilterPybind11NativeEnumMembers,
FilterPybind11ViewClasses,
FilterPybindInternals,
FilterTypingModuleAttributes,
Expand Down Expand Up @@ -284,6 +285,7 @@ class Parser(
FixValueReprRandomAddress,
FixRedundantBuiltinsAnnotation,
FilterPybindInternals,
FilterPybind11NativeEnumMembers,
FilterPybind11ViewClasses,
FixRedundantMethodsFromBuiltinObject,
RemoveSelfAnnotation,
Expand Down
32 changes: 32 additions & 0 deletions pybind11_stubgen/parser/mixins/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,35 @@ def handle_module_member(
return None

return result


class FilterPybind11NativeEnumMembers(IParser):
@staticmethod
def _is_sunder(name: str) -> bool:
"""Match CPython Enum's reserved ``_sunder_`` names.

Keep this in sync with CPython's ``enum._is_sunder``:
https://github.com/python/cpython/blob/3.14/Lib/enum.py#L57-L66

These names are rejected by ``EnumDict.__setitem__`` unless explicitly
allowlisted by ``enum`` itself:
https://github.com/python/cpython/blob/3.14/Lib/enum.py#L342-L367
"""
return (
len(name) > 2
and name[0] == name[-1] == "_"
and name[1] != "_"
and name[-2] != "_"
)

def handle_class_member(
self, path: QualifiedName, class_: type, obj: Any
) -> Docstring | Alias | Class | list[Method] | Field | Property | None:
name = str(path[-1])
# py::native_enum exposes Enum internals via __dict__, but emitting them
# into a stub that later gets executed as Python can fail at import time.
if hasattr(class_, "__pybind11_native_enum__") and (
name == "__pybind11_native_enum__" or self._is_sunder(name)
):
return None
return super().handle_class_member(path, class_, obj)
20 changes: 20 additions & 0 deletions tests/py-demo/bindings/src/modules/enum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

#include <demo/sublibA/ConsoleColors.h>

#if PYBIND11_VERSION_AT_LEAST(3,0)
# include <pybind11/native_enum.h>
#endif

#if PYBIND11_VERSION_AT_LEAST(3,0)
namespace {
enum class NativeColor : int {
Red = 1,
Blue = 2,
};
} // namespace
#endif

void bind_enum_module(py::module&&m) {

py::enum_<demo::sublibA::ConsoleForegroundColor>(m, "ConsoleForegroundColor")
Expand All @@ -16,4 +29,11 @@ void bind_enum_module(py::module&&m) {
"accept_defaulted_enum",
[](const demo::sublibA::ConsoleForegroundColor &color) {},
py::arg("color") = demo::sublibA::ConsoleForegroundColor::None_);

#if PYBIND11_VERSION_AT_LEAST(3,0)
py::native_enum<NativeColor>(m, "NativeColor", "enum.IntEnum")
.value("Red", NativeColor::Red)
.value("Blue", NativeColor::Blue)
.finalize();
#endif
}
1 change: 0 additions & 1 deletion tests/stubs/python-3.10

This file was deleted.

1 change: 1 addition & 0 deletions tests/stubs/python-3.10/pybind11-v2.11
1 change: 1 addition & 0 deletions tests/stubs/python-3.10/pybind11-v2.12
1 change: 1 addition & 0 deletions tests/stubs/python-3.10/pybind11-v2.13
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from __future__ import annotations

from demo._bindings import (
aliases,
classes,
eigen,
enum,
flawed_bindings,
functions,
hidden_builtins,
issues,
methods,
numpy,
properties,
stl,
stl_bind,
typing,
values,
)

from . import _bindings, core, pure_python

__all__: list[str] = [
"aliases",
"classes",
"core",
"eigen",
"enum",
"flawed_bindings",
"functions",
"hidden_builtins",
"issues",
"methods",
"numpy",
"properties",
"pure_python",
"stl",
"stl_bind",
"typing",
"values",
"version",
]
version: str = "0.0.0"
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from . import (
aliases,
classes,
eigen,
enum,
flawed_bindings,
functions,
hidden_builtins,
issues,
methods,
numpy,
properties,
stl,
stl_bind,
typing,
values,
)

__all__: list[str] = [
"aliases",
"classes",
"eigen",
"enum",
"flawed_bindings",
"functions",
"hidden_builtins",
"issues",
"methods",
"numpy",
"properties",
"stl",
"stl_bind",
"typing",
"values",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from __future__ import annotations

import typing

import numpy
from numpy import random

import demo._bindings.enum
from demo._bindings.aliases.foreign_method_arg import Bar2 as foreign_type_alias
from demo._bindings.aliases.foreign_return import get_foo as foreign_class_alias

from . import (
foreign_arg,
foreign_attr,
foreign_class_member,
foreign_method_arg,
foreign_method_return,
foreign_return,
missing_self_arg,
)

__all__: list[str] = [
"Color",
"Dummy",
"foreign_arg",
"foreign_attr",
"foreign_class_alias",
"foreign_class_member",
"foreign_enum_default",
"foreign_method_arg",
"foreign_method_return",
"foreign_return",
"foreign_type_alias",
"func",
"local_func_alias",
"local_type_alias",
"missing_self_arg",
"random",
]

class Dummy:
linalg = numpy.linalg

class Color:
pass

def foreign_enum_default(
color: typing.Any = demo._bindings.enum.ConsoleForegroundColor.Blue,
) -> None: ...
def func(arg0: typing.SupportsInt | typing.SupportsIndex) -> int: ...

local_type_alias = Color
local_func_alias = func
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["set_foo"]

def set_foo(arg0: demo._bindings.classes.Foo) -> int: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["value"]
value: demo._bindings.classes.Foo # value = <demo._bindings.classes.Foo object>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from __future__ import annotations

import typing

import demo._bindings.classes

__all__: list[str] = ["Bar1"]

class Bar1:
foo: typing.ClassVar[
demo._bindings.classes.Foo
] # value = <demo._bindings.classes.Foo object>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["Bar2"]

class Bar2:
def set_foo(self, arg0: demo._bindings.classes.Foo) -> int: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["Bar3"]

class Bar3:
@staticmethod
def get_foo() -> demo._bindings.classes.Foo: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["get_foo"]

def get_foo() -> demo._bindings.classes.Foo: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from __future__ import annotations

import demo._bindings.classes

__all__: list[str] = ["Bar4"]

class Bar4:
def set_foo(self: demo._bindings.classes.Foo) -> int: ...
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from __future__ import annotations

import typing

__all__: list[str] = [
"CppException",
"Derived",
"Foo",
"MyBase",
"Outer",
"ParIter",
"ParIterBase",
"ParticleContainer",
]

class Outer:
class Inner:
class NestedEnum:
"""
Members:

ONE

TWO
"""

ONE: typing.ClassVar[Outer.Inner.NestedEnum] # value = <NestedEnum.ONE: 1>
TWO: typing.ClassVar[Outer.Inner.NestedEnum] # value = <NestedEnum.TWO: 2>
__members__: typing.ClassVar[
dict[str, Outer.Inner.NestedEnum]
] # value = {'ONE': <NestedEnum.ONE: 1>, 'TWO': <NestedEnum.TWO: 2>}
def __eq__(self, other: typing.Any) -> bool: ...
def __getstate__(self) -> int: ...
def __hash__(self) -> int: ...
def __index__(self) -> int: ...
def __init__(
self, value: typing.SupportsInt | typing.SupportsIndex
) -> None: ...
def __int__(self) -> int: ...
def __ne__(self, other: typing.Any) -> bool: ...
def __repr__(self) -> str: ...
def __setstate__(
self, state: typing.SupportsInt | typing.SupportsIndex
) -> None: ...
def __str__(self) -> str: ...
@property
def name(self) -> str: ...
@property
def value(self) -> int: ...

value: Outer.Inner.NestedEnum

inner: Outer.Inner

class MyBase:
class Inner:
pass

name: str

class Derived(MyBase):
@property
def count(self) -> int: ...
@count.setter
def count(self, arg0: typing.SupportsInt | typing.SupportsIndex) -> None: ...

class Foo:
class FooChild:
def __init__(self) -> None: ...
def g(self) -> None: ...

def __init__(self) -> None: ...
def f(self) -> None: ...

class ParIterBase:
@property
def level(self) -> int: ...
@level.setter
def level(self, arg0: typing.SupportsInt | typing.SupportsIndex) -> None: ...

class ParIter(ParIterBase):
def __init__(
self,
particle_container: ParticleContainer,
level: typing.SupportsInt | typing.SupportsIndex,
) -> None: ...

class ParticleContainer:
name: str
Iterator = ParIter
def process(self, arg0: ParIter) -> None: ...

class CppException(Exception):
pass
Loading
Loading