Skip to content

Commit 8d3cedb

Browse files
committed
Add test for mixing STL casters and local binders across modules
One module uses a generic vector caster from `<pybind11/stl.h>` while the other exports `std::vector<int>` with a local `py:bind_vector`.
1 parent eb0f1cc commit 8d3cedb

File tree

4 files changed

+66
-17
lines changed

4 files changed

+66
-17
lines changed

tests/local_bindings.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ using MixedLocalGlobal = LocalBase<4>;
2121
/// Mixed: global first, then local (which fails)
2222
using MixedGlobalLocal = LocalBase<5>;
2323

24+
using LocalVec = std::vector<LocalType>;
25+
using LocalVec2 = std::vector<NonLocal2>;
26+
using LocalMap = std::unordered_map<std::string, LocalType>;
27+
using NonLocalVec = std::vector<NonLocalType>;
28+
using NonLocalVec2 = std::vector<NonLocal2>;
29+
using NonLocalMap = std::unordered_map<std::string, NonLocalType>;
30+
using NonLocalMap2 = std::unordered_map<std::string, uint8_t>;
31+
2432
// Simple bindings (used with the above):
2533
template <typename T, int Adjust, typename... Args>
2634
py::class_<T> bind_local(Args && ...args) {

tests/pybind11_cross_module_tests.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "pybind11_tests.h"
1111
#include "local_bindings.h"
1212
#include <pybind11/stl_bind.h>
13+
#include <numeric>
1314

1415
PYBIND11_MODULE(pybind11_cross_module_tests, m) {
1516
m.doc() = "pybind11 cross-module test module";
@@ -44,25 +45,25 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
4445

4546
// test_stl_bind_local
4647
// stl_bind.h binders defaults to py::module_local if the types are local or converting:
47-
py::bind_vector<std::vector<LocalType>>(m, "LocalVec");
48-
py::bind_map<std::unordered_map<std::string, LocalType>>(m, "LocalMap");
48+
py::bind_vector<LocalVec>(m, "LocalVec");
49+
py::bind_map<LocalMap>(m, "LocalMap");
50+
51+
// test_stl_bind_global
4952
// and global if the type (or one of the types, for the map) is global (so these will fail,
5053
// assuming pybind11_tests is already loaded):
5154
m.def("register_nonlocal_vec", [m]() {
52-
py::bind_vector<std::vector<NonLocalType>>(m, "NonLocalVec");
55+
py::bind_vector<NonLocalVec>(m, "NonLocalVec");
5356
});
5457
m.def("register_nonlocal_map", [m]() {
55-
py::bind_map<std::unordered_map<std::string, NonLocalType>>(m, "NonLocalMap");
58+
py::bind_map<NonLocalMap>(m, "NonLocalMap");
5659
});
57-
58-
// test_stl_bind_global
5960
// The default can, however, be overridden to global using `py::module_local()` or
6061
// `py::module_local(false)`.
6162
// Explicitly made local:
62-
py::bind_vector<std::vector<NonLocal2>>(m, "NonLocalVec2", py::module_local());
63+
py::bind_vector<NonLocalVec2>(m, "NonLocalVec2", py::module_local());
6364
// Explicitly made global (and so will fail to bind):
6465
m.def("register_nonlocal_map2", [m]() {
65-
py::bind_map<std::unordered_map<std::string, uint8_t>>(m, "NonLocalMap2", py::module_local(false));
66+
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false));
6667
});
6768

6869
// test_mixed_local_global
@@ -79,4 +80,11 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
7980

8081
// test_internal_locals_differ
8182
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); });
83+
84+
// test_stl_caster_vs_stl_bind
85+
py::bind_vector<std::vector<int>>(m, "VectorInt");
86+
87+
m.def("load_vector_via_binding", [](std::vector<int> &v) {
88+
return std::accumulate(v.begin(), v.end(), 0);
89+
});
8290
}

tests/test_local_bindings.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@
1010

1111
#include "pybind11_tests.h"
1212
#include "local_bindings.h"
13+
#include <pybind11/stl.h>
1314
#include <pybind11/stl_bind.h>
15+
#include <numeric>
1416

15-
TEST_SUBMODULE(local_bindings, m) {
17+
PYBIND11_MAKE_OPAQUE(LocalVec);
18+
PYBIND11_MAKE_OPAQUE(LocalVec2);
19+
PYBIND11_MAKE_OPAQUE(LocalMap);
20+
PYBIND11_MAKE_OPAQUE(NonLocalVec);
21+
PYBIND11_MAKE_OPAQUE(NonLocalMap);
22+
PYBIND11_MAKE_OPAQUE(NonLocalMap2);
1623

24+
TEST_SUBMODULE(local_bindings, m) {
1725
// test_local_bindings
1826
// Register a class with py::module_local:
1927
bind_local<LocalType, -1>(m, "LocalType", py::module_local())
@@ -45,17 +53,17 @@ TEST_SUBMODULE(local_bindings, m) {
4553

4654
// test_stl_bind_local
4755
// stl_bind.h binders defaults to py::module_local if the types are local or converting:
48-
py::bind_vector<std::vector<LocalType>>(m, "LocalVec");
49-
py::bind_map<std::unordered_map<std::string, LocalType>>(m, "LocalMap");
56+
py::bind_vector<LocalVec>(m, "LocalVec");
57+
py::bind_map<LocalMap>(m, "LocalMap");
5058
// and global if the type (or one of the types, for the map) is global:
51-
py::bind_vector<std::vector<NonLocalType>>(m, "NonLocalVec");
52-
py::bind_map<std::unordered_map<std::string, NonLocalType>>(m, "NonLocalMap");
59+
py::bind_vector<NonLocalVec>(m, "NonLocalVec");
60+
py::bind_map<NonLocalMap>(m, "NonLocalMap");
5361

5462
// test_stl_bind_global
5563
// They can, however, be overridden to global using `py::module_local(false)`:
5664
bind_local<NonLocal2, 10>(m, "NonLocal2");
57-
py::bind_vector<std::vector<NonLocal2>>(m, "LocalVec2", py::module_local());
58-
py::bind_map<std::unordered_map<std::string, uint8_t>>(m, "NonLocalMap2", py::module_local(false));
65+
py::bind_vector<LocalVec2>(m, "LocalVec2", py::module_local());
66+
py::bind_map<NonLocalMap2>(m, "NonLocalMap2", py::module_local(false));
5967

6068
// test_mixed_local_global
6169
// We try this both with the global type registered first and vice versa (the order shouldn't
@@ -71,4 +79,9 @@ TEST_SUBMODULE(local_bindings, m) {
7179

7280
// test_internal_locals_differ
7381
m.def("local_cpp_types_addr", []() { return (uintptr_t) &py::detail::registered_local_types_cpp(); });
82+
83+
// test_stl_caster_vs_stl_bind
84+
m.def("load_vector_via_caster", [](std::vector<int> v) {
85+
return std::accumulate(v.begin(), v.end(), 0);
86+
});
7487
}

tests/test_local_bindings.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44

55

66
def test_local_bindings():
7-
"""Tests that duplicate py::local class bindings work across modules"""
7+
"""Tests that duplicate `py::module_local` class bindings work across modules"""
88

99
# Make sure we can load the second module with the conflicting (but local) definition:
1010
import pybind11_cross_module_tests as cm
1111

1212
i1 = m.LocalType(5)
13-
1413
assert i1.get() == 4
1514
assert i1.get3() == 8
1615

@@ -138,3 +137,24 @@ def test_internal_locals_differ():
138137
"""Makes sure the internal local type map differs across the two modules"""
139138
import pybind11_cross_module_tests as cm
140139
assert m.local_cpp_types_addr() != cm.local_cpp_types_addr()
140+
141+
142+
def test_stl_caster_vs_stl_bind(msg):
143+
"""One module uses a generic vector caster from `<pybind11/stl.h>` while the other
144+
exports `std::vector<int>` via `py:bind_vector` and `py::module_local`"""
145+
import pybind11_cross_module_tests as cm
146+
147+
v1 = cm.VectorInt([1, 2, 3])
148+
assert m.load_vector_via_caster(v1) == 6
149+
assert cm.load_vector_via_binding(v1) == 6
150+
151+
v2 = [1, 2, 3]
152+
assert m.load_vector_via_caster(v2) == 6
153+
with pytest.raises(TypeError) as excinfo:
154+
cm.load_vector_via_binding(v2) == 6
155+
assert msg(excinfo.value) == """
156+
load_vector_via_binding(): incompatible function arguments. The following argument types are supported:
157+
1. (arg0: pybind11_cross_module_tests.VectorInt) -> int
158+
159+
Invoked with: [1, 2, 3]
160+
""" # noqa: E501 line too long

0 commit comments

Comments
 (0)