Skip to content

Commit 2450152

Browse files
committed
Add tests for py3 enums
1 parent 1f1bc0e commit 2450152

File tree

3 files changed

+77
-1
lines changed

3 files changed

+77
-1
lines changed

tests/conftest.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ def pytest_namespace():
200200
except ImportError:
201201
have_eigen = False
202202
pypy = platform.python_implementation() == "PyPy"
203+
py3 = sys.version_info.major == 3
203204

204205
skipif = pytest.mark.skipif
205206
return {
@@ -211,7 +212,8 @@ def pytest_namespace():
211212
'requires_eigen_and_scipy': skipif(not have_eigen or not scipy,
212213
reason="eigen and/or scipy are not installed"),
213214
'unsupported_on_pypy': skipif(pypy, reason="unsupported on PyPy"),
214-
'gc_collect': gc_collect
215+
'gc_collect': gc_collect,
216+
'requires_py3': skipif(not py3, reason='requires Python 3')
215217
}
216218

217219

tests/test_enum.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,23 @@ class ClassWithUnscopedEnum {
3737
}
3838
};
3939

40+
enum Py3Enum {
41+
A = -42,
42+
B = 1,
43+
C = 42,
44+
};
45+
46+
enum class Py3EnumScoped : short {
47+
X = 10,
48+
Y = -1024,
49+
};
50+
51+
enum class Py3EnumEmpty {};
52+
53+
enum class Py3EnumNonUnique {
54+
X = 1
55+
};
56+
4057
std::string test_scoped_enum(ScopedEnum z) {
4158
return "ScopedEnum::" + std::string(z == ScopedEnum::Two ? "Two" : "Three");
4259
}
@@ -59,6 +76,33 @@ test_initializer enums([](py::module &m) {
5976
.value("Execute", Flags::Execute)
6077
.export_values();
6178

79+
#if PY_VERSION_HEX >= 0x03000000
80+
py::py3_enum<Py3EnumEmpty>(m, "Py3EnumEmpty");
81+
82+
py::py3_enum<Py3Enum>(m, "Py3Enum")
83+
.value("A", Py3Enum::A)
84+
.value("B", Py3Enum::B)
85+
.value("C", Py3Enum::C);
86+
87+
py::py3_enum<Py3EnumScoped>(m, "Py3EnumScoped")
88+
.value("X", Py3EnumScoped::X)
89+
.value("Y", Py3EnumScoped::Y);
90+
91+
m.def("make_py3_enum", [](bool x) {
92+
return x ? Py3EnumScoped::X : Py3EnumScoped::Y;
93+
});
94+
95+
m.def("take_py3_enum", [](Py3EnumScoped x) {
96+
return x == Py3EnumScoped::X;
97+
});
98+
99+
m.def("non_unique_py3_enum", [=]() {
100+
py::py3_enum<Py3EnumNonUnique>(m, "Py3EnumNonUnique")
101+
.value("X", Py3EnumNonUnique::X)
102+
.value("Y", Py3EnumNonUnique::X);
103+
});
104+
#endif
105+
62106
py::class_<ClassWithUnscopedEnum> exenum_class(m, "ClassWithUnscopedEnum");
63107
exenum_class.def_static("test_function", &ClassWithUnscopedEnum::test_function);
64108
py::enum_<ClassWithUnscopedEnum::EMode>(exenum_class, "EMode")

tests/test_enum.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,3 +115,33 @@ def test_binary_operators():
115115
state2 = ~state
116116
assert state2 == -7
117117
assert int(state ^ state2) == -1
118+
119+
120+
@pytest.requires_py3
121+
def test_py3_enum():
122+
from pybind11_tests import (
123+
Py3Enum, Py3EnumEmpty, Py3EnumScoped,
124+
make_py3_enum, take_py3_enum, non_unique_py3_enum
125+
)
126+
127+
from enum import IntEnum
128+
129+
expected = {
130+
Py3Enum: [('A', -42), ('B', 1), ('C', 42)],
131+
Py3EnumEmpty: [],
132+
Py3EnumScoped: [('X', 10), ('Y', -1024)]
133+
}
134+
135+
for tp, entries in expected.items():
136+
assert issubclass(tp, IntEnum)
137+
assert sorted(tp.__members__.items()) == entries
138+
139+
assert make_py3_enum(True) is Py3EnumScoped.X
140+
assert make_py3_enum(False) is Py3EnumScoped.Y
141+
142+
assert take_py3_enum(Py3EnumScoped.X)
143+
assert not take_py3_enum(Py3EnumScoped.Y)
144+
145+
with pytest.raises(ValueError) as excinfo:
146+
non_unique_py3_enum()
147+
assert 'duplicate values found' in str(excinfo.value)

0 commit comments

Comments
 (0)