Skip to content

Commit fc924bd

Browse files
committed
stl_bind: apply py::local attribute by default
This applies the `py::local` attribute by default to `stl_bind` types.
1 parent ebf6b2b commit fc924bd

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

include/pybind11/stl_bind.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,10 +376,10 @@ NAMESPACE_END(detail)
376376
// std::vector
377377
//
378378
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
379-
class_<Vector, holder_type> bind_vector(module &m, std::string const &name, Args&&... args) {
379+
class_<Vector, holder_type> bind_vector(handle scope, std::string const &name, Args&&... args) {
380380
using Class_ = class_<Vector, holder_type>;
381381

382-
Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
382+
Class_ cl(scope, name.c_str(), pybind11::local(), std::forward<Args>(args)...);
383383

384384
// Declare the buffer interface if a buffer_protocol() is passed in
385385
detail::vector_buffer<Vector, Class_, Args...>(cl);
@@ -531,12 +531,12 @@ template <typename Map, typename Class_> auto map_if_insertion_operator(Class_ &
531531
NAMESPACE_END(detail)
532532

533533
template <typename Map, typename holder_type = std::unique_ptr<Map>, typename... Args>
534-
class_<Map, holder_type> bind_map(module &m, const std::string &name, Args&&... args) {
534+
class_<Map, holder_type> bind_map(handle scope, const std::string &name, Args&&... args) {
535535
using KeyType = typename Map::key_type;
536536
using MappedType = typename Map::mapped_type;
537537
using Class_ = class_<Map, holder_type>;
538538

539-
Class_ cl(m, name.c_str(), std::forward<Args>(args)...);
539+
Class_ cl(scope, name.c_str(), pybind11::local(), std::forward<Args>(args)...);
540540

541541
cl.def(init<>());
542542

tests/pybind11_local_bindings.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,20 @@ PYBIND11_MODULE(pybind11_local_bindings, m) {
2727
m.def("register_nonlocal", [m]() {
2828
bind_local<NonLocalType, 0>(m, "NonLocalType");
2929
});
30+
31+
// stl_bind.h binders defaults to py::local. Note that the value doesn't have to be local, even
32+
// if the overall container is:
33+
py::bind_vector<std::vector<LocalType>>(m, "LocalVec");
34+
py::bind_vector<std::vector<NonLocalType>>(m, "LocalVec2");
35+
py::bind_map<std::unordered_map<std::string, LocalType>>(m, "LocalMap");
36+
py::bind_map<std::unordered_map<std::string, NonLocalType>>(m, "LocalMap2");
37+
38+
// stl_bind binders can, however, be overridden to global using `py::local(false)` (in which
39+
// case this will fail):
40+
m.def("register_nonlocal_stl1", [m]() {
41+
py::bind_vector<std::vector<NonLocal2>>(m, "NonLocalVec", py::local(false));
42+
});
43+
m.def("register_nonlocal_stl2", [m]() {
44+
py::bind_map<std::unordered_map<std::string, NonLocal2>>(m, "NonLocalMap", py::local(false));
45+
});
3046
}

tests/test_local_bindings.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,15 @@ TEST_SUBMODULE(local_bindings, m) {
2727
.def(py::init<int>())
2828
.def("get", [](LocalType &i) { return i.i; })
2929
;
30+
31+
// stl_bind.h binders defaults to py::local:
32+
py::bind_vector<std::vector<LocalType>>(m, "LocalVec");
33+
py::bind_vector<std::vector<NonLocalType>>(m, "LocalVec2");
34+
py::bind_map<std::unordered_map<std::string, LocalType>>(m, "LocalMap");
35+
py::bind_map<std::unordered_map<std::string, NonLocalType>>(m, "LocalMap2");
36+
37+
// They can, however, be overridden to global using `py::local(false)`:
38+
bind_local<NonLocal2, 10>(m, "NonLocal2");
39+
py::bind_vector<std::vector<NonLocal2>>(m, "NonLocalVec", py::local(false));
40+
py::bind_map<std::unordered_map<std::string, NonLocal2>>(m, "NonLocalMap", py::local(false));
3041
}

tests/test_local_bindings.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,50 @@ def test_nonlocal_failure():
3737
with pytest.raises(RuntimeError) as excinfo:
3838
m2.register_nonlocal()
3939
assert str(excinfo.value) == 'generic_type: type "NonLocalType" is already registered!'
40+
41+
42+
def test_stl_bind_local():
43+
import pybind11_local_bindings as m2
44+
45+
v1, v2 = m1.LocalVec(), m2.LocalVec()
46+
v1.append(m1.LocalType(1))
47+
v1.append(m1.LocalType(2))
48+
v2.append(m2.LocalType(1))
49+
v2.append(m2.LocalType(2))
50+
51+
with pytest.raises(TypeError):
52+
v1.append(m2.LocalType(3))
53+
with pytest.raises(TypeError):
54+
v2.append(m1.LocalType(3))
55+
56+
assert [i.get() for i in v1] == [0, 1]
57+
assert [i.get() for i in v2] == [2, 3]
58+
59+
v3, v4 = m1.LocalVec2(), m2.LocalVec2()
60+
v3.append(m1.NonLocalType(1))
61+
v3.append(m1.NonLocalType(2))
62+
v4.append(m1.NonLocalType(1))
63+
v4.append(m1.NonLocalType(2))
64+
65+
assert [i.get() for i in v3] == [1, 2]
66+
assert [i.get() for i in v4] == [1, 2]
67+
68+
d1, d2 = m1.LocalMap(), m2.LocalMap()
69+
d1["a"] = v1[0]
70+
d1["b"] = v1[1]
71+
d2["c"] = v2[0]
72+
d2["d"] = v2[1]
73+
assert {i: d1[i].get() for i in d1} == {'a': 0, 'b': 1}
74+
assert {i: d2[i].get() for i in d2} == {'c': 2, 'd': 3}
75+
76+
77+
def test_stl_bind_global():
78+
import pybind11_local_bindings as m2
79+
80+
with pytest.raises(RuntimeError) as excinfo:
81+
m2.register_nonlocal_stl1()
82+
assert str(excinfo.value) == 'generic_type: type "NonLocalVec" is already registered!'
83+
84+
with pytest.raises(RuntimeError) as excinfo:
85+
m2.register_nonlocal_stl2()
86+
assert str(excinfo.value) == 'generic_type: type "NonLocalMap" is already registered!'

0 commit comments

Comments
 (0)