From 5e6ec496522b313e34af3de91f6c0565f68e3552 Mon Sep 17 00:00:00 2001 From: David Vo Date: Sun, 20 Sep 2020 09:12:19 +1000 Subject: [PATCH] Add enum value to enum repr (#2126) This changes enum reprs to look like `` similarly to the Python enum module. This keeps the str of enums as `Enum.name`, like the Python enum module. --- include/pybind11/pybind11.h | 34 ++++++++++++++++++---------------- tests/test_enum.py | 7 ++++++- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 7c5286a92..c1d17f7ee 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1486,6 +1486,16 @@ detail::initimpl::pickle_factory pickle(GetState &&g, SetSta } PYBIND11_NAMESPACE_BEGIN(detail) + +inline str enum_name(handle arg) { + dict entries = arg.get_type().attr("__entries"); + for (const auto &kv : entries) { + if (handle(kv.second[int_(0)]).equal(arg)) + return pybind11::str(kv.first); + } + return "???"; +} + struct enum_base { enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { } @@ -1495,29 +1505,21 @@ struct enum_base { auto static_property = handle((PyObject *) get_internals().static_property_type); m_base.attr("__repr__") = cpp_function( - [](handle arg) -> str { + [](object arg) -> str { handle type = type::handle_of(arg); object type_name = type.attr("__name__"); - dict entries = type.attr("__entries"); - for (auto kv : entries) { - object other = kv.second[int_(0)]; - if (other.equal(arg)) - return pybind11::str("{}.{}").format(type_name, kv.first); - } - return pybind11::str("{}.???").format(type_name); + return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg)); }, name("__repr__"), is_method(m_base) ); - m_base.attr("name") = property(cpp_function( + m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base))); + + m_base.attr("__str__") = cpp_function( [](handle arg) -> str { - dict entries = type::handle_of(arg).attr("__entries"); - for (auto kv : entries) { - if (handle(kv.second[int_(0)]).equal(arg)) - return pybind11::str(kv.first); - } - return "???"; + object type_name = type::handle_of(arg).attr("__name__"); + return pybind11::str("{}.{}").format(type_name, enum_name(arg)); }, name("name"), is_method(m_base) - )); + ); m_base.attr("__doc__") = static_property(cpp_function( [](handle arg) -> std::string { diff --git a/tests/test_enum.py b/tests/test_enum.py index bfaa193e9..46cb37946 100644 --- a/tests/test_enum.py +++ b/tests/test_enum.py @@ -7,6 +7,9 @@ def test_unscoped_enum(): assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne" assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo" assert str(m.EOne) == "UnscopedEnum.EOne" + assert repr(m.UnscopedEnum.EOne) == "" + assert repr(m.UnscopedEnum.ETwo) == "" + assert repr(m.EOne) == "" # name property assert m.UnscopedEnum.EOne.name == "EOne" @@ -143,6 +146,8 @@ def test_scoped_enum(): def test_implicit_conversion(): assert str(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode" assert str(m.ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode" + assert repr(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "" + assert repr(m.ClassWithUnscopedEnum.EFirstMode) == "" f = m.ClassWithUnscopedEnum.test_function first = m.ClassWithUnscopedEnum.EFirstMode @@ -167,7 +172,7 @@ def test_implicit_conversion(): x[f(first)] = 3 x[f(second)] = 4 # Hashing test - assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}" + assert repr(x) == "{: 3, : 4}" def test_binary_operators():