Skip to content

Commit 2cbed02

Browse files
enum: Add Enum.value property
1 parent 79b0e2c commit 2cbed02

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

include/pybind11/pybind11.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "options.h"
4747
#include "detail/class.h"
4848
#include "detail/init.h"
49+
#include "detail/typeid.h"
4950

5051
#include <memory>
5152
#include <vector>
@@ -1569,6 +1570,23 @@ inline str enum_name(handle arg) {
15691570
return "???";
15701571
}
15711572

1573+
template <typename Type, typename Scalar, bool is_convertible = true>
1574+
struct enum_value {
1575+
static Scalar run(handle arg) {
1576+
Type value = pybind11::cast<Type>(arg);
1577+
return static_cast<Scalar>(value);
1578+
}
1579+
};
1580+
1581+
template <typename Type, typename Scalar>
1582+
struct enum_value<Type, Scalar, false> {
1583+
static Scalar run(handle) {
1584+
throw pybind11::cast_error(
1585+
"Enum for " + type_id<Type>() + " is not convertible to " +
1586+
type_id<Scalar>());
1587+
}
1588+
};
1589+
15721590
struct enum_base {
15731591
enum_base(handle base, handle parent) : m_base(base), m_parent(parent) { }
15741592

@@ -1728,8 +1746,14 @@ template <typename Type> class enum_ : public class_<Type> {
17281746
: class_<Type>(scope, name, extra...), m_base(*this, scope) {
17291747
constexpr bool is_arithmetic = detail::any_of<std::is_same<arithmetic, Extra>...>::value;
17301748
constexpr bool is_convertible = std::is_convertible<Type, Scalar>::value;
1749+
auto property = handle((PyObject *) &PyProperty_Type);
17311750
m_base.init(is_arithmetic, is_convertible);
17321751

1752+
attr("value") = property(
1753+
cpp_function(
1754+
&detail::enum_value<Type, Scalar, is_convertible>::run,
1755+
pybind11::name("value"), is_method(*this)));
1756+
17331757
def(init([](Scalar i) { return static_cast<Type>(i); }), arg("value"));
17341758
def("__int__", [](Type value) { return (Scalar) value; });
17351759
#if PY_MAJOR_VERSION < 3

tests/test_enum.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,24 @@ def test_unscoped_enum():
1313

1414
# name property
1515
assert m.UnscopedEnum.EOne.name == "EOne"
16+
assert m.UnscopedEnum.EOne.value == 1
1617
assert m.UnscopedEnum.ETwo.name == "ETwo"
17-
assert m.EOne.name == "EOne"
18-
# name readonly
18+
assert m.UnscopedEnum.ETwo.value == 2
19+
assert m.EOne is m.UnscopedEnum.EOne
20+
# name, value readonly
1921
with pytest.raises(AttributeError):
2022
m.UnscopedEnum.EOne.name = ""
21-
# name returns a copy
22-
foo = m.UnscopedEnum.EOne.name
23-
foo = "bar"
23+
with pytest.raises(AttributeError):
24+
m.UnscopedEnum.EOne.value = 10
25+
# name, value returns a copy
26+
# TODO: Neither the name nor value tests actually check against aliasing.
27+
# Use a mutable type that has reference semantics.
28+
nonaliased_name = m.UnscopedEnum.EOne.name
29+
nonaliased_name = "bar" # noqa: F841
2430
assert m.UnscopedEnum.EOne.name == "EOne"
31+
nonaliased_value = m.UnscopedEnum.EOne.value
32+
nonaliased_value = 10 # noqa: F841
33+
assert m.UnscopedEnum.EOne.value == 1
2534

2635
# __members__ property
2736
assert m.UnscopedEnum.__members__ == {
@@ -33,8 +42,8 @@ def test_unscoped_enum():
3342
with pytest.raises(AttributeError):
3443
m.UnscopedEnum.__members__ = {}
3544
# __members__ returns a copy
36-
foo = m.UnscopedEnum.__members__
37-
foo["bar"] = "baz"
45+
nonaliased_members = m.UnscopedEnum.__members__
46+
nonaliased_members["bar"] = "baz"
3847
assert m.UnscopedEnum.__members__ == {
3948
"EOne": m.UnscopedEnum.EOne,
4049
"ETwo": m.UnscopedEnum.ETwo,

0 commit comments

Comments
 (0)