1import pytest 2 3from pybind11_tests import local_bindings as m 4 5 6def test_load_external(): 7 """Load a `py::module_local` type that's only registered in an external module""" 8 import pybind11_cross_module_tests as cm 9 10 assert m.load_external1(cm.ExternalType1(11)) == 11 11 assert m.load_external2(cm.ExternalType2(22)) == 22 12 13 with pytest.raises(TypeError) as excinfo: 14 assert m.load_external2(cm.ExternalType1(21)) == 21 15 assert "incompatible function arguments" in str(excinfo.value) 16 17 with pytest.raises(TypeError) as excinfo: 18 assert m.load_external1(cm.ExternalType2(12)) == 12 19 assert "incompatible function arguments" in str(excinfo.value) 20 21 22def test_local_bindings(): 23 """Tests that duplicate `py::module_local` class bindings work across modules""" 24 25 # Make sure we can load the second module with the conflicting (but local) definition: 26 import pybind11_cross_module_tests as cm 27 28 i1 = m.LocalType(5) 29 assert i1.get() == 4 30 assert i1.get3() == 8 31 32 i2 = cm.LocalType(10) 33 assert i2.get() == 11 34 assert i2.get2() == 12 35 36 assert not hasattr(i1, 'get2') 37 assert not hasattr(i2, 'get3') 38 39 # Loading within the local module 40 assert m.local_value(i1) == 5 41 assert cm.local_value(i2) == 10 42 43 # Cross-module loading works as well (on failure, the type loader looks for 44 # external module-local converters): 45 assert m.local_value(i2) == 10 46 assert cm.local_value(i1) == 5 47 48 49def test_nonlocal_failure(): 50 """Tests that attempting to register a non-local type in multiple modules fails""" 51 import pybind11_cross_module_tests as cm 52 53 with pytest.raises(RuntimeError) as excinfo: 54 cm.register_nonlocal() 55 assert str(excinfo.value) == 'generic_type: type "NonLocalType" is already registered!' 56 57 58def test_duplicate_local(): 59 """Tests expected failure when registering a class twice with py::local in the same module""" 60 with pytest.raises(RuntimeError) as excinfo: 61 m.register_local_external() 62 import pybind11_tests 63 assert str(excinfo.value) == ( 64 'generic_type: type "LocalExternal" is already registered!' 65 if hasattr(pybind11_tests, 'class_') else 'test_class not enabled') 66 67 68def test_stl_bind_local(): 69 import pybind11_cross_module_tests as cm 70 71 v1, v2 = m.LocalVec(), cm.LocalVec() 72 v1.append(m.LocalType(1)) 73 v1.append(m.LocalType(2)) 74 v2.append(cm.LocalType(1)) 75 v2.append(cm.LocalType(2)) 76 77 # Cross module value loading: 78 v1.append(cm.LocalType(3)) 79 v2.append(m.LocalType(3)) 80 81 assert [i.get() for i in v1] == [0, 1, 2] 82 assert [i.get() for i in v2] == [2, 3, 4] 83 84 v3, v4 = m.NonLocalVec(), cm.NonLocalVec2() 85 v3.append(m.NonLocalType(1)) 86 v3.append(m.NonLocalType(2)) 87 v4.append(m.NonLocal2(3)) 88 v4.append(m.NonLocal2(4)) 89 90 assert [i.get() for i in v3] == [1, 2] 91 assert [i.get() for i in v4] == [13, 14] 92 93 d1, d2 = m.LocalMap(), cm.LocalMap() 94 d1["a"] = v1[0] 95 d1["b"] = v1[1] 96 d2["c"] = v2[0] 97 d2["d"] = v2[1] 98 assert {i: d1[i].get() for i in d1} == {'a': 0, 'b': 1} 99 assert {i: d2[i].get() for i in d2} == {'c': 2, 'd': 3} 100 101 102def test_stl_bind_global(): 103 import pybind11_cross_module_tests as cm 104 105 with pytest.raises(RuntimeError) as excinfo: 106 cm.register_nonlocal_map() 107 assert str(excinfo.value) == 'generic_type: type "NonLocalMap" is already registered!' 108 109 with pytest.raises(RuntimeError) as excinfo: 110 cm.register_nonlocal_vec() 111 assert str(excinfo.value) == 'generic_type: type "NonLocalVec" is already registered!' 112 113 with pytest.raises(RuntimeError) as excinfo: 114 cm.register_nonlocal_map2() 115 assert str(excinfo.value) == 'generic_type: type "NonLocalMap2" is already registered!' 116 117 118def test_mixed_local_global(): 119 """Local types take precedence over globally registered types: a module with a `module_local` 120 type can be registered even if the type is already registered globally. With the module, 121 casting will go to the local type; outside the module casting goes to the global type.""" 122 import pybind11_cross_module_tests as cm 123 m.register_mixed_global() 124 m.register_mixed_local() 125 126 a = [] 127 a.append(m.MixedGlobalLocal(1)) 128 a.append(m.MixedLocalGlobal(2)) 129 a.append(m.get_mixed_gl(3)) 130 a.append(m.get_mixed_lg(4)) 131 132 assert [x.get() for x in a] == [101, 1002, 103, 1004] 133 134 cm.register_mixed_global_local() 135 cm.register_mixed_local_global() 136 a.append(m.MixedGlobalLocal(5)) 137 a.append(m.MixedLocalGlobal(6)) 138 a.append(cm.MixedGlobalLocal(7)) 139 a.append(cm.MixedLocalGlobal(8)) 140 a.append(m.get_mixed_gl(9)) 141 a.append(m.get_mixed_lg(10)) 142 a.append(cm.get_mixed_gl(11)) 143 a.append(cm.get_mixed_lg(12)) 144 145 assert [x.get() for x in a] == \ 146 [101, 1002, 103, 1004, 105, 1006, 207, 2008, 109, 1010, 211, 2012] 147 148 149def test_internal_locals_differ(): 150 """Makes sure the internal local type map differs across the two modules""" 151 import pybind11_cross_module_tests as cm 152 assert m.local_cpp_types_addr() != cm.local_cpp_types_addr() 153 154 155def test_stl_caster_vs_stl_bind(msg): 156 """One module uses a generic vector caster from `<pybind11/stl.h>` while the other 157 exports `std::vector<int>` via `py:bind_vector` and `py::module_local`""" 158 import pybind11_cross_module_tests as cm 159 160 v1 = cm.VectorInt([1, 2, 3]) 161 assert m.load_vector_via_caster(v1) == 6 162 assert cm.load_vector_via_binding(v1) == 6 163 164 v2 = [1, 2, 3] 165 assert m.load_vector_via_caster(v2) == 6 166 with pytest.raises(TypeError) as excinfo: 167 cm.load_vector_via_binding(v2) == 6 168 assert msg(excinfo.value) == """ 169 load_vector_via_binding(): incompatible function arguments. The following argument types are supported: 170 1. (arg0: pybind11_cross_module_tests.VectorInt) -> int 171 172 Invoked with: [1, 2, 3] 173 """ # noqa: E501 line too long 174 175 176def test_cross_module_calls(): 177 import pybind11_cross_module_tests as cm 178 179 v1 = m.LocalVec() 180 v1.append(m.LocalType(1)) 181 v2 = cm.LocalVec() 182 v2.append(cm.LocalType(2)) 183 184 # Returning the self pointer should get picked up as returning an existing 185 # instance (even when that instance is of a foreign, non-local type). 186 assert m.return_self(v1) is v1 187 assert cm.return_self(v2) is v2 188 assert m.return_self(v2) is v2 189 assert cm.return_self(v1) is v1 190 191 assert m.LocalVec is not cm.LocalVec 192 # Returning a copy, on the other hand, always goes to the local type, 193 # regardless of where the source type came from. 194 assert type(m.return_copy(v1)) is m.LocalVec 195 assert type(m.return_copy(v2)) is m.LocalVec 196 assert type(cm.return_copy(v1)) is cm.LocalVec 197 assert type(cm.return_copy(v2)) is cm.LocalVec 198 199 # Test the example given in the documentation (which also tests inheritance casting): 200 mycat = m.Cat("Fluffy") 201 mydog = cm.Dog("Rover") 202 assert mycat.get_name() == "Fluffy" 203 assert mydog.name() == "Rover" 204 assert m.Cat.__base__.__name__ == "Pet" 205 assert cm.Dog.__base__.__name__ == "Pet" 206 assert m.Cat.__base__ is not cm.Dog.__base__ 207 assert m.pet_name(mycat) == "Fluffy" 208 assert m.pet_name(mydog) == "Rover" 209 assert cm.pet_name(mycat) == "Fluffy" 210 assert cm.pet_name(mydog) == "Rover" 211 212 assert m.MixGL is not cm.MixGL 213 a = m.MixGL(1) 214 b = cm.MixGL(2) 215 assert m.get_gl_value(a) == 11 216 assert m.get_gl_value(b) == 12 217 assert cm.get_gl_value(a) == 101 218 assert cm.get_gl_value(b) == 102 219 220 c, d = m.MixGL2(3), cm.MixGL2(4) 221 with pytest.raises(TypeError) as excinfo: 222 m.get_gl_value(c) 223 assert "incompatible function arguments" in str(excinfo.value) 224 with pytest.raises(TypeError) as excinfo: 225 m.get_gl_value(d) 226 assert "incompatible function arguments" in str(excinfo.value) 227