Merge branch 'master' into sh_merge_master

This commit is contained in:
Ralf W. Grosse-Kunstleve
2024-08-13 11:45:11 -07:00
10 changed files with 449 additions and 43 deletions

View File

@@ -10,6 +10,10 @@ name = "pybind11_tests"
version = "0.0.1"
dependencies = ["pytest", "pytest-timeout", "numpy", "scipy"]
[tool.scikit-build]
# Hide a warning while we also support CMake < 3.15
cmake.version = ">=3.15"
[tool.scikit-build.cmake.define]
PYBIND11_FINDPYTHON = true

View File

@@ -167,6 +167,14 @@ struct type_caster<ReferenceSensitiveOptional<T>>
} // namespace detail
} // namespace PYBIND11_NAMESPACE
int pass_std_vector_int(const std::vector<int> &v) {
int zum = 100;
for (const int i : v) {
zum += 2 * i;
}
return zum;
}
TEST_SUBMODULE(stl, m) {
// test_vector
m.def("cast_vector", []() { return std::vector<int>{1}; });
@@ -546,4 +554,30 @@ TEST_SUBMODULE(stl, m) {
[]() { return new std::vector<bool>(4513); },
// Without explicitly specifying `take_ownership`, this function leaks.
py::return_value_policy::take_ownership);
m.def("pass_std_vector_int", pass_std_vector_int);
m.def("pass_std_vector_pair_int", [](const std::vector<std::pair<int, int>> &v) {
int zum = 0;
for (const auto &ij : v) {
zum += ij.first * 100 + ij.second;
}
return zum;
});
m.def("pass_std_array_int_2", [](const std::array<int, 2> &a) {
return pass_std_vector_int(std::vector<int>(a.begin(), a.end())) + 1;
});
m.def("pass_std_set_int", [](const std::set<int> &s) {
int zum = 200;
for (const int i : s) {
zum += 3 * i;
}
return zum;
});
m.def("pass_std_map_int", [](const std::map<int, int> &m) {
int zum = 500;
for (const auto &p : m) {
zum += p.first * 1000 + p.second;
}
return zum;
});
}

View File

@@ -381,3 +381,129 @@ def test_return_vector_bool_raw_ptr():
v = m.return_vector_bool_raw_ptr()
assert isinstance(v, list)
assert len(v) == 4513
@pytest.mark.parametrize(
("fn", "offset"), [(m.pass_std_vector_int, 0), (m.pass_std_array_int_2, 1)]
)
def test_pass_std_vector_int(fn, offset):
assert fn([7, 13]) == 140 + offset
assert fn({6, 2}) == 116 + offset
assert fn({"x": 8, "y": 11}.values()) == 138 + offset
assert fn({3: None, 9: None}.keys()) == 124 + offset
assert fn(i for i in [4, 17]) == 142 + offset
assert fn(map(lambda i: i * 3, [8, 7])) == 190 + offset # noqa: C417
with pytest.raises(TypeError):
fn({"x": 0, "y": 1})
with pytest.raises(TypeError):
fn({})
def test_pass_std_vector_pair_int():
fn = m.pass_std_vector_pair_int
assert fn({1: 2, 3: 4}.items()) == 406
assert fn(zip([5, 17], [13, 9])) == 2222
def test_list_caster_fully_consumes_generator_object():
def gen_invalid():
yield from [1, 2.0, 3]
gen_obj = gen_invalid()
with pytest.raises(TypeError):
m.pass_std_vector_int(gen_obj)
assert not tuple(gen_obj)
def test_pass_std_set_int():
fn = m.pass_std_set_int
assert fn({3, 15}) == 254
assert fn({5: None, 12: None}.keys()) == 251
with pytest.raises(TypeError):
fn([])
with pytest.raises(TypeError):
fn({})
with pytest.raises(TypeError):
fn({}.values())
with pytest.raises(TypeError):
fn(i for i in [])
def test_set_caster_dict_keys_failure():
dict_keys = {1: None, 2.0: None, 3: None}.keys()
# The asserts does not really exercise anything in pybind11, but if one of
# them fails in some future version of Python, the set_caster load
# implementation may need to be revisited.
assert tuple(dict_keys) == (1, 2.0, 3)
assert tuple(dict_keys) == (1, 2.0, 3)
with pytest.raises(TypeError):
m.pass_std_set_int(dict_keys)
assert tuple(dict_keys) == (1, 2.0, 3)
class FakePyMappingMissingItems:
def __getitem__(self, _):
raise RuntimeError("Not expected to be called.")
class FakePyMappingWithItems(FakePyMappingMissingItems):
def items(self):
return ((1, 3), (2, 4))
class FakePyMappingBadItems(FakePyMappingMissingItems):
def items(self):
return ((1, 2), (3, "x"))
class FakePyMappingItemsNotCallable(FakePyMappingMissingItems):
@property
def items(self):
return ((1, 2), (3, 4))
class FakePyMappingItemsWithArg(FakePyMappingMissingItems):
def items(self, _):
return ((1, 2), (3, 4))
class FakePyMappingGenObj(FakePyMappingMissingItems):
def __init__(self, gen_obj):
super().__init__()
self.gen_obj = gen_obj
def items(self):
yield from self.gen_obj
def test_pass_std_map_int():
fn = m.pass_std_map_int
assert fn({1: 2, 3: 4}) == 4506
with pytest.raises(TypeError):
fn([])
assert fn(FakePyMappingWithItems()) == 3507
with pytest.raises(TypeError):
fn(FakePyMappingMissingItems())
with pytest.raises(TypeError):
fn(FakePyMappingBadItems())
with pytest.raises(TypeError):
fn(FakePyMappingItemsNotCallable())
with pytest.raises(TypeError):
fn(FakePyMappingItemsWithArg())
@pytest.mark.parametrize(
("items", "expected_exception"),
[
(((1, 2), (3, "x"), (4, 5)), TypeError),
(((1, 2), (3, 4, 5), (6, 7)), ValueError),
],
)
def test_map_caster_fully_consumes_generator_object(items, expected_exception):
def gen_invalid():
yield from items
gen_obj = gen_invalid()
with pytest.raises(expected_exception):
m.pass_std_map_int(FakePyMappingGenObj(gen_obj))
assert not tuple(gen_obj)