Skip to content

Commit 77a72c0

Browse files
auscompgeekYannickJadoul
authored andcommitted
Add enum value to enum repr
This changes enum reprs to look like `<Enum.name: value>` similarly to the Python enum module. This keeps the str of enums as `Enum.name`, like the Python enum module.
1 parent 87828c7 commit 77a72c0

File tree

2 files changed

+24
-17
lines changed

2 files changed

+24
-17
lines changed

include/pybind11/pybind11.h

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1481,6 +1481,16 @@ detail::initimpl::pickle_factory<GetState, SetState> pickle(GetState &&g, SetSta
14811481
}
14821482

14831483
PYBIND11_NAMESPACE_BEGIN(detail)
1484+
1485+
inline str enum_name(handle arg) {
1486+
dict entries = arg.get_type().attr("__entries");
1487+
for (const auto &kv : entries) {
1488+
if (handle(kv.second[int_(0)]).equal(arg))
1489+
return pybind11::str(kv.first);
1490+
}
1491+
return "???";
1492+
}
1493+
14841494
struct enum_base {
14851495
enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { }
14861496

@@ -1490,29 +1500,21 @@ struct enum_base {
14901500
auto static_property = handle((PyObject *) get_internals().static_property_type);
14911501

14921502
m_base.attr("__repr__") = cpp_function(
1493-
[](handle arg) -> str {
1503+
[](object arg) -> str {
14941504
handle type = type::handle_of(arg);
14951505
object type_name = type.attr("__name__");
1496-
dict entries = type.attr("__entries");
1497-
for (const auto &kv : entries) {
1498-
object other = kv.second[int_(0)];
1499-
if (other.equal(arg))
1500-
return pybind11::str("{}.{}").format(type_name, kv.first);
1501-
}
1502-
return pybind11::str("{}.???").format(type_name);
1506+
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
15031507
}, name("__repr__"), is_method(m_base)
15041508
);
15051509

1506-
m_base.attr("name") = property(cpp_function(
1510+
m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base)));
1511+
1512+
m_base.attr("__str__") = cpp_function(
15071513
[](handle arg) -> str {
1508-
dict entries = type::handle_of(arg).attr("__entries");
1509-
for (const auto &kv : entries) {
1510-
if (handle(kv.second[int_(0)]).equal(arg))
1511-
return pybind11::str(kv.first);
1512-
}
1513-
return "???";
1514+
object type_name = type::handle_of(arg).attr("__name__");
1515+
return pybind11::str("{}.{}").format(type_name, enum_name(arg));
15141516
}, name("name"), is_method(m_base)
1515-
));
1517+
);
15161518

15171519
m_base.attr("__doc__") = static_property(cpp_function(
15181520
[](handle arg) -> std::string {

tests/test_enum.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ def test_unscoped_enum():
77
assert str(m.UnscopedEnum.EOne) == "UnscopedEnum.EOne"
88
assert str(m.UnscopedEnum.ETwo) == "UnscopedEnum.ETwo"
99
assert str(m.EOne) == "UnscopedEnum.EOne"
10+
assert repr(m.UnscopedEnum.EOne) == "<UnscopedEnum.EOne: 1>"
11+
assert repr(m.UnscopedEnum.ETwo) == "<UnscopedEnum.ETwo: 2>"
12+
assert repr(m.EOne) == "<UnscopedEnum.EOne: 1>"
1013

1114
# name property
1215
assert m.UnscopedEnum.EOne.name == "EOne"
@@ -143,6 +146,8 @@ def test_scoped_enum():
143146
def test_implicit_conversion():
144147
assert str(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "EMode.EFirstMode"
145148
assert str(m.ClassWithUnscopedEnum.EFirstMode) == "EMode.EFirstMode"
149+
assert repr(m.ClassWithUnscopedEnum.EMode.EFirstMode) == "<EMode.EFirstMode: 1>"
150+
assert repr(m.ClassWithUnscopedEnum.EFirstMode) == "<EMode.EFirstMode: 1>"
146151

147152
f = m.ClassWithUnscopedEnum.test_function
148153
first = m.ClassWithUnscopedEnum.EFirstMode
@@ -167,7 +172,7 @@ def test_implicit_conversion():
167172
x[f(first)] = 3
168173
x[f(second)] = 4
169174
# Hashing test
170-
assert str(x) == "{EMode.EFirstMode: 3, EMode.ESecondMode: 4}"
175+
assert repr(x) == "{<EMode.EFirstMode: 1>: 3, <EMode.ESecondMode: 2>: 4}"
171176

172177

173178
def test_binary_operators():

0 commit comments

Comments
 (0)