mirror of
https://github.com/pybind/pybind11.git
synced 2026-06-11 00:38:47 +00:00
Fix missing pythonic type hints for native_enum (#5619)
* fix missing pythonic type hints for native_enum * Fix __qualname__ in native_enum * Rename `enum class native` → `func_sig_rendering`. Add to `ENUM_TYPES_AND_MEMBERS`. Move new code around to fit in more organically. "native" is used in so many places here, it would be very difficult to pin-point where the specific enum type is used. Similarly, "value" is difficult to pin-point. Changed to "nested_value". --------- Co-authored-by: Bryn Lloyd <12702862+dyollb@users.noreply.github.com> Co-authored-by: Ralf W. Grosse-Kunstleve <rgrossekunst@nvidia.com>
This commit is contained in:
@@ -182,6 +182,10 @@ inline void native_enum_data::finalize() {
|
||||
if (module_name) {
|
||||
py_enum.attr("__module__") = module_name;
|
||||
}
|
||||
if (hasattr(parent_scope, "__qualname__")) {
|
||||
const auto parent_qualname = parent_scope.attr("__qualname__").cast<std::string>();
|
||||
py_enum.attr("__qualname__") = str(parent_qualname + "." + enum_name.cast<std::string>());
|
||||
}
|
||||
parent_scope.attr(enum_name) = py_enum;
|
||||
if (export_values_flag) {
|
||||
for (auto member : members) {
|
||||
|
||||
@@ -160,6 +160,9 @@ inline std::string generate_function_signature(const char *type_caster_name_fiel
|
||||
handle th((PyObject *) tinfo->type);
|
||||
signature += th.attr("__module__").cast<std::string>() + "."
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
} else if (auto th = detail::global_internals_native_enum_type_map_get_item(*t)) {
|
||||
signature += th.attr("__module__").cast<std::string>() + "."
|
||||
+ th.attr("__qualname__").cast<std::string>();
|
||||
} else if (func_rec->is_new_style_constructor && arg_index == 0) {
|
||||
// A new-style `__init__` takes `self` as `value_and_holder`.
|
||||
// Rewrite it to the proper class type.
|
||||
|
||||
@@ -29,6 +29,8 @@ enum class member_doc { mem0, mem1, mem2 };
|
||||
|
||||
struct class_with_enum {
|
||||
enum class in_class { one, two };
|
||||
|
||||
in_class nested_value = in_class::one;
|
||||
};
|
||||
|
||||
// https://github.com/protocolbuffers/protobuf/blob/d70b5c5156858132decfdbae0a1103e6a5cb1345/src/google/protobuf/generated_enum_util.h#L52-L53
|
||||
@@ -40,6 +42,8 @@ enum some_proto_enum : int { Zero, One };
|
||||
template <>
|
||||
struct is_proto_enum<some_proto_enum> : std::true_type {};
|
||||
|
||||
enum class func_sig_rendering {};
|
||||
|
||||
} // namespace test_native_enum
|
||||
|
||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
||||
@@ -124,11 +128,20 @@ TEST_SUBMODULE(native_enum, m) {
|
||||
.value("two", class_with_enum::in_class::two)
|
||||
.finalize();
|
||||
|
||||
py_class_with_enum.def(py::init())
|
||||
.def_readwrite("nested_value", &class_with_enum::nested_value);
|
||||
|
||||
m.def("isinstance_color", [](const py::object &obj) { return py::isinstance<color>(obj); });
|
||||
|
||||
m.def("pass_color", [](color e) { return static_cast<int>(e); });
|
||||
m.def("return_color", [](int i) { return static_cast<color>(i); });
|
||||
|
||||
py::native_enum<func_sig_rendering>(m, "func_sig_rendering", "enum.Enum").finalize();
|
||||
m.def(
|
||||
"pass_and_return_func_sig_rendering",
|
||||
[](func_sig_rendering e) { return e; },
|
||||
py::arg("e"));
|
||||
|
||||
m.def("pass_some_proto_enum", [](some_proto_enum) { return py::none(); });
|
||||
m.def("return_some_proto_enum", []() { return some_proto_enum::Zero; });
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ MEMBER_DOC_MEMBERS = (
|
||||
("mem2", 2),
|
||||
)
|
||||
|
||||
FUNC_SIG_RENDERING_MEMBERS = ()
|
||||
|
||||
ENUM_TYPES_AND_MEMBERS = (
|
||||
(m.smallenum, SMALLENUM_MEMBERS),
|
||||
(m.color, COLOR_MEMBERS),
|
||||
@@ -62,6 +64,7 @@ ENUM_TYPES_AND_MEMBERS = (
|
||||
(m.flags_uint, FLAGS_UINT_MEMBERS),
|
||||
(m.export_values, EXPORT_VALUES_MEMBERS),
|
||||
(m.member_doc, MEMBER_DOC_MEMBERS),
|
||||
(m.func_sig_rendering, FUNC_SIG_RENDERING_MEMBERS),
|
||||
(m.class_with_enum.in_class, CLASS_WITH_ENUM_IN_CLASS_MEMBERS),
|
||||
)
|
||||
|
||||
@@ -84,15 +87,10 @@ def test_enum_members(enum_type, members):
|
||||
def test_pickle_roundtrip(enum_type, members):
|
||||
for name, _ in members:
|
||||
orig = enum_type[name]
|
||||
if enum_type is m.class_with_enum.in_class:
|
||||
# This is a general pickle limitation.
|
||||
with pytest.raises(pickle.PicklingError):
|
||||
pickle.dumps(orig)
|
||||
else:
|
||||
# This only works if __module__ is correct.
|
||||
serialized = pickle.dumps(orig)
|
||||
restored = pickle.loads(serialized)
|
||||
assert restored == orig
|
||||
# This only works if __module__ is correct.
|
||||
serialized = pickle.dumps(orig)
|
||||
restored = pickle.loads(serialized)
|
||||
assert restored == orig
|
||||
|
||||
|
||||
@pytest.mark.parametrize("enum_type", [m.flags_uchar, m.flags_uint])
|
||||
@@ -139,7 +137,7 @@ def test_pass_color_success():
|
||||
def test_pass_color_fail():
|
||||
with pytest.raises(TypeError) as excinfo:
|
||||
m.pass_color(None)
|
||||
assert "test_native_enum::color" in str(excinfo.value)
|
||||
assert "pybind11_tests.native_enum.color" in str(excinfo.value)
|
||||
|
||||
|
||||
def test_return_color_success():
|
||||
@@ -155,6 +153,22 @@ def test_return_color_fail():
|
||||
assert str(excinfo_cast.value) == str(excinfo_direct.value)
|
||||
|
||||
|
||||
def test_property_type_hint():
|
||||
prop = m.class_with_enum.__dict__["nested_value"]
|
||||
assert isinstance(prop, property)
|
||||
assert prop.fget.__doc__.startswith(
|
||||
"(self: pybind11_tests.native_enum.class_with_enum)"
|
||||
" -> pybind11_tests.native_enum.class_with_enum.in_class"
|
||||
)
|
||||
|
||||
|
||||
def test_func_sig_rendering():
|
||||
assert m.pass_and_return_func_sig_rendering.__doc__.startswith(
|
||||
"pass_and_return_func_sig_rendering(e: pybind11_tests.native_enum.func_sig_rendering)"
|
||||
" -> pybind11_tests.native_enum.func_sig_rendering"
|
||||
)
|
||||
|
||||
|
||||
def test_type_caster_enum_type_enabled_false():
|
||||
# This is really only a "does it compile" test.
|
||||
assert m.pass_some_proto_enum(None) is None
|
||||
|
||||
Reference in New Issue
Block a user