pybind / pybind/pybind11-stubgen
Type hint issues for enum after updating to pybind11 3.0.1
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 361
- Forks
- 75
- PR merge metrics
- No merged PRs in 30d
Description
Hi,
I have some issues after updating pybind11 from v2.13.6 to v3.0.1.
I tried to reduce everything to a small example.
I'm using conan to get pybind11.
My python requirements are contained in requirements.txt
conan==2.22.2
pybind11_stubgen==2.5.5
My conan requirements are contained in conanfile.txt
[requires]
pybind11/3.0.1
[layout]
cmake_layout
[generators]
CMakeDeps
CMakeToolchain
My CMakeLists.txt reads
cmake_minimum_required(VERSION 3.31)
project(pybind11test CXX)
find_package(pybind11)
pybind11_add_module(pybind11test pybind11test.cpp)
And my source file pybind11test.cpp is
#include <pybind11/pybind11.h>
#include <pybind11/native_enum.h>
#include <pybind11/stl.h>
namespace py = pybind11;
using namespace py::literals;
enum class Enum1
{
VALUE1 = 0,
VALUE2 = 1
};
struct Struct
{
size_t value = 100;
std::optional<Enum1> enum1{};
};
PYBIND11_MODULE(pybind11test, pybind11test_module)
{
py::native_enum<Enum1>(pybind11test_module, "Enum1", "enum.Enum")
.value("VALUE1", Enum1::VALUE1)
.value("VALUE2", Enum1::VALUE2)
.finalize();
// py::enum_<Enum1>(pybind11test_module, "Enum1")
// .value("VALUE1", Enum1::VALUE1)
// .value("VALUE2", Enum1::VALUE2);
py::class_<Struct>(pybind11test_module, "Struct")
.def(py::init<>())
.def(py::init<size_t>(), "Struct", "value"_a)
.def(py::init<size_t, Enum1>(), "Struct", "value"_a, "enum1"_a)
.def_readwrite("value", &Struct::value)
.def_readwrite("enum1", &Struct::enum1);
}
If I run the following commands:
pip install -r requirements.txt
conan install .
cmake --preset conan-release
cmake --build --preset conan-release
PYTHONPATH=./build/Release/ pybind11-stubgen pybind11test
I get the following stubs file:
from __future__ import annotations
import enum
import typing
__all__: list[str] = ['Enum1', 'Struct']
class Enum1(enum.Enum):
VALUE1: typing.ClassVar[Enum1] # value = <Enum1.VALUE1: 0>
VALUE2: typing.ClassVar[Enum1] # value = <Enum1.VALUE2: 1>
class Struct:
enum1: pybind11test.Enum1 | None
@typing.overload
def __init__(self) -> None:
...
@typing.overload
def __init__(self, value: typing.SupportsInt) -> None:
"""
Struct
"""
@typing.overload
def __init__(self, value: typing.SupportsInt, enum1: Enum1) -> None:
"""
Struct
"""
@property
def value(self) -> int:
...
@value.setter
def value(self, arg0: typing.SupportsInt) -> None:
...
My issue is the that member variable enum1 of struct Struct is getting type pybind11test.Enum1 - which mypy complains about. The argument to the constructor in Struct gets the type Enum1 which seems to be okay.
If I run everything which the previous pybind11 version (change version in conanfile.txt and use the old enum-wrapping mechanism) I get:
from __future__ import annotations
import typing
__all__: list[str] = ['Enum1', 'Struct']
class Enum1:
"""
Members:
VALUE1
VALUE2
"""
VALUE1: typing.ClassVar[Enum1] # value = <Enum1.VALUE1: 0>
VALUE2: typing.ClassVar[Enum1] # value = <Enum1.VALUE2: 1>
__members__: typing.ClassVar[dict[str, Enum1]] # value = {'VALUE1': <Enum1.VALUE1: 0>, 'VALUE2': <Enum1.VALUE2: 1>}
def __eq__(self, other: typing.Any) -> bool:
...
def __getstate__(self) -> int:
...
def __hash__(self) -> int:
...
def __index__(self) -> int:
...
def __init__(self, value: int) -> None:
...
def __int__(self) -> int:
...
def __ne__(self, other: typing.Any) -> bool:
...
def __repr__(self) -> str:
...
def __setstate__(self, state: int) -> None:
...
def __str__(self) -> str:
...
@property
def name(self) -> str:
...
@property
def value(self) -> int:
...
class Struct:
enum1: Enum1 | None
value: int
@typing.overload
def __init__(self) -> None:
...
@typing.overload
def __init__(self, value: int) -> None:
"""
Struct
"""
@typing.overload
def __init__(self, value: int, enum1: Enum1) -> None:
"""
Struct
"""
Here the enum contains more methods, but the types in the struct are more consistent (in my opinion) and also mypy doesn't complain.
Is there a way to configure stubgen so that it uses the shorter type hint for both function arguments and member variables? This would help me here.
If I use the new pybind11 version with the old enum_-mechanism I get the old stubs except for the type of the member variable - this remains pybind11test.Enum1.
I also get another mypy-error with the new type hints:
Detected enum "pybind11test.Enum1" in a type stub with zero members. There is a chance this is due to a recent change in the semantics of enum membership. If so, use
member = valueto mark an enum member, instead ofmember: type
Mypy points me to https://typing.python.org/en/latest/spec/enums.html#defining-members - maybe this could also be of interest.
Thanks and best regards
oz
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the report using requirements.txt, conanfile.txt, CMakeLists.txt, and pybind11test.cpp, then inspect the generated pybind11test stubs alongside the reported mypy errors. Trace how native_enum and class member annotations are rendered; done means enum references are consistent between constructor arguments and members, and the generated stub no longer triggers the zero-member enum diagnostic.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp, python
- Domain
- developer-experience, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100