1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
from __future__ import annotations
import enum
import pickle
import pytest
import env
from pybind11_tests import native_enum as m
SMALLENUM_MEMBERS = (
("a", 0),
("b", 1),
("c", 2),
)
COLOR_MEMBERS = (
("red", 0),
("yellow", 1),
("green", 20),
("blue", 21),
)
ALTITUDE_MEMBERS = (
("high", "h"),
("low", "l"),
)
FLAGS_UCHAR_MEMBERS = (
("bit0", 0x1),
("bit1", 0x2),
("bit2", 0x4),
)
FLAGS_UINT_MEMBERS = (
("bit0", 0x1),
("bit1", 0x2),
("bit2", 0x4),
)
CLASS_WITH_ENUM_IN_CLASS_MEMBERS = (
("one", 0),
("two", 1),
)
EXPORT_VALUES_MEMBERS = (
("exv0", 0),
("exv1", 1),
)
MEMBER_DOC_MEMBERS = (
("mem0", 0),
("mem1", 1),
("mem2", 2),
)
FUNC_SIG_RENDERING_MEMBERS = ()
ENUM_TYPES_AND_MEMBERS = (
(m.smallenum, SMALLENUM_MEMBERS),
(m.color, COLOR_MEMBERS),
(m.altitude, ALTITUDE_MEMBERS),
(m.flags_uchar, FLAGS_UCHAR_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),
)
ENUM_TYPES = [_[0] for _ in ENUM_TYPES_AND_MEMBERS]
@pytest.mark.parametrize("enum_type", ENUM_TYPES)
def test_enum_type(enum_type):
assert isinstance(enum_type, enum.EnumMeta)
assert enum_type.__module__ == m.__name__
@pytest.mark.parametrize(("enum_type", "members"), ENUM_TYPES_AND_MEMBERS)
def test_enum_members(enum_type, members):
for name, value in members:
assert enum_type[name].value == value
@pytest.mark.parametrize(("enum_type", "members"), ENUM_TYPES_AND_MEMBERS)
def test_pickle_roundtrip(enum_type, members):
for name, _ in members:
orig = enum_type[name]
# 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])
def test_enum_flag(enum_type):
bits02 = enum_type.bit0 | enum_type.bit2
assert enum_type.bit0 in bits02
assert enum_type.bit1 not in bits02
assert enum_type.bit2 in bits02
def test_export_values():
assert m.exv0 is m.export_values.exv0
assert m.exv1 is m.export_values.exv1
def test_class_doc():
pure_native = enum.IntEnum("pure_native", (("mem", 0),))
assert m.smallenum.__doc__ == "doc smallenum"
assert m.color.__doc__ == pure_native.__doc__
def test_member_doc():
pure_native = enum.IntEnum("pure_native", (("mem", 0),))
assert m.member_doc.mem0.__doc__ == "docA"
assert m.member_doc.mem1.__doc__ == pure_native.mem.__doc__
assert m.member_doc.mem2.__doc__ == "docC"
def test_pybind11_isinstance_color():
for name, _ in COLOR_MEMBERS:
assert m.isinstance_color(m.color[name])
assert not m.isinstance_color(m.color)
for name, _ in SMALLENUM_MEMBERS:
assert not m.isinstance_color(m.smallenum[name])
assert not m.isinstance_color(m.smallenum)
assert not m.isinstance_color(None)
def test_pass_color_success():
for name, value in COLOR_MEMBERS:
assert m.pass_color(m.color[name]) == value
def test_pass_color_fail():
with pytest.raises(TypeError) as excinfo:
m.pass_color(None)
assert "pybind11_tests.native_enum.color" in str(excinfo.value)
def test_return_color_success():
for name, value in COLOR_MEMBERS:
assert m.return_color(value) == m.color[name]
def test_return_color_fail():
with pytest.raises(ValueError) as excinfo_direct:
m.color(2)
with pytest.raises(ValueError) as excinfo_cast:
m.return_color(2)
assert str(excinfo_cast.value) == str(excinfo_direct.value)
def test_return_color_ptr():
assert m.return_color_const_ptr() == m.color.red
assert m.return_color_mutbl_ptr() == m.color.green
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
assert m.return_some_proto_enum() is None
@pytest.mark.skipif(isinstance(m.obj_cast_color_ptr, str), reason=m.obj_cast_color_ptr)
def test_obj_cast_color_ptr():
with pytest.raises(RuntimeError) as excinfo:
m.obj_cast_color_ptr(m.color.red)
assert str(excinfo.value) == "Unable to cast native enum type to reference"
def test_py_cast_color_handle():
for name, value in COLOR_MEMBERS:
assert m.py_cast_color_handle(m.color[name]) == value
def test_exercise_import_or_getattr_leading_dot():
with pytest.raises(ValueError) as excinfo:
m.exercise_import_or_getattr(m, ".")
assert str(excinfo.value) == "Invalid fully-qualified name `.` (native_type_name)"
def test_exercise_import_or_getattr_bad_top_level():
with pytest.raises(ImportError) as excinfo:
m.exercise_import_or_getattr(m, "NeVeRLaNd")
assert (
str(excinfo.value)
== "Failed to import top-level module `NeVeRLaNd` (native_type_name)"
)
def test_exercise_import_or_getattr_dot_dot():
with pytest.raises(ValueError) as excinfo:
m.exercise_import_or_getattr(m, "enum..")
assert (
str(excinfo.value) == "Invalid fully-qualified name `enum..` (native_type_name)"
)
def test_exercise_import_or_getattr_bad_enum_attr():
with pytest.raises(ImportError) as excinfo:
m.exercise_import_or_getattr(m, "enum.NoNeXiStInG")
lines = str(excinfo.value).splitlines()
assert len(lines) >= 5
assert (
lines[0]
== "Failed to import or getattr `NoNeXiStInG` from `enum` (native_type_name)"
)
assert lines[1] == "-------- getattr exception --------"
ix = lines.index("-------- import exception --------")
assert ix >= 3
assert len(lines) > ix + 0
def test_native_enum_data_missing_finalize_error_message():
msg = m.native_enum_data_missing_finalize_error_message("Fake")
assert msg == 'pybind11::native_enum<...>("Fake", ...): MISSING .finalize()'
@pytest.mark.parametrize(
"func", [m.native_enum_ctor_malformed_utf8, m.native_enum_value_malformed_utf8]
)
def test_native_enum_malformed_utf8(func):
if env.GRAALPY and func is m.native_enum_ctor_malformed_utf8:
pytest.skip("GraalPy does not raise UnicodeDecodeError")
malformed_utf8 = b"\x80"
with pytest.raises(UnicodeDecodeError):
func(malformed_utf8)
def test_native_enum_double_finalize():
with pytest.raises(RuntimeError) as excinfo:
m.native_enum_double_finalize(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_native_enum_double_finalize"): DOUBLE finalize'
)
def test_native_enum_value_after_finalize():
with pytest.raises(RuntimeError) as excinfo:
m.native_enum_value_after_finalize(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_native_enum_value_after_finalize"): value after finalize'
)
def test_double_registration_native_enum():
with pytest.raises(RuntimeError) as excinfo:
m.double_registration_native_enum(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_double_registration_native_enum") is already registered!'
)
def test_native_enum_name_clash():
m.fake_native_enum_name_clash = None
with pytest.raises(RuntimeError) as excinfo:
m.native_enum_name_clash(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_native_enum_name_clash"):'
" an object with that name is already defined"
)
def test_native_enum_value_name_clash():
m.fake_native_enum_value_name_clash_x = None
with pytest.raises(RuntimeError) as excinfo:
m.native_enum_value_name_clash(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_native_enum_value_name_clash")'
'.value("fake_native_enum_value_name_clash_x"):'
" an object with that name is already defined"
)
def test_double_registration_enum_before_native_enum():
with pytest.raises(RuntimeError) as excinfo:
m.double_registration_enum_before_native_enum(m)
assert (
str(excinfo.value)
== 'pybind11::native_enum<...>("fake_enum_first") is already registered'
" as a `pybind11::enum_` or `pybind11::class_`!"
)
def test_double_registration_native_enum_before_enum():
with pytest.raises(RuntimeError) as excinfo:
m.double_registration_native_enum_before_enum(m)
assert (
str(excinfo.value)
== 'pybind11::enum_ "name_must_be_different_to_reach_desired_code_path"'
" is already registered as a pybind11::native_enum!"
)
def test_native_enum_missing_finalize_failure():
if not isinstance(m.native_enum_missing_finalize_failure, str):
m.native_enum_missing_finalize_failure()
pytest.fail("Process termination expected.")
|